Merge "Merge Android Pie into master"
This commit is contained in:
249
Android.bp
249
Android.bp
@@ -64,6 +64,7 @@ java_library {
|
||||
"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",
|
||||
@@ -98,6 +99,8 @@ java_library {
|
||||
"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",
|
||||
@@ -113,10 +116,12 @@ java_library {
|
||||
"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",
|
||||
@@ -125,7 +130,6 @@ java_library {
|
||||
"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/IPackageInstallObserver.aidl",
|
||||
"core/java/android/content/pm/IPackageInstallObserver2.aidl",
|
||||
"core/java/android/content/pm/IPackageInstaller.aidl",
|
||||
"core/java/android/content/pm/IPackageInstallerCallback.aidl",
|
||||
@@ -144,12 +148,13 @@ java_library {
|
||||
":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/IFingerprintClientActiveCallback.aidl",
|
||||
"core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl",
|
||||
"core/java/android/hardware/hdmi/IHdmiControlCallback.aidl",
|
||||
"core/java/android/hardware/hdmi/IHdmiControlService.aidl",
|
||||
@@ -171,7 +176,12 @@ java_library {
|
||||
"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",
|
||||
@@ -208,14 +218,12 @@ java_library {
|
||||
"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/IBatteryPropertiesListener.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/IHardwarePropertiesManager.aidl",
|
||||
"core/java/android/os/IIncidentManager.aidl",
|
||||
"core/java/android/os/IIncidentReportCompletedListener.aidl",
|
||||
"core/java/android/os/IIncidentReportStatusListener.aidl",
|
||||
"core/java/android/os/IMaintenanceActivityListener.aidl",
|
||||
"core/java/android/os/IMessenger.aidl",
|
||||
@@ -229,6 +237,9 @@ java_library {
|
||||
"core/java/android/os/IRecoverySystemProgressListener.aidl",
|
||||
"core/java/android/os/IRemoteCallback.aidl",
|
||||
"core/java/android/os/ISchedulingPolicyService.aidl",
|
||||
"core/java/android/os/IStatsCompanionService.aidl",
|
||||
"core/java/android/os/IStatsManager.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",
|
||||
@@ -238,9 +249,10 @@ java_library {
|
||||
"core/java/android/os/storage/IStorageEventListener.aidl",
|
||||
"core/java/android/os/storage/IStorageShutdownObserver.aidl",
|
||||
"core/java/android/os/storage/IObbActionListener.aidl",
|
||||
"core/java/android/security/IKeystoreService.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",
|
||||
@@ -265,6 +277,7 @@ java_library {
|
||||
"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",
|
||||
@@ -305,6 +318,10 @@ java_library {
|
||||
"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",
|
||||
@@ -322,6 +339,10 @@ java_library {
|
||||
"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",
|
||||
@@ -335,9 +356,9 @@ java_library {
|
||||
"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/IAssistScreenshotReceiver.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",
|
||||
@@ -352,8 +373,8 @@ java_library {
|
||||
"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/car/ICarServiceHelper.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",
|
||||
@@ -383,7 +404,6 @@ java_library {
|
||||
"core/java/com/android/internal/widget/ICheckCredentialProgressCallback.aidl",
|
||||
"core/java/com/android/internal/widget/ILockSettings.aidl",
|
||||
"core/java/com/android/internal/widget/IRemoteViewsFactory.aidl",
|
||||
"core/java/com/android/internal/widget/IRemoteViewsAdapterConnection.aidl",
|
||||
"keystore/java/android/security/IKeyChainAliasCallback.aidl",
|
||||
"keystore/java/android/security/IKeyChainService.aidl",
|
||||
"location/java/android/location/IBatchedLocationCallback.aidl",
|
||||
@@ -401,9 +421,10 @@ java_library {
|
||||
"location/java/android/location/IGpsGeofenceHardware.aidl",
|
||||
"location/java/android/location/INetInitiatedListener.aidl",
|
||||
"location/java/com/android/internal/location/ILocationProvider.aidl",
|
||||
"media/java/android/media/IAudioService.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",
|
||||
@@ -412,7 +433,8 @@ java_library {
|
||||
"media/java/android/media/IMediaScannerListener.aidl",
|
||||
"media/java/android/media/IMediaScannerService.aidl",
|
||||
"media/java/android/media/IPlaybackConfigDispatcher.aidl",
|
||||
"media/java/android/media/IPlayer.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",
|
||||
@@ -439,6 +461,8 @@ java_library {
|
||||
"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",
|
||||
@@ -475,7 +499,7 @@ java_library {
|
||||
"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/IImsSmsListener.aidl",
|
||||
"telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl",
|
||||
"telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl",
|
||||
"telephony/java/android/telephony/mbms/IDownloadStatusListener.aidl",
|
||||
@@ -546,13 +570,17 @@ java_library {
|
||||
"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/IWifiAwareEventCallback.aidl",
|
||||
"wifi/java/android/net/wifi/aware/IWifiAwareManager.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",
|
||||
"wifi/java/android/net/wifi/IRttManager.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",
|
||||
@@ -561,7 +589,9 @@ java_library {
|
||||
|
||||
":libupdate_engine_aidl",
|
||||
|
||||
":storaged_aidl",
|
||||
":netd_aidl",
|
||||
":vold_aidl",
|
||||
":installd_aidl",
|
||||
|
||||
"lowpan/java/android/net/lowpan/ILowpanEnergyScanCallback.aidl",
|
||||
@@ -575,9 +605,14 @@ java_library {
|
||||
"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",
|
||||
|
||||
":framework-statslog-gen",
|
||||
],
|
||||
|
||||
aidl: {
|
||||
@@ -605,14 +640,25 @@ java_library {
|
||||
"system/update_engine/binder_bindings",
|
||||
"frameworks/native/aidl/binder",
|
||||
"frameworks/av/camera/aidl",
|
||||
"frameworks/av/media/libaudioclient/aidl",
|
||||
"frameworks/native/aidl/gui",
|
||||
"system/core/storaged/binder",
|
||||
"system/netd/server/binder",
|
||||
"system/vold/binder",
|
||||
"system/bt/binder",
|
||||
"system/security/keystore/binder",
|
||||
],
|
||||
|
||||
generate_get_transaction_name: true
|
||||
},
|
||||
|
||||
exclude_srcs: [
|
||||
// See comment on framework-atb-backward-compatibility module below
|
||||
"core/java/android/content/pm/AndroidTestBaseUpdater.java",
|
||||
// See comment on framework-oahl-backward-compatibility module below
|
||||
"core/java/android/content/pm/OrgApacheHttpLegacyUpdater.java",
|
||||
],
|
||||
|
||||
no_framework_libs: true,
|
||||
libs: [
|
||||
"conscrypt",
|
||||
@@ -625,19 +671,25 @@ java_library {
|
||||
"framework-protos",
|
||||
"android.hidl.base-V1.0-java",
|
||||
"android.hardware.cas-V1.0-java",
|
||||
"android.hardware.contexthub-V1.0-java",
|
||||
"android.hardware.health-V1.0-java-constants",
|
||||
"android.hardware.thermal-V1.0-java-constants",
|
||||
"android.hardware.tv.input-V1.0-java-constants",
|
||||
"android.hardware.usb-V1.0-java-constants",
|
||||
"android.hardware.usb-V1.1-java-constants",
|
||||
"android.hardware.vibrator-V1.0-java-constants",
|
||||
"android.hardware.vibrator-V1.1-java-constants",
|
||||
"android.hardware.vibrator-V1.0-java",
|
||||
"android.hardware.vibrator-V1.1-java",
|
||||
"android.hardware.vibrator-V1.2-java",
|
||||
"android.hardware.wifi-V1.0-java-constants",
|
||||
"android.hardware.radio-V1.0-java",
|
||||
"android.hardware.usb.gadget-V1.0-java",
|
||||
],
|
||||
|
||||
// Loaded with System.loadLibrary by android.view.textclassifier
|
||||
required: ["libtextclassifier"],
|
||||
required: [
|
||||
"libtextclassifier",
|
||||
"libmedia2_jni",
|
||||
],
|
||||
|
||||
javac_shard_size: 150,
|
||||
|
||||
@@ -647,6 +699,38 @@ java_library {
|
||||
],
|
||||
}
|
||||
|
||||
// A temporary build target that is conditionally included on the bootclasspath if
|
||||
// org.apache.http.legacy library has been removed and which provides support for
|
||||
// maintaining backwards compatibility for APKs that target pre-P and depend on
|
||||
// org.apache.http.legacy classes. This is used iff REMOVE_OAHL_FROM_BCP=true is
|
||||
// specified on the build command line.
|
||||
java_library {
|
||||
name: "framework-oahl-backward-compatibility",
|
||||
installable: true,
|
||||
srcs: [
|
||||
"core/java/android/content/pm/OrgApacheHttpLegacyUpdater.java",
|
||||
],
|
||||
}
|
||||
|
||||
// A temporary build target that is conditionally included on the bootclasspath if
|
||||
// android.test.base library has been removed and which provides support for
|
||||
// maintaining backwards compatibility for APKs that target pre-P and depend on
|
||||
// android.test.base classes. This is used iff REMOVE_ATB_FROM_BCP=true is
|
||||
// specified on the build command line.
|
||||
java_library {
|
||||
name: "framework-atb-backward-compatibility",
|
||||
srcs: [
|
||||
"core/java/android/content/pm/AndroidTestBaseUpdater.java",
|
||||
],
|
||||
}
|
||||
|
||||
genrule {
|
||||
name: "framework-statslog-gen",
|
||||
tools: ["stats-log-api-gen"],
|
||||
cmd: "$(location stats-log-api-gen) --java $(out)",
|
||||
out: ["android/util/StatsLogInternal.java"],
|
||||
}
|
||||
|
||||
gensrcs {
|
||||
name: "framework-javastream-protos",
|
||||
depfile: true,
|
||||
@@ -667,7 +751,10 @@ gensrcs {
|
||||
" $(in) " +
|
||||
"&& $(location soong_zip) -jar -o $(out) -C $(genDir)/$(in) -D $(genDir)/$(in)",
|
||||
|
||||
srcs: ["core/proto/**/*.proto"],
|
||||
srcs: [
|
||||
"core/proto/**/*.proto",
|
||||
"libs/incident/**/*.proto",
|
||||
],
|
||||
output_extension: "srcjar",
|
||||
}
|
||||
|
||||
@@ -681,10 +768,29 @@ java_library {
|
||||
"libphonenumber-platform",
|
||||
"nist-sip",
|
||||
"tagsoup",
|
||||
"rappor",
|
||||
],
|
||||
dxflags: ["--core-library"],
|
||||
}
|
||||
|
||||
// ==== java proto host library ==============================
|
||||
java_library_host {
|
||||
name: "platformprotos",
|
||||
srcs: [
|
||||
"cmds/am/proto/instrumentation_data.proto",
|
||||
"core/proto/**/*.proto",
|
||||
"libs/incident/proto/**/*.proto",
|
||||
"cmds/statsd/src/**/*.proto",
|
||||
],
|
||||
proto: {
|
||||
include_dirs: ["external/protobuf/src"],
|
||||
type: "full",
|
||||
},
|
||||
errorprone: {
|
||||
javacflags: ["-Xep:MissingOverride:OFF"], // b/72714520
|
||||
},
|
||||
}
|
||||
|
||||
// ==== c++ proto device library ==============================
|
||||
cc_library {
|
||||
name: "libplatformprotos",
|
||||
@@ -699,26 +805,22 @@ cc_library {
|
||||
"-Werror",
|
||||
"-Wno-unused-parameter",
|
||||
],
|
||||
|
||||
srcs: [
|
||||
"core/proto/**/*.proto",
|
||||
"libs/incident/**/*.proto",
|
||||
],
|
||||
|
||||
target: {
|
||||
host: {
|
||||
proto: {
|
||||
type: "full",
|
||||
},
|
||||
srcs: [
|
||||
"core/proto/**/*.proto",
|
||||
"libs/incident/**/*.proto",
|
||||
],
|
||||
},
|
||||
android: {
|
||||
proto: {
|
||||
type: "lite",
|
||||
},
|
||||
// We only build the protos that are optimized for the lite
|
||||
// runtime, as well as the only protos that are actually
|
||||
// needed by the device.
|
||||
srcs: [
|
||||
"core/proto/android/service/graphicsstats.proto",
|
||||
],
|
||||
shared: {
|
||||
enabled: false,
|
||||
},
|
||||
@@ -726,6 +828,41 @@ cc_library {
|
||||
},
|
||||
}
|
||||
|
||||
gensrcs {
|
||||
name: "gen-platform-proto-constants",
|
||||
depfile: true,
|
||||
|
||||
tools: [
|
||||
"aprotoc",
|
||||
"protoc-gen-cppstream",
|
||||
],
|
||||
|
||||
srcs: [
|
||||
"core/proto/android/os/backtrace.proto",
|
||||
"core/proto/android/os/batterytype.proto",
|
||||
"core/proto/android/os/cpufreq.proto",
|
||||
"core/proto/android/os/cpuinfo.proto",
|
||||
"core/proto/android/os/kernelwake.proto",
|
||||
"core/proto/android/os/pagetypeinfo.proto",
|
||||
"core/proto/android/os/procrank.proto",
|
||||
"core/proto/android/os/ps.proto",
|
||||
"core/proto/android/os/system_properties.proto",
|
||||
"core/proto/android/util/event_log_tags.proto",
|
||||
],
|
||||
|
||||
// Append protoc-gen-cppstream tool's PATH otherwise aprotoc can't find the plugin tool
|
||||
cmd: "mkdir -p $(genDir) " +
|
||||
"&& $(location aprotoc) " +
|
||||
" --plugin=$(location protoc-gen-cppstream) " +
|
||||
" --dependency_out=$(depfile) " +
|
||||
" --cppstream_out=$(genDir) " +
|
||||
" -Iexternal/protobuf/src " +
|
||||
" -I . " +
|
||||
" $(in)",
|
||||
|
||||
output_extension: "proto.h",
|
||||
}
|
||||
|
||||
subdirs = [
|
||||
"cmds/*",
|
||||
"core/*",
|
||||
@@ -869,11 +1006,6 @@ framework_docs_args = "-android -manifest $(location core/res/AndroidManifest.xm
|
||||
doc_defaults {
|
||||
name: "api-stubs-default",
|
||||
srcs: [
|
||||
// test mock src files.
|
||||
"test-mock/src/android/test/mock/**/*.java",
|
||||
// test runner excluding mock src files.
|
||||
"test-runner/src/**/*.java",
|
||||
"test-base/src/**/*.java",
|
||||
":opt-telephony-srcs",
|
||||
":opt-net-voip-srcs",
|
||||
":openjdk_javadoc_files",
|
||||
@@ -920,15 +1052,6 @@ doc_defaults {
|
||||
installable: false,
|
||||
}
|
||||
|
||||
check_last_released_api_args = "-hide 2 -hide 3 -hide 4 -hide 5 -hide 6 -hide 24 -hide 25 -hide 26 -hide 27 " +
|
||||
"-error 7 -error 8 -error 9 -error 10 -error 11 -error 12 -error 13 -error 14 -error 15 " +
|
||||
"-error 16 -error 17 -error 18"
|
||||
|
||||
check_current_api_args = "-error 2 -error 3 -error 4 -error 5 -error 6 " +
|
||||
"-error 7 -error 8 -error 9 -error 10 -error 11 -error 12 -error 13 -error 14 -error 15 "+
|
||||
"-error 16 -error 17 -error 18 -error 19 -error 20 -error 21 -error 23 -error 24 " +
|
||||
"-error 25 -error 26 -error 27"
|
||||
|
||||
droiddoc {
|
||||
name: "api-stubs-docs",
|
||||
defaults: ["api-stubs-default"],
|
||||
@@ -947,12 +1070,17 @@ droiddoc {
|
||||
last_released: {
|
||||
api_file: ":last-released-public-api",
|
||||
removed_api_file: "api/removed.txt",
|
||||
args: check_last_released_api_args,
|
||||
args: "-hide 2 -hide 3 -hide 4 -hide 5 -hide 6 -hide 24 -hide 25 -hide 26 -hide 27 " +
|
||||
"-error 7 -error 8 -error 9 -error 10 -error 11 -error 12 -error 13 -error 14 -error 15 " +
|
||||
"-error 16 -error 17 -error 18 -error 31",
|
||||
},
|
||||
current: {
|
||||
api_file: "api/current.txt",
|
||||
removed_api_file: "api/removed.txt",
|
||||
args: check_current_api_args,
|
||||
args: "-error 2 -error 3 -error 4 -error 5 -error 6 " +
|
||||
"-error 7 -error 8 -error 9 -error 10 -error 11 -error 12 -error 13 -error 14 -error 15 " +
|
||||
"-error 16 -error 17 -error 18 -error 19 -error 20 -error 21 -error 23 -error 24 " +
|
||||
"-error 25 -error 26 -error 27",
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -969,8 +1097,6 @@ droiddoc {
|
||||
],
|
||||
api_tag_name: "SYSTEM",
|
||||
api_filename: "system-api.txt",
|
||||
private_api_filename: "system-private.txt",
|
||||
private_dex_api_filename: "system-private-dex.txt",
|
||||
removed_api_filename: "system-removed.txt",
|
||||
exact_api_filename: "system-exact.txt",
|
||||
args: framework_docs_args + " -referenceonly -showAnnotation android.annotation.SystemApi -nodocs",
|
||||
@@ -978,12 +1104,17 @@ droiddoc {
|
||||
last_released: {
|
||||
api_file: ":last-released-system-api",
|
||||
removed_api_file: "api/system-removed.txt",
|
||||
args: check_last_released_api_args,
|
||||
args: "-hide 2 -hide 3 -hide 4 -hide 5 -hide 6 -hide 24 -hide 25 -hide 26 -hide 27 " +
|
||||
"-error 7 -error 8 -error 9 -error 10 -error 11 -error 12 -error 13 -error 14 -error 15 " +
|
||||
"-error 16 -error 17 -error 18 -error 31",
|
||||
},
|
||||
current: {
|
||||
api_file: "api/system-current.txt",
|
||||
removed_api_file: "api/system-removed.txt",
|
||||
args: check_current_api_args,
|
||||
args: "-error 2 -error 3 -error 4 -error 5 -error 6 " +
|
||||
"-error 7 -error 8 -error 9 -error 10 -error 11 -error 12 -error 13 -error 14 -error 15 " +
|
||||
"-error 16 -error 17 -error 18 -error 19 -error 20 -error 21 -error 23 -error 24 " +
|
||||
"-error 25 -error 26 -error 27",
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1007,7 +1138,10 @@ droiddoc {
|
||||
current: {
|
||||
api_file: "api/test-current.txt",
|
||||
removed_api_file: "api/test-removed.txt",
|
||||
args: check_current_api_args,
|
||||
args: "-error 2 -error 3 -error 4 -error 5 -error 6 " +
|
||||
"-error 7 -error 8 -error 9 -error 10 -error 11 -error 12 -error 13 -error 14 -error 15 " +
|
||||
"-error 16 -error 17 -error 18 -error 19 -error 20 -error 21 -error 23 -error 24 " +
|
||||
"-error 25 -error 26 -error 27",
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1022,7 +1156,7 @@ doc_defaults {
|
||||
":non_openjdk_javadoc_files",
|
||||
":android_icu4j_src_files_for_docs",
|
||||
":gen-ojluni-jaif-annotated-srcs",
|
||||
":frameworks-data-binding-srcs",
|
||||
"test-mock/src/**/*.java",
|
||||
"test-runner/src/**/*.java",
|
||||
],
|
||||
exclude_srcs: [
|
||||
@@ -1132,6 +1266,21 @@ droiddoc {
|
||||
static_doc_properties: "docs/source.properties",
|
||||
}
|
||||
|
||||
droiddoc {
|
||||
name: "offline-system-sdk-referenceonly-docs",
|
||||
defaults: ["framework-docs-default"],
|
||||
hdf: [
|
||||
"android.whichdoc offline",
|
||||
],
|
||||
proofread_file: "offline-system-sdk-referenceonly-docs-proofrerad.txt",
|
||||
args: framework_docs_args + " -hide 101 -hide 104 -hide 108" +
|
||||
" -showAnnotation android.annotation.SystemApi " +
|
||||
" -offlinemode -title \"Android System SDK\" -referenceonly",
|
||||
write_sdk_values: true,
|
||||
static_doc_index_redirect: "docs/docs-documentation-redirect.html",
|
||||
static_doc_properties: "docs/source.properties",
|
||||
}
|
||||
|
||||
droiddoc {
|
||||
name: "online-sdk-docs",
|
||||
defaults: ["framework-docs-default"],
|
||||
|
||||
124
Android.mk
124
Android.mk
@@ -72,17 +72,10 @@ non_base_dirs := \
|
||||
../opt/net/voip/src/java/android/net/rtp \
|
||||
../opt/net/voip/src/java/android/net/sip \
|
||||
|
||||
framework_base_android_test_mock_src_files := \
|
||||
$(call all-java-files-under, test-mock/src/android/test/mock)
|
||||
|
||||
framework_base_android_test_runner_excluding_mock_src_files := \
|
||||
$(filter-out $(framework_base_android_test_mock_src_files), $(call all-java-files-under, test-runner/src))
|
||||
|
||||
# Find all files in specific directories (relative to frameworks/base)
|
||||
# to document and check apis
|
||||
files_to_check_apis := \
|
||||
$(call find-other-java-files, \
|
||||
test-base/src \
|
||||
$(non_base_dirs) \
|
||||
)
|
||||
|
||||
@@ -106,7 +99,8 @@ files_to_check_apis_generated := \
|
||||
files_to_document := \
|
||||
$(files_to_check_apis) \
|
||||
$(call find-other-java-files,\
|
||||
$(addprefix ../../, $(FRAMEWORKS_DATA_BINDING_JAVA_SRC_DIRS)) \
|
||||
test-base/src \
|
||||
test-mock/src \
|
||||
test-runner/src)
|
||||
|
||||
# These are relative to frameworks/base
|
||||
@@ -126,8 +120,6 @@ framework_docs_LOCAL_SRC_FILES := \
|
||||
|
||||
# These are relative to frameworks/base
|
||||
framework_docs_LOCAL_API_CHECK_SRC_FILES := \
|
||||
$(framework_base_android_test_mock_src_files) \
|
||||
$(framework_base_android_test_runner_excluding_mock_src_files) \
|
||||
$(files_to_check_apis) \
|
||||
$(common_src_files) \
|
||||
|
||||
@@ -153,7 +145,6 @@ framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES := \
|
||||
ext \
|
||||
framework \
|
||||
voip-common \
|
||||
android.test.mock \
|
||||
|
||||
# Platform docs can refer to Support Library APIs, but we don't actually build
|
||||
# them as part of the docs target, so we need to include them on the classpath.
|
||||
@@ -251,6 +242,72 @@ framework_docs_LOCAL_DROIDDOC_OPTIONS += \
|
||||
-federate SupportLib https://developer.android.com \
|
||||
-federationapi SupportLib prebuilts/sdk/current/support-api.txt
|
||||
|
||||
# Federate AndroidX references against local API file.
|
||||
framework_docs_LOCAL_DROIDDOC_OPTIONS += \
|
||||
-federate AndroidX https://developer.android.com \
|
||||
-federationapi AndroidX prebuilts/sdk/current/androidx-api.txt
|
||||
|
||||
# Get the highest numbered api txt for the given api level.
|
||||
# $(1): the api level (e.g. public, system)
|
||||
define highest_sdk_txt
|
||||
$(HISTORICAL_SDK_VERSIONS_ROOT)/$(lastword $(call numerically_sort, \
|
||||
$(patsubst \
|
||||
$(HISTORICAL_SDK_VERSIONS_ROOT)/%,\
|
||||
%,\
|
||||
$(wildcard $(HISTORICAL_SDK_VERSIONS_ROOT)/*/$(1)/api/android.txt)\
|
||||
) \
|
||||
))
|
||||
endef
|
||||
|
||||
# ==== Public API diff ===========================
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := $(framework_docs_LOCAL_API_CHECK_SRC_FILES)
|
||||
LOCAL_GENERATED_SOURCES := $(framework_docs_LOCAL_GENERATED_SOURCES)
|
||||
LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
|
||||
LOCAL_JAVA_LIBRARIES := $(framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES)
|
||||
LOCAL_MODULE_CLASS := $(framework_docs_LOCAL_MODULE_CLASS)
|
||||
LOCAL_ADDITIONAL_JAVA_DIR := $(framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR)
|
||||
LOCAL_ADDITIONAL_DEPENDENCIES := \
|
||||
$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES) \
|
||||
$(INTERNAL_PLATFORM_API_FILE)
|
||||
|
||||
LOCAL_MODULE := offline-sdk-referenceonly
|
||||
|
||||
# Basename, because apidiff adds .txt internally.
|
||||
LOCAL_APIDIFF_OLDAPI := $(basename $(call highest_sdk_txt,public))
|
||||
LOCAL_APIDIFF_NEWAPI := $(LOCAL_PATH)/../../$(basename $(INTERNAL_PLATFORM_API_FILE))
|
||||
|
||||
include $(BUILD_APIDIFF)
|
||||
|
||||
# Hack to get diffs included in docs output
|
||||
out_zip := $(OUT_DOCS)/$(LOCAL_MODULE)-docs.zip
|
||||
$(out_zip): $(full_target)
|
||||
|
||||
# ==== System API diff ===========================
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES := $(framework_docs_LOCAL_API_CHECK_SRC_FILES)
|
||||
LOCAL_GENERATED_SOURCES := $(framework_docs_LOCAL_GENERATED_SOURCES)
|
||||
LOCAL_SRCJARS:=$(framework_docs_LOCAL_SRCJARS)
|
||||
LOCAL_JAVA_LIBRARIES := $(framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES)
|
||||
LOCAL_MODULE_CLASS := $(framework_docs_LOCAL_MODULE_CLASS)
|
||||
LOCAL_ADDITIONAL_JAVA_DIR := $(framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR)
|
||||
LOCAL_ADDITIONAL_DEPENDENCIES := \
|
||||
$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES) \
|
||||
$(INTERNAL_PLATFORM_SYSTEM_API_FILE)
|
||||
|
||||
LOCAL_MODULE := offline-system-sdk-referenceonly
|
||||
|
||||
# Basename, because apidiff adds .txt internally.
|
||||
LOCAL_APIDIFF_OLDAPI := $(basename $(call highest_sdk_txt,system))
|
||||
LOCAL_APIDIFF_NEWAPI := $(LOCAL_PATH)/../../$(basename $(INTERNAL_PLATFORM_SYSTEM_API_FILE))
|
||||
|
||||
include $(BUILD_APIDIFF)
|
||||
|
||||
# Hack to get diffs included in docs output
|
||||
out_zip := $(OUT_DOCS)/$(LOCAL_MODULE)-docs.zip
|
||||
$(out_zip): $(full_target)
|
||||
|
||||
$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_API_FILE))
|
||||
$(call dist-for-goals,sdk,$(INTERNAL_PLATFORM_SYSTEM_API_FILE))
|
||||
@@ -264,21 +321,36 @@ $(OUT_DOCS)/offline-sdk-timestamp: $(OUT_DOCS)/offline-sdk-docs-docs.zip
|
||||
$(hide) mkdir -p $(OUT_DOCS)/offline-sdk
|
||||
( unzip -qo $< -d $(OUT_DOCS)/offline-sdk && touch -f $@ ) || exit 1
|
||||
|
||||
# ==== java proto host library ==============================
|
||||
# ==== java proto device library (for test only) ==============================
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := platformprotos
|
||||
LOCAL_PROTOC_OPTIMIZE_TYPE := full
|
||||
LOCAL_MODULE := platformprotosnano
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
LOCAL_PROTOC_OPTIMIZE_TYPE := nano
|
||||
LOCAL_PROTOC_FLAGS := \
|
||||
-Iexternal/protobuf/src
|
||||
LOCAL_SOURCE_FILES_ALL_GENERATED := true
|
||||
LOCAL_PROTO_JAVA_OUTPUT_PARAMS := \
|
||||
store_unknown_fields = true
|
||||
LOCAL_SDK_VERSION := current
|
||||
LOCAL_SRC_FILES := \
|
||||
$(call all-proto-files-under, core/proto) \
|
||||
$(call all-proto-files-under, libs/incident/proto)
|
||||
# b/72714520
|
||||
LOCAL_ERROR_PRONE_FLAGS := -Xep:MissingOverride:OFF
|
||||
include $(BUILD_HOST_JAVA_LIBRARY)
|
||||
$(call all-proto-files-under, libs/incident/proto/android/os)
|
||||
include $(BUILD_STATIC_JAVA_LIBRARY)
|
||||
|
||||
|
||||
# ==== java proto device library (for test only) ==============================
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := platformprotoslite
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
LOCAL_PROTOC_OPTIMIZE_TYPE := lite
|
||||
LOCAL_PROTOC_FLAGS := \
|
||||
-Iexternal/protobuf/src
|
||||
LOCAL_SRC_FILES := \
|
||||
$(call all-proto-files-under, core/proto) \
|
||||
$(call all-proto-files-under, libs/incident/proto/android/os)
|
||||
# Protos have lots of MissingOverride and similar.
|
||||
LOCAL_ERROR_PRONE_FLAGS := -XepDisableAllChecks
|
||||
include $(BUILD_STATIC_JAVA_LIBRARY)
|
||||
|
||||
# ==== hiddenapi lists =======================================
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
@@ -394,20 +466,6 @@ $(LOCAL_BLACKLIST): $(LOCAL_SRC_ALL) $(LOCAL_LIGHT_GREYLIST) $(LOCAL_DARK_GREYLI
|
||||
$(call assert-has-no-overlap,$@,$(LOCAL_DARK_GREYLIST))
|
||||
$(call assert-is-subset,$(LOCAL_SRC_FORCE_BLACKLIST),$@)
|
||||
|
||||
# Build AOSP blacklist
|
||||
# ============================================================
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_LIGHT_GREYLIST_FILE := frameworks/base/config/hiddenapi-p-light-greylist.txt
|
||||
LOCAL_BLACKLIST_FILE := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/hiddenapi-aosp-blacklist.txt
|
||||
|
||||
.PHONY: hiddenapi-aosp-blacklist
|
||||
hiddenapi-aosp-blacklist: $(LOCAL_BLACKLIST_FILE)
|
||||
|
||||
$(LOCAL_BLACKLIST_FILE): $(LOCAL_LIGHT_GREYLIST_FILE) $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE)
|
||||
LC_COLLATE=C comm -13 <(sort $(LOCAL_LIGHT_GREYLIST_FILE)) \
|
||||
<(sort $(INTERNAL_PLATFORM_PRIVATE_DEX_API_FILE)) > $@
|
||||
|
||||
# Include subdirectory makefiles
|
||||
# ============================================================
|
||||
|
||||
|
||||
@@ -243,6 +243,7 @@ $(call add-clean-step, rm -f $(OUT_DIR)/target/common/obj/APPS/FeatureSplit2_int
|
||||
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/hardware)
|
||||
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/core/java/android/os/storage/*)
|
||||
$(call add-clean-step, rm -rf $(OUT_DIR)/host/common/obj/JAVA_LIBRARIES/platformprotos_intermediates)
|
||||
$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/statsdprotolite_intermediates)
|
||||
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/com.android.mediadrm.signer.jar)
|
||||
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/com.android.location.provider.jar)
|
||||
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/com.android.future.usb.accessory.jar)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
[Hook Scripts]
|
||||
checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
|
||||
-fw core/java/android/
|
||||
graphics/java/android
|
||||
core/tests/coretests/src/android/
|
||||
packages/PrintRecommendationService/
|
||||
packages/PrintSpooler/
|
||||
@@ -8,4 +9,8 @@ checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPL
|
||||
services/usb/
|
||||
telephony/
|
||||
|
||||
api_lint_hook = ${REPO_ROOT}/frameworks/base/tools/apilint/apilint_sha.sh ${PREUPLOAD_COMMIT}
|
||||
|
||||
strings_lint_hook = ${REPO_ROOT}/frameworks/base/tools/stringslint/stringslint_sha.sh ${PREUPLOAD_COMMIT}
|
||||
|
||||
hidden_api_txt_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/checksorted_sha.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
|
||||
|
||||
@@ -4,12 +4,16 @@ include $(CLEAR_VARS)
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
|
||||
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
|
||||
LOCAL_SRC_FILES := $(call all-java-files-under, src)
|
||||
LOCAL_SRC_FILES := \
|
||||
$(call all-java-files-under, src) \
|
||||
src/android/os/ISomeService.aidl
|
||||
|
||||
LOCAL_STATIC_JAVA_LIBRARIES := \
|
||||
android-support-test \
|
||||
apct-perftests-utils \
|
||||
legacy-android-test
|
||||
guava
|
||||
|
||||
LOCAL_JAVA_LIBRARIES := android.test.base
|
||||
|
||||
LOCAL_PACKAGE_NAME := CorePerfTests
|
||||
LOCAL_PRIVATE_PLATFORM_APIS := true
|
||||
@@ -19,4 +23,6 @@ LOCAL_JNI_SHARED_LIBRARIES := libperftestscore_jni
|
||||
# Use google-fonts/dancing-script for the performance metrics
|
||||
LOCAL_ASSET_DIR := $(TOP)/external/google-fonts/dancing-script
|
||||
|
||||
LOCAL_COMPATIBILITY_SUITE += device-tests
|
||||
|
||||
include $(BUILD_PACKAGE)
|
||||
|
||||
@@ -2,9 +2,20 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.perftests.core">
|
||||
|
||||
<permission android:name="com.android.perftests.core.TestPermission" />
|
||||
<uses-permission android:name="com.android.perftests.core.TestPermission" />
|
||||
|
||||
<uses-permission
|
||||
android:name="android.permission.GET_ACCOUNTS" />
|
||||
|
||||
<application>
|
||||
<uses-library android:name="android.test.runner" />
|
||||
<activity android:name="android.perftests.utils.StubActivity" />
|
||||
<activity android:name="android.perftests.utils.StubActivity">
|
||||
<intent-filter>
|
||||
<action android:name="com.android.perftests.core.PERFTEST" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<service android:name="android.os.SomeService" android:exported="false" android:process=":some_service" />
|
||||
</application>
|
||||
|
||||
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
|
||||
|
||||
28
apct-tests/perftests/core/AndroidTest.xml
Normal file
28
apct-tests/perftests/core/AndroidTest.xml
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
<configuration description="Runs CorePerfTests metric instrumentation.">
|
||||
<option name="test-suite-tag" value="apct" />
|
||||
<option name="test-suite-tag" value="apct-metric-instrumentation" />
|
||||
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
|
||||
<option name="cleanup-apks" value="true" />
|
||||
<option name="test-file-name" value="CorePerfTests.apk" />
|
||||
</target_preparer>
|
||||
|
||||
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
|
||||
<option name="package" value="com.android.perftests.core" />
|
||||
<option name="hidden-api-checks" value="false"/>
|
||||
</test>
|
||||
</configuration>
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.accounts;
|
||||
|
||||
import static junit.framework.Assert.fail;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.filters.LargeTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.util.Log;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@LargeTest
|
||||
public class AccountManagerPerfTest {
|
||||
|
||||
@Rule
|
||||
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
|
||||
|
||||
@Test
|
||||
public void testGetAccounts() {
|
||||
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final Context context = InstrumentationRegistry.getTargetContext();
|
||||
if (context.checkSelfPermission(Manifest.permission.GET_ACCOUNTS)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
fail("Missing required GET_ACCOUNTS permission");
|
||||
}
|
||||
AccountManager accountManager = AccountManager.get(context);
|
||||
while (state.keepRunning()) {
|
||||
accountManager.getAccounts();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* 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.app;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
import android.perftests.utils.StubActivity;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.filters.LargeTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
// Due to b/71353150, you might get "java.lang.AssertionError: Binder ProxyMap has too many
|
||||
// entries", but it's flaky. Adding "Runtime.getRuntime().gc()" between each iteration solves
|
||||
// the problem, but it doesn't seem like it's currently needed.
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@LargeTest
|
||||
public class PendingIntentPerfTest {
|
||||
|
||||
private Context mContext;
|
||||
|
||||
@Rule
|
||||
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
|
||||
|
||||
private Intent mIntent;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = InstrumentationRegistry.getTargetContext();
|
||||
mIntent = StubActivity.createLaunchIntent(mContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Benchmark time to create a PendingIntent.
|
||||
*/
|
||||
@Test
|
||||
public void create() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
state.resumeTiming();
|
||||
|
||||
final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, mIntent,
|
||||
0);
|
||||
|
||||
state.pauseTiming();
|
||||
pendingIntent.cancel();
|
||||
state.resumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Benchmark time to create a PendingIntent with FLAG_CANCEL_CURRENT, already having an active
|
||||
* PendingIntent.
|
||||
*/
|
||||
@Test
|
||||
public void createWithCancelFlag() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final PendingIntent previousPendingIntent = PendingIntent.getActivity(mContext, 0,
|
||||
mIntent, 0);
|
||||
state.resumeTiming();
|
||||
|
||||
final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, mIntent,
|
||||
PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
|
||||
state.pauseTiming();
|
||||
pendingIntent.cancel();
|
||||
state.resumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Benchmark time to create a PendingIntent with FLAG_UPDATE_CURRENT, already having an active
|
||||
* PendingIntent.
|
||||
*/
|
||||
@Test
|
||||
public void createWithUpdateFlag() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final PendingIntent previousPendingIntent = PendingIntent.getActivity(mContext, 0,
|
||||
mIntent, 0);
|
||||
state.resumeTiming();
|
||||
|
||||
final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, mIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
state.pauseTiming();
|
||||
previousPendingIntent.cancel();
|
||||
pendingIntent.cancel();
|
||||
state.resumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Benchmark time to cancel a PendingIntent.
|
||||
*/
|
||||
@Test
|
||||
public void cancel() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0,
|
||||
mIntent, 0);
|
||||
state.resumeTiming();
|
||||
|
||||
pendingIntent.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License
|
||||
*/
|
||||
|
||||
package android.database;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.os.Bundle;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.filters.LargeTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Performance tests for measuring amount of data written during typical DB operations
|
||||
*
|
||||
* <p>To run: bit CorePerfTests:android.database.SQLiteDatabaseIoPerfTest
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@LargeTest
|
||||
public class SQLiteDatabaseIoPerfTest {
|
||||
private static final String TAG = "SQLiteDatabaseIoPerfTest";
|
||||
private static final String DB_NAME = "db_io_perftest";
|
||||
private static final int DEFAULT_DATASET_SIZE = 500;
|
||||
|
||||
private Long mWriteBytes;
|
||||
|
||||
private SQLiteDatabase mDatabase;
|
||||
private Context mContext;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = InstrumentationRegistry.getTargetContext();
|
||||
mContext.deleteDatabase(DB_NAME);
|
||||
mDatabase = mContext.openOrCreateDatabase(DB_NAME, Context.MODE_PRIVATE, null);
|
||||
mDatabase.execSQL("CREATE TABLE T1 "
|
||||
+ "(_ID INTEGER PRIMARY KEY, COL_A INTEGER, COL_B VARCHAR(100), COL_C REAL)");
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
mDatabase.close();
|
||||
mContext.deleteDatabase(DB_NAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDatabaseModifications() {
|
||||
startMeasuringWrites();
|
||||
ContentValues cv = new ContentValues();
|
||||
String[] whereArg = new String[1];
|
||||
for (int i = 0; i < DEFAULT_DATASET_SIZE; i++) {
|
||||
cv.put("_ID", i);
|
||||
cv.put("COL_A", i);
|
||||
cv.put("COL_B", "NewValue");
|
||||
cv.put("COL_C", 1.0);
|
||||
assertEquals(i, mDatabase.insert("T1", null, cv));
|
||||
}
|
||||
cv = new ContentValues();
|
||||
for (int i = 0; i < DEFAULT_DATASET_SIZE; i++) {
|
||||
cv.put("COL_B", "UpdatedValue");
|
||||
cv.put("COL_C", 1.1);
|
||||
whereArg[0] = String.valueOf(i);
|
||||
assertEquals(1, mDatabase.update("T1", cv, "_ID=?", whereArg));
|
||||
}
|
||||
for (int i = 0; i < DEFAULT_DATASET_SIZE; i++) {
|
||||
whereArg[0] = String.valueOf(i);
|
||||
assertEquals(1, mDatabase.delete("T1", "_ID=?", whereArg));
|
||||
}
|
||||
// Make sure all changes are written to disk
|
||||
mDatabase.close();
|
||||
long bytes = endMeasuringWrites();
|
||||
sendResults("testDatabaseModifications" , bytes);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertsWithTransactions() {
|
||||
startMeasuringWrites();
|
||||
final int txSize = 10;
|
||||
ContentValues cv = new ContentValues();
|
||||
for (int i = 0; i < DEFAULT_DATASET_SIZE * 5; i++) {
|
||||
if (i % txSize == 0) {
|
||||
mDatabase.beginTransaction();
|
||||
}
|
||||
if (i % txSize == txSize-1) {
|
||||
mDatabase.setTransactionSuccessful();
|
||||
mDatabase.endTransaction();
|
||||
|
||||
}
|
||||
cv.put("_ID", i);
|
||||
cv.put("COL_A", i);
|
||||
cv.put("COL_B", "NewValue");
|
||||
cv.put("COL_C", 1.0);
|
||||
assertEquals(i, mDatabase.insert("T1", null, cv));
|
||||
}
|
||||
// Make sure all changes are written to disk
|
||||
mDatabase.close();
|
||||
long bytes = endMeasuringWrites();
|
||||
sendResults("testInsertsWithTransactions" , bytes);
|
||||
}
|
||||
|
||||
private void startMeasuringWrites() {
|
||||
Preconditions.checkState(mWriteBytes == null, "Measurement already started");
|
||||
mWriteBytes = getIoStats().get("write_bytes");
|
||||
}
|
||||
|
||||
private long endMeasuringWrites() {
|
||||
Preconditions.checkState(mWriteBytes != null, "Measurement wasn't started");
|
||||
Long newWriteBytes = getIoStats().get("write_bytes");
|
||||
return newWriteBytes - mWriteBytes;
|
||||
}
|
||||
|
||||
private void sendResults(String testName, long writeBytes) {
|
||||
Log.i(TAG, testName + " write_bytes: " + writeBytes);
|
||||
Bundle status = new Bundle();
|
||||
status.putLong("write_bytes", writeBytes);
|
||||
InstrumentationRegistry.getInstrumentation().sendStatus(Activity.RESULT_OK, status);
|
||||
}
|
||||
|
||||
private static Map<String, Long> getIoStats() {
|
||||
String ioStat = "/proc/self/io";
|
||||
Map<String, Long> results = new ArrayMap<>();
|
||||
try {
|
||||
List<String> lines = Files.readAllLines(new File(ioStat).toPath());
|
||||
for (String line : lines) {
|
||||
line = line.trim();
|
||||
String[] split = line.split(":");
|
||||
if (split.length == 2) {
|
||||
try {
|
||||
String key = split[0].trim();
|
||||
Long value = Long.valueOf(split[1].trim());
|
||||
results.put(key, value);
|
||||
} catch (NumberFormatException e) {
|
||||
Log.e(TAG, "Cannot parse number from " + line);
|
||||
}
|
||||
} else if (line.isEmpty()) {
|
||||
Log.e(TAG, "Cannot parse line " + line);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Can't read: " + ioStat, e);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License
|
||||
*/
|
||||
|
||||
package android.database;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.filters.LargeTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Performance tests for typical CRUD operations and loading rows into the Cursor
|
||||
*
|
||||
* <p>To run: bit CorePerfTests:android.database.SQLiteDatabasePerfTest
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@LargeTest
|
||||
public class SQLiteDatabasePerfTest {
|
||||
// TODO b/64262688 Add Concurrency tests to compare WAL vs DELETE read/write
|
||||
private static final String DB_NAME = "dbperftest";
|
||||
private static final int DEFAULT_DATASET_SIZE = 1000;
|
||||
|
||||
@Rule
|
||||
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
|
||||
private SQLiteDatabase mDatabase;
|
||||
private Context mContext;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mContext = InstrumentationRegistry.getTargetContext();
|
||||
mContext.deleteDatabase(DB_NAME);
|
||||
mDatabase = mContext.openOrCreateDatabase(DB_NAME, Context.MODE_PRIVATE, null);
|
||||
mDatabase.execSQL("CREATE TABLE T1 "
|
||||
+ "(_ID INTEGER PRIMARY KEY, COL_A INTEGER, COL_B VARCHAR(100), COL_C REAL)");
|
||||
mDatabase.execSQL("CREATE TABLE T2 ("
|
||||
+ "_ID INTEGER PRIMARY KEY, COL_A VARCHAR(100), T1_ID INTEGER,"
|
||||
+ "FOREIGN KEY(T1_ID) REFERENCES T1 (_ID))");
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
mDatabase.close();
|
||||
mContext.deleteDatabase(DB_NAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSelect() {
|
||||
insertT1TestDataSet();
|
||||
|
||||
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
|
||||
Random rnd = new Random(0);
|
||||
while (state.keepRunning()) {
|
||||
int index = rnd.nextInt(DEFAULT_DATASET_SIZE);
|
||||
try (Cursor cursor = mDatabase.rawQuery("SELECT _ID, COL_A, COL_B, COL_C FROM T1 "
|
||||
+ "WHERE _ID=?", new String[]{String.valueOf(index)})) {
|
||||
assertTrue(cursor.moveToNext());
|
||||
assertEquals(index, cursor.getInt(0));
|
||||
assertEquals(index, cursor.getInt(1));
|
||||
assertEquals("T1Value" + index, cursor.getString(2));
|
||||
assertEquals(1.1 * index, cursor.getDouble(3), 0.0000001d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSelectMultipleRows() {
|
||||
insertT1TestDataSet();
|
||||
|
||||
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
Random rnd = new Random(0);
|
||||
final int querySize = 50;
|
||||
while (state.keepRunning()) {
|
||||
int index = rnd.nextInt(DEFAULT_DATASET_SIZE - querySize - 1);
|
||||
try (Cursor cursor = mDatabase.rawQuery("SELECT _ID, COL_A, COL_B, COL_C FROM T1 "
|
||||
+ "WHERE _ID BETWEEN ? and ? ORDER BY _ID",
|
||||
new String[]{String.valueOf(index), String.valueOf(index + querySize - 1)})) {
|
||||
int i = 0;
|
||||
while(cursor.moveToNext()) {
|
||||
assertEquals(index, cursor.getInt(0));
|
||||
assertEquals(index, cursor.getInt(1));
|
||||
assertEquals("T1Value" + index, cursor.getString(2));
|
||||
assertEquals(1.1 * index, cursor.getDouble(3), 0.0000001d);
|
||||
index++;
|
||||
i++;
|
||||
}
|
||||
assertEquals(querySize, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCursorIterateForward() {
|
||||
// A larger dataset is needed to exceed default CursorWindow size
|
||||
int datasetSize = DEFAULT_DATASET_SIZE * 50;
|
||||
insertT1TestDataSet(datasetSize);
|
||||
|
||||
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
try (Cursor cursor = mDatabase
|
||||
.rawQuery("SELECT _ID, COL_A, COL_B, COL_C FROM T1 ORDER BY _ID", null)) {
|
||||
int i = 0;
|
||||
while(cursor.moveToNext()) {
|
||||
assertEquals(i, cursor.getInt(0));
|
||||
assertEquals(i, cursor.getInt(1));
|
||||
assertEquals("T1Value" + i, cursor.getString(2));
|
||||
assertEquals(1.1 * i, cursor.getDouble(3), 0.0000001d);
|
||||
i++;
|
||||
}
|
||||
assertEquals(datasetSize, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCursorIterateBackwards() {
|
||||
// A larger dataset is needed to exceed default CursorWindow size
|
||||
int datasetSize = DEFAULT_DATASET_SIZE * 50;
|
||||
insertT1TestDataSet(datasetSize);
|
||||
|
||||
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
try (Cursor cursor = mDatabase
|
||||
.rawQuery("SELECT _ID, COL_A, COL_B, COL_C FROM T1 ORDER BY _ID", null)) {
|
||||
int i = datasetSize - 1;
|
||||
while(cursor.moveToPosition(i)) {
|
||||
assertEquals(i, cursor.getInt(0));
|
||||
assertEquals(i, cursor.getInt(1));
|
||||
assertEquals("T1Value" + i, cursor.getString(2));
|
||||
assertEquals(1.1 * i, cursor.getDouble(3), 0.0000001d);
|
||||
i--;
|
||||
}
|
||||
assertEquals(-1, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInnerJoin() {
|
||||
mDatabase.setForeignKeyConstraintsEnabled(true);
|
||||
mDatabase.beginTransaction();
|
||||
insertT1TestDataSet();
|
||||
insertT2TestDataSet();
|
||||
mDatabase.setTransactionSuccessful();
|
||||
mDatabase.endTransaction();
|
||||
|
||||
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
|
||||
Random rnd = new Random(0);
|
||||
while (state.keepRunning()) {
|
||||
int index = rnd.nextInt(1000);
|
||||
try (Cursor cursor = mDatabase.rawQuery(
|
||||
"SELECT T1._ID, T1.COL_A, T1.COL_B, T1.COL_C, T2.COL_A FROM T1 "
|
||||
+ "INNER JOIN T2 on T2.T1_ID=T1._ID WHERE T1._ID = ?",
|
||||
new String[]{String.valueOf(index)})) {
|
||||
assertTrue(cursor.moveToNext());
|
||||
assertEquals(index, cursor.getInt(0));
|
||||
assertEquals(index, cursor.getInt(1));
|
||||
assertEquals("T1Value" + index, cursor.getString(2));
|
||||
assertEquals(1.1 * index, cursor.getDouble(3), 0.0000001d);
|
||||
assertEquals("T2Value" + index, cursor.getString(4));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsert() {
|
||||
insertT1TestDataSet();
|
||||
|
||||
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
|
||||
ContentValues cv = new ContentValues();
|
||||
cv.put("_ID", DEFAULT_DATASET_SIZE);
|
||||
cv.put("COL_B", "NewValue");
|
||||
cv.put("COL_C", 1.1);
|
||||
String[] deleteArgs = new String[]{String.valueOf(DEFAULT_DATASET_SIZE)};
|
||||
while (state.keepRunning()) {
|
||||
assertEquals(DEFAULT_DATASET_SIZE, mDatabase.insert("T1", null, cv));
|
||||
state.pauseTiming();
|
||||
assertEquals(1, mDatabase.delete("T1", "_ID=?", deleteArgs));
|
||||
state.resumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDelete() {
|
||||
insertT1TestDataSet();
|
||||
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
String[] deleteArgs = new String[]{String.valueOf(DEFAULT_DATASET_SIZE)};
|
||||
Object[] insertsArgs = new Object[]{DEFAULT_DATASET_SIZE, DEFAULT_DATASET_SIZE,
|
||||
"ValueToDelete", 1.1};
|
||||
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
mDatabase.execSQL("INSERT INTO T1 VALUES (?, ?, ?, ?)", insertsArgs);
|
||||
state.resumeTiming();
|
||||
assertEquals(1, mDatabase.delete("T1", "_ID=?", deleteArgs));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdate() {
|
||||
insertT1TestDataSet();
|
||||
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
|
||||
Random rnd = new Random(0);
|
||||
int i = 0;
|
||||
ContentValues cv = new ContentValues();
|
||||
String[] argArray = new String[1];
|
||||
while (state.keepRunning()) {
|
||||
int id = rnd.nextInt(DEFAULT_DATASET_SIZE);
|
||||
cv.put("COL_A", i);
|
||||
cv.put("COL_B", "UpdatedValue");
|
||||
cv.put("COL_C", i);
|
||||
argArray[0] = String.valueOf(id);
|
||||
assertEquals(1, mDatabase.update("T1", cv, "_ID=?", argArray));
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
private void insertT1TestDataSet() {
|
||||
insertT1TestDataSet(DEFAULT_DATASET_SIZE);
|
||||
}
|
||||
|
||||
private void insertT1TestDataSet(int size) {
|
||||
mDatabase.beginTransaction();
|
||||
for (int i = 0; i < size; i++) {
|
||||
mDatabase.execSQL("INSERT INTO T1 VALUES (?, ?, ?, ?)",
|
||||
new Object[]{i, i, "T1Value" + i, i * 1.1});
|
||||
}
|
||||
mDatabase.setTransactionSuccessful();
|
||||
mDatabase.endTransaction();
|
||||
}
|
||||
|
||||
private void insertT2TestDataSet() {
|
||||
mDatabase.beginTransaction();
|
||||
for (int i = 0; i < DEFAULT_DATASET_SIZE; i++) {
|
||||
mDatabase.execSQL("INSERT INTO T2 VALUES (?, ?, ?)",
|
||||
new Object[]{i, "T2Value" + i, i});
|
||||
}
|
||||
mDatabase.setTransactionSuccessful();
|
||||
mDatabase.endTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,6 @@ public class CanvasPerfTest {
|
||||
|
||||
while (state.keepRunning()) {
|
||||
canvas = node.start(200, 200);
|
||||
canvas.setHighContrastText(false);
|
||||
int save = canvas.save();
|
||||
canvas.clipRect(1, 1, 199, 199);
|
||||
canvas.insertReorderBarrier();
|
||||
|
||||
@@ -80,11 +80,11 @@ public class PaintMeasureTextTest {
|
||||
}
|
||||
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
if (mCacheMode == DONT_USE_CACHE) {
|
||||
state.pauseTiming();
|
||||
Canvas.freeTextLayoutCaches();
|
||||
state.resumeTiming();
|
||||
}
|
||||
state.resumeTiming();
|
||||
|
||||
paint.measureText(mText);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.os;
|
||||
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
import android.support.test.filters.LargeTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.internal.os.BinderCallsStats;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
|
||||
/**
|
||||
* Performance tests for {@link BinderCallsStats}
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@LargeTest
|
||||
public class BinderCallsStatsPerfTest {
|
||||
|
||||
@Rule
|
||||
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
|
||||
private BinderCallsStats mBinderCallsStats;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mBinderCallsStats = new BinderCallsStats(true);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void timeCallSession() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
Binder b = new Binder();
|
||||
int i = 0;
|
||||
while (state.keepRunning()) {
|
||||
BinderCallsStats.CallSession s = mBinderCallsStats.callStarted(b, i % 100);
|
||||
mBinderCallsStats.callEnded(s);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void timeCallSessionTrackingDisabled() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
Binder b = new Binder();
|
||||
mBinderCallsStats = new BinderCallsStats(false);
|
||||
while (state.keepRunning()) {
|
||||
BinderCallsStats.CallSession s = mBinderCallsStats.callStarted(b, 0);
|
||||
mBinderCallsStats.callEnded(s);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package android.os;
|
||||
|
||||
interface ISomeService {
|
||||
void readDisk(int times);
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package android.os;
|
||||
|
||||
import static android.content.pm.PackageManager.PERMISSION_DENIED;
|
||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.filters.LargeTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@LargeTest
|
||||
public class PackageManagerPerfTest {
|
||||
private static final String PERMISSION_NAME_EXISTS =
|
||||
"com.android.perftests.core.TestPermission";
|
||||
private static final String PERMISSION_NAME_DOESNT_EXIST =
|
||||
"com.android.perftests.core.TestBadPermission";
|
||||
private static final ComponentName TEST_ACTIVITY =
|
||||
new ComponentName("com.android.perftests.core", "android.perftests.utils.StubActivity");
|
||||
|
||||
@Rule
|
||||
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
|
||||
|
||||
@Test
|
||||
public void testCheckPermissionExists() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
|
||||
final String packageName = TEST_ACTIVITY.getPackageName();
|
||||
|
||||
while (state.keepRunning()) {
|
||||
int ret = pm.checkPermission(PERMISSION_NAME_EXISTS, packageName);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCheckPermissionDoesntExist() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
|
||||
final String packageName = TEST_ACTIVITY.getPackageName();
|
||||
|
||||
while (state.keepRunning()) {
|
||||
int ret = pm.checkPermission(PERMISSION_NAME_DOESNT_EXIST, packageName);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQueryIntentActivities() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
|
||||
final Intent intent = new Intent("com.android.perftests.core.PERFTEST");
|
||||
|
||||
while (state.keepRunning()) {
|
||||
pm.queryIntentActivities(intent, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPackageInfo() throws Exception {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
|
||||
final String packageName = TEST_ACTIVITY.getPackageName();
|
||||
|
||||
while (state.keepRunning()) {
|
||||
pm.getPackageInfo(packageName, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetApplicationInfo() throws Exception {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
|
||||
final String packageName = TEST_ACTIVITY.getPackageName();
|
||||
|
||||
while (state.keepRunning()) {
|
||||
pm.getApplicationInfo(packageName, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetActivityInfo() throws Exception {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final PackageManager pm = InstrumentationRegistry.getTargetContext().getPackageManager();
|
||||
|
||||
while (state.keepRunning()) {
|
||||
pm.getActivityInfo(TEST_ACTIVITY, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,10 @@ import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@LargeTest
|
||||
public class ParcelPerfTest {
|
||||
@@ -167,4 +171,80 @@ public class ParcelPerfTest {
|
||||
Parcel.obtain().recycle();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void timeWriteException() {
|
||||
timeWriteException(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void timeWriteExceptionWithStackTraceParceling() {
|
||||
timeWriteException(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void timeReadException() {
|
||||
timeReadException(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void timeReadExceptionWithStackTraceParceling() {
|
||||
timeReadException(true);
|
||||
}
|
||||
|
||||
private void timeWriteException(boolean enableParceling) {
|
||||
if (enableParceling) {
|
||||
Parcel.setStackTraceParceling(true);
|
||||
}
|
||||
try {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
Parcel p = Parcel.obtain();
|
||||
SecurityException e = new SecurityException("TestMessage");
|
||||
while (state.keepRunning()) {
|
||||
p.setDataPosition(0);
|
||||
p.writeException(e);
|
||||
}
|
||||
} finally {
|
||||
if (enableParceling) {
|
||||
Parcel.setStackTraceParceling(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void timeReadException(boolean enableParceling) {
|
||||
if (enableParceling) {
|
||||
Parcel.setStackTraceParceling(true);
|
||||
}
|
||||
try {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
Parcel p = Parcel.obtain();
|
||||
String msg = "TestMessage";
|
||||
p.writeException(new SecurityException(msg));
|
||||
p.setDataPosition(0);
|
||||
// First verify that remote cause is set (if parceling is enabled)
|
||||
try {
|
||||
p.readException();
|
||||
} catch (SecurityException e) {
|
||||
assertEquals(e.getMessage(), msg);
|
||||
if (enableParceling) {
|
||||
assertTrue(e.getCause() instanceof RemoteException);
|
||||
} else {
|
||||
assertNull(e.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
while (state.keepRunning()) {
|
||||
p.setDataPosition(0);
|
||||
try {
|
||||
p.readException();
|
||||
} catch (SecurityException expected) {
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (enableParceling) {
|
||||
Parcel.setStackTraceParceling(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
41
apct-tests/perftests/core/src/android/os/PssPerfTest.java
Normal file
41
apct-tests/perftests/core/src/android/os/PssPerfTest.java
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.os;
|
||||
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
import android.support.test.filters.LargeTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@LargeTest
|
||||
public class PssPerfTest {
|
||||
@Rule
|
||||
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
|
||||
|
||||
@Test
|
||||
public void testPss() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
Debug.getPss();
|
||||
}
|
||||
}
|
||||
}
|
||||
42
apct-tests/perftests/core/src/android/os/SomeService.java
Normal file
42
apct-tests/perftests/core/src/android/os/SomeService.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package android.os;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/** Service in separate process available for calling over binder. */
|
||||
public class SomeService extends Service {
|
||||
|
||||
private File mTempFile;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
try {
|
||||
mTempFile = File.createTempFile("foo", "bar");
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private final ISomeService.Stub mBinder =
|
||||
new ISomeService.Stub() {
|
||||
public void readDisk(int times) {
|
||||
for (int i = 0; i < times; i++) {
|
||||
mTempFile.exists();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return mBinder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
mTempFile.delete();
|
||||
}
|
||||
}
|
||||
127
apct-tests/perftests/core/src/android/os/StrictModeTest.java
Normal file
127
apct-tests/perftests/core/src/android/os/StrictModeTest.java
Normal file
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package android.os;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.net.Uri;
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.filters.LargeTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import com.google.common.util.concurrent.SettableFuture;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@LargeTest
|
||||
public class StrictModeTest {
|
||||
@Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
|
||||
|
||||
@Test
|
||||
public void timeVmViolation() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().detectAll().penaltyLog().build());
|
||||
causeVmViolations(state);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void timeVmViolationNoStrictMode() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
causeVmViolations(state);
|
||||
}
|
||||
|
||||
private static void causeVmViolations(BenchmarkState state) {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.setDataAndType(Uri.parse("content://com.example/foobar"), "image/jpeg");
|
||||
final Context context = InstrumentationRegistry.getTargetContext();
|
||||
while (state.keepRunning()) {
|
||||
context.startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void timeThreadViolation() throws IOException {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
StrictMode.setThreadPolicy(
|
||||
new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
|
||||
causeThreadViolations(state);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void timeThreadViolationNoStrictMode() throws IOException {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
causeThreadViolations(state);
|
||||
}
|
||||
|
||||
private static void causeThreadViolations(BenchmarkState state) throws IOException {
|
||||
final File test = File.createTempFile("foo", "bar");
|
||||
while (state.keepRunning()) {
|
||||
test.exists();
|
||||
}
|
||||
test.delete();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void timeCrossBinderThreadViolation() throws Exception {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
StrictMode.setThreadPolicy(
|
||||
new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
|
||||
causeCrossProcessThreadViolations(state);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void timeCrossBinderThreadViolationNoStrictMode() throws Exception {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
causeCrossProcessThreadViolations(state);
|
||||
}
|
||||
|
||||
private static void causeCrossProcessThreadViolations(BenchmarkState state)
|
||||
throws ExecutionException, InterruptedException, RemoteException {
|
||||
final Context context = InstrumentationRegistry.getTargetContext();
|
||||
|
||||
SettableFuture<IBinder> binder = SettableFuture.create();
|
||||
ServiceConnection connection =
|
||||
new ServiceConnection() {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName className, IBinder service) {
|
||||
binder.set(service);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName arg0) {
|
||||
binder.set(null);
|
||||
}
|
||||
};
|
||||
context.bindService(
|
||||
new Intent(context, SomeService.class), connection, Context.BIND_AUTO_CREATE);
|
||||
ISomeService someService = ISomeService.Stub.asInterface(binder.get());
|
||||
while (state.keepRunning()) {
|
||||
// Violate strictmode heavily.
|
||||
someService.readDisk(10);
|
||||
}
|
||||
context.unbindService(connection);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package android.text;
|
||||
|
||||
import static android.text.Layout.Alignment.ALIGN_NORMAL;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
import android.support.test.filters.LargeTest;
|
||||
import android.text.NonEditableTextGenerator.TextType;
|
||||
import android.view.DisplayListCanvas;
|
||||
import android.view.RenderNode;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Performance test for {@link BoringLayout} create and draw.
|
||||
*/
|
||||
@LargeTest
|
||||
@RunWith(Parameterized.class)
|
||||
public class BoringLayoutCreateDrawPerfTest {
|
||||
|
||||
private static final boolean[] BOOLEANS = new boolean[]{false, true};
|
||||
private static final float SPACING_ADD = 10f;
|
||||
private static final float SPACING_MULT = 1.5f;
|
||||
|
||||
@Parameterized.Parameters(name = "cached={3},{1}chars,{0}")
|
||||
public static Collection cases() {
|
||||
final List<Object[]> params = new ArrayList<>();
|
||||
for (int length : new int[]{128}) {
|
||||
for (boolean cached : BOOLEANS) {
|
||||
for (TextType textType : new TextType[]{TextType.STRING,
|
||||
TextType.SPANNABLE_BUILDER}) {
|
||||
params.add(new Object[]{textType.name(), length, textType, cached});
|
||||
}
|
||||
}
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
@Rule
|
||||
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
|
||||
|
||||
private final int mLength;
|
||||
private final TextType mTextType;
|
||||
private final boolean mCached;
|
||||
private final TextPaint mTextPaint;
|
||||
|
||||
public BoringLayoutCreateDrawPerfTest(String label, int length, TextType textType,
|
||||
boolean cached) {
|
||||
mLength = length;
|
||||
mCached = cached;
|
||||
mTextType = textType;
|
||||
mTextPaint = new TextPaint();
|
||||
mTextPaint.setTextSize(10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Measures the creation time for {@link BoringLayout}.
|
||||
*/
|
||||
@Test
|
||||
public void timeCreate() throws Exception {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
|
||||
state.pauseTiming();
|
||||
Canvas.freeTextLayoutCaches();
|
||||
final CharSequence text = createRandomText();
|
||||
// isBoring result is calculated in another test, we want to measure only the
|
||||
// create time for Boring without isBoring check. Therefore it is calculated here.
|
||||
final BoringLayout.Metrics metrics = BoringLayout.isBoring(text, mTextPaint);
|
||||
if (mCached) createLayout(text, metrics);
|
||||
state.resumeTiming();
|
||||
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
if (!mCached) Canvas.freeTextLayoutCaches();
|
||||
state.resumeTiming();
|
||||
|
||||
createLayout(text, metrics);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Measures the draw time for {@link BoringLayout} or {@link StaticLayout}.
|
||||
*/
|
||||
@Test
|
||||
public void timeDraw() throws Throwable {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
|
||||
state.pauseTiming();
|
||||
Canvas.freeTextLayoutCaches();
|
||||
final RenderNode node = RenderNode.create("benchmark", null);
|
||||
final CharSequence text = createRandomText();
|
||||
final BoringLayout.Metrics metrics = BoringLayout.isBoring(text, mTextPaint);
|
||||
final Layout layout = createLayout(text, metrics);
|
||||
state.resumeTiming();
|
||||
|
||||
while (state.keepRunning()) {
|
||||
|
||||
state.pauseTiming();
|
||||
final DisplayListCanvas canvas = node.start(1200, 200);
|
||||
final int save = canvas.save();
|
||||
if (!mCached) Canvas.freeTextLayoutCaches();
|
||||
state.resumeTiming();
|
||||
|
||||
layout.draw(canvas);
|
||||
|
||||
state.pauseTiming();
|
||||
canvas.restoreToCount(save);
|
||||
node.end(canvas);
|
||||
state.resumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
private CharSequence createRandomText() {
|
||||
return new NonEditableTextGenerator(new Random(0))
|
||||
.setSequenceLength(mLength)
|
||||
.setCreateBoring(true)
|
||||
.setTextType(mTextType)
|
||||
.build();
|
||||
}
|
||||
|
||||
private Layout createLayout(CharSequence text,
|
||||
BoringLayout.Metrics metrics) {
|
||||
return BoringLayout.make(text, mTextPaint, Integer.MAX_VALUE /*width*/,
|
||||
ALIGN_NORMAL, SPACING_MULT, SPACING_ADD, metrics, true /*includePad*/);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package android.text;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
import android.support.test.filters.LargeTest;
|
||||
import android.text.NonEditableTextGenerator.TextType;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Performance test for {@link BoringLayout#isBoring(CharSequence, TextPaint)}.
|
||||
*/
|
||||
@LargeTest
|
||||
@RunWith(Parameterized.class)
|
||||
public class BoringLayoutIsBoringPerfTest {
|
||||
|
||||
private static final boolean[] BOOLEANS = new boolean[]{false, true};
|
||||
|
||||
@Parameterized.Parameters(name = "cached={4},{1}chars,{0}")
|
||||
public static Collection cases() {
|
||||
final List<Object[]> params = new ArrayList<>();
|
||||
for (int length : new int[]{128}) {
|
||||
for (boolean boring : BOOLEANS) {
|
||||
for (boolean cached : BOOLEANS) {
|
||||
for (TextType textType : new TextType[]{TextType.STRING,
|
||||
TextType.SPANNABLE_BUILDER}) {
|
||||
params.add(new Object[]{
|
||||
(boring ? "Boring" : "NotBoring") + "," + textType.name(),
|
||||
length, boring, textType, cached});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
@Rule
|
||||
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
|
||||
|
||||
private final int mLength;
|
||||
private final TextType mTextType;
|
||||
private final boolean mCreateBoring;
|
||||
private final boolean mCached;
|
||||
private final TextPaint mTextPaint;
|
||||
|
||||
public BoringLayoutIsBoringPerfTest(String label, int length, boolean boring, TextType textType,
|
||||
boolean cached) {
|
||||
mLength = length;
|
||||
mCreateBoring = boring;
|
||||
mCached = cached;
|
||||
mTextType = textType;
|
||||
mTextPaint = new TextPaint();
|
||||
mTextPaint.setTextSize(10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Measure the time for the {@link BoringLayout#isBoring(CharSequence, TextPaint)}.
|
||||
*/
|
||||
@Test
|
||||
public void timeIsBoring() throws Exception {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
|
||||
state.pauseTiming();
|
||||
Canvas.freeTextLayoutCaches();
|
||||
final CharSequence text = createRandomText();
|
||||
if (mCached) BoringLayout.isBoring(text, mTextPaint);
|
||||
state.resumeTiming();
|
||||
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
if (!mCached) Canvas.freeTextLayoutCaches();
|
||||
state.resumeTiming();
|
||||
|
||||
BoringLayout.isBoring(text, mTextPaint);
|
||||
}
|
||||
}
|
||||
|
||||
private CharSequence createRandomText() {
|
||||
return new NonEditableTextGenerator(new Random(0))
|
||||
.setSequenceLength(mLength)
|
||||
.setCreateBoring(mCreateBoring)
|
||||
.setTextType(mTextType)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -16,35 +16,28 @@
|
||||
|
||||
package android.text;
|
||||
|
||||
import android.app.Activity;
|
||||
import static android.text.Layout.Alignment.ALIGN_NORMAL;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Paint.FontMetricsInt;
|
||||
import android.os.Bundle;
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
import android.perftests.utils.StubActivity;
|
||||
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.filters.LargeTest;
|
||||
import android.support.test.rule.ActivityTestRule;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.text.style.ReplacementSpan;
|
||||
import android.util.ArraySet;
|
||||
|
||||
import static android.text.Layout.Alignment.ALIGN_NORMAL;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Locale;
|
||||
import java.util.Random;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Random;
|
||||
|
||||
@LargeTest
|
||||
@RunWith(Parameterized.class)
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
package android.text;
|
||||
|
||||
import static android.text.Spanned.SPAN_INCLUSIVE_INCLUSIVE;
|
||||
|
||||
import android.text.style.BulletSpan;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class NonEditableTextGenerator {
|
||||
|
||||
enum TextType {
|
||||
STRING,
|
||||
SPANNED,
|
||||
SPANNABLE_BUILDER
|
||||
}
|
||||
|
||||
private boolean mCreateBoring;
|
||||
private TextType mTextType;
|
||||
private int mSequenceLength;
|
||||
private final Random mRandom;
|
||||
|
||||
public NonEditableTextGenerator(Random random) {
|
||||
mRandom = random;
|
||||
}
|
||||
|
||||
public NonEditableTextGenerator setCreateBoring(boolean createBoring) {
|
||||
mCreateBoring = createBoring;
|
||||
return this;
|
||||
}
|
||||
|
||||
public NonEditableTextGenerator setTextType(TextType textType) {
|
||||
mTextType = textType;
|
||||
return this;
|
||||
}
|
||||
|
||||
public NonEditableTextGenerator setSequenceLength(int sequenceLength) {
|
||||
mSequenceLength = sequenceLength;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sample charSequence generated:
|
||||
* NRjPzjvUadHmH ExoEoTqfx pCLw qtndsqfpk AqajVCbgjGZ igIeC dfnXRgA
|
||||
*/
|
||||
public CharSequence build() {
|
||||
final RandomCharSequenceGenerator sequenceGenerator = new RandomCharSequenceGenerator(
|
||||
mRandom);
|
||||
if (mSequenceLength > 0) {
|
||||
sequenceGenerator.setSequenceLength(mSequenceLength);
|
||||
}
|
||||
|
||||
final CharSequence charSequence = sequenceGenerator.buildLatinSequence();
|
||||
|
||||
switch (mTextType) {
|
||||
case SPANNED:
|
||||
case SPANNABLE_BUILDER:
|
||||
return createSpannable(charSequence);
|
||||
case STRING:
|
||||
default:
|
||||
return createString(charSequence);
|
||||
}
|
||||
}
|
||||
|
||||
private Spannable createSpannable(CharSequence charSequence) {
|
||||
final Spannable spannable = (mTextType == TextType.SPANNABLE_BUILDER) ?
|
||||
new SpannableStringBuilder(charSequence) : new SpannableString(charSequence);
|
||||
|
||||
if (!mCreateBoring) {
|
||||
// add a paragraph style to make it non boring
|
||||
spannable.setSpan(new BulletSpan(), 0, spannable.length(), SPAN_INCLUSIVE_INCLUSIVE);
|
||||
}
|
||||
|
||||
spannable.setSpan(new Object(), 0, spannable.length(), SPAN_INCLUSIVE_INCLUSIVE);
|
||||
spannable.setSpan(new Object(), 0, 1, SPAN_INCLUSIVE_INCLUSIVE);
|
||||
|
||||
return spannable;
|
||||
}
|
||||
|
||||
private String createString(CharSequence charSequence) {
|
||||
if (mCreateBoring) {
|
||||
return charSequence.toString();
|
||||
} else {
|
||||
// BoringLayout checks to see if there is a surrogate pair and if so tells that
|
||||
// the charSequence is not suitable for boring. Add an emoji to make it non boring.
|
||||
// Emoji is added instead of RTL, since emoji stays in the same run and is a more
|
||||
// common case.
|
||||
return charSequence.toString() + "\uD83D\uDC68\uD83C\uDFFF";
|
||||
}
|
||||
}
|
||||
|
||||
public static class RandomCharSequenceGenerator {
|
||||
|
||||
private static final int DEFAULT_MIN_WORD_LENGTH = 3;
|
||||
private static final int DEFAULT_MAX_WORD_LENGTH = 15;
|
||||
private static final int DEFAULT_SEQUENCE_LENGTH = 256;
|
||||
|
||||
private int mMinWordLength = DEFAULT_MIN_WORD_LENGTH;
|
||||
private int mMaxWordLength = DEFAULT_MAX_WORD_LENGTH;
|
||||
private int mSequenceLength = DEFAULT_SEQUENCE_LENGTH;
|
||||
private final Random mRandom;
|
||||
|
||||
public RandomCharSequenceGenerator(Random random) {
|
||||
mRandom = random;
|
||||
}
|
||||
|
||||
public RandomCharSequenceGenerator setSequenceLength(int sequenceLength) {
|
||||
mSequenceLength = sequenceLength;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CharSequence buildLatinSequence() {
|
||||
final StringBuilder result = new StringBuilder();
|
||||
while (result.length() < mSequenceLength) {
|
||||
// add random word
|
||||
result.append(buildLatinWord());
|
||||
result.append(' ');
|
||||
}
|
||||
return result.substring(0, mSequenceLength);
|
||||
}
|
||||
|
||||
public CharSequence buildLatinWord() {
|
||||
final StringBuilder result = new StringBuilder();
|
||||
// create a random length that is (mMinWordLength + random amount of chars) where
|
||||
// total size is less than mMaxWordLength
|
||||
final int length = mRandom.nextInt(mMaxWordLength - mMinWordLength) + mMinWordLength;
|
||||
while (result.length() < length) {
|
||||
// add random letter
|
||||
int base = mRandom.nextInt(2) == 0 ? 'A' : 'a';
|
||||
result.append(Character.toChars(mRandom.nextInt(26) + base));
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package android.text;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
import android.support.test.filters.LargeTest;
|
||||
import android.view.DisplayListCanvas;
|
||||
import android.view.RenderNode;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Performance test for single line measure and draw using {@link Paint} and {@link Canvas}.
|
||||
*/
|
||||
@LargeTest
|
||||
@RunWith(Parameterized.class)
|
||||
public class PaintMeasureDrawPerfTest {
|
||||
|
||||
private static final boolean[] BOOLEANS = new boolean[]{false, true};
|
||||
|
||||
@Parameterized.Parameters(name = "cached={1},{0}chars")
|
||||
public static Collection cases() {
|
||||
final List<Object[]> params = new ArrayList<>();
|
||||
for (int length : new int[]{128}) {
|
||||
for (boolean cached : BOOLEANS) {
|
||||
params.add(new Object[]{length, cached});
|
||||
}
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
@Rule
|
||||
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
|
||||
|
||||
private final int mLength;
|
||||
private final boolean mCached;
|
||||
private final TextPaint mTextPaint;
|
||||
|
||||
|
||||
public PaintMeasureDrawPerfTest(int length, boolean cached) {
|
||||
mLength = length;
|
||||
mCached = cached;
|
||||
mTextPaint = new TextPaint();
|
||||
mTextPaint.setTextSize(10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Measure the time for {@link Paint#measureText(String)}
|
||||
*/
|
||||
@Test
|
||||
public void timeMeasure() throws Exception {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
|
||||
state.pauseTiming();
|
||||
Canvas.freeTextLayoutCaches();
|
||||
final String text = createRandomText();
|
||||
if (mCached) mTextPaint.measureText(text);
|
||||
state.resumeTiming();
|
||||
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
if (!mCached) Canvas.freeTextLayoutCaches();
|
||||
state.resumeTiming();
|
||||
|
||||
mTextPaint.measureText(text);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Measures the time for {@link Canvas#drawText(String, float, float, Paint)}
|
||||
*/
|
||||
@Test
|
||||
public void timeDraw() throws Throwable {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
|
||||
state.pauseTiming();
|
||||
Canvas.freeTextLayoutCaches();
|
||||
final RenderNode node = RenderNode.create("benchmark", null);
|
||||
final String text = createRandomText();
|
||||
if (mCached) mTextPaint.measureText(text);
|
||||
state.resumeTiming();
|
||||
|
||||
while (state.keepRunning()) {
|
||||
|
||||
state.pauseTiming();
|
||||
final DisplayListCanvas canvas = node.start(1200, 200);
|
||||
final int save = canvas.save();
|
||||
if (!mCached) Canvas.freeTextLayoutCaches();
|
||||
state.resumeTiming();
|
||||
|
||||
canvas.drawText(text, 0 /*x*/, 100 /*y*/, mTextPaint);
|
||||
|
||||
state.pauseTiming();
|
||||
canvas.restoreToCount(save);
|
||||
node.end(canvas);
|
||||
state.resumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
private String createRandomText() {
|
||||
return (String) new NonEditableTextGenerator(new Random(0))
|
||||
.setSequenceLength(mLength)
|
||||
.setCreateBoring(true)
|
||||
.setTextType(NonEditableTextGenerator.TextType.STRING)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* 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.text;
|
||||
|
||||
import static android.text.TextDirectionHeuristics.LTR;
|
||||
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
|
||||
import android.support.test.filters.LargeTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Typeface;
|
||||
import android.text.Layout;
|
||||
import android.text.style.TextAppearanceSpan;
|
||||
import android.view.DisplayListCanvas;
|
||||
import android.view.RenderNode;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.nio.CharBuffer;
|
||||
import java.util.Random;
|
||||
|
||||
@LargeTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class PrecomputedTextMemoryUsageTest {
|
||||
private static final int WORD_LENGTH = 9; // Random word has 9 characters.
|
||||
private static final boolean NO_STYLE_TEXT = false;
|
||||
|
||||
private static TextPaint PAINT = new TextPaint();
|
||||
|
||||
private static int TRIAL_COUNT = 100;
|
||||
|
||||
public PrecomputedTextMemoryUsageTest() {}
|
||||
|
||||
private TextPerfUtils mTextUtil = new TextPerfUtils();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mTextUtil.resetRandom(0 /* seed */);
|
||||
}
|
||||
|
||||
private void reportMemoryUsage(int memoryUsage, String key) {
|
||||
Bundle status = new Bundle();
|
||||
status.putInt(key + "_median", memoryUsage);
|
||||
InstrumentationRegistry.getInstrumentation().sendStatus(Activity.RESULT_OK, status);
|
||||
}
|
||||
|
||||
private int median(int[] values) {
|
||||
return values.length % 2 == 0 ?
|
||||
(values[values.length / 2] + values[values.length / 2 - 1]) / 2:
|
||||
values[values.length / 2];
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMemoryUsage_NoHyphenation() {
|
||||
int[] memories = new int[TRIAL_COUNT];
|
||||
final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
|
||||
.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
|
||||
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
|
||||
.build();
|
||||
|
||||
// Report median of randomly generated PrecomputedText.
|
||||
for (int i = 0; i < TRIAL_COUNT; ++i) {
|
||||
memories[i] = PrecomputedText.create(
|
||||
mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), param)
|
||||
.getMemoryUsage();
|
||||
}
|
||||
reportMemoryUsage(median(memories), "MemoryUsage_NoHyphenation");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMemoryUsage_Hyphenation() {
|
||||
int[] memories = new int[TRIAL_COUNT];
|
||||
final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
|
||||
.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
|
||||
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
|
||||
.build();
|
||||
|
||||
// Report median of randomly generated PrecomputedText.
|
||||
for (int i = 0; i < TRIAL_COUNT; ++i) {
|
||||
memories[i] = PrecomputedText.create(
|
||||
mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), param)
|
||||
.getMemoryUsage();
|
||||
}
|
||||
reportMemoryUsage(median(memories), "MemoryUsage_Hyphenation");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMemoryUsage_NoHyphenation_WidthOnly() {
|
||||
int[] memories = new int[TRIAL_COUNT];
|
||||
final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
|
||||
.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
|
||||
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
|
||||
.build();
|
||||
|
||||
// Report median of randomly generated PrecomputedText.
|
||||
for (int i = 0; i < TRIAL_COUNT; ++i) {
|
||||
CharSequence cs = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
|
||||
PrecomputedText.ParagraphInfo[] paragraphInfo =
|
||||
PrecomputedText.createMeasuredParagraphs(cs, param, 0, cs.length(), false);
|
||||
memories[i] = 0;
|
||||
for (PrecomputedText.ParagraphInfo info : paragraphInfo) {
|
||||
memories[i] += info.measured.getMemoryUsage();
|
||||
}
|
||||
}
|
||||
reportMemoryUsage(median(memories), "MemoryUsage_NoHyphenation_WidthOnly");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMemoryUsage_Hyphenatation_WidthOnly() {
|
||||
int[] memories = new int[TRIAL_COUNT];
|
||||
final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
|
||||
.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
|
||||
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
|
||||
.build();
|
||||
|
||||
// Report median of randomly generated PrecomputedText.
|
||||
for (int i = 0; i < TRIAL_COUNT; ++i) {
|
||||
CharSequence cs = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
|
||||
PrecomputedText.ParagraphInfo[] paragraphInfo =
|
||||
PrecomputedText.createMeasuredParagraphs(cs, param, 0, cs.length(), false);
|
||||
memories[i] = 0;
|
||||
for (PrecomputedText.ParagraphInfo info : paragraphInfo) {
|
||||
memories[i] += info.measured.getMemoryUsage();
|
||||
}
|
||||
}
|
||||
reportMemoryUsage(median(memories), "MemoryUsage_Hyphenation_WidthOnly");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* 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.text;
|
||||
|
||||
import static android.text.TextDirectionHeuristics.LTR;
|
||||
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
|
||||
import android.support.test.filters.LargeTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Typeface;
|
||||
import android.text.Layout;
|
||||
import android.text.style.TextAppearanceSpan;
|
||||
import android.view.DisplayListCanvas;
|
||||
import android.view.RenderNode;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.nio.CharBuffer;
|
||||
import java.util.Random;
|
||||
|
||||
@LargeTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class PrecomputedTextPerfTest {
|
||||
private static final int WORD_LENGTH = 9; // Random word has 9 characters.
|
||||
private static final int WORDS_IN_LINE = 8; // Roughly, 8 words in a line.
|
||||
private static final boolean NO_STYLE_TEXT = false;
|
||||
private static final boolean STYLE_TEXT = true;
|
||||
|
||||
private static TextPaint PAINT = new TextPaint();
|
||||
private static final int TEXT_WIDTH = WORDS_IN_LINE * WORD_LENGTH * (int) PAINT.getTextSize();
|
||||
|
||||
public PrecomputedTextPerfTest() {}
|
||||
|
||||
@Rule
|
||||
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
|
||||
|
||||
private TextPerfUtils mTextUtil = new TextPerfUtils();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mTextUtil.resetRandom(0 /* seed */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate_NoStyled_Hyphenation() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
|
||||
.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
|
||||
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
|
||||
.build();
|
||||
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
|
||||
state.resumeTiming();
|
||||
|
||||
PrecomputedText.create(text, param);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate_NoStyled_NoHyphenation() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
|
||||
.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
|
||||
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
|
||||
.build();
|
||||
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
|
||||
state.resumeTiming();
|
||||
|
||||
PrecomputedText.create(text, param);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate_NoStyled_Hyphenation_WidthOnly() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
|
||||
.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
|
||||
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
|
||||
.build();
|
||||
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
|
||||
state.resumeTiming();
|
||||
|
||||
PrecomputedText.create(text, param);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate_NoStyled_NoHyphenation_WidthOnly() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
|
||||
.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
|
||||
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
|
||||
.build();
|
||||
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
|
||||
state.resumeTiming();
|
||||
|
||||
PrecomputedText.create(text, param);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate_Styled_Hyphenation() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
|
||||
.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
|
||||
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
|
||||
.build();
|
||||
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
|
||||
state.resumeTiming();
|
||||
|
||||
PrecomputedText.create(text, param);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate_Styled_NoHyphenation() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
|
||||
.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
|
||||
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
|
||||
.build();
|
||||
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
|
||||
state.resumeTiming();
|
||||
|
||||
PrecomputedText.create(text, param);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate_Styled_Hyphenation_WidthOnly() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
|
||||
.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
|
||||
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
|
||||
.build();
|
||||
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
|
||||
state.resumeTiming();
|
||||
|
||||
PrecomputedText.create(text, param);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate_Styled_NoHyphenation_WidthOnly() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final PrecomputedText.Params param = new PrecomputedText.Params.Builder(PAINT)
|
||||
.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
|
||||
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
|
||||
.build();
|
||||
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
|
||||
state.resumeTiming();
|
||||
|
||||
PrecomputedText.create(text, param);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package android.text;
|
||||
|
||||
import static android.text.Layout.Alignment.ALIGN_NORMAL;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
import android.support.test.filters.LargeTest;
|
||||
import android.text.NonEditableTextGenerator.TextType;
|
||||
import android.view.DisplayListCanvas;
|
||||
import android.view.RenderNode;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Performance test for multi line, single style {@link StaticLayout} creation/draw.
|
||||
*/
|
||||
@LargeTest
|
||||
@RunWith(Parameterized.class)
|
||||
public class StaticLayoutCreateDrawPerfTest {
|
||||
|
||||
private static final boolean[] BOOLEANS = new boolean[]{false, true};
|
||||
|
||||
private static final float SPACING_ADD = 10f;
|
||||
private static final float SPACING_MULT = 1.5f;
|
||||
|
||||
@Rule
|
||||
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
|
||||
|
||||
@Parameterized.Parameters(name = "cached={3},{1}chars,{0}")
|
||||
public static Collection cases() {
|
||||
final List<Object[]> params = new ArrayList<>();
|
||||
for (int length : new int[]{128}) {
|
||||
for (boolean cached : BOOLEANS) {
|
||||
for (TextType textType : new TextType[]{TextType.STRING,
|
||||
TextType.SPANNABLE_BUILDER}) {
|
||||
params.add(new Object[]{textType.name(), length, textType, cached});
|
||||
}
|
||||
}
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
private final int mLineWidth;
|
||||
private final int mLength;
|
||||
private final TextType mTextType;
|
||||
private final boolean mCached;
|
||||
private final TextPaint mTextPaint;
|
||||
|
||||
public StaticLayoutCreateDrawPerfTest(String label, int length, TextType textType,
|
||||
boolean cached) {
|
||||
mLength = length;
|
||||
mTextType = textType;
|
||||
mCached = cached;
|
||||
mTextPaint = new TextPaint();
|
||||
mTextPaint.setTextSize(10);
|
||||
mLineWidth = Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Measures the creation time for a multi line {@link StaticLayout}.
|
||||
*/
|
||||
@Test
|
||||
public void timeCreate() throws Exception {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
|
||||
state.pauseTiming();
|
||||
Canvas.freeTextLayoutCaches();
|
||||
final CharSequence text = createRandomText(mLength);
|
||||
createLayout(text);
|
||||
state.resumeTiming();
|
||||
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
if (!mCached) Canvas.freeTextLayoutCaches();
|
||||
state.resumeTiming();
|
||||
|
||||
createLayout(text);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Measures the draw time for a multi line {@link StaticLayout}.
|
||||
*/
|
||||
@Test
|
||||
public void timeDraw() throws Exception {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
|
||||
state.pauseTiming();
|
||||
Canvas.freeTextLayoutCaches();
|
||||
final RenderNode node = RenderNode.create("benchmark", null);
|
||||
final CharSequence text = createRandomText(mLength);
|
||||
final Layout layout = createLayout(text);
|
||||
state.resumeTiming();
|
||||
|
||||
while (state.keepRunning()) {
|
||||
|
||||
state.pauseTiming();
|
||||
final DisplayListCanvas canvas = node.start(1200, 200);
|
||||
int save = canvas.save();
|
||||
if (!mCached) Canvas.freeTextLayoutCaches();
|
||||
state.resumeTiming();
|
||||
|
||||
layout.draw(canvas);
|
||||
|
||||
state.pauseTiming();
|
||||
canvas.restoreToCount(save);
|
||||
node.end(canvas);
|
||||
state.resumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
private Layout createLayout(CharSequence text) {
|
||||
return StaticLayout.Builder.obtain(text, 0 /*start*/, text.length() /*end*/, mTextPaint,
|
||||
mLineWidth)
|
||||
.setAlignment(ALIGN_NORMAL)
|
||||
.setIncludePad(true)
|
||||
.setLineSpacing(SPACING_ADD, SPACING_MULT)
|
||||
.build();
|
||||
}
|
||||
|
||||
private CharSequence createRandomText(int length) {
|
||||
return new NonEditableTextGenerator(new Random(0))
|
||||
.setSequenceLength(length)
|
||||
.setTextType(mTextType)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* 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.text;
|
||||
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
|
||||
import android.support.test.filters.LargeTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@LargeTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class StaticLayoutMultithreadPerfTest {
|
||||
private static final int WORD_LENGTH = 9; // Random word has 9 characters.
|
||||
private static final int WORDS_IN_LINE = 8; // Roughly, 8 words in a line.
|
||||
private static final boolean NO_STYLE_TEXT = false;
|
||||
|
||||
private static TextPaint PAINT = new TextPaint();
|
||||
private static final int TEXT_WIDTH = WORDS_IN_LINE * WORD_LENGTH * (int) PAINT.getTextSize();
|
||||
|
||||
public StaticLayoutMultithreadPerfTest() {}
|
||||
|
||||
@Rule
|
||||
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
|
||||
|
||||
private CountDownLatch mStartLatch;
|
||||
private AtomicBoolean mThreadState; // True for running, False for stopped.
|
||||
|
||||
private static final long TIMEOUT_MS = 5000;
|
||||
|
||||
private Thread[] startBackgroundThread(int numOfThreads) {
|
||||
mStartLatch = new CountDownLatch(numOfThreads);
|
||||
mThreadState = new AtomicBoolean(true);
|
||||
|
||||
Thread[] threads = new Thread[numOfThreads];
|
||||
for (int i = 0; i < numOfThreads; ++i) {
|
||||
final int seed = i + 1;
|
||||
threads[i] = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final TextPerfUtils util = new TextPerfUtils();
|
||||
util.resetRandom(seed);
|
||||
|
||||
mStartLatch.countDown();
|
||||
while (mThreadState.get()) {
|
||||
final CharSequence text = util.nextRandomParagraph(
|
||||
WORD_LENGTH, NO_STYLE_TEXT);
|
||||
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (int i = 0; i < numOfThreads; ++i) {
|
||||
threads[i].start();
|
||||
}
|
||||
|
||||
try {
|
||||
mStartLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return threads;
|
||||
}
|
||||
|
||||
private void finishThreads(Thread[] threads) {
|
||||
mThreadState.set(false);
|
||||
for (Thread thread : threads) {
|
||||
try {
|
||||
thread.join();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
mStartLatch = null;
|
||||
mThreadState = null;
|
||||
}
|
||||
|
||||
private void runRandomTest(int numOfTotalThreads) {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final TextPerfUtils util = new TextPerfUtils();
|
||||
Thread[] threads = startBackgroundThread(numOfTotalThreads - 1);
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final CharSequence text = util.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
|
||||
state.resumeTiming();
|
||||
|
||||
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
|
||||
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
|
||||
.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
|
||||
.build();
|
||||
}
|
||||
finishThreads(threads);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate_RandomText_Thread_1() {
|
||||
runRandomTest(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate_RandomText_Thread_2() {
|
||||
runRandomTest(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate_RandomText_Thread_4() {
|
||||
runRandomTest(4);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,402 @@
|
||||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package android.text;
|
||||
|
||||
import static android.text.TextDirectionHeuristics.LTR;
|
||||
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
|
||||
import android.support.test.filters.LargeTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Typeface;
|
||||
import android.text.Layout;
|
||||
import android.text.style.TextAppearanceSpan;
|
||||
import android.view.DisplayListCanvas;
|
||||
import android.view.RenderNode;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.nio.CharBuffer;
|
||||
import java.util.Random;
|
||||
|
||||
@LargeTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class StaticLayoutPerfTest {
|
||||
private static final int WORD_LENGTH = 9; // Random word has 9 characters.
|
||||
private static final int WORDS_IN_LINE = 8; // Roughly, 8 words in a line.
|
||||
private static final boolean NO_STYLE_TEXT = false;
|
||||
private static final boolean STYLE_TEXT = true;
|
||||
|
||||
private static TextPaint PAINT = new TextPaint();
|
||||
private static final int TEXT_WIDTH = WORDS_IN_LINE * WORD_LENGTH * (int) PAINT.getTextSize();
|
||||
|
||||
public StaticLayoutPerfTest() {}
|
||||
|
||||
@Rule
|
||||
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
|
||||
|
||||
private TextPerfUtils mTextUtil = new TextPerfUtils();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mTextUtil.resetRandom(0 /* seed */);
|
||||
}
|
||||
|
||||
private PrecomputedText makeMeasured(CharSequence text, TextPaint paint) {
|
||||
PrecomputedText.Params param = new PrecomputedText.Params.Builder(paint).build();
|
||||
return PrecomputedText.create(text, param);
|
||||
}
|
||||
|
||||
private PrecomputedText makeMeasured(CharSequence text, TextPaint paint, int strategy,
|
||||
int frequency) {
|
||||
PrecomputedText.Params param = new PrecomputedText.Params.Builder(paint)
|
||||
.setHyphenationFrequency(frequency).setBreakStrategy(strategy).build();
|
||||
return PrecomputedText.create(text, param);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate_FixedText_NoStyle_Greedy_NoHyphenation() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
|
||||
while (state.keepRunning()) {
|
||||
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
|
||||
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
|
||||
.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate_RandomText_NoStyled_Greedy_NoHyphenation() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
|
||||
state.resumeTiming();
|
||||
|
||||
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
|
||||
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
|
||||
.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate_RandomText_NoStyled_Greedy_Hyphenation() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
|
||||
state.resumeTiming();
|
||||
|
||||
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
|
||||
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
|
||||
.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate_RandomText_NoStyled_Balanced_NoHyphenation() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
|
||||
state.resumeTiming();
|
||||
|
||||
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
|
||||
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
|
||||
.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate_RandomText_NoStyled_Balanced_Hyphenation() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
|
||||
state.resumeTiming();
|
||||
|
||||
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
|
||||
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
|
||||
.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate_RandomText_Styled_Greedy_NoHyphenation() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
|
||||
state.resumeTiming();
|
||||
|
||||
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
|
||||
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
|
||||
.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate_PrecomputedText_NoStyled_Greedy_NoHyphenation() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final PrecomputedText text = makeMeasured(
|
||||
mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT,
|
||||
Layout.BREAK_STRATEGY_SIMPLE, Layout.HYPHENATION_FREQUENCY_NONE);
|
||||
state.resumeTiming();
|
||||
|
||||
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
|
||||
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
|
||||
.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate_PrecomputedText_NoStyled_Greedy_Hyphenation() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final PrecomputedText text = makeMeasured(
|
||||
mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT,
|
||||
Layout.BREAK_STRATEGY_SIMPLE, Layout.HYPHENATION_FREQUENCY_NORMAL);
|
||||
state.resumeTiming();
|
||||
|
||||
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
|
||||
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
|
||||
.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate_PrecomputedText_NoStyled_Balanced_NoHyphenation() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final PrecomputedText text = makeMeasured(
|
||||
mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT,
|
||||
Layout.BREAK_STRATEGY_BALANCED, Layout.HYPHENATION_FREQUENCY_NONE);
|
||||
state.resumeTiming();
|
||||
|
||||
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
|
||||
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
|
||||
.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate_PrecomputedText_NoStyled_Balanced_Hyphenation() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final PrecomputedText text = makeMeasured(
|
||||
mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT,
|
||||
Layout.BREAK_STRATEGY_BALANCED, Layout.HYPHENATION_FREQUENCY_NORMAL);
|
||||
state.resumeTiming();
|
||||
|
||||
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
|
||||
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
|
||||
.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreate_PrecomputedText_Styled_Greedy_NoHyphenation() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final PrecomputedText text = makeMeasured(
|
||||
mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT,
|
||||
Layout.BREAK_STRATEGY_SIMPLE, Layout.HYPHENATION_FREQUENCY_NONE);
|
||||
state.resumeTiming();
|
||||
|
||||
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
|
||||
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
|
||||
.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDraw_FixedText_NoStyled() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
|
||||
final RenderNode node = RenderNode.create("benchmark", null);
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final StaticLayout layout =
|
||||
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
|
||||
final DisplayListCanvas c = node.start(1200, 200);
|
||||
state.resumeTiming();
|
||||
|
||||
layout.draw(c);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDraw_RandomText_Styled() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final RenderNode node = RenderNode.create("benchmark", null);
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
|
||||
final StaticLayout layout =
|
||||
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
|
||||
final DisplayListCanvas c = node.start(1200, 200);
|
||||
state.resumeTiming();
|
||||
|
||||
layout.draw(c);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDraw_RandomText_NoStyled() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final RenderNode node = RenderNode.create("benchmark", null);
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
|
||||
final StaticLayout layout =
|
||||
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
|
||||
final DisplayListCanvas c = node.start(1200, 200);
|
||||
state.resumeTiming();
|
||||
|
||||
layout.draw(c);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDraw_RandomText_Styled_WithoutCache() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final RenderNode node = RenderNode.create("benchmark", null);
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT);
|
||||
final StaticLayout layout =
|
||||
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
|
||||
final DisplayListCanvas c = node.start(1200, 200);
|
||||
Canvas.freeTextLayoutCaches();
|
||||
state.resumeTiming();
|
||||
|
||||
layout.draw(c);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDraw_RandomText_NoStyled_WithoutCache() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final RenderNode node = RenderNode.create("benchmark", null);
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
|
||||
final StaticLayout layout =
|
||||
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
|
||||
final DisplayListCanvas c = node.start(1200, 200);
|
||||
Canvas.freeTextLayoutCaches();
|
||||
state.resumeTiming();
|
||||
|
||||
layout.draw(c);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDraw_PrecomputedText_Styled() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final RenderNode node = RenderNode.create("benchmark", null);
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final PrecomputedText text = makeMeasured(
|
||||
mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT);
|
||||
final StaticLayout layout =
|
||||
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
|
||||
final DisplayListCanvas c = node.start(1200, 200);
|
||||
state.resumeTiming();
|
||||
|
||||
layout.draw(c);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDraw_PrecomputedText_NoStyled() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final RenderNode node = RenderNode.create("benchmark", null);
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final PrecomputedText text = makeMeasured(
|
||||
mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT);
|
||||
final StaticLayout layout =
|
||||
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
|
||||
final DisplayListCanvas c = node.start(1200, 200);
|
||||
state.resumeTiming();
|
||||
|
||||
layout.draw(c);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDraw_PrecomputedText_Styled_WithoutCache() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final RenderNode node = RenderNode.create("benchmark", null);
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final PrecomputedText text = makeMeasured(
|
||||
mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT);
|
||||
final StaticLayout layout =
|
||||
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
|
||||
final DisplayListCanvas c = node.start(1200, 200);
|
||||
Canvas.freeTextLayoutCaches();
|
||||
state.resumeTiming();
|
||||
|
||||
layout.draw(c);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDraw_PrecomputedText_NoStyled_WithoutCache() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
final RenderNode node = RenderNode.create("benchmark", null);
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final PrecomputedText text = makeMeasured(
|
||||
mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT);
|
||||
final StaticLayout layout =
|
||||
StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build();
|
||||
final DisplayListCanvas c = node.start(1200, 200);
|
||||
Canvas.freeTextLayoutCaches();
|
||||
state.resumeTiming();
|
||||
|
||||
layout.draw(c);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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.text;
|
||||
|
||||
import static android.text.TextDirectionHeuristics.LTR;
|
||||
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
|
||||
import android.support.test.filters.LargeTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Typeface;
|
||||
import android.text.Layout;
|
||||
import android.text.style.TextAppearanceSpan;
|
||||
import android.view.DisplayListCanvas;
|
||||
import android.view.RenderNode;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.nio.CharBuffer;
|
||||
import java.util.Random;
|
||||
|
||||
public class TextPerfUtils {
|
||||
|
||||
private static final int PARA_LENGTH = 500; // Number of characters in a paragraph.
|
||||
|
||||
private Random mRandom = new Random(0);
|
||||
|
||||
private static final String ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
|
||||
private static final int ALPHABET_LENGTH = ALPHABET.length();
|
||||
|
||||
private static final ColorStateList TEXT_COLOR = ColorStateList.valueOf(0x00000000);
|
||||
private static final String[] FAMILIES = { "sans-serif", "serif", "monospace" };
|
||||
private static final int[] STYLES = {
|
||||
Typeface.NORMAL, Typeface.BOLD, Typeface.ITALIC, Typeface.BOLD_ITALIC
|
||||
};
|
||||
|
||||
private final char[] mBuffer = new char[PARA_LENGTH];
|
||||
|
||||
public void resetRandom(long seed) {
|
||||
mRandom = new Random(seed);
|
||||
}
|
||||
|
||||
public CharSequence nextRandomParagraph(int wordLen, boolean applyRandomStyle) {
|
||||
for (int i = 0; i < PARA_LENGTH; i++) {
|
||||
if (i % (wordLen + 1) == wordLen) {
|
||||
mBuffer[i] = ' ';
|
||||
} else {
|
||||
mBuffer[i] = ALPHABET.charAt(mRandom.nextInt(ALPHABET_LENGTH));
|
||||
}
|
||||
}
|
||||
|
||||
CharSequence cs = CharBuffer.wrap(mBuffer);
|
||||
if (!applyRandomStyle) {
|
||||
return cs;
|
||||
}
|
||||
|
||||
SpannableStringBuilder ssb = new SpannableStringBuilder(cs);
|
||||
for (int i = 0; i < ssb.length(); i += wordLen + 1) {
|
||||
final int spanStart = i;
|
||||
final int spanEnd = (i + wordLen) > ssb.length() ? ssb.length() : i + wordLen;
|
||||
|
||||
final TextAppearanceSpan span = new TextAppearanceSpan(
|
||||
FAMILIES[mRandom.nextInt(FAMILIES.length)],
|
||||
STYLES[mRandom.nextInt(STYLES.length)],
|
||||
24 + mRandom.nextInt(32), // text size. min 24 max 56
|
||||
TEXT_COLOR, TEXT_COLOR);
|
||||
|
||||
ssb.setSpan(span, spanStart, spanEnd, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
return ssb;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
package android.text;
|
||||
|
||||
import static android.view.View.MeasureSpec.AT_MOST;
|
||||
import static android.view.View.MeasureSpec.UNSPECIFIED;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.filters.LargeTest;
|
||||
import android.text.NonEditableTextGenerator.TextType;
|
||||
import android.view.DisplayListCanvas;
|
||||
import android.view.RenderNode;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Performance test for {@link TextView} measure/draw.
|
||||
*/
|
||||
@LargeTest
|
||||
@RunWith(Parameterized.class)
|
||||
public class TextViewSetTextMeasurePerfTest {
|
||||
|
||||
private static final boolean[] BOOLEANS = new boolean[]{false, true};
|
||||
|
||||
@Rule
|
||||
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
|
||||
|
||||
@Parameterized.Parameters(name = "cached={3},{1}chars,{0}")
|
||||
public static Collection cases() {
|
||||
final List<Object[]> params = new ArrayList<>();
|
||||
for (int length : new int[]{128}) {
|
||||
for (boolean cached : BOOLEANS) {
|
||||
for (TextType textType : new TextType[]{TextType.STRING,
|
||||
TextType.SPANNABLE_BUILDER}) {
|
||||
params.add(new Object[]{textType.name(), length, textType, cached});
|
||||
}
|
||||
}
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
private final int mLineWidth;
|
||||
private final int mLength;
|
||||
private final TextType mTextType;
|
||||
private final boolean mCached;
|
||||
private final TextPaint mTextPaint;
|
||||
|
||||
public TextViewSetTextMeasurePerfTest(String label, int length, TextType textType,
|
||||
boolean cached) {
|
||||
mLength = length;
|
||||
mTextType = textType;
|
||||
mCached = cached;
|
||||
mTextPaint = new TextPaint();
|
||||
mTextPaint.setTextSize(10);
|
||||
mLineWidth = Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Measures the time to setText and measure for a {@link TextView}.
|
||||
*/
|
||||
@Test
|
||||
public void timeCreate() throws Exception {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
|
||||
state.pauseTiming();
|
||||
Canvas.freeTextLayoutCaches();
|
||||
final CharSequence text = createRandomText(mLength);
|
||||
final TextView textView = new TextView(InstrumentationRegistry.getTargetContext());
|
||||
textView.setText(text);
|
||||
state.resumeTiming();
|
||||
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
textView.setTextLocale(Locale.UK);
|
||||
textView.setTextLocale(Locale.US);
|
||||
if (!mCached) Canvas.freeTextLayoutCaches();
|
||||
state.resumeTiming();
|
||||
|
||||
textView.setText(text);
|
||||
textView.measure(AT_MOST | mLineWidth, UNSPECIFIED);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Measures the time to draw for a {@link TextView}.
|
||||
*/
|
||||
@Test
|
||||
public void timeDraw() throws Exception {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
|
||||
state.pauseTiming();
|
||||
Canvas.freeTextLayoutCaches();
|
||||
final RenderNode node = RenderNode.create("benchmark", null);
|
||||
final CharSequence text = createRandomText(mLength);
|
||||
final TextView textView = new TextView(InstrumentationRegistry.getTargetContext());
|
||||
textView.setText(text);
|
||||
state.resumeTiming();
|
||||
|
||||
while (state.keepRunning()) {
|
||||
|
||||
state.pauseTiming();
|
||||
final DisplayListCanvas canvas = node.start(1200, 200);
|
||||
int save = canvas.save();
|
||||
textView.setTextLocale(Locale.UK);
|
||||
textView.setTextLocale(Locale.US);
|
||||
if (!mCached) Canvas.freeTextLayoutCaches();
|
||||
state.resumeTiming();
|
||||
|
||||
textView.draw(canvas);
|
||||
|
||||
state.pauseTiming();
|
||||
canvas.restoreToCount(save);
|
||||
node.end(canvas);
|
||||
state.resumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
private CharSequence createRandomText(int length) {
|
||||
return new NonEditableTextGenerator(new Random(0))
|
||||
.setSequenceLength(length)
|
||||
.setCreateBoring(false)
|
||||
.setTextType(mTextType)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -16,31 +16,26 @@
|
||||
|
||||
package android.widget;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
import android.perftests.utils.StubActivity;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.filters.LargeTest;
|
||||
import android.support.test.rule.ActivityTestRule;
|
||||
import android.text.Selection;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View.MeasureSpec;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.filters.LargeTest;
|
||||
import android.support.test.rule.ActivityTestRule;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
@LargeTest
|
||||
@RunWith(Parameterized.class)
|
||||
|
||||
@@ -16,31 +16,26 @@
|
||||
|
||||
package android.widget;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
import android.perftests.utils.StubActivity;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.filters.LargeTest;
|
||||
import android.support.test.rule.ActivityTestRule;
|
||||
import android.text.Selection;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View.MeasureSpec;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.filters.LargeTest;
|
||||
import android.support.test.rule.ActivityTestRule;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
@LargeTest
|
||||
@RunWith(Parameterized.class)
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License
|
||||
*/
|
||||
|
||||
package android.widget;
|
||||
|
||||
import static android.view.View.MeasureSpec.AT_MOST;
|
||||
import static android.view.View.MeasureSpec.EXACTLY;
|
||||
import static android.view.View.MeasureSpec.UNSPECIFIED;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.Typeface;
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.filters.LargeTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.style.TextAppearanceSpan;
|
||||
import android.view.LayoutInflater;
|
||||
|
||||
import com.android.perftests.core.R;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.Rule;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@LargeTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class TextViewOnMeasurePerfTest {
|
||||
private static final String MULTILINE_TEXT =
|
||||
"Lorem ipsum dolor sit amet, \n"+
|
||||
"consectetur adipiscing elit, \n" +
|
||||
"sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. \n" +
|
||||
"Ut enim ad minim veniam, \n" +
|
||||
"quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\n" +
|
||||
"Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat " +
|
||||
"nulla pariatur.\n" +
|
||||
"Excepteur sint occaecat cupidatat non proident, \n" +
|
||||
"sunt in culpa qui officia deserunt mollit anim id est laborum.\n";
|
||||
|
||||
private static final int VIEW_WIDTH = 1000;
|
||||
private static final int VIEW_HEIGHT = 1000;
|
||||
private static final CharSequence COMPLEX_MULTILINE_TEXT;
|
||||
static {
|
||||
final SpannableStringBuilder ssb = new SpannableStringBuilder();
|
||||
|
||||
// To emphasize, append multiline text 10 times.
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
ssb.append(MULTILINE_TEXT);
|
||||
}
|
||||
|
||||
final ColorStateList[] COLORS = {
|
||||
ColorStateList.valueOf(0xFFFF0000), // RED
|
||||
ColorStateList.valueOf(0xFF00FF00), // GREEN
|
||||
ColorStateList.valueOf(0xFF0000FF), // BLUE
|
||||
};
|
||||
|
||||
final int[] STYLES = {
|
||||
Typeface.NORMAL, Typeface.BOLD, Typeface.ITALIC, Typeface.BOLD_ITALIC
|
||||
};
|
||||
|
||||
final String[] FAMILIES = { "sans-serif", "serif", "monospace" };
|
||||
|
||||
// Append random span to text.
|
||||
final Random random = new Random(0);
|
||||
for (int pos = 0; pos < ssb.length();) {
|
||||
final TextAppearanceSpan span = new TextAppearanceSpan(
|
||||
FAMILIES[random.nextInt(FAMILIES.length)],
|
||||
STYLES[random.nextInt(STYLES.length)],
|
||||
24 + random.nextInt(32), // text size. minimum 24
|
||||
COLORS[random.nextInt(COLORS.length)],
|
||||
COLORS[random.nextInt(COLORS.length)]);
|
||||
int spanLength = 1 + random.nextInt(9); // Up to 9 span length.
|
||||
if (pos + spanLength > ssb.length()) {
|
||||
spanLength = ssb.length() - pos;
|
||||
}
|
||||
ssb.setSpan(span, pos, pos + spanLength, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
|
||||
pos += spanLength;
|
||||
}
|
||||
COMPLEX_MULTILINE_TEXT = ssb;
|
||||
}
|
||||
|
||||
@Rule
|
||||
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
|
||||
|
||||
@Test
|
||||
public void testMeasure_AtMost() throws Throwable {
|
||||
final Context context = InstrumentationRegistry.getTargetContext();
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
|
||||
final TextView textView = new TextView(context);
|
||||
textView.setText(COMPLEX_MULTILINE_TEXT);
|
||||
|
||||
while (state.keepRunning()) {
|
||||
// Changing locale to invalidate internal layout.
|
||||
textView.setTextLocale(Locale.UK);
|
||||
textView.setTextLocale(Locale.US);
|
||||
|
||||
textView.measure(AT_MOST | VIEW_WIDTH, AT_MOST | VIEW_HEIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMeasure_Exactly() throws Throwable {
|
||||
final Context context = InstrumentationRegistry.getTargetContext();
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
|
||||
final TextView textView = new TextView(context);
|
||||
textView.setText(COMPLEX_MULTILINE_TEXT);
|
||||
|
||||
while (state.keepRunning()) {
|
||||
// Changing locale to invalidate internal layout.
|
||||
textView.setTextLocale(Locale.UK);
|
||||
textView.setTextLocale(Locale.US);
|
||||
|
||||
textView.measure(EXACTLY | VIEW_WIDTH, EXACTLY | VIEW_HEIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMeasure_Unspecified() throws Throwable {
|
||||
final Context context = InstrumentationRegistry.getTargetContext();
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
|
||||
final TextView textView = new TextView(context);
|
||||
textView.setText(COMPLEX_MULTILINE_TEXT);
|
||||
|
||||
while (state.keepRunning()) {
|
||||
// Changing locale to invalidate internal layout.
|
||||
textView.setTextLocale(Locale.UK);
|
||||
textView.setTextLocale(Locale.US);
|
||||
|
||||
textView.measure(UNSPECIFIED, UNSPECIFIED);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,455 @@
|
||||
/*
|
||||
* 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.widget;
|
||||
|
||||
import static android.view.View.MeasureSpec.AT_MOST;
|
||||
import static android.view.View.MeasureSpec.EXACTLY;
|
||||
import static android.view.View.MeasureSpec.UNSPECIFIED;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.Canvas;
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.filters.LargeTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.text.PrecomputedText;
|
||||
import android.text.Layout;
|
||||
import android.text.BoringLayout;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextPaint;
|
||||
import android.text.style.TextAppearanceSpan;
|
||||
import android.view.LayoutInflater;
|
||||
import android.text.TextPerfUtils;
|
||||
import android.view.View.MeasureSpec;
|
||||
import android.view.DisplayListCanvas;
|
||||
import android.view.RenderNode;
|
||||
|
||||
import com.android.perftests.core.R;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.Rule;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import static android.widget.TextView.UNKNOWN_BORING;
|
||||
|
||||
@LargeTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class TextViewPrecomputedTextPerfTest {
|
||||
private static final int WORD_LENGTH = 9; // Random word has 9 characters.
|
||||
private static final int WORDS_IN_LINE = 8; // Roughly, 8 words in a line.
|
||||
private static final boolean NO_STYLE_TEXT = false;
|
||||
private static final boolean STYLE_TEXT = true;
|
||||
|
||||
private static TextPaint PAINT = new TextPaint();
|
||||
private static final int TEXT_WIDTH = WORDS_IN_LINE * WORD_LENGTH * (int) PAINT.getTextSize();
|
||||
|
||||
public TextViewPrecomputedTextPerfTest() {}
|
||||
|
||||
private static class TestableTextView extends TextView {
|
||||
public TestableTextView(Context ctx) {
|
||||
super(ctx);
|
||||
}
|
||||
|
||||
public void onMeasure(int w, int h) {
|
||||
super.onMeasure(w, h);
|
||||
}
|
||||
|
||||
public void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
@Rule
|
||||
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
|
||||
|
||||
private TextPerfUtils mTextUtil = new TextPerfUtils();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mTextUtil.resetRandom(0 /* seed */);
|
||||
}
|
||||
|
||||
private static Context getContext() {
|
||||
return InstrumentationRegistry.getTargetContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNewLayout_RandomText() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
BoringLayout.Metrics metrics = new BoringLayout.Metrics();
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
|
||||
final TextView textView = new TextView(getContext());
|
||||
textView.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED);
|
||||
textView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL);
|
||||
textView.setText(text);
|
||||
Canvas.freeTextLayoutCaches();
|
||||
state.resumeTiming();
|
||||
|
||||
textView.makeNewLayout(TEXT_WIDTH, TEXT_WIDTH, UNKNOWN_BORING, UNKNOWN_BORING,
|
||||
TEXT_WIDTH, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNewLayout_RandomText_Selectable() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
BoringLayout.Metrics metrics = new BoringLayout.Metrics();
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
|
||||
final TextView textView = new TextView(getContext());
|
||||
textView.setTextIsSelectable(true);
|
||||
textView.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED);
|
||||
textView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL);
|
||||
textView.setText(text);
|
||||
Canvas.freeTextLayoutCaches();
|
||||
state.resumeTiming();
|
||||
|
||||
textView.makeNewLayout(TEXT_WIDTH, TEXT_WIDTH, UNKNOWN_BORING, UNKNOWN_BORING,
|
||||
TEXT_WIDTH, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNewLayout_PrecomputedText() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
BoringLayout.Metrics metrics = new BoringLayout.Metrics();
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final PrecomputedText.Params params = new PrecomputedText.Params.Builder(PAINT)
|
||||
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
|
||||
.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED).build();
|
||||
final CharSequence text = PrecomputedText.create(
|
||||
mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), params);
|
||||
final TextView textView = new TextView(getContext());
|
||||
textView.setTextMetricsParams(params);
|
||||
textView.setText(text);
|
||||
Canvas.freeTextLayoutCaches();
|
||||
state.resumeTiming();
|
||||
|
||||
textView.makeNewLayout(TEXT_WIDTH, TEXT_WIDTH, UNKNOWN_BORING, UNKNOWN_BORING,
|
||||
TEXT_WIDTH, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNewLayout_PrecomputedText_Selectable() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
BoringLayout.Metrics metrics = new BoringLayout.Metrics();
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final PrecomputedText.Params params = new PrecomputedText.Params.Builder(PAINT)
|
||||
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
|
||||
.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED).build();
|
||||
final CharSequence text = PrecomputedText.create(
|
||||
mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), params);
|
||||
final TextView textView = new TextView(getContext());
|
||||
textView.setTextIsSelectable(true);
|
||||
textView.setTextMetricsParams(params);
|
||||
textView.setText(text);
|
||||
Canvas.freeTextLayoutCaches();
|
||||
state.resumeTiming();
|
||||
|
||||
textView.makeNewLayout(TEXT_WIDTH, TEXT_WIDTH, UNKNOWN_BORING, UNKNOWN_BORING,
|
||||
TEXT_WIDTH, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetText_RandomText() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
BoringLayout.Metrics metrics = new BoringLayout.Metrics();
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
|
||||
final TextView textView = new TextView(getContext());
|
||||
textView.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED);
|
||||
textView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL);
|
||||
Canvas.freeTextLayoutCaches();
|
||||
state.resumeTiming();
|
||||
|
||||
textView.setText(text);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetText_RandomText_Selectable() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
BoringLayout.Metrics metrics = new BoringLayout.Metrics();
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
|
||||
final TextView textView = new TextView(getContext());
|
||||
textView.setTextIsSelectable(true);
|
||||
textView.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED);
|
||||
textView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL);
|
||||
Canvas.freeTextLayoutCaches();
|
||||
state.resumeTiming();
|
||||
|
||||
textView.setText(text);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetText_PrecomputedText() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
BoringLayout.Metrics metrics = new BoringLayout.Metrics();
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final PrecomputedText.Params params = new PrecomputedText.Params.Builder(PAINT)
|
||||
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
|
||||
.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED).build();
|
||||
final CharSequence text = PrecomputedText.create(
|
||||
mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), params);
|
||||
final TextView textView = new TextView(getContext());
|
||||
textView.setTextMetricsParams(params);
|
||||
Canvas.freeTextLayoutCaches();
|
||||
state.resumeTiming();
|
||||
|
||||
textView.setText(text);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetText_PrecomputedText_Selectable() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
BoringLayout.Metrics metrics = new BoringLayout.Metrics();
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final PrecomputedText.Params params = new PrecomputedText.Params.Builder(PAINT)
|
||||
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
|
||||
.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED).build();
|
||||
final CharSequence text = PrecomputedText.create(
|
||||
mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), params);
|
||||
final TextView textView = new TextView(getContext());
|
||||
textView.setTextIsSelectable(true);
|
||||
textView.setTextMetricsParams(params);
|
||||
Canvas.freeTextLayoutCaches();
|
||||
state.resumeTiming();
|
||||
|
||||
textView.setText(text);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnMeasure_RandomText() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
int width = MeasureSpec.makeMeasureSpec(TEXT_WIDTH, MeasureSpec.AT_MOST);
|
||||
int height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
|
||||
final TestableTextView textView = new TestableTextView(getContext());
|
||||
textView.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED);
|
||||
textView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL);
|
||||
textView.setText(text);
|
||||
textView.nullLayouts();
|
||||
Canvas.freeTextLayoutCaches();
|
||||
state.resumeTiming();
|
||||
|
||||
textView.onMeasure(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnMeasure_RandomText_Selectable() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
int width = MeasureSpec.makeMeasureSpec(TEXT_WIDTH, MeasureSpec.AT_MOST);
|
||||
int height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
|
||||
final TestableTextView textView = new TestableTextView(getContext());
|
||||
textView.setTextIsSelectable(true);
|
||||
textView.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED);
|
||||
textView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL);
|
||||
textView.setText(text);
|
||||
textView.nullLayouts();
|
||||
Canvas.freeTextLayoutCaches();
|
||||
state.resumeTiming();
|
||||
|
||||
textView.onMeasure(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnMeasure_PrecomputedText() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
int width = MeasureSpec.makeMeasureSpec(TEXT_WIDTH, MeasureSpec.AT_MOST);
|
||||
int height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final PrecomputedText.Params params = new PrecomputedText.Params.Builder(PAINT)
|
||||
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
|
||||
.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED).build();
|
||||
final CharSequence text = PrecomputedText.create(
|
||||
mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), params);
|
||||
final TestableTextView textView = new TestableTextView(getContext());
|
||||
textView.setTextMetricsParams(params);
|
||||
textView.setText(text);
|
||||
textView.nullLayouts();
|
||||
Canvas.freeTextLayoutCaches();
|
||||
state.resumeTiming();
|
||||
|
||||
textView.onMeasure(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnMeasure_PrecomputedText_Selectable() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
int width = MeasureSpec.makeMeasureSpec(TEXT_WIDTH, MeasureSpec.AT_MOST);
|
||||
int height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final PrecomputedText.Params params = new PrecomputedText.Params.Builder(PAINT)
|
||||
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
|
||||
.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED).build();
|
||||
final CharSequence text = PrecomputedText.create(
|
||||
mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), params);
|
||||
final TestableTextView textView = new TestableTextView(getContext());
|
||||
textView.setTextIsSelectable(true);
|
||||
textView.setTextMetricsParams(params);
|
||||
textView.setText(text);
|
||||
textView.nullLayouts();
|
||||
Canvas.freeTextLayoutCaches();
|
||||
state.resumeTiming();
|
||||
|
||||
textView.onMeasure(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnDraw_RandomText() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
int width = MeasureSpec.makeMeasureSpec(TEXT_WIDTH, MeasureSpec.AT_MOST);
|
||||
int height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
|
||||
final RenderNode node = RenderNode.create("benchmark", null);
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
|
||||
final TestableTextView textView = new TestableTextView(getContext());
|
||||
textView.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED);
|
||||
textView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL);
|
||||
textView.setText(text);
|
||||
textView.measure(width, height);
|
||||
textView.layout(0, 0, textView.getMeasuredWidth(), textView.getMeasuredHeight());
|
||||
final DisplayListCanvas c = node.start(
|
||||
textView.getMeasuredWidth(), textView.getMeasuredHeight());
|
||||
textView.nullLayouts();
|
||||
Canvas.freeTextLayoutCaches();
|
||||
state.resumeTiming();
|
||||
|
||||
textView.onDraw(c);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnDraw_RandomText_Selectable() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
int width = MeasureSpec.makeMeasureSpec(TEXT_WIDTH, MeasureSpec.AT_MOST);
|
||||
int height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
|
||||
final RenderNode node = RenderNode.create("benchmark", null);
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT);
|
||||
final TestableTextView textView = new TestableTextView(getContext());
|
||||
textView.setTextIsSelectable(true);
|
||||
textView.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED);
|
||||
textView.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL);
|
||||
textView.setText(text);
|
||||
textView.measure(width, height);
|
||||
textView.layout(0, 0, textView.getMeasuredWidth(), textView.getMeasuredHeight());
|
||||
final DisplayListCanvas c = node.start(
|
||||
textView.getMeasuredWidth(), textView.getMeasuredHeight());
|
||||
textView.nullLayouts();
|
||||
Canvas.freeTextLayoutCaches();
|
||||
state.resumeTiming();
|
||||
|
||||
textView.onDraw(c);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnDraw_PrecomputedText() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
int width = MeasureSpec.makeMeasureSpec(TEXT_WIDTH, MeasureSpec.AT_MOST);
|
||||
int height = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
|
||||
final RenderNode node = RenderNode.create("benchmark", null);
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final PrecomputedText.Params params = new PrecomputedText.Params.Builder(PAINT)
|
||||
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
|
||||
.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED).build();
|
||||
final CharSequence text = PrecomputedText.create(
|
||||
mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), params);
|
||||
final TestableTextView textView = new TestableTextView(getContext());
|
||||
textView.setTextMetricsParams(params);
|
||||
textView.setText(text);
|
||||
textView.measure(width, height);
|
||||
textView.layout(0, 0, textView.getMeasuredWidth(), textView.getMeasuredHeight());
|
||||
final DisplayListCanvas c = node.start(
|
||||
textView.getMeasuredWidth(), textView.getMeasuredHeight());
|
||||
textView.nullLayouts();
|
||||
Canvas.freeTextLayoutCaches();
|
||||
state.resumeTiming();
|
||||
|
||||
textView.onDraw(c);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnDraw_PrecomputedText_Selectable() {
|
||||
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
int width = MeasureSpec.makeMeasureSpec(MeasureSpec.AT_MOST, TEXT_WIDTH);
|
||||
int height = MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0);
|
||||
final RenderNode node = RenderNode.create("benchmark", null);
|
||||
while (state.keepRunning()) {
|
||||
state.pauseTiming();
|
||||
final PrecomputedText.Params params = new PrecomputedText.Params.Builder(PAINT)
|
||||
.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
|
||||
.setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED).build();
|
||||
final CharSequence text = PrecomputedText.create(
|
||||
mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), params);
|
||||
final TestableTextView textView = new TestableTextView(getContext());
|
||||
textView.setTextIsSelectable(true);
|
||||
textView.setTextMetricsParams(params);
|
||||
textView.setText(text);
|
||||
textView.measure(width, height);
|
||||
textView.layout(0, 0, textView.getMeasuredWidth(), textView.getMeasuredHeight());
|
||||
final DisplayListCanvas c = node.start(
|
||||
textView.getMeasuredWidth(), textView.getMeasuredHeight());
|
||||
textView.nullLayouts();
|
||||
Canvas.freeTextLayoutCaches();
|
||||
state.resumeTiming();
|
||||
|
||||
textView.onDraw(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,27 +16,21 @@
|
||||
|
||||
package android.widget;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
import android.util.Log;
|
||||
|
||||
import android.perftests.utils.BenchmarkState;
|
||||
import android.perftests.utils.PerfStatusReporter;
|
||||
import android.perftests.utils.StubActivity;
|
||||
import android.support.test.filters.LargeTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.support.test.rule.ActivityTestRule;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Collection;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Locale;
|
||||
|
||||
@LargeTest
|
||||
@RunWith(Parameterized.class)
|
||||
|
||||
@@ -20,11 +20,14 @@ LOCAL_MODULE_TAGS := tests
|
||||
LOCAL_SRC_FILES := $(call all-java-files-under, src)
|
||||
|
||||
LOCAL_STATIC_JAVA_LIBRARIES := \
|
||||
android-support-test
|
||||
android-support-test \
|
||||
ub-uiautomator
|
||||
|
||||
LOCAL_PACKAGE_NAME := MultiUserPerfTests
|
||||
LOCAL_PRIVATE_PLATFORM_APIS := true
|
||||
|
||||
LOCAL_COMPATIBILITY_SUITE += device-tests
|
||||
|
||||
LOCAL_CERTIFICATE := platform
|
||||
|
||||
include $(BUILD_PACKAGE)
|
||||
|
||||
28
apct-tests/perftests/multiuser/AndroidTest.xml
Normal file
28
apct-tests/perftests/multiuser/AndroidTest.xml
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
<configuration description="Runs MultiUserPerfTests metric instrumentation.">
|
||||
<option name="test-suite-tag" value="apct" />
|
||||
<option name="test-suite-tag" value="apct-metric-instrumentation" />
|
||||
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
|
||||
<option name="cleanup-apks" value="true" />
|
||||
<option name="test-file-name" value="MultiUserPerfTests.apk" />
|
||||
</target_preparer>
|
||||
|
||||
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
|
||||
<option name="package" value="com.android.perftests.multiuser" />
|
||||
<option name="hidden-api-checks" value="false"/>
|
||||
</test>
|
||||
</configuration>
|
||||
@@ -17,13 +17,16 @@ package android.multiuser;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.SystemClock;
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
import android.support.test.uiautomator.UiDevice;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
// Based on //platform/frameworks/base/apct-tests/perftests/utils/BenchmarkState.java
|
||||
public class BenchmarkRunner {
|
||||
|
||||
private static long COOL_OFF_PERIOD_MS = 2000;
|
||||
private static final long COOL_OFF_PERIOD_MS = 1000;
|
||||
|
||||
private static final int NUM_ITERATIONS = 4;
|
||||
|
||||
@@ -70,9 +73,13 @@ public class BenchmarkRunner {
|
||||
}
|
||||
|
||||
private void prepareForNextRun() {
|
||||
// TODO: Once http://b/63115387 is fixed, look into using "am wait-for-broadcast-idle"
|
||||
// command instead of waiting for a fixed amount of time.
|
||||
SystemClock.sleep(COOL_OFF_PERIOD_MS);
|
||||
try {
|
||||
UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
||||
.executeShellCommand("am wait-for-broadcast-idle");
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Cannot execute shell command", e);
|
||||
}
|
||||
mStartTimeNs = System.nanoTime();
|
||||
mPausedDurationNs = 0;
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
@@ -78,10 +77,7 @@ public final class BenchmarkState {
|
||||
|
||||
// Statistics. These values will be filled when the benchmark has finished.
|
||||
// The computation needs double precision, but long int is fine for final reporting.
|
||||
private long mMedian = 0;
|
||||
private double mMean = 0.0;
|
||||
private double mStandardDeviation = 0.0;
|
||||
private long mMin = 0;
|
||||
private Stats mStats;
|
||||
|
||||
// Individual duration in nano seconds.
|
||||
private ArrayList<Long> mResults = new ArrayList<>();
|
||||
@@ -90,36 +86,6 @@ public final class BenchmarkState {
|
||||
return TimeUnit.MILLISECONDS.toNanos(ms);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates statistics.
|
||||
*/
|
||||
private void calculateSatistics() {
|
||||
final int size = mResults.size();
|
||||
if (size <= 1) {
|
||||
throw new IllegalStateException("At least two results are necessary.");
|
||||
}
|
||||
|
||||
Collections.sort(mResults);
|
||||
mMedian = size % 2 == 0 ? (mResults.get(size / 2) + mResults.get(size / 2 + 1)) / 2 :
|
||||
mResults.get(size / 2);
|
||||
|
||||
mMin = mResults.get(0);
|
||||
for (int i = 0; i < size; ++i) {
|
||||
long result = mResults.get(i);
|
||||
mMean += result;
|
||||
if (result < mMin) {
|
||||
mMin = result;
|
||||
}
|
||||
}
|
||||
mMean /= (double) size;
|
||||
|
||||
for (int i = 0; i < size; ++i) {
|
||||
final double tmp = mResults.get(i) - mMean;
|
||||
mStandardDeviation += tmp * tmp;
|
||||
}
|
||||
mStandardDeviation = Math.sqrt(mStandardDeviation / (double) (size - 1));
|
||||
}
|
||||
|
||||
// Stops the benchmark timer.
|
||||
// This method can be called only when the timer is running.
|
||||
public void pauseTiming() {
|
||||
@@ -173,7 +139,7 @@ public final class BenchmarkState {
|
||||
if (ENABLE_PROFILING) {
|
||||
Debug.stopMethodTracing();
|
||||
}
|
||||
calculateSatistics();
|
||||
mStats = new Stats(mResults);
|
||||
mState = FINISHED;
|
||||
return false;
|
||||
}
|
||||
@@ -224,28 +190,28 @@ public final class BenchmarkState {
|
||||
if (mState != FINISHED) {
|
||||
throw new IllegalStateException("The benchmark hasn't finished");
|
||||
}
|
||||
return (long) mMean;
|
||||
return (long) mStats.getMean();
|
||||
}
|
||||
|
||||
private long median() {
|
||||
if (mState != FINISHED) {
|
||||
throw new IllegalStateException("The benchmark hasn't finished");
|
||||
}
|
||||
return mMedian;
|
||||
return mStats.getMedian();
|
||||
}
|
||||
|
||||
private long min() {
|
||||
if (mState != FINISHED) {
|
||||
throw new IllegalStateException("The benchmark hasn't finished");
|
||||
}
|
||||
return mMin;
|
||||
return mStats.getMin();
|
||||
}
|
||||
|
||||
private long standardDeviation() {
|
||||
if (mState != FINISHED) {
|
||||
throw new IllegalStateException("The benchmark hasn't finished");
|
||||
}
|
||||
return (long) mStandardDeviation;
|
||||
return (long) mStats.getStandardDeviation();
|
||||
}
|
||||
|
||||
private String summaryLine() {
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* 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.perftests.utils;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Instrumentation;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Provides a benchmark framework.
|
||||
*
|
||||
* This differs from BenchmarkState in that rather than the class measuring the the elapsed time,
|
||||
* the test passes in the elapsed time.
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* public void sampleMethod() {
|
||||
* ManualBenchmarkState state = new ManualBenchmarkState();
|
||||
*
|
||||
* int[] src = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
|
||||
* long elapsedTime = 0;
|
||||
* while (state.keepRunning(elapsedTime)) {
|
||||
* long startTime = System.nanoTime();
|
||||
* int[] dest = new int[src.length];
|
||||
* System.arraycopy(src, 0, dest, 0, src.length);
|
||||
* elapsedTime = System.nanoTime() - startTime;
|
||||
* }
|
||||
* System.out.println(state.summaryLine());
|
||||
* }
|
||||
*
|
||||
* Or use the PerfManualStatusReporter TestRule.
|
||||
*
|
||||
* Make sure that the overhead of checking the clock does not noticeably affect the results.
|
||||
*/
|
||||
public final class ManualBenchmarkState {
|
||||
private static final String TAG = ManualBenchmarkState.class.getSimpleName();
|
||||
|
||||
// TODO: Tune these values.
|
||||
// warm-up for duration
|
||||
private static final long WARMUP_DURATION_NS = TimeUnit.SECONDS.toNanos(5);
|
||||
// minimum iterations to warm-up for
|
||||
private static final int WARMUP_MIN_ITERATIONS = 8;
|
||||
|
||||
// target testing for duration
|
||||
private static final long TARGET_TEST_DURATION_NS = TimeUnit.SECONDS.toNanos(16);
|
||||
private static final int MAX_TEST_ITERATIONS = 1000000;
|
||||
private static final int MIN_TEST_ITERATIONS = 10;
|
||||
|
||||
private static final int NOT_STARTED = 0; // The benchmark has not started yet.
|
||||
private static final int WARMUP = 1; // The benchmark is warming up.
|
||||
private static final int RUNNING = 2; // The benchmark is running.
|
||||
private static final int FINISHED = 3; // The benchmark has stopped.
|
||||
|
||||
private int mState = NOT_STARTED; // Current benchmark state.
|
||||
|
||||
private long mWarmupStartTime = 0;
|
||||
private int mWarmupIterations = 0;
|
||||
|
||||
private int mMaxIterations = 0;
|
||||
|
||||
// Individual duration in nano seconds.
|
||||
private ArrayList<Long> mResults = new ArrayList<>();
|
||||
|
||||
// Statistics. These values will be filled when the benchmark has finished.
|
||||
// The computation needs double precision, but long int is fine for final reporting.
|
||||
private Stats mStats;
|
||||
|
||||
private void beginBenchmark(long warmupDuration, int iterations) {
|
||||
mMaxIterations = (int) (TARGET_TEST_DURATION_NS / (warmupDuration / iterations));
|
||||
mMaxIterations = Math.min(MAX_TEST_ITERATIONS,
|
||||
Math.max(mMaxIterations, MIN_TEST_ITERATIONS));
|
||||
mState = RUNNING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Judges whether the benchmark needs more samples.
|
||||
*
|
||||
* For the usage, see class comment.
|
||||
*/
|
||||
public boolean keepRunning(long duration) {
|
||||
if (duration < 0) {
|
||||
throw new RuntimeException("duration is negative: " + duration);
|
||||
}
|
||||
switch (mState) {
|
||||
case NOT_STARTED:
|
||||
mState = WARMUP;
|
||||
mWarmupStartTime = System.nanoTime();
|
||||
return true;
|
||||
case WARMUP: {
|
||||
final long timeSinceStartingWarmup = System.nanoTime() - mWarmupStartTime;
|
||||
++mWarmupIterations;
|
||||
if (mWarmupIterations >= WARMUP_MIN_ITERATIONS
|
||||
&& timeSinceStartingWarmup >= WARMUP_DURATION_NS) {
|
||||
beginBenchmark(timeSinceStartingWarmup, mWarmupIterations);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case RUNNING: {
|
||||
mResults.add(duration);
|
||||
final boolean keepRunning = mResults.size() < mMaxIterations;
|
||||
if (!keepRunning) {
|
||||
mStats = new Stats(mResults);
|
||||
mState = FINISHED;
|
||||
}
|
||||
return keepRunning;
|
||||
}
|
||||
case FINISHED:
|
||||
throw new IllegalStateException("The benchmark has finished.");
|
||||
default:
|
||||
throw new IllegalStateException("The benchmark is in an unknown state.");
|
||||
}
|
||||
}
|
||||
|
||||
private String summaryLine() {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("Summary: ");
|
||||
sb.append("median=").append(mStats.getMedian()).append("ns, ");
|
||||
sb.append("mean=").append(mStats.getMean()).append("ns, ");
|
||||
sb.append("min=").append(mStats.getMin()).append("ns, ");
|
||||
sb.append("max=").append(mStats.getMax()).append("ns, ");
|
||||
sb.append("sigma=").append(mStats.getStandardDeviation()).append(", ");
|
||||
sb.append("iteration=").append(mResults.size()).append(", ");
|
||||
sb.append("values=").append(mResults.toString());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public void sendFullStatusReport(Instrumentation instrumentation, String key) {
|
||||
if (mState != FINISHED) {
|
||||
throw new IllegalStateException("The benchmark hasn't finished");
|
||||
}
|
||||
Log.i(TAG, key + summaryLine());
|
||||
final Bundle status = new Bundle();
|
||||
status.putLong(key + "_median", mStats.getMedian());
|
||||
status.putLong(key + "_mean", (long) mStats.getMean());
|
||||
status.putLong(key + "_percentile90", mStats.getPercentile90());
|
||||
status.putLong(key + "_percentile95", mStats.getPercentile95());
|
||||
status.putLong(key + "_stddev", (long) mStats.getStandardDeviation());
|
||||
instrumentation.sendStatus(Activity.RESULT_OK, status);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.perftests.utils;
|
||||
|
||||
import android.support.test.InstrumentationRegistry;
|
||||
|
||||
import org.junit.rules.TestRule;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runners.model.Statement;
|
||||
|
||||
/**
|
||||
* Use this rule to make sure we report the status after the test success.
|
||||
*
|
||||
* <code>
|
||||
*
|
||||
* @Rule public PerfManualStatusReporter mPerfStatusReporter = new PerfManualStatusReporter();
|
||||
* @Test public void functionName() {
|
||||
* ManualBenchmarkState state = mPerfStatusReporter.getBenchmarkState();
|
||||
*
|
||||
* int[] src = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
|
||||
* long elapsedTime = 0;
|
||||
* while (state.keepRunning(elapsedTime)) {
|
||||
* long startTime = System.nanoTime();
|
||||
* int[] dest = new int[src.length];
|
||||
* System.arraycopy(src, 0, dest, 0, src.length);
|
||||
* elapsedTime = System.nanoTime() - startTime;
|
||||
* }
|
||||
* }
|
||||
* </code>
|
||||
*
|
||||
* When test succeeded, the status report will use the key as
|
||||
* "functionName_*"
|
||||
*/
|
||||
|
||||
public class PerfManualStatusReporter implements TestRule {
|
||||
private final ManualBenchmarkState mState;
|
||||
|
||||
public PerfManualStatusReporter() {
|
||||
mState = new ManualBenchmarkState();
|
||||
}
|
||||
|
||||
public ManualBenchmarkState getBenchmarkState() {
|
||||
return mState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement apply(Statement base, Description description) {
|
||||
return new Statement() {
|
||||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
base.evaluate();
|
||||
|
||||
mState.sendFullStatusReport(InstrumentationRegistry.getInstrumentation(),
|
||||
description.getMethodName());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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.perftests.utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class Stats {
|
||||
private long mMedian, mMin, mMax, mPercentile90, mPercentile95;
|
||||
private double mMean, mStandardDeviation;
|
||||
|
||||
/* Calculate stats in constructor. */
|
||||
public Stats(List<Long> values) {
|
||||
// make a copy since we're modifying it
|
||||
values = new ArrayList<>(values);
|
||||
final int size = values.size();
|
||||
if (size < 2) {
|
||||
throw new IllegalArgumentException("At least two results are necessary.");
|
||||
}
|
||||
|
||||
Collections.sort(values);
|
||||
|
||||
mMin = values.get(0);
|
||||
mMax = values.get(values.size() - 1);
|
||||
|
||||
mMedian = size % 2 == 0 ? (values.get(size / 2) + values.get(size / 2 - 1)) / 2 :
|
||||
values.get(size / 2);
|
||||
mPercentile90 = getPercentile(values, 90);
|
||||
mPercentile95 = getPercentile(values, 95);
|
||||
|
||||
for (int i = 0; i < size; ++i) {
|
||||
long result = values.get(i);
|
||||
mMean += result;
|
||||
}
|
||||
mMean /= (double) size;
|
||||
|
||||
for (int i = 0; i < size; ++i) {
|
||||
final double tmp = values.get(i) - mMean;
|
||||
mStandardDeviation += tmp * tmp;
|
||||
}
|
||||
mStandardDeviation = Math.sqrt(mStandardDeviation / (double) (size - 1));
|
||||
}
|
||||
|
||||
public double getMean() {
|
||||
return mMean;
|
||||
}
|
||||
|
||||
public long getMedian() {
|
||||
return mMedian;
|
||||
}
|
||||
|
||||
public long getMax() {
|
||||
return mMax;
|
||||
}
|
||||
|
||||
public long getMin() {
|
||||
return mMin;
|
||||
}
|
||||
|
||||
public double getStandardDeviation() {
|
||||
return mStandardDeviation;
|
||||
}
|
||||
|
||||
public long getPercentile90() {
|
||||
return mPercentile90;
|
||||
}
|
||||
|
||||
public long getPercentile95() {
|
||||
return mPercentile95;
|
||||
}
|
||||
|
||||
private static long getPercentile(List<Long> values, int percentile) {
|
||||
if (percentile < 0 || percentile > 100) {
|
||||
throw new IllegalArgumentException(
|
||||
"invalid percentile " + percentile + ", should be 0-100");
|
||||
}
|
||||
int idx = (values.size() - 1) * percentile / 100;
|
||||
return values.get(idx);
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,14 @@
|
||||
package android.perftests.utils;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
public class StubActivity extends Activity {
|
||||
}
|
||||
public static Intent createLaunchIntent(Context context) {
|
||||
final Intent intent = new Intent();
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.setClass(context, StubActivity.class);
|
||||
return intent;
|
||||
}
|
||||
}
|
||||
|
||||
3468
api/current.txt
3468
api/current.txt
File diff suppressed because it is too large
Load Diff
138
api/removed.txt
138
api/removed.txt
@@ -54,6 +54,54 @@ package android.app.admin {
|
||||
|
||||
}
|
||||
|
||||
package android.app.job {
|
||||
|
||||
public class JobInfo implements android.os.Parcelable {
|
||||
method public deprecated long getEstimatedNetworkBytes();
|
||||
}
|
||||
|
||||
public static final class JobInfo.Builder {
|
||||
method public deprecated android.app.job.JobInfo.Builder setEstimatedNetworkBytes(long);
|
||||
method public deprecated android.app.job.JobInfo.Builder setIsPrefetch(boolean);
|
||||
}
|
||||
|
||||
public final class JobWorkItem implements android.os.Parcelable {
|
||||
ctor public deprecated JobWorkItem(android.content.Intent, long);
|
||||
method public deprecated long getEstimatedNetworkBytes();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.app.slice {
|
||||
|
||||
public final class Slice implements android.os.Parcelable {
|
||||
field public static final deprecated java.lang.String EXTRA_SLIDER_VALUE = "android.app.slice.extra.SLIDER_VALUE";
|
||||
field public static final deprecated java.lang.String SUBTYPE_SLIDER = "slider";
|
||||
}
|
||||
|
||||
public static class Slice.Builder {
|
||||
ctor public deprecated Slice.Builder(android.net.Uri);
|
||||
method public deprecated android.app.slice.Slice.Builder addTimestamp(long, java.lang.String, java.util.List<java.lang.String>);
|
||||
method public deprecated android.app.slice.Slice.Builder setSpec(android.app.slice.SliceSpec);
|
||||
}
|
||||
|
||||
public final class SliceItem implements android.os.Parcelable {
|
||||
method public deprecated long getTimestamp();
|
||||
field public static final deprecated java.lang.String FORMAT_TIMESTAMP = "long";
|
||||
}
|
||||
|
||||
public class SliceManager {
|
||||
method public deprecated android.app.slice.Slice bindSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
|
||||
method public deprecated android.app.slice.Slice bindSlice(android.content.Intent, java.util.List<android.app.slice.SliceSpec>);
|
||||
method public deprecated void pinSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
|
||||
}
|
||||
|
||||
public abstract class SliceProvider extends android.content.ContentProvider {
|
||||
method public deprecated android.app.slice.Slice onBindSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.app.usage {
|
||||
|
||||
public final class StorageStats implements android.os.Parcelable {
|
||||
@@ -150,6 +198,34 @@ package android.graphics {
|
||||
public class Canvas {
|
||||
method public deprecated boolean clipRegion(android.graphics.Region, android.graphics.Region.Op);
|
||||
method public deprecated boolean clipRegion(android.graphics.Region);
|
||||
method public deprecated int save(int);
|
||||
field public static final deprecated int CLIP_SAVE_FLAG = 2; // 0x2
|
||||
field public static final deprecated int CLIP_TO_LAYER_SAVE_FLAG = 16; // 0x10
|
||||
field public static final deprecated int FULL_COLOR_LAYER_SAVE_FLAG = 8; // 0x8
|
||||
field public static final deprecated int HAS_ALPHA_LAYER_SAVE_FLAG = 4; // 0x4
|
||||
field public static final deprecated int MATRIX_SAVE_FLAG = 1; // 0x1
|
||||
}
|
||||
|
||||
public final class ImageDecoder implements java.lang.AutoCloseable {
|
||||
method public deprecated boolean getAsAlphaMask();
|
||||
method public deprecated boolean getConserveMemory();
|
||||
method public deprecated boolean getDecodeAsAlphaMask();
|
||||
method public deprecated boolean getMutable();
|
||||
method public deprecated boolean getRequireUnpremultiplied();
|
||||
method public deprecated android.graphics.ImageDecoder setAsAlphaMask(boolean);
|
||||
method public deprecated void setConserveMemory(boolean);
|
||||
method public deprecated android.graphics.ImageDecoder setDecodeAsAlphaMask(boolean);
|
||||
method public deprecated android.graphics.ImageDecoder setMutable(boolean);
|
||||
method public deprecated android.graphics.ImageDecoder setRequireUnpremultiplied(boolean);
|
||||
method public deprecated android.graphics.ImageDecoder setResize(int, int);
|
||||
method public deprecated android.graphics.ImageDecoder setResize(int);
|
||||
field public static final deprecated int ERROR_SOURCE_ERROR = 3; // 0x3
|
||||
field public static final deprecated int ERROR_SOURCE_EXCEPTION = 1; // 0x1
|
||||
field public static final deprecated int ERROR_SOURCE_INCOMPLETE = 2; // 0x2
|
||||
}
|
||||
|
||||
public static deprecated class ImageDecoder.IncompleteException extends java.io.IOException {
|
||||
ctor public ImageDecoder.IncompleteException();
|
||||
}
|
||||
|
||||
public deprecated class LayerRasterizer extends android.graphics.Rasterizer {
|
||||
@@ -173,6 +249,16 @@ package android.graphics {
|
||||
|
||||
}
|
||||
|
||||
package android.graphics.drawable {
|
||||
|
||||
public class AnimatedImageDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Animatable2 {
|
||||
method public deprecated int getLoopCount(int);
|
||||
method public deprecated void setLoopCount(int);
|
||||
field public static final deprecated int LOOP_INFINITE = -1; // 0xffffffff
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.hardware {
|
||||
|
||||
public final class HardwareBuffer implements java.lang.AutoCloseable android.os.Parcelable {
|
||||
@@ -235,23 +321,14 @@ package android.net {
|
||||
public static abstract class NetworkBadging.Badging implements java.lang.annotation.Annotation {
|
||||
}
|
||||
|
||||
public abstract class PskKeyManager implements javax.net.ssl.KeyManager {
|
||||
ctor public PskKeyManager();
|
||||
method public java.lang.String chooseClientKeyIdentity(java.lang.String, java.net.Socket);
|
||||
method public java.lang.String chooseClientKeyIdentity(java.lang.String, javax.net.ssl.SSLEngine);
|
||||
method public java.lang.String chooseServerKeyIdentityHint(java.net.Socket);
|
||||
method public java.lang.String chooseServerKeyIdentityHint(javax.net.ssl.SSLEngine);
|
||||
method public javax.crypto.SecretKey getKey(java.lang.String, java.lang.String, java.net.Socket);
|
||||
method public javax.crypto.SecretKey getKey(java.lang.String, java.lang.String, javax.net.ssl.SSLEngine);
|
||||
field public static final int MAX_IDENTITY_HINT_LENGTH_BYTES = 128; // 0x80
|
||||
field public static final int MAX_IDENTITY_LENGTH_BYTES = 128; // 0x80
|
||||
field public static final int MAX_KEY_LENGTH_BYTES = 256; // 0x100
|
||||
}
|
||||
|
||||
public class SSLCertificateSocketFactory extends javax.net.ssl.SSLSocketFactory {
|
||||
method public static deprecated org.apache.http.conn.ssl.SSLSocketFactory getHttpSocketFactory(int, android.net.SSLSessionCache);
|
||||
}
|
||||
|
||||
public class TrafficStats {
|
||||
method public static deprecated void setThreadStatsUidSelf();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.os {
|
||||
@@ -270,6 +347,20 @@ package android.os {
|
||||
ctor public RecoverySystem();
|
||||
}
|
||||
|
||||
public static final class StrictMode.ThreadPolicy.Builder {
|
||||
method public android.os.StrictMode.ThreadPolicy.Builder penaltyListener(android.os.StrictMode.OnThreadViolationListener, java.util.concurrent.Executor);
|
||||
}
|
||||
|
||||
public static final class StrictMode.VmPolicy.Builder {
|
||||
method public android.os.StrictMode.VmPolicy.Builder penaltyListener(android.os.StrictMode.OnVmViolationListener, java.util.concurrent.Executor);
|
||||
}
|
||||
|
||||
public final class SystemClock {
|
||||
method public static java.time.Clock elapsedRealtimeClock();
|
||||
method public static java.time.Clock uptimeClock();
|
||||
method public static deprecated java.time.Clock uptimeMillisClock();
|
||||
}
|
||||
|
||||
public class TestLooperManager {
|
||||
method public deprecated android.os.MessageQueue getQueue();
|
||||
}
|
||||
@@ -442,17 +533,14 @@ package android.speech.tts {
|
||||
|
||||
}
|
||||
|
||||
package android.test.mock {
|
||||
package android.telephony {
|
||||
|
||||
public class MockContext extends android.content.Context {
|
||||
method public android.content.SharedPreferences getSharedPreferences(java.io.File, int);
|
||||
method public java.io.File getSharedPreferencesPath(java.lang.String);
|
||||
public class NetworkScan {
|
||||
method public deprecated void stop() throws android.os.RemoteException;
|
||||
}
|
||||
|
||||
public deprecated class MockPackageManager extends android.content.pm.PackageManager {
|
||||
method public deprecated java.lang.String getDefaultBrowserPackageName(int);
|
||||
method public deprecated boolean setDefaultBrowserPackageName(java.lang.String, int);
|
||||
method public boolean setInstantAppCookie(byte[]);
|
||||
public class TelephonyManager {
|
||||
method public deprecated android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, android.telephony.TelephonyScanManager.NetworkScanCallback);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -519,14 +607,6 @@ package android.view {
|
||||
|
||||
}
|
||||
|
||||
package android.view.accessibility {
|
||||
|
||||
public final class AccessibilityWindowInfo implements android.os.Parcelable {
|
||||
method public boolean inPictureInPicture();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.webkit {
|
||||
|
||||
public class WebViewClient {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,5 @@
|
||||
package android.app {
|
||||
|
||||
public abstract deprecated class EphemeralResolverService extends android.app.InstantAppResolverService {
|
||||
ctor public EphemeralResolverService();
|
||||
method public android.os.Looper getLooper();
|
||||
method public abstract deprecated java.util.List<android.content.pm.EphemeralResolveInfo> onEphemeralResolveInfoList(int[], int);
|
||||
method public android.content.pm.EphemeralResolveInfo onGetEphemeralIntentFilter(java.lang.String);
|
||||
method public java.util.List<android.content.pm.EphemeralResolveInfo> onGetEphemeralResolveInfo(int[]);
|
||||
}
|
||||
|
||||
public class Notification implements android.os.Parcelable {
|
||||
method public static java.lang.Class<? extends android.app.Notification.Style> getNotificationStyleClass(java.lang.String);
|
||||
}
|
||||
@@ -31,10 +23,7 @@ package android.content {
|
||||
|
||||
public class Intent implements java.lang.Cloneable android.os.Parcelable {
|
||||
field public static final deprecated java.lang.String ACTION_DEVICE_INITIALIZATION_WIZARD = "android.intent.action.DEVICE_INITIALIZATION_WIZARD";
|
||||
field public static final deprecated java.lang.String ACTION_EPHEMERAL_RESOLVER_SETTINGS = "android.intent.action.EPHEMERAL_RESOLVER_SETTINGS";
|
||||
field public static final deprecated java.lang.String ACTION_INSTALL_EPHEMERAL_PACKAGE = "android.intent.action.INSTALL_EPHEMERAL_PACKAGE";
|
||||
field public static final deprecated java.lang.String ACTION_MASTER_CLEAR = "android.intent.action.MASTER_CLEAR";
|
||||
field public static final deprecated java.lang.String ACTION_RESOLVE_EPHEMERAL_PACKAGE = "android.intent.action.RESOLVE_EPHEMERAL_PACKAGE";
|
||||
field public static final deprecated java.lang.String ACTION_SERVICE_STATE = "android.intent.action.SERVICE_STATE";
|
||||
field public static final deprecated java.lang.String EXTRA_CDMA_DEFAULT_ROAMING_INDICATOR = "cdmaDefaultRoamingIndicator";
|
||||
field public static final deprecated java.lang.String EXTRA_CDMA_ROAMING_INDICATOR = "cdmaRoamingIndicator";
|
||||
@@ -62,51 +51,16 @@ package android.content {
|
||||
|
||||
}
|
||||
|
||||
package android.content.pm {
|
||||
|
||||
public final deprecated class EphemeralIntentFilter implements android.os.Parcelable {
|
||||
ctor public EphemeralIntentFilter(java.lang.String, java.util.List<android.content.IntentFilter>);
|
||||
method public int describeContents();
|
||||
method public java.util.List<android.content.IntentFilter> getFilters();
|
||||
method public java.lang.String getSplitName();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator<android.content.pm.EphemeralIntentFilter> CREATOR;
|
||||
}
|
||||
|
||||
public final deprecated class EphemeralResolveInfo implements android.os.Parcelable {
|
||||
ctor public deprecated EphemeralResolveInfo(android.net.Uri, java.lang.String, java.util.List<android.content.IntentFilter>);
|
||||
ctor public deprecated EphemeralResolveInfo(android.content.pm.EphemeralResolveInfo.EphemeralDigest, java.lang.String, java.util.List<android.content.pm.EphemeralIntentFilter>);
|
||||
ctor public EphemeralResolveInfo(android.content.pm.EphemeralResolveInfo.EphemeralDigest, java.lang.String, java.util.List<android.content.pm.EphemeralIntentFilter>, int);
|
||||
ctor public EphemeralResolveInfo(java.lang.String, java.lang.String, java.util.List<android.content.pm.EphemeralIntentFilter>);
|
||||
method public int describeContents();
|
||||
method public byte[] getDigestBytes();
|
||||
method public int getDigestPrefix();
|
||||
method public deprecated java.util.List<android.content.IntentFilter> getFilters();
|
||||
method public java.util.List<android.content.pm.EphemeralIntentFilter> getIntentFilters();
|
||||
method public java.lang.String getPackageName();
|
||||
method public int getVersionCode();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator<android.content.pm.EphemeralResolveInfo> CREATOR;
|
||||
field public static final java.lang.String SHA_ALGORITHM = "SHA-256";
|
||||
}
|
||||
|
||||
public static final class EphemeralResolveInfo.EphemeralDigest implements android.os.Parcelable {
|
||||
ctor public EphemeralResolveInfo.EphemeralDigest(java.lang.String);
|
||||
method public int describeContents();
|
||||
method public byte[][] getDigestBytes();
|
||||
method public int[] getDigestPrefix();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator<android.content.pm.EphemeralResolveInfo.EphemeralDigest> CREATOR;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.media.tv {
|
||||
|
||||
public final class TvInputManager {
|
||||
method public android.media.tv.TvInputManager.Hardware acquireTvInputHardware(int, android.media.tv.TvInputManager.HardwareCallback, android.media.tv.TvInputInfo);
|
||||
}
|
||||
|
||||
public static final class TvInputManager.Hardware {
|
||||
method public boolean dispatchKeyEventToHdmi(android.view.KeyEvent);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.net.wifi {
|
||||
@@ -122,29 +76,6 @@ package android.net.wifi {
|
||||
field public boolean untrusted;
|
||||
}
|
||||
|
||||
public class WifiConnectionStatistics implements android.os.Parcelable {
|
||||
ctor public WifiConnectionStatistics();
|
||||
ctor public WifiConnectionStatistics(android.net.wifi.WifiConnectionStatistics);
|
||||
method public int describeContents();
|
||||
method public void incrementOrAddUntrusted(java.lang.String, int, int);
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator<android.net.wifi.WifiConnectionStatistics> CREATOR;
|
||||
field public int num24GhzConnected;
|
||||
field public int num5GhzConnected;
|
||||
field public int numAutoJoinAttempt;
|
||||
field public int numAutoRoamAttempt;
|
||||
field public int numWifiManagerJoinAttempt;
|
||||
field public java.util.HashMap<java.lang.String, android.net.wifi.WifiNetworkConnectionStatistics> untrustedNetworkHistory;
|
||||
}
|
||||
|
||||
public class WifiManager {
|
||||
method public deprecated java.util.List<android.net.wifi.BatchedScanResult> getBatchedScanResults();
|
||||
method public android.net.wifi.WifiConnectionStatistics getConnectionStatistics();
|
||||
method public deprecated boolean isBatchedScanSupported();
|
||||
method public deprecated boolean setWifiApEnabled(android.net.wifi.WifiConfiguration, boolean);
|
||||
method public deprecated boolean startLocationRestrictedScan(android.os.WorkSource);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.os {
|
||||
@@ -160,6 +91,38 @@ package android.os {
|
||||
|
||||
}
|
||||
|
||||
package android.security.keystore.recovery {
|
||||
|
||||
public final class KeyChainSnapshot implements android.os.Parcelable {
|
||||
method public deprecated byte[] getTrustedHardwarePublicKey();
|
||||
}
|
||||
|
||||
public class RecoveryController {
|
||||
method public deprecated byte[] generateAndStoreKey(java.lang.String, byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
|
||||
method public deprecated java.security.Key generateKey(java.lang.String, byte[]) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.LockScreenRequiredException;
|
||||
method public deprecated java.util.List<java.lang.String> getAliases(java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
|
||||
method public deprecated android.security.keystore.recovery.KeyChainSnapshot getRecoveryData() throws android.security.keystore.recovery.InternalRecoveryServiceException;
|
||||
method public deprecated int getRecoveryStatus(java.lang.String, java.lang.String) throws android.security.keystore.recovery.InternalRecoveryServiceException;
|
||||
method public deprecated void initRecoveryService(java.lang.String, byte[]) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
|
||||
method public deprecated void setRecoveryStatus(java.lang.String, java.lang.String, int) throws android.security.keystore.recovery.InternalRecoveryServiceException, android.content.pm.PackageManager.NameNotFoundException;
|
||||
}
|
||||
|
||||
public class RecoverySession implements java.lang.AutoCloseable {
|
||||
method public deprecated java.util.Map<java.lang.String, byte[]> recoverKeys(byte[], java.util.List<android.security.keystore.recovery.WrappedApplicationKey>) throws android.security.keystore.recovery.DecryptionFailedException, android.security.keystore.recovery.InternalRecoveryServiceException, android.security.keystore.recovery.SessionExpiredException;
|
||||
method public deprecated byte[] start(byte[], byte[], byte[], java.util.List<android.security.keystore.recovery.KeyChainProtectionParams>) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
|
||||
method public deprecated byte[] start(java.security.cert.CertPath, byte[], byte[], java.util.List<android.security.keystore.recovery.KeyChainProtectionParams>) throws java.security.cert.CertificateException, android.security.keystore.recovery.InternalRecoveryServiceException;
|
||||
}
|
||||
|
||||
public final class WrappedApplicationKey implements android.os.Parcelable {
|
||||
method public deprecated byte[] getAccount();
|
||||
}
|
||||
|
||||
public static class WrappedApplicationKey.Builder {
|
||||
method public deprecated android.security.keystore.recovery.WrappedApplicationKey.Builder setAccount(byte[]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.service.notification {
|
||||
|
||||
public abstract class NotificationListenerService extends android.app.Service {
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
package android {
|
||||
|
||||
public static final class Manifest.permission {
|
||||
field public static final java.lang.String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING";
|
||||
field public static final java.lang.String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE";
|
||||
field public static final java.lang.String CONFIGURE_DISPLAY_BRIGHTNESS = "android.permission.CONFIGURE_DISPLAY_BRIGHTNESS";
|
||||
field public static final java.lang.String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.animation {
|
||||
|
||||
public class ValueAnimator extends android.animation.Animator {
|
||||
@@ -11,21 +22,103 @@ package android.app {
|
||||
|
||||
public class ActivityManager {
|
||||
method public void addOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener, int);
|
||||
method public void alwaysShowUnsupportedCompileSdkWarning(android.content.ComponentName);
|
||||
method public int getPackageImportance(java.lang.String);
|
||||
method public long getTotalRam();
|
||||
method public int getUidImportance(int);
|
||||
method public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener);
|
||||
method public void removeStacksInWindowingModes(int[]) throws java.lang.SecurityException;
|
||||
method public void removeStacksWithActivityTypes(int[]) throws java.lang.SecurityException;
|
||||
method public void resizeStack(int, android.graphics.Rect) throws java.lang.SecurityException;
|
||||
method public void setTaskWindowingMode(int, int, boolean) throws java.lang.SecurityException;
|
||||
method public void setTaskWindowingModeSplitScreenPrimary(int, int, boolean, boolean, android.graphics.Rect, boolean) throws java.lang.SecurityException;
|
||||
method public static boolean supportsMultiWindow(android.content.Context);
|
||||
method public static boolean supportsSplitScreenMultiWindow(android.content.Context);
|
||||
field public static final int SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT = 1; // 0x1
|
||||
field public static final int SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT = 0; // 0x0
|
||||
}
|
||||
|
||||
public static abstract interface ActivityManager.OnUidImportanceListener {
|
||||
method public abstract void onUidImportance(int, int);
|
||||
}
|
||||
|
||||
public static class ActivityManager.StackId {
|
||||
field public static final int INVALID_STACK_ID = -1; // 0xffffffff
|
||||
}
|
||||
|
||||
public static class ActivityManager.TaskDescription implements android.os.Parcelable {
|
||||
method public java.lang.String getIconFilename();
|
||||
method public int getIconResource();
|
||||
}
|
||||
|
||||
public class ActivityOptions {
|
||||
method public void setLaunchStackId(int);
|
||||
method public void setLaunchActivityType(int);
|
||||
method public void setLaunchTaskId(int);
|
||||
method public void setLaunchWindowingMode(int);
|
||||
method public void setTaskOverlay(boolean, boolean);
|
||||
}
|
||||
|
||||
public class AppOpsManager {
|
||||
method public static java.lang.String[] getOpStrs();
|
||||
method public boolean isOperationActive(int, int, java.lang.String);
|
||||
method public void setMode(int, int, java.lang.String, int);
|
||||
method public void startWatchingActive(int[], android.app.AppOpsManager.OnOpActiveChangedListener);
|
||||
method public void stopWatchingActive(android.app.AppOpsManager.OnOpActiveChangedListener);
|
||||
field public static final java.lang.String OPSTR_ACCEPT_HANDOVER = "android:accept_handover";
|
||||
field public static final java.lang.String OPSTR_ACCESS_NOTIFICATIONS = "android:access_notifications";
|
||||
field public static final java.lang.String OPSTR_ACTIVATE_VPN = "android:activate_vpn";
|
||||
field public static final java.lang.String OPSTR_ASSIST_SCREENSHOT = "android:assist_screenshot";
|
||||
field public static final java.lang.String OPSTR_ASSIST_STRUCTURE = "android:assist_structure";
|
||||
field public static final java.lang.String OPSTR_AUDIO_ACCESSIBILITY_VOLUME = "android:audio_accessibility_volume";
|
||||
field public static final java.lang.String OPSTR_AUDIO_ALARM_VOLUME = "android:audio_alarm_volume";
|
||||
field public static final java.lang.String OPSTR_AUDIO_BLUETOOTH_VOLUME = "android:audio_bluetooth_volume";
|
||||
field public static final java.lang.String OPSTR_AUDIO_MASTER_VOLUME = "android:audio_master_volume";
|
||||
field public static final java.lang.String OPSTR_AUDIO_MEDIA_VOLUME = "android:audio_media_volume";
|
||||
field public static final java.lang.String OPSTR_AUDIO_NOTIFICATION_VOLUME = "android:audio_notification_volume";
|
||||
field public static final java.lang.String OPSTR_AUDIO_RING_VOLUME = "android:audio_ring_volume";
|
||||
field public static final java.lang.String OPSTR_AUDIO_VOICE_VOLUME = "android:audio_voice_volume";
|
||||
field public static final java.lang.String OPSTR_BIND_ACCESSIBILITY_SERVICE = "android:bind_accessibility_service";
|
||||
field public static final java.lang.String OPSTR_CHANGE_WIFI_STATE = "android:change_wifi_state";
|
||||
field public static final java.lang.String OPSTR_GET_ACCOUNTS = "android:get_accounts";
|
||||
field public static final java.lang.String OPSTR_GPS = "android:gps";
|
||||
field public static final java.lang.String OPSTR_INSTANT_APP_START_FOREGROUND = "android:instant_app_start_foreground";
|
||||
field public static final java.lang.String OPSTR_MANAGE_IPSEC_TUNNELS = "android:manage_ipsec_tunnels";
|
||||
field public static final java.lang.String OPSTR_MUTE_MICROPHONE = "android:mute_microphone";
|
||||
field public static final java.lang.String OPSTR_NEIGHBORING_CELLS = "android:neighboring_cells";
|
||||
field public static final java.lang.String OPSTR_PLAY_AUDIO = "android:play_audio";
|
||||
field public static final java.lang.String OPSTR_POST_NOTIFICATION = "android:post_notification";
|
||||
field public static final java.lang.String OPSTR_PROJECT_MEDIA = "android:project_media";
|
||||
field public static final java.lang.String OPSTR_READ_CLIPBOARD = "android:read_clipboard";
|
||||
field public static final java.lang.String OPSTR_READ_ICC_SMS = "android:read_icc_sms";
|
||||
field public static final java.lang.String OPSTR_RECEIVE_EMERGENCY_BROADCAST = "android:receive_emergency_broadcast";
|
||||
field public static final java.lang.String OPSTR_REQUEST_DELETE_PACKAGES = "android:request_delete_packages";
|
||||
field public static final java.lang.String OPSTR_REQUEST_INSTALL_PACKAGES = "android:request_install_packages";
|
||||
field public static final java.lang.String OPSTR_RUN_ANY_IN_BACKGROUND = "android:run_any_in_background";
|
||||
field public static final java.lang.String OPSTR_RUN_IN_BACKGROUND = "android:run_in_background";
|
||||
field public static final java.lang.String OPSTR_START_FOREGROUND = "android:start_foreground";
|
||||
field public static final java.lang.String OPSTR_TAKE_AUDIO_FOCUS = "android:take_audio_focus";
|
||||
field public static final java.lang.String OPSTR_TAKE_MEDIA_BUTTONS = "android:take_media_buttons";
|
||||
field public static final java.lang.String OPSTR_TOAST_WINDOW = "android:toast_window";
|
||||
field public static final java.lang.String OPSTR_TURN_SCREEN_ON = "android:turn_screen_on";
|
||||
field public static final java.lang.String OPSTR_VIBRATE = "android:vibrate";
|
||||
field public static final java.lang.String OPSTR_WAKE_LOCK = "android:wake_lock";
|
||||
field public static final java.lang.String OPSTR_WIFI_SCAN = "android:wifi_scan";
|
||||
field public static final java.lang.String OPSTR_WRITE_CLIPBOARD = "android:write_clipboard";
|
||||
field public static final java.lang.String OPSTR_WRITE_ICC_SMS = "android:write_icc_sms";
|
||||
field public static final java.lang.String OPSTR_WRITE_SMS = "android:write_sms";
|
||||
field public static final java.lang.String OPSTR_WRITE_WALLPAPER = "android:write_wallpaper";
|
||||
field public static final int OP_RECORD_AUDIO = 27; // 0x1b
|
||||
field public static final int OP_SYSTEM_ALERT_WINDOW = 24; // 0x18
|
||||
}
|
||||
|
||||
public static abstract interface AppOpsManager.OnOpActiveChangedListener {
|
||||
method public abstract void onOpActiveChanged(int, int, java.lang.String, boolean);
|
||||
}
|
||||
|
||||
public final class NotificationChannelGroup implements android.os.Parcelable {
|
||||
method public void setBlocked(boolean);
|
||||
}
|
||||
|
||||
public class NotificationManager {
|
||||
method public android.content.ComponentName getEffectsSuppressor();
|
||||
}
|
||||
@@ -37,8 +130,8 @@ package android.app {
|
||||
public final class UiAutomation {
|
||||
method public void destroy();
|
||||
method public android.os.ParcelFileDescriptor[] executeShellCommandRw(java.lang.String);
|
||||
method public boolean grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
|
||||
method public boolean revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
|
||||
method public deprecated boolean grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
|
||||
method public deprecated boolean revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
|
||||
}
|
||||
|
||||
public class UiModeManager {
|
||||
@@ -46,6 +139,34 @@ package android.app {
|
||||
method public boolean isUiModeLocked();
|
||||
}
|
||||
|
||||
public class WindowConfiguration implements java.lang.Comparable android.os.Parcelable {
|
||||
ctor public WindowConfiguration();
|
||||
method public int compareTo(android.app.WindowConfiguration);
|
||||
method public int describeContents();
|
||||
method public int getActivityType();
|
||||
method public android.graphics.Rect getAppBounds();
|
||||
method public android.graphics.Rect getBounds();
|
||||
method public int getWindowingMode();
|
||||
method public void setActivityType(int);
|
||||
method public void setAppBounds(android.graphics.Rect);
|
||||
method public void setBounds(android.graphics.Rect);
|
||||
method public void setTo(android.app.WindowConfiguration);
|
||||
method public void setWindowingMode(int);
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final int ACTIVITY_TYPE_ASSISTANT = 4; // 0x4
|
||||
field public static final int ACTIVITY_TYPE_HOME = 2; // 0x2
|
||||
field public static final int ACTIVITY_TYPE_RECENTS = 3; // 0x3
|
||||
field public static final int ACTIVITY_TYPE_STANDARD = 1; // 0x1
|
||||
field public static final int ACTIVITY_TYPE_UNDEFINED = 0; // 0x0
|
||||
field public static final int WINDOWING_MODE_FREEFORM = 5; // 0x5
|
||||
field public static final int WINDOWING_MODE_FULLSCREEN = 1; // 0x1
|
||||
field public static final int WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY = 4; // 0x4
|
||||
field public static final int WINDOWING_MODE_PINNED = 2; // 0x2
|
||||
field public static final int WINDOWING_MODE_SPLIT_SCREEN_PRIMARY = 3; // 0x3
|
||||
field public static final int WINDOWING_MODE_SPLIT_SCREEN_SECONDARY = 4; // 0x4
|
||||
field public static final int WINDOWING_MODE_UNDEFINED = 0; // 0x0
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.app.admin {
|
||||
@@ -60,15 +181,36 @@ package android.app.admin {
|
||||
method public boolean isDeviceManaged();
|
||||
field public static final java.lang.String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_ALLOWED";
|
||||
field public static final java.lang.String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_DISALLOWED";
|
||||
field public static final java.lang.String ACTION_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED";
|
||||
field public static final java.lang.String EXTRA_RESTRICTION = "android.app.extra.RESTRICTION";
|
||||
}
|
||||
|
||||
public static final class SecurityLog.SecurityEvent implements android.os.Parcelable {
|
||||
ctor public SecurityLog.SecurityEvent(long, byte[]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.app.backup {
|
||||
|
||||
public class BackupManager {
|
||||
method public android.content.Intent getConfigurationIntent(java.lang.String);
|
||||
method public android.content.Intent getDataManagementIntent(java.lang.String);
|
||||
method public java.lang.String getDataManagementLabel(java.lang.String);
|
||||
method public java.lang.String getDestinationString(java.lang.String);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.app.usage {
|
||||
|
||||
public class NetworkStatsManager {
|
||||
method public void setPollForce(boolean);
|
||||
}
|
||||
|
||||
public class StorageStatsManager {
|
||||
method public boolean isQuotaSupported(java.util.UUID);
|
||||
method public boolean isReservedSupported(java.util.UUID);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -80,17 +222,20 @@ package android.content {
|
||||
}
|
||||
|
||||
public abstract class Context {
|
||||
method public abstract int getUserId();
|
||||
}
|
||||
|
||||
public class ContextWrapper extends android.content.Context {
|
||||
method public android.os.UserHandle getUser();
|
||||
method public int getUserId();
|
||||
method public void setAutofillCompatibilityEnabled(boolean);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.content.pm {
|
||||
|
||||
public class ActivityInfo extends android.content.pm.ComponentInfo implements android.os.Parcelable {
|
||||
method public static boolean isTranslucentOrFloating(android.content.res.TypedArray);
|
||||
field public static final int RESIZE_MODE_RESIZEABLE = 2; // 0x2
|
||||
}
|
||||
|
||||
public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
|
||||
method public boolean isPrivilegedApp();
|
||||
method public boolean isSystemApp();
|
||||
@@ -103,8 +248,26 @@ package android.content.pm {
|
||||
public abstract class PackageManager {
|
||||
method public abstract java.lang.String getDefaultBrowserPackageNameAsUser(int);
|
||||
method public abstract int getInstallReason(java.lang.String, android.os.UserHandle);
|
||||
method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
|
||||
method public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplicationsAsUser(int, int);
|
||||
method public abstract java.lang.String[] getNamesForUids(int[]);
|
||||
method public abstract java.lang.String getPermissionControllerPackageName();
|
||||
method public abstract java.lang.String getServicesSystemSharedLibraryPackageName();
|
||||
method public abstract java.lang.String getSharedSystemSharedLibraryPackageName();
|
||||
method public abstract boolean isPermissionReviewModeEnabled();
|
||||
field public static final java.lang.String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage";
|
||||
field public static final java.lang.String FEATURE_FILE_BASED_ENCRYPTION = "android.software.file_based_encryption";
|
||||
field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000
|
||||
field public static final int MATCH_KNOWN_PACKAGES = 4202496; // 0x402000
|
||||
}
|
||||
|
||||
public class PermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
|
||||
field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
|
||||
field public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 32768; // 0x8000
|
||||
}
|
||||
|
||||
public final class ShortcutInfo implements android.os.Parcelable {
|
||||
method public boolean isVisibleToPublisher();
|
||||
}
|
||||
|
||||
public class ShortcutManager {
|
||||
@@ -113,6 +276,14 @@ package android.content.pm {
|
||||
|
||||
}
|
||||
|
||||
package android.content.res {
|
||||
|
||||
public final class Configuration implements java.lang.Comparable android.os.Parcelable {
|
||||
field public final android.app.WindowConfiguration windowConfiguration;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.database.sqlite {
|
||||
|
||||
public final class SQLiteDirectCursorDriver implements android.database.sqlite.SQLiteCursorDriver {
|
||||
@@ -133,11 +304,20 @@ package android.database.sqlite {
|
||||
method public static int getWALAutoCheckpoint();
|
||||
method public static int getWALConnectionPoolSize();
|
||||
method public static java.lang.String getWALSyncMode();
|
||||
method public static boolean isCompatibilityWalSupported();
|
||||
method public static int releaseMemory();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.graphics {
|
||||
|
||||
public final class ImageDecoder implements java.lang.AutoCloseable {
|
||||
method public static android.graphics.ImageDecoder.Source createSource(android.content.res.Resources, java.io.InputStream, int);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.graphics.drawable {
|
||||
|
||||
public class AdaptiveIconDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Drawable.Callback {
|
||||
@@ -170,6 +350,59 @@ package android.hardware.camera2 {
|
||||
|
||||
}
|
||||
|
||||
package android.hardware.display {
|
||||
|
||||
public final class AmbientBrightnessDayStats implements android.os.Parcelable {
|
||||
method public int describeContents();
|
||||
method public float[] getBucketBoundaries();
|
||||
method public java.time.LocalDate getLocalDate();
|
||||
method public float[] getStats();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator<android.hardware.display.AmbientBrightnessDayStats> CREATOR;
|
||||
}
|
||||
|
||||
public final class BrightnessChangeEvent implements android.os.Parcelable {
|
||||
method public int describeContents();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessChangeEvent> CREATOR;
|
||||
field public final float batteryLevel;
|
||||
field public final float brightness;
|
||||
field public final int colorTemperature;
|
||||
field public final boolean isDefaultBrightnessConfig;
|
||||
field public final boolean isUserSetBrightness;
|
||||
field public final float lastBrightness;
|
||||
field public final long[] luxTimestamps;
|
||||
field public final float[] luxValues;
|
||||
field public final boolean nightMode;
|
||||
field public final java.lang.String packageName;
|
||||
field public final float powerBrightnessFactor;
|
||||
field public final long timeStamp;
|
||||
}
|
||||
|
||||
public final class BrightnessConfiguration implements android.os.Parcelable {
|
||||
method public int describeContents();
|
||||
method public android.util.Pair<float[], float[]> getCurve();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessConfiguration> CREATOR;
|
||||
}
|
||||
|
||||
public static class BrightnessConfiguration.Builder {
|
||||
ctor public BrightnessConfiguration.Builder(float[], float[]);
|
||||
method public android.hardware.display.BrightnessConfiguration build();
|
||||
method public android.hardware.display.BrightnessConfiguration.Builder setDescription(java.lang.String);
|
||||
}
|
||||
|
||||
public final class DisplayManager {
|
||||
method public java.util.List<android.hardware.display.AmbientBrightnessDayStats> getAmbientBrightnessStats();
|
||||
method public android.hardware.display.BrightnessConfiguration getBrightnessConfiguration();
|
||||
method public java.util.List<android.hardware.display.BrightnessChangeEvent> getBrightnessEvents();
|
||||
method public android.hardware.display.BrightnessConfiguration getDefaultBrightnessConfiguration();
|
||||
method public android.graphics.Point getStableDisplaySize();
|
||||
method public void setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.location {
|
||||
|
||||
public final class GnssClock implements android.os.Parcelable {
|
||||
@@ -198,20 +431,20 @@ package android.location {
|
||||
ctor public GnssMeasurement();
|
||||
method public void reset();
|
||||
method public void resetAutomaticGainControlLevel();
|
||||
method public void resetCarrierCycles();
|
||||
method public deprecated void resetCarrierCycles();
|
||||
method public void resetCarrierFrequencyHz();
|
||||
method public void resetCarrierPhase();
|
||||
method public void resetCarrierPhaseUncertainty();
|
||||
method public deprecated void resetCarrierPhase();
|
||||
method public deprecated void resetCarrierPhaseUncertainty();
|
||||
method public void resetSnrInDb();
|
||||
method public void set(android.location.GnssMeasurement);
|
||||
method public void setAccumulatedDeltaRangeMeters(double);
|
||||
method public void setAccumulatedDeltaRangeState(int);
|
||||
method public void setAccumulatedDeltaRangeUncertaintyMeters(double);
|
||||
method public void setAutomaticGainControlLevelInDb(double);
|
||||
method public void setCarrierCycles(long);
|
||||
method public deprecated void setCarrierCycles(long);
|
||||
method public void setCarrierFrequencyHz(float);
|
||||
method public void setCarrierPhase(double);
|
||||
method public void setCarrierPhaseUncertainty(double);
|
||||
method public deprecated void setCarrierPhase(double);
|
||||
method public deprecated void setCarrierPhaseUncertainty(double);
|
||||
method public void setCn0DbHz(double);
|
||||
method public void setConstellationType(int);
|
||||
method public void setMultipathIndicator(int);
|
||||
@@ -223,6 +456,7 @@ package android.location {
|
||||
method public void setState(int);
|
||||
method public void setSvid(int);
|
||||
method public void setTimeOffsetNanos(double);
|
||||
field public static final int ADR_STATE_ALL = 31; // 0x1f
|
||||
}
|
||||
|
||||
public final class GnssMeasurementsEvent implements android.os.Parcelable {
|
||||
@@ -242,7 +476,88 @@ package android.location {
|
||||
}
|
||||
|
||||
public class LocationManager {
|
||||
method public int getGnssYearOfHardware();
|
||||
method public java.lang.String[] getBackgroundThrottlingWhitelist();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.media {
|
||||
|
||||
public final class AudioFocusRequest {
|
||||
method public android.media.AudioManager.OnAudioFocusChangeListener getOnAudioFocusChangeListener();
|
||||
}
|
||||
|
||||
public final class AudioFormat implements android.os.Parcelable {
|
||||
method public static int channelCountFromInChannelMask(int);
|
||||
method public static int channelCountFromOutChannelMask(int);
|
||||
method public static int getBytesPerSample(int);
|
||||
method public static boolean isEncodingLinearPcm(int);
|
||||
}
|
||||
|
||||
public final class AudioPresentation {
|
||||
ctor public AudioPresentation(int, int, java.util.Map<java.lang.String, java.lang.String>, java.lang.String, int, boolean, boolean, boolean);
|
||||
method public int getPresentationId();
|
||||
method public int getProgramId();
|
||||
}
|
||||
|
||||
public final class BufferingParams implements android.os.Parcelable {
|
||||
method public int describeContents();
|
||||
method public int getInitialMarkMs();
|
||||
method public int getResumePlaybackMarkMs();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator<android.media.BufferingParams> CREATOR;
|
||||
}
|
||||
|
||||
public static class BufferingParams.Builder {
|
||||
ctor public BufferingParams.Builder();
|
||||
ctor public BufferingParams.Builder(android.media.BufferingParams);
|
||||
method public android.media.BufferingParams build();
|
||||
method public android.media.BufferingParams.Builder setInitialMarkMs(int);
|
||||
method public android.media.BufferingParams.Builder setResumePlaybackMarkMs(int);
|
||||
}
|
||||
|
||||
public class MediaPlayer implements android.media.AudioRouting android.media.VolumeAutomation {
|
||||
method public android.media.BufferingParams getBufferingParams();
|
||||
method public void setBufferingParams(android.media.BufferingParams);
|
||||
}
|
||||
|
||||
public final class PlaybackParams implements android.os.Parcelable {
|
||||
method public int getAudioStretchMode();
|
||||
method public android.media.PlaybackParams setAudioStretchMode(int);
|
||||
}
|
||||
|
||||
public static final class VolumeShaper.Configuration.Builder {
|
||||
method public android.media.VolumeShaper.Configuration.Builder setOptionFlags(int);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.media.audiofx {
|
||||
|
||||
public class AudioEffect {
|
||||
method public static int byteArrayToInt(byte[]);
|
||||
method public static short byteArrayToShort(byte[]);
|
||||
method public int getParameter(byte[], byte[]) throws java.lang.IllegalStateException;
|
||||
method public int getParameter(int, byte[]) throws java.lang.IllegalStateException;
|
||||
method public int getParameter(int, int[]) throws java.lang.IllegalStateException;
|
||||
method public int getParameter(int, short[]) throws java.lang.IllegalStateException;
|
||||
method public int getParameter(int[], short[]) throws java.lang.IllegalStateException;
|
||||
method public static byte[] intToByteArray(int);
|
||||
method public static boolean isEffectTypeAvailable(java.util.UUID);
|
||||
method public static boolean isError(int);
|
||||
method public int setParameter(byte[], byte[]) throws java.lang.IllegalStateException;
|
||||
method public int setParameter(int, int) throws java.lang.IllegalStateException;
|
||||
method public int setParameter(int, short) throws java.lang.IllegalStateException;
|
||||
method public int setParameter(int, byte[]) throws java.lang.IllegalStateException;
|
||||
method public int setParameter(int[], int[]) throws java.lang.IllegalStateException;
|
||||
method public int setParameter(int[], byte[]) throws java.lang.IllegalStateException;
|
||||
method public void setParameterListener(android.media.audiofx.AudioEffect.OnParameterChangeListener);
|
||||
method public static byte[] shortToByteArray(short);
|
||||
field public static final java.util.UUID EFFECT_TYPE_NULL;
|
||||
}
|
||||
|
||||
public static abstract interface AudioEffect.OnParameterChangeListener {
|
||||
method public abstract void onParameterChange(android.media.audiofx.AudioEffect, int, byte[], byte[]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -269,9 +584,22 @@ package android.net {
|
||||
|
||||
package android.os {
|
||||
|
||||
public static class Build.VERSION {
|
||||
field public static final int FIRST_SDK_INT;
|
||||
field public static final int RESOURCES_SDK_INT;
|
||||
}
|
||||
|
||||
public class DeviceIdleManager {
|
||||
method public java.lang.String[] getSystemPowerWhitelist();
|
||||
method public java.lang.String[] getSystemPowerWhitelistExceptIdle();
|
||||
}
|
||||
|
||||
public class Environment {
|
||||
method public static java.io.File buildPath(java.io.File, java.lang.String...);
|
||||
}
|
||||
|
||||
public class IncidentManager {
|
||||
method public void reportIncident(android.os.IncidentReportArgs);
|
||||
method public void reportIncident(java.lang.String, byte[]);
|
||||
}
|
||||
|
||||
public final class IncidentReportArgs implements android.os.Parcelable {
|
||||
@@ -282,7 +610,6 @@ package android.os {
|
||||
method public boolean containsSection(int);
|
||||
method public int describeContents();
|
||||
method public boolean isAll();
|
||||
method public static android.os.IncidentReportArgs parseSetting(java.lang.String) throws java.lang.IllegalArgumentException;
|
||||
method public void readFromParcel(android.os.Parcel);
|
||||
method public int sectionCount();
|
||||
method public void setAll(boolean);
|
||||
@@ -290,24 +617,83 @@ package android.os {
|
||||
field public static final android.os.Parcelable.Creator<android.os.IncidentReportArgs> CREATOR;
|
||||
}
|
||||
|
||||
public final class PowerManager {
|
||||
method public void nap(long);
|
||||
}
|
||||
|
||||
public class Process {
|
||||
method public static final int getThreadScheduler(int) throws java.lang.IllegalArgumentException;
|
||||
}
|
||||
|
||||
public final class StrictMode {
|
||||
method public static void setViolationListener(android.os.StrictMode.ViolationListener);
|
||||
public final class RemoteCallback implements android.os.Parcelable {
|
||||
ctor public RemoteCallback(android.os.RemoteCallback.OnResultListener);
|
||||
ctor public RemoteCallback(android.os.RemoteCallback.OnResultListener, android.os.Handler);
|
||||
method public int describeContents();
|
||||
method public void sendResult(android.os.Bundle);
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator<android.os.RemoteCallback> CREATOR;
|
||||
}
|
||||
|
||||
public static abstract interface StrictMode.ViolationListener {
|
||||
method public abstract void onViolation(java.lang.String);
|
||||
public static abstract interface RemoteCallback.OnResultListener {
|
||||
method public abstract void onResult(android.os.Bundle);
|
||||
}
|
||||
|
||||
public final class StrictMode {
|
||||
method public static void setViolationLogger(android.os.StrictMode.ViolationLogger);
|
||||
field public static final int DETECT_CUSTOM = 8; // 0x8
|
||||
field public static final int DETECT_DISK_READ = 2; // 0x2
|
||||
field public static final int DETECT_DISK_WRITE = 1; // 0x1
|
||||
field public static final int DETECT_NETWORK = 4; // 0x4
|
||||
field public static final int DETECT_RESOURCE_MISMATCH = 16; // 0x10
|
||||
field public static final int DETECT_UNBUFFERED_IO = 32; // 0x20
|
||||
field public static final int DETECT_VM_ACTIVITY_LEAKS = 1024; // 0x400
|
||||
field public static final int DETECT_VM_CLEARTEXT_NETWORK = 16384; // 0x4000
|
||||
field public static final int DETECT_VM_CLOSABLE_LEAKS = 512; // 0x200
|
||||
field public static final int DETECT_VM_CONTENT_URI_WITHOUT_PERMISSION = 32768; // 0x8000
|
||||
field public static final int DETECT_VM_CURSOR_LEAKS = 256; // 0x100
|
||||
field public static final int DETECT_VM_FILE_URI_EXPOSURE = 8192; // 0x2000
|
||||
field public static final int DETECT_VM_INSTANCE_LEAKS = 2048; // 0x800
|
||||
field public static final int DETECT_VM_NON_SDK_API_USAGE = 1073741824; // 0x40000000
|
||||
field public static final int DETECT_VM_REGISTRATION_LEAKS = 4096; // 0x1000
|
||||
field public static final int DETECT_VM_UNTAGGED_SOCKET = -2147483648; // 0x80000000
|
||||
}
|
||||
|
||||
public static final class StrictMode.ViolationInfo implements android.os.Parcelable {
|
||||
ctor public StrictMode.ViolationInfo(android.os.Parcel);
|
||||
ctor public StrictMode.ViolationInfo(android.os.Parcel, boolean);
|
||||
method public int describeContents();
|
||||
method public void dump(android.util.Printer, java.lang.String);
|
||||
method public int getPolicyMask();
|
||||
method public java.lang.String getStackTrace();
|
||||
method public int getViolationBit();
|
||||
method public java.lang.String getViolationDetails();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator<android.os.StrictMode.ViolationInfo> CREATOR;
|
||||
field public java.lang.String broadcastIntentAction;
|
||||
field public int durationMillis;
|
||||
field public int numAnimationsRunning;
|
||||
field public long numInstances;
|
||||
field public java.lang.String[] tags;
|
||||
field public int violationNumThisLoop;
|
||||
field public long violationUptimeMillis;
|
||||
}
|
||||
|
||||
public static abstract interface StrictMode.ViolationLogger {
|
||||
method public abstract void log(android.os.StrictMode.ViolationInfo);
|
||||
}
|
||||
|
||||
public class SystemProperties {
|
||||
method public static java.lang.String get(java.lang.String, java.lang.String);
|
||||
}
|
||||
|
||||
public final class UserHandle implements android.os.Parcelable {
|
||||
method public static int getAppId(int);
|
||||
method public int getIdentifier();
|
||||
field public static final android.os.UserHandle SYSTEM;
|
||||
}
|
||||
|
||||
public class UserManager {
|
||||
method public static boolean isSplitSystemUser();
|
||||
field public static final java.lang.String ACTION_USER_RESTRICTIONS_CHANGED = "android.os.action.USER_RESTRICTIONS_CHANGED";
|
||||
}
|
||||
|
||||
@@ -332,8 +718,24 @@ package android.provider {
|
||||
field public static final java.lang.String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS";
|
||||
}
|
||||
|
||||
public static final class Settings.Global extends android.provider.Settings.NameValueTable {
|
||||
field public static final java.lang.String AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES = "autofill_compat_mode_allowed_packages";
|
||||
field public static final java.lang.String HIDDEN_API_BLACKLIST_EXEMPTIONS = "hidden_api_blacklist_exemptions";
|
||||
field public static final java.lang.String LOCATION_GLOBAL_KILL_SWITCH = "location_global_kill_switch";
|
||||
field public static final java.lang.String LOW_POWER_MODE = "low_power";
|
||||
field public static final java.lang.String LOW_POWER_MODE_STICKY = "low_power_sticky";
|
||||
field public static final java.lang.String USE_OPEN_WIFI_PACKAGE = "use_open_wifi_package";
|
||||
}
|
||||
|
||||
public static final class Settings.Secure extends android.provider.Settings.NameValueTable {
|
||||
field public static final java.lang.String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED = "accessibility_display_magnification_enabled";
|
||||
field public static final java.lang.String AUTOFILL_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification";
|
||||
field public static final java.lang.String AUTOFILL_SERVICE = "autofill_service";
|
||||
field public static final java.lang.String AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT = "autofill_user_data_max_category_count";
|
||||
field public static final java.lang.String AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE = "autofill_user_data_max_field_classification_size";
|
||||
field public static final java.lang.String AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE = "autofill_user_data_max_user_data_size";
|
||||
field public static final java.lang.String AUTOFILL_USER_DATA_MAX_VALUE_LENGTH = "autofill_user_data_max_value_length";
|
||||
field public static final java.lang.String AUTOFILL_USER_DATA_MIN_VALUE_LENGTH = "autofill_user_data_min_value_length";
|
||||
field public static final java.lang.String DISABLED_PRINT_SERVICES = "disabled_print_services";
|
||||
field public static final deprecated java.lang.String ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES = "enabled_notification_policy_access_packages";
|
||||
field public static final java.lang.String SYNC_PARENT_SOUNDS = "sync_parent_sounds";
|
||||
@@ -347,6 +749,15 @@ package android.provider {
|
||||
|
||||
}
|
||||
|
||||
package android.security {
|
||||
|
||||
public class KeyStoreException extends java.lang.Exception {
|
||||
ctor public KeyStoreException(int, java.lang.String);
|
||||
method public int getErrorCode();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.security.keystore {
|
||||
|
||||
public abstract class AttestationUtils {
|
||||
@@ -356,28 +767,70 @@ package android.security.keystore {
|
||||
field public static final int ID_TYPE_SERIAL = 1; // 0x1
|
||||
}
|
||||
|
||||
public static final class KeyGenParameterSpec.Builder {
|
||||
method public android.security.keystore.KeyGenParameterSpec.Builder setUniqueIdIncluded(boolean);
|
||||
}
|
||||
|
||||
public final class KeyProtection implements java.security.KeyStore.ProtectionParameter {
|
||||
method public long getBoundToSpecificSecureUserId();
|
||||
}
|
||||
|
||||
public static final class KeyProtection.Builder {
|
||||
method public android.security.keystore.KeyProtection.Builder setBoundToSpecificSecureUserId(long);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.service.autofill {
|
||||
|
||||
public final class CharSequenceTransformation implements android.os.Parcelable android.service.autofill.Transformation {
|
||||
public final class CharSequenceTransformation extends android.service.autofill.InternalTransformation implements android.os.Parcelable android.service.autofill.Transformation {
|
||||
method public void apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int) throws java.lang.Exception;
|
||||
}
|
||||
|
||||
public final class ImageTransformation implements android.os.Parcelable android.service.autofill.Transformation {
|
||||
public final class DateTransformation extends android.service.autofill.InternalTransformation implements android.os.Parcelable android.service.autofill.Transformation {
|
||||
method public void apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int) throws java.lang.Exception;
|
||||
}
|
||||
|
||||
public final class LuhnChecksumValidator implements android.os.Parcelable android.service.autofill.Validator {
|
||||
public final class DateValueSanitizer extends android.service.autofill.InternalSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
|
||||
method public android.view.autofill.AutofillValue sanitize(android.view.autofill.AutofillValue);
|
||||
}
|
||||
|
||||
public final class FillResponse implements android.os.Parcelable {
|
||||
method public int getFlags();
|
||||
}
|
||||
|
||||
public final class ImageTransformation extends android.service.autofill.InternalTransformation implements android.os.Parcelable android.service.autofill.Transformation {
|
||||
method public void apply(android.service.autofill.ValueFinder, android.widget.RemoteViews, int) throws java.lang.Exception;
|
||||
}
|
||||
|
||||
public abstract class InternalSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
|
||||
ctor public InternalSanitizer();
|
||||
}
|
||||
|
||||
public abstract class InternalTransformation implements android.os.Parcelable android.service.autofill.Transformation {
|
||||
ctor public InternalTransformation();
|
||||
}
|
||||
|
||||
public abstract class InternalValidator implements android.os.Parcelable android.service.autofill.Validator {
|
||||
ctor public InternalValidator();
|
||||
method public abstract boolean isValid(android.service.autofill.ValueFinder);
|
||||
}
|
||||
|
||||
public final class LuhnChecksumValidator extends android.service.autofill.InternalValidator implements android.os.Parcelable android.service.autofill.Validator {
|
||||
method public boolean isValid(android.service.autofill.ValueFinder);
|
||||
}
|
||||
|
||||
public final class RegexValidator implements android.os.Parcelable android.service.autofill.Validator {
|
||||
public final class RegexValidator extends android.service.autofill.InternalValidator implements android.os.Parcelable android.service.autofill.Validator {
|
||||
method public boolean isValid(android.service.autofill.ValueFinder);
|
||||
}
|
||||
|
||||
public final class TextValueSanitizer extends android.service.autofill.InternalSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
|
||||
method public android.view.autofill.AutofillValue sanitize(android.view.autofill.AutofillValue);
|
||||
}
|
||||
|
||||
public abstract interface ValueFinder {
|
||||
method public abstract java.lang.String findByAutofillId(android.view.autofill.AutofillId);
|
||||
method public default java.lang.String findByAutofillId(android.view.autofill.AutofillId);
|
||||
method public abstract android.view.autofill.AutofillValue findRawValueByAutofillId(android.view.autofill.AutofillId);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -397,6 +850,11 @@ package android.service.notification {
|
||||
field public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR;
|
||||
field public static final java.lang.String KEY_PEOPLE = "key_people";
|
||||
field public static final java.lang.String KEY_SNOOZE_CRITERIA = "key_snooze_criteria";
|
||||
field public static final java.lang.String KEY_USER_SENTIMENT = "key_user_sentiment";
|
||||
}
|
||||
|
||||
public abstract class ConditionProviderService extends android.app.Service {
|
||||
method public boolean isBound();
|
||||
}
|
||||
|
||||
public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService {
|
||||
@@ -410,6 +868,36 @@ package android.service.notification {
|
||||
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
|
||||
}
|
||||
|
||||
public abstract class NotificationListenerService extends android.app.Service {
|
||||
method public void onNotificationRemoved(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap, android.service.notification.NotificationStats, int);
|
||||
}
|
||||
|
||||
public final class NotificationStats implements android.os.Parcelable {
|
||||
ctor public NotificationStats();
|
||||
ctor protected NotificationStats(android.os.Parcel);
|
||||
method public int describeContents();
|
||||
method public int getDismissalSurface();
|
||||
method public boolean hasDirectReplied();
|
||||
method public boolean hasExpanded();
|
||||
method public boolean hasInteracted();
|
||||
method public boolean hasSeen();
|
||||
method public boolean hasSnoozed();
|
||||
method public boolean hasViewedSettings();
|
||||
method public void setDirectReplied();
|
||||
method public void setDismissalSurface(int);
|
||||
method public void setExpanded();
|
||||
method public void setSeen();
|
||||
method public void setSnoozed();
|
||||
method public void setViewedSettings();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator<android.service.notification.NotificationStats> CREATOR;
|
||||
field public static final int DISMISSAL_AOD = 2; // 0x2
|
||||
field public static final int DISMISSAL_NOT_DISMISSED = -1; // 0xffffffff
|
||||
field public static final int DISMISSAL_OTHER = 0; // 0x0
|
||||
field public static final int DISMISSAL_PEEK = 1; // 0x1
|
||||
field public static final int DISMISSAL_SHADE = 3; // 0x3
|
||||
}
|
||||
|
||||
public final class SnoozeCriterion implements android.os.Parcelable {
|
||||
ctor public SnoozeCriterion(java.lang.String, java.lang.CharSequence, java.lang.CharSequence);
|
||||
ctor protected SnoozeCriterion(android.os.Parcel);
|
||||
@@ -451,10 +939,15 @@ package android.telephony {
|
||||
|
||||
public class ServiceState implements android.os.Parcelable {
|
||||
method public void setCdmaSystemAndNetworkId(int, int);
|
||||
method public void setCellBandwidths(int[]);
|
||||
method public void setChannelNumber(int);
|
||||
method public void setRilDataRadioTechnology(int);
|
||||
method public void setRilVoiceRadioTechnology(int);
|
||||
}
|
||||
|
||||
public class TelephonyManager {
|
||||
method public int getCarrierIdListVersion();
|
||||
method public void refreshUiccProfile();
|
||||
method public void setCarrierTestOverride(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
|
||||
field public static final int UNKNOWN_CARRIER_ID_LIST_VERSION = -1; // 0xffffffff
|
||||
}
|
||||
@@ -539,17 +1032,13 @@ package android.telephony.mbms.vendor {
|
||||
|
||||
}
|
||||
|
||||
package android.test.mock {
|
||||
package android.text {
|
||||
|
||||
public class MockContext extends android.content.Context {
|
||||
method public int getUserId();
|
||||
}
|
||||
|
||||
public deprecated class MockPackageManager extends android.content.pm.PackageManager {
|
||||
method public java.lang.String getDefaultBrowserPackageNameAsUser(int);
|
||||
method public int getInstallReason(java.lang.String, android.os.UserHandle);
|
||||
method public java.lang.String getPermissionControllerPackageName();
|
||||
method public boolean isPermissionReviewModeEnabled();
|
||||
public static final class Selection.MemoryTextWatcher implements android.text.TextWatcher {
|
||||
ctor public Selection.MemoryTextWatcher();
|
||||
method public void afterTextChanged(android.text.Editable);
|
||||
method public void beforeTextChanged(java.lang.CharSequence, int, int, int);
|
||||
method public void onTextChanged(java.lang.CharSequence, int, int, int);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -690,25 +1179,25 @@ package android.util.proto {
|
||||
field public static final long FIELD_COUNT_UNKNOWN = 0L; // 0x0L
|
||||
field public static final int FIELD_ID_MASK = -8; // 0xfffffff8
|
||||
field public static final int FIELD_ID_SHIFT = 3; // 0x3
|
||||
field public static final long FIELD_TYPE_BOOL = 55834574848L; // 0xd00000000L
|
||||
field public static final long FIELD_TYPE_BYTES = 64424509440L; // 0xf00000000L
|
||||
field public static final long FIELD_TYPE_BOOL = 34359738368L; // 0x800000000L
|
||||
field public static final long FIELD_TYPE_BYTES = 51539607552L; // 0xc00000000L
|
||||
field public static final long FIELD_TYPE_DOUBLE = 4294967296L; // 0x100000000L
|
||||
field public static final long FIELD_TYPE_ENUM = 68719476736L; // 0x1000000000L
|
||||
field public static final long FIELD_TYPE_FIXED32 = 38654705664L; // 0x900000000L
|
||||
field public static final long FIELD_TYPE_FIXED64 = 42949672960L; // 0xa00000000L
|
||||
field public static final long FIELD_TYPE_ENUM = 60129542144L; // 0xe00000000L
|
||||
field public static final long FIELD_TYPE_FIXED32 = 30064771072L; // 0x700000000L
|
||||
field public static final long FIELD_TYPE_FIXED64 = 25769803776L; // 0x600000000L
|
||||
field public static final long FIELD_TYPE_FLOAT = 8589934592L; // 0x200000000L
|
||||
field public static final long FIELD_TYPE_INT32 = 12884901888L; // 0x300000000L
|
||||
field public static final long FIELD_TYPE_INT64 = 17179869184L; // 0x400000000L
|
||||
field public static final long FIELD_TYPE_INT32 = 21474836480L; // 0x500000000L
|
||||
field public static final long FIELD_TYPE_INT64 = 12884901888L; // 0x300000000L
|
||||
field public static final long FIELD_TYPE_MASK = 1095216660480L; // 0xff00000000L
|
||||
field public static final long FIELD_TYPE_OBJECT = 73014444032L; // 0x1100000000L
|
||||
field public static final long FIELD_TYPE_SFIXED32 = 47244640256L; // 0xb00000000L
|
||||
field public static final long FIELD_TYPE_SFIXED64 = 51539607552L; // 0xc00000000L
|
||||
field public static final long FIELD_TYPE_MESSAGE = 47244640256L; // 0xb00000000L
|
||||
field public static final long FIELD_TYPE_SFIXED32 = 64424509440L; // 0xf00000000L
|
||||
field public static final long FIELD_TYPE_SFIXED64 = 68719476736L; // 0x1000000000L
|
||||
field public static final int FIELD_TYPE_SHIFT = 32; // 0x20
|
||||
field public static final long FIELD_TYPE_SINT32 = 30064771072L; // 0x700000000L
|
||||
field public static final long FIELD_TYPE_SINT64 = 34359738368L; // 0x800000000L
|
||||
field public static final long FIELD_TYPE_STRING = 60129542144L; // 0xe00000000L
|
||||
field public static final long FIELD_TYPE_UINT32 = 21474836480L; // 0x500000000L
|
||||
field public static final long FIELD_TYPE_UINT64 = 25769803776L; // 0x600000000L
|
||||
field public static final long FIELD_TYPE_SINT32 = 73014444032L; // 0x1100000000L
|
||||
field public static final long FIELD_TYPE_SINT64 = 77309411328L; // 0x1200000000L
|
||||
field public static final long FIELD_TYPE_STRING = 38654705664L; // 0x900000000L
|
||||
field public static final long FIELD_TYPE_UINT32 = 55834574848L; // 0xd00000000L
|
||||
field public static final long FIELD_TYPE_UINT64 = 17179869184L; // 0x400000000L
|
||||
field public static final long FIELD_TYPE_UNKNOWN = 0L; // 0x0L
|
||||
field public static final java.lang.String TAG = "ProtoOutputStream";
|
||||
field public static final int WIRE_TYPE_END_GROUP = 4; // 0x4
|
||||
@@ -732,6 +1221,15 @@ package android.view {
|
||||
method public boolean isUiFocusable();
|
||||
}
|
||||
|
||||
public final class Choreographer {
|
||||
method public static long getFrameDelay();
|
||||
method public void postCallback(int, java.lang.Runnable, java.lang.Object);
|
||||
method public void postCallbackDelayed(int, java.lang.Runnable, java.lang.Object, long);
|
||||
method public void removeCallbacks(int, java.lang.Runnable, java.lang.Object);
|
||||
method public static void setFrameDelay(long);
|
||||
field public static final int CALLBACK_ANIMATION = 1; // 0x1
|
||||
}
|
||||
|
||||
public class FocusFinder {
|
||||
method public static void sort(android.view.View[], int, int, android.view.ViewGroup, boolean);
|
||||
}
|
||||
@@ -754,6 +1252,7 @@ package android.view {
|
||||
|
||||
public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback {
|
||||
method public android.view.View getTooltipView();
|
||||
method public static boolean isDefaultFocusHighlightEnabled();
|
||||
method public boolean isDefaultFocusHighlightNeeded(android.graphics.drawable.Drawable, android.graphics.drawable.Drawable);
|
||||
method public boolean restoreFocusInCluster(int);
|
||||
method public boolean restoreFocusNotInCluster();
|
||||
@@ -781,6 +1280,7 @@ package android.view.accessibility {
|
||||
|
||||
public class AccessibilityNodeInfo implements android.os.Parcelable {
|
||||
method public static void setNumInstancesInUseCounter(java.util.concurrent.atomic.AtomicInteger);
|
||||
method public void writeToParcelNoRecycle(android.os.Parcel, int);
|
||||
}
|
||||
|
||||
public final class AccessibilityWindowInfo implements android.os.Parcelable {
|
||||
@@ -789,16 +1289,38 @@ package android.view.accessibility {
|
||||
|
||||
}
|
||||
|
||||
package android.view.animation {
|
||||
|
||||
public class AnimationUtils {
|
||||
method public static void lockAnimationClock(long);
|
||||
method public static void unlockAnimationClock();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.view.autofill {
|
||||
|
||||
public final class AutofillId implements android.os.Parcelable {
|
||||
ctor public AutofillId(int);
|
||||
ctor public AutofillId(android.view.autofill.AutofillId, int);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.view.inputmethod {
|
||||
|
||||
public final class InputMethodManager {
|
||||
method public boolean isInputMethodPickerShown();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.widget {
|
||||
|
||||
public abstract class AbsListView extends android.widget.AdapterView implements android.widget.Filter.FilterListener android.text.TextWatcher android.view.ViewTreeObserver.OnGlobalLayoutListener android.view.ViewTreeObserver.OnTouchModeChangeListener {
|
||||
method public final boolean shouldDrawSelector();
|
||||
}
|
||||
|
||||
public class CalendarView extends android.widget.FrameLayout {
|
||||
method public boolean getBoundsForDate(long, android.graphics.Rect);
|
||||
}
|
||||
@@ -809,6 +1331,17 @@ package android.widget {
|
||||
field public static final int MODE_SPINNER = 1; // 0x1
|
||||
}
|
||||
|
||||
public final class Magnifier {
|
||||
method public android.graphics.Bitmap getContent();
|
||||
method public static android.graphics.PointF getMagnifierDefaultSize();
|
||||
method public android.graphics.Rect getWindowPositionOnScreen();
|
||||
method public void setOnOperationCompleteCallback(android.widget.Magnifier.Callback);
|
||||
}
|
||||
|
||||
public static abstract interface Magnifier.Callback {
|
||||
method public abstract void onOperationComplete();
|
||||
}
|
||||
|
||||
public class NumberPicker extends android.widget.LinearLayout {
|
||||
method public java.lang.CharSequence getDisplayedValueForCurrentSelection();
|
||||
}
|
||||
@@ -821,6 +1354,10 @@ package android.widget {
|
||||
method public boolean isPopupShowing();
|
||||
}
|
||||
|
||||
public class TextClock extends android.widget.TextView {
|
||||
method public void disableClockTick();
|
||||
}
|
||||
|
||||
public class TimePicker extends android.widget.FrameLayout {
|
||||
method public android.view.View getAmView();
|
||||
method public android.view.View getHourView();
|
||||
|
||||
@@ -28,6 +28,7 @@ message ResultsBundleEntry {
|
||||
optional double value_double = 5;
|
||||
optional sint64 value_long = 6;
|
||||
optional ResultsBundle value_bundle = 7;
|
||||
optional bytes value_bytes = 8;
|
||||
}
|
||||
|
||||
message ResultsBundle {
|
||||
|
||||
@@ -98,7 +98,8 @@ public class Am extends BaseCommand {
|
||||
static final class MyShellCallback extends ShellCallback {
|
||||
boolean mActive = true;
|
||||
|
||||
@Override public ParcelFileDescriptor onOpenOutputFile(String path, String seLinuxContext) {
|
||||
@Override public ParcelFileDescriptor onOpenFile(String path, String seLinuxContext,
|
||||
String mode) {
|
||||
if (!mActive) {
|
||||
System.err.println("Open attempt after active for: " + path);
|
||||
return null;
|
||||
@@ -159,7 +160,11 @@ public class Am extends BaseCommand {
|
||||
} else if (opt.equals("-r")) {
|
||||
instrument.rawMode = true;
|
||||
} else if (opt.equals("-m")) {
|
||||
instrument.proto = true;
|
||||
instrument.protoStd = true;
|
||||
} else if (opt.equals("-f")) {
|
||||
instrument.protoFile = true;
|
||||
if (peekNextArg() != null && !peekNextArg().startsWith("-"))
|
||||
instrument.logPath = nextArg();
|
||||
} else if (opt.equals("-e")) {
|
||||
final String argKey = nextArgRequired();
|
||||
final String argValue = nextArgRequired();
|
||||
|
||||
@@ -25,23 +25,42 @@ import android.content.pm.IPackageManager;
|
||||
import android.content.pm.InstrumentationInfo;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.UserHandle;
|
||||
import android.util.AndroidException;
|
||||
import android.util.proto.ProtoOutputStream;
|
||||
import android.view.IWindowManager;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
|
||||
/**
|
||||
* Runs the am instrument command
|
||||
*
|
||||
* Test Result Code:
|
||||
* 1 - Test running
|
||||
* 0 - Test passed
|
||||
* -2 - assertion failure
|
||||
* -1 - other exceptions
|
||||
*
|
||||
* Session Result Code:
|
||||
* -1: Success
|
||||
* other: Failure
|
||||
*/
|
||||
public class Instrument {
|
||||
public static final String DEFAULT_LOG_DIR = "instrument-logs";
|
||||
|
||||
private final IActivityManager mAm;
|
||||
private final IPackageManager mPm;
|
||||
private final IWindowManager mWm;
|
||||
@@ -50,7 +69,9 @@ public class Instrument {
|
||||
public String profileFile = null;
|
||||
public boolean wait = false;
|
||||
public boolean rawMode = false;
|
||||
public boolean proto = false;
|
||||
boolean protoStd = false; // write proto to stdout
|
||||
boolean protoFile = false; // write proto to a file
|
||||
String logPath = null;
|
||||
public boolean noWindowAnimation = false;
|
||||
public boolean disableHiddenApiChecks = false;
|
||||
public String abi = null;
|
||||
@@ -183,18 +204,49 @@ public class Instrument {
|
||||
* Printer for the protobuf based status reporting.
|
||||
*/
|
||||
private class ProtoStatusReporter implements StatusReporter {
|
||||
|
||||
private File mLog;
|
||||
|
||||
ProtoStatusReporter() {
|
||||
if (protoFile) {
|
||||
if (logPath == null) {
|
||||
File logDir = new File(Environment.getLegacyExternalStorageDirectory(),
|
||||
DEFAULT_LOG_DIR);
|
||||
if (!logDir.exists() && !logDir.mkdirs()) {
|
||||
System.err.format("Unable to create log directory: %s\n",
|
||||
logDir.getAbsolutePath());
|
||||
protoFile = false;
|
||||
return;
|
||||
}
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd-hhmmss-SSS", Locale.US);
|
||||
String fileName = String.format("log-%s.instrumentation_data_proto",
|
||||
format.format(new Date()));
|
||||
mLog = new File(logDir, fileName);
|
||||
} else {
|
||||
mLog = new File(Environment.getLegacyExternalStorageDirectory(), logPath);
|
||||
File logDir = mLog.getParentFile();
|
||||
if (!logDir.exists() && !logDir.mkdirs()) {
|
||||
System.err.format("Unable to create log directory: %s\n",
|
||||
logDir.getAbsolutePath());
|
||||
protoFile = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (mLog.exists()) mLog.delete();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInstrumentationStatusLocked(ComponentName name, int resultCode,
|
||||
Bundle results) {
|
||||
final ProtoOutputStream proto = new ProtoOutputStream();
|
||||
|
||||
final long token = proto.startRepeatedObject(InstrumentationData.Session.TEST_STATUS);
|
||||
|
||||
proto.writeSInt32(InstrumentationData.TestStatus.RESULT_CODE, resultCode);
|
||||
final long token = proto.start(InstrumentationData.Session.TEST_STATUS);
|
||||
proto.write(InstrumentationData.TestStatus.RESULT_CODE, resultCode);
|
||||
writeBundle(proto, InstrumentationData.TestStatus.RESULTS, results);
|
||||
proto.end(token);
|
||||
|
||||
proto.endRepeatedObject(token);
|
||||
writeProtoToStdout(proto);
|
||||
outputProto(proto);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -202,80 +254,87 @@ public class Instrument {
|
||||
Bundle results) {
|
||||
final ProtoOutputStream proto = new ProtoOutputStream();
|
||||
|
||||
final long token = proto.startObject(InstrumentationData.Session.SESSION_STATUS);
|
||||
|
||||
proto.writeEnum(InstrumentationData.SessionStatus.STATUS_CODE,
|
||||
final long token = proto.start(InstrumentationData.Session.SESSION_STATUS);
|
||||
proto.write(InstrumentationData.SessionStatus.STATUS_CODE,
|
||||
InstrumentationData.SESSION_FINISHED);
|
||||
proto.writeSInt32(InstrumentationData.SessionStatus.RESULT_CODE, resultCode);
|
||||
proto.write(InstrumentationData.SessionStatus.RESULT_CODE, resultCode);
|
||||
writeBundle(proto, InstrumentationData.SessionStatus.RESULTS, results);
|
||||
proto.end(token);
|
||||
|
||||
proto.endObject(token);
|
||||
writeProtoToStdout(proto);
|
||||
outputProto(proto);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String errorText, boolean commandError) {
|
||||
final ProtoOutputStream proto = new ProtoOutputStream();
|
||||
|
||||
final long token = proto.startObject(InstrumentationData.Session.SESSION_STATUS);
|
||||
|
||||
proto.writeEnum(InstrumentationData.SessionStatus.STATUS_CODE,
|
||||
final long token = proto.start(InstrumentationData.Session.SESSION_STATUS);
|
||||
proto.write(InstrumentationData.SessionStatus.STATUS_CODE,
|
||||
InstrumentationData.SESSION_ABORTED);
|
||||
proto.writeString(InstrumentationData.SessionStatus.ERROR_TEXT, errorText);
|
||||
proto.write(InstrumentationData.SessionStatus.ERROR_TEXT, errorText);
|
||||
proto.end(token);
|
||||
|
||||
proto.endObject(token);
|
||||
writeProtoToStdout(proto);
|
||||
outputProto(proto);
|
||||
}
|
||||
|
||||
private void writeBundle(ProtoOutputStream proto, long fieldId, Bundle bundle) {
|
||||
final long bundleToken = proto.startObject(fieldId);
|
||||
final long bundleToken = proto.start(fieldId);
|
||||
|
||||
for (final String key: sorted(bundle.keySet())) {
|
||||
final long entryToken = proto.startRepeatedObject(
|
||||
InstrumentationData.ResultsBundle.ENTRIES);
|
||||
|
||||
proto.writeString(InstrumentationData.ResultsBundleEntry.KEY, key);
|
||||
proto.write(InstrumentationData.ResultsBundleEntry.KEY, key);
|
||||
|
||||
final Object val = bundle.get(key);
|
||||
if (val instanceof String) {
|
||||
proto.writeString(InstrumentationData.ResultsBundleEntry.VALUE_STRING,
|
||||
proto.write(InstrumentationData.ResultsBundleEntry.VALUE_STRING,
|
||||
(String)val);
|
||||
} else if (val instanceof Byte) {
|
||||
proto.writeSInt32(InstrumentationData.ResultsBundleEntry.VALUE_INT,
|
||||
proto.write(InstrumentationData.ResultsBundleEntry.VALUE_INT,
|
||||
((Byte)val).intValue());
|
||||
} else if (val instanceof Double) {
|
||||
proto.writeDouble(InstrumentationData.ResultsBundleEntry.VALUE_DOUBLE,
|
||||
((Double)val).doubleValue());
|
||||
proto.write(InstrumentationData.ResultsBundleEntry.VALUE_DOUBLE, (double)val);
|
||||
} else if (val instanceof Float) {
|
||||
proto.writeFloat(InstrumentationData.ResultsBundleEntry.VALUE_FLOAT,
|
||||
((Float)val).floatValue());
|
||||
proto.write(InstrumentationData.ResultsBundleEntry.VALUE_FLOAT, (float)val);
|
||||
} else if (val instanceof Integer) {
|
||||
proto.writeSInt32(InstrumentationData.ResultsBundleEntry.VALUE_INT,
|
||||
((Integer)val).intValue());
|
||||
proto.write(InstrumentationData.ResultsBundleEntry.VALUE_INT, (int)val);
|
||||
} else if (val instanceof Long) {
|
||||
proto.writeSInt64(InstrumentationData.ResultsBundleEntry.VALUE_LONG,
|
||||
((Long)val).longValue());
|
||||
proto.write(InstrumentationData.ResultsBundleEntry.VALUE_LONG, (long)val);
|
||||
} else if (val instanceof Short) {
|
||||
proto.writeSInt32(InstrumentationData.ResultsBundleEntry.VALUE_INT,
|
||||
((Short)val).intValue());
|
||||
proto.write(InstrumentationData.ResultsBundleEntry.VALUE_INT, (short)val);
|
||||
} else if (val instanceof Bundle) {
|
||||
writeBundle(proto, InstrumentationData.ResultsBundleEntry.VALUE_BUNDLE,
|
||||
(Bundle)val);
|
||||
} else if (val instanceof byte[]) {
|
||||
proto.write(InstrumentationData.ResultsBundleEntry.VALUE_BYTES, (byte[])val);
|
||||
}
|
||||
|
||||
proto.endRepeatedObject(entryToken);
|
||||
proto.end(entryToken);
|
||||
}
|
||||
|
||||
proto.endObject(bundleToken);
|
||||
proto.end(bundleToken);
|
||||
}
|
||||
|
||||
private void writeProtoToStdout(ProtoOutputStream proto) {
|
||||
try {
|
||||
System.out.write(proto.getBytes());
|
||||
System.out.flush();
|
||||
} catch (IOException ex) {
|
||||
System.err.println("Error writing finished response: ");
|
||||
ex.printStackTrace(System.err);
|
||||
private void outputProto(ProtoOutputStream proto) {
|
||||
byte[] out = proto.getBytes();
|
||||
if (protoStd) {
|
||||
try {
|
||||
System.out.write(out);
|
||||
System.out.flush();
|
||||
} catch (IOException ex) {
|
||||
System.err.println("Error writing finished response: ");
|
||||
ex.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
if (protoFile) {
|
||||
try (OutputStream os = new FileOutputStream(mLog, true)) {
|
||||
os.write(proto.getBytes());
|
||||
os.flush();
|
||||
} catch (IOException ex) {
|
||||
System.err.format("Cannot write to %s:\n", mLog.getAbsolutePath());
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -379,7 +438,7 @@ public class Instrument {
|
||||
|
||||
try {
|
||||
// Choose which output we will do.
|
||||
if (proto) {
|
||||
if (protoFile || protoStd) {
|
||||
reporter = new ProtoStatusReporter();
|
||||
} else if (wait) {
|
||||
reporter = new TextStatusReporter(rawMode);
|
||||
@@ -401,7 +460,7 @@ public class Instrument {
|
||||
mWm.setAnimationScale(2, 0.0f);
|
||||
}
|
||||
|
||||
// Figure out which component we are tring to do.
|
||||
// Figure out which component we are trying to do.
|
||||
final ComponentName cn = parseComponentName(componentNameArg);
|
||||
|
||||
// Choose an ABI if necessary
|
||||
|
||||
@@ -23,8 +23,8 @@ import android.app.backup.IBackupManager;
|
||||
import android.app.backup.IBackupObserver;
|
||||
import android.app.backup.IRestoreObserver;
|
||||
import android.app.backup.IRestoreSession;
|
||||
import android.app.backup.RestoreSet;
|
||||
import android.app.backup.ISelectBackupTransportCallback;
|
||||
import android.app.backup.RestoreSet;
|
||||
import android.content.ComponentName;
|
||||
import android.content.pm.IPackageManager;
|
||||
import android.content.pm.PackageInfo;
|
||||
@@ -37,6 +37,7 @@ import android.util.ArraySet;
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
@@ -296,6 +297,10 @@ public final class Bmgr {
|
||||
super.backupFinished(status);
|
||||
System.out.println("Backup finished with result: "
|
||||
+ convertBackupStatusToString(status));
|
||||
if (status == BackupManager.ERROR_BACKUP_CANCELLED) {
|
||||
System.out.println("Backups can be cancelled if a backup is already running, check "
|
||||
+ "backup dumpsys");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,7 +322,7 @@ public final class Bmgr {
|
||||
case BackupManager.ERROR_TRANSPORT_QUOTA_EXCEEDED:
|
||||
return "Size quota exceeded";
|
||||
case BackupManager.ERROR_BACKUP_CANCELLED:
|
||||
return "Backup Cancelled";
|
||||
return "Backup cancelled";
|
||||
default:
|
||||
return "Unknown error";
|
||||
}
|
||||
@@ -339,18 +344,16 @@ public final class Bmgr {
|
||||
System.err.println(PM_NOT_RUNNING_ERR);
|
||||
}
|
||||
if (installedPackages != null) {
|
||||
List<String> packages = new ArrayList<>();
|
||||
for (PackageInfo pi : installedPackages) {
|
||||
try {
|
||||
if (mBmgr.isAppEligibleForBackup(pi.packageName)) {
|
||||
packages.add(pi.packageName);
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
System.err.println(e.toString());
|
||||
System.err.println(BMGR_NOT_RUNNING_ERR);
|
||||
}
|
||||
String[] packages =
|
||||
installedPackages.stream().map(p -> p.packageName).toArray(String[]::new);
|
||||
String[] filteredPackages = {};
|
||||
try {
|
||||
filteredPackages = mBmgr.filterAppsEligibleForBackup(packages);
|
||||
} catch (RemoteException e) {
|
||||
System.err.println(e.toString());
|
||||
System.err.println(BMGR_NOT_RUNNING_ERR);
|
||||
}
|
||||
backupNowPackages(packages, nonIncrementalBackup);
|
||||
backupNowPackages(Arrays.asList(filteredPackages), nonIncrementalBackup);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,16 +24,15 @@ LOCAL_SRC_FILES:= \
|
||||
BootAnimationUtil.cpp \
|
||||
|
||||
ifeq ($(PRODUCT_IOT),true)
|
||||
LOCAL_SRC_FILES += \
|
||||
iot/iotbootanimation_main.cpp \
|
||||
iot/BootAction.cpp
|
||||
|
||||
LOCAL_SHARED_LIBRARIES += \
|
||||
libandroidthings \
|
||||
libbase \
|
||||
libbinder
|
||||
libchrome \
|
||||
|
||||
LOCAL_STATIC_LIBRARIES += cpufeatures
|
||||
LOCAL_SRC_FILES += \
|
||||
iot/iotbootanimation_main.cpp \
|
||||
iot/BootAction.cpp \
|
||||
iot/BootParameters.cpp \
|
||||
|
||||
else
|
||||
|
||||
@@ -77,13 +76,19 @@ LOCAL_SHARED_LIBRARIES := \
|
||||
libutils \
|
||||
libbinder \
|
||||
libui \
|
||||
libskia \
|
||||
libhwui \
|
||||
libEGL \
|
||||
libGLESv1_CM \
|
||||
libgui \
|
||||
libtinyalsa \
|
||||
libbase
|
||||
|
||||
ifeq ($(PRODUCT_IOT),true)
|
||||
|
||||
LOCAL_INIT_RC := iot/bootanim_iot.rc
|
||||
|
||||
endif # PRODUCT_IOT
|
||||
|
||||
ifdef TARGET_32_BIT_SURFACEFLINGER
|
||||
LOCAL_32_BIT_ONLY := true
|
||||
endif
|
||||
|
||||
@@ -66,9 +66,12 @@
|
||||
namespace android {
|
||||
|
||||
static const char OEM_BOOTANIMATION_FILE[] = "/oem/media/bootanimation.zip";
|
||||
static const char PRODUCT_BOOTANIMATION_FILE[] = "/product/media/bootanimation.zip";
|
||||
static const char SYSTEM_BOOTANIMATION_FILE[] = "/system/media/bootanimation.zip";
|
||||
static const char PRODUCT_ENCRYPTED_BOOTANIMATION_FILE[] = "/product/media/bootanimation-encrypted.zip";
|
||||
static const char SYSTEM_ENCRYPTED_BOOTANIMATION_FILE[] = "/system/media/bootanimation-encrypted.zip";
|
||||
static const char OEM_SHUTDOWNANIMATION_FILE[] = "/oem/media/shutdownanimation.zip";
|
||||
static const char PRODUCT_SHUTDOWNANIMATION_FILE[] = "/product/media/shutdownanimation.zip";
|
||||
static const char SYSTEM_SHUTDOWNANIMATION_FILE[] = "/system/media/shutdownanimation.zip";
|
||||
|
||||
static const char SYSTEM_DATA_DIR_PATH[] = "/data/system";
|
||||
@@ -260,9 +263,9 @@ status_t BootAnimation::readyToRun() {
|
||||
sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"),
|
||||
dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565);
|
||||
|
||||
SurfaceComposerClient::openGlobalTransaction();
|
||||
control->setLayer(0x40000000);
|
||||
SurfaceComposerClient::closeGlobalTransaction();
|
||||
SurfaceComposerClient::Transaction t;
|
||||
t.setLayer(control, 0x40000000)
|
||||
.apply();
|
||||
|
||||
sp<Surface> s = control->getSurface();
|
||||
|
||||
@@ -308,14 +311,20 @@ status_t BootAnimation::readyToRun() {
|
||||
bool encryptedAnimation = atoi(decrypt) != 0 ||
|
||||
!strcmp("trigger_restart_min_framework", decrypt);
|
||||
|
||||
if (!mShuttingDown && encryptedAnimation &&
|
||||
(access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0)) {
|
||||
mZipFileName = SYSTEM_ENCRYPTED_BOOTANIMATION_FILE;
|
||||
return NO_ERROR;
|
||||
if (!mShuttingDown && encryptedAnimation) {
|
||||
static const char* encryptedBootFiles[] =
|
||||
{PRODUCT_ENCRYPTED_BOOTANIMATION_FILE, SYSTEM_ENCRYPTED_BOOTANIMATION_FILE};
|
||||
for (const char* f : encryptedBootFiles) {
|
||||
if (access(f, R_OK) == 0) {
|
||||
mZipFileName = f;
|
||||
return NO_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
static const char* bootFiles[] = {OEM_BOOTANIMATION_FILE, SYSTEM_BOOTANIMATION_FILE};
|
||||
static const char* bootFiles[] =
|
||||
{PRODUCT_BOOTANIMATION_FILE, OEM_BOOTANIMATION_FILE, SYSTEM_BOOTANIMATION_FILE};
|
||||
static const char* shutdownFiles[] =
|
||||
{OEM_SHUTDOWNANIMATION_FILE, SYSTEM_SHUTDOWNANIMATION_FILE};
|
||||
{PRODUCT_SHUTDOWNANIMATION_FILE, OEM_SHUTDOWNANIMATION_FILE, SYSTEM_SHUTDOWNANIMATION_FILE};
|
||||
|
||||
for (const char* f : (!mShuttingDown ? bootFiles : shutdownFiles)) {
|
||||
if (access(f, R_OK) == 0) {
|
||||
|
||||
@@ -30,7 +30,7 @@ The first line defines the general parameters of the animation:
|
||||
|
||||
It is followed by a number of rows of the form:
|
||||
|
||||
TYPE COUNT PAUSE PATH [#RGBHEX CLOCK]
|
||||
TYPE COUNT PAUSE PATH [#RGBHEX [CLOCK1 [CLOCK2]]]
|
||||
|
||||
* **TYPE:** a single char indicating what type of animation segment this is:
|
||||
+ `p` -- this part will play unless interrupted by the end of the boot
|
||||
@@ -39,11 +39,38 @@ It is followed by a number of rows of the form:
|
||||
* **PAUSE:** number of FRAMES to delay after this part ends
|
||||
* **PATH:** directory in which to find the frames for this part (e.g. `part0`)
|
||||
* **RGBHEX:** _(OPTIONAL)_ a background color, specified as `#RRGGBB`
|
||||
* **CLOCK:** _(OPTIONAL)_ the y-coordinate at which to draw the current time (for watches)
|
||||
* **CLOCK1, CLOCK2:** _(OPTIONAL)_ the coordinates at which to draw the current time (for watches):
|
||||
+ If only `CLOCK1` is provided it is the y-coordinate of the clock and the x-coordinate
|
||||
defaults to `c`
|
||||
+ If both `CLOCK1` and `CLOCK2` are provided then `CLOCK1` is the x-coordinate and `CLOCK2` is
|
||||
the y-coodinate
|
||||
+ Values can be either a positive integer, a negative integer, or `c`
|
||||
- `c` -- will centre the text
|
||||
- `n` -- will position the text n pixels from the start; left edge for x-axis, bottom edge
|
||||
for y-axis
|
||||
- `-n` -- will position the text n pixels from the end; right edge for x-axis, top edge
|
||||
for y-axis
|
||||
- Examples:
|
||||
* `-24` or `c -24` will position the text 24 pixels from the top of the screen,
|
||||
centred horizontally
|
||||
* `16 c` will position the text 16 pixels from the left of the screen, centred
|
||||
vertically
|
||||
* `-32 32` will position the text such that the bottom right corner is 32 pixels above
|
||||
and 32 pixels left of the edges of the screen
|
||||
|
||||
There is also a special TYPE, `$SYSTEM`, that loads `/system/media/bootanimation.zip`
|
||||
and plays that.
|
||||
|
||||
## clock_font.png
|
||||
|
||||
The file used to draw the time on top of the boot animation. The font format is as follows:
|
||||
* The file specifies glyphs for the ascii characters 32-127 (0x20-0x7F), both regular weight and
|
||||
bold weight.
|
||||
* The image is divided into a grid of characters
|
||||
* There are 16 columns and 6 rows
|
||||
* Each row is divided in half: regular weight glyphs on the top half, bold glyphs on the bottom
|
||||
* For a NxM image each character glyph will be N/16 pixels wide and M/(12*2) pixels high
|
||||
|
||||
## loading and playing frames
|
||||
|
||||
Each part is scanned and loaded directly from the zip archive. Within a part directory, every file
|
||||
|
||||
@@ -18,17 +18,11 @@
|
||||
|
||||
#define LOG_TAG "BootAction"
|
||||
|
||||
#include <android-base/strings.h>
|
||||
#include <cpu-features.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include <pio/peripheral_manager_client.h>
|
||||
#include <utils/Log.h>
|
||||
|
||||
using android::base::Split;
|
||||
using android::base::Join;
|
||||
using android::base::StartsWith;
|
||||
using android::base::EndsWith;
|
||||
|
||||
namespace android {
|
||||
|
||||
BootAction::~BootAction() {
|
||||
@@ -37,7 +31,8 @@ BootAction::~BootAction() {
|
||||
}
|
||||
}
|
||||
|
||||
bool BootAction::init(const std::string& libraryPath, const std::string& config) {
|
||||
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.
|
||||
@@ -51,16 +46,12 @@ bool BootAction::init(const std::string& libraryPath, const std::string& config)
|
||||
ALOGD("Peripheralmanager is up.");
|
||||
APeripheralManagerClient_delete(client);
|
||||
|
||||
std::string path_to_lib = libraryPath;
|
||||
if (!parseConfig(config, &path_to_lib)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ALOGI("Loading boot action %s", path_to_lib.c_str());
|
||||
mLibHandle = dlopen(path_to_lib.c_str(), RTLD_NOW);
|
||||
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",
|
||||
path_to_lib.c_str(), dlerror());
|
||||
libraryPath.c_str(), dlerror());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -87,7 +78,7 @@ bool BootAction::init(const std::string& libraryPath, const std::string& config)
|
||||
}
|
||||
|
||||
ALOGD("Entering boot_action_init");
|
||||
bool result = mLibInit();
|
||||
bool result = mLibInit(parameters.data(), parameters.size());
|
||||
ALOGD("Returned from boot_action_init");
|
||||
return result;
|
||||
}
|
||||
@@ -115,61 +106,4 @@ bool BootAction::loadSymbol(const char* symbol, void** loaded) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool BootAction::parseConfig(const std::string& config, std::string* path) {
|
||||
auto lines = Split(config, "\n");
|
||||
|
||||
if (lines.size() < 1) {
|
||||
ALOGE("Config format invalid, expected one line, found %d",
|
||||
lines.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t lineNumber = 0;
|
||||
auto& line1 = lines.at(lineNumber);
|
||||
while (StartsWith(line1, "#")) {
|
||||
if (lines.size() < ++lineNumber) {
|
||||
ALOGE("Config file contains no non-comment lines.");
|
||||
return false;
|
||||
}
|
||||
line1 = lines.at(lineNumber);
|
||||
}
|
||||
|
||||
const std::string libraryNameToken("LIBRARY_NAME=");
|
||||
if (!StartsWith(line1, libraryNameToken.c_str())) {
|
||||
ALOGE("Invalid config format, expected second line to start with %s "
|
||||
"Instead found: %s", libraryNameToken.c_str(), line1.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string libraryName = line1.substr(libraryNameToken.length());
|
||||
|
||||
*path += "/";
|
||||
*path += architectureDirectory();
|
||||
*path += "/";
|
||||
*path += libraryName;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* BootAction::architectureDirectory() {
|
||||
switch(android_getCpuFamily()) {
|
||||
case ANDROID_CPU_FAMILY_ARM:
|
||||
return "arm";
|
||||
case ANDROID_CPU_FAMILY_X86:
|
||||
return "x86";
|
||||
case ANDROID_CPU_FAMILY_MIPS:
|
||||
return "mips";
|
||||
case ANDROID_CPU_FAMILY_ARM64:
|
||||
return "arm64";
|
||||
case ANDROID_CPU_FAMILY_X86_64:
|
||||
return "x86_64";
|
||||
case ANDROID_CPU_FAMILY_MIPS64:
|
||||
return "mips64";
|
||||
default:
|
||||
ALOGE("Unsupported cpu family: %d", android_getCpuFamily());
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
|
||||
@@ -18,7 +18,9 @@
|
||||
#define _BOOTANIMATION_BOOTACTION_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <boot_action/boot_action.h> // libandroidthings native API.
|
||||
#include <utils/RefBase.h>
|
||||
|
||||
namespace android {
|
||||
@@ -26,11 +28,10 @@ namespace android {
|
||||
class BootAction : public RefBase {
|
||||
public:
|
||||
~BootAction();
|
||||
// Parse the contents of the config file. We expect one line:
|
||||
// LIBRARY_NAME=
|
||||
//
|
||||
// LIBRARY_NAME is the name of the shared library that contains the boot action.
|
||||
bool init(const std::string& libraryPath, const std::string& config);
|
||||
|
||||
// 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.
|
||||
@@ -43,13 +44,12 @@ public:
|
||||
void shutdown();
|
||||
|
||||
private:
|
||||
typedef bool (*libInit)();
|
||||
typedef bool (*libInit)(const ABootActionParameter* parameters,
|
||||
size_t num_parameters);
|
||||
typedef void (*libStartPart)(int partNumber, int playNumber);
|
||||
typedef void (*libShutdown)();
|
||||
|
||||
bool parseConfig(const std::string& config, std::string* path);
|
||||
bool loadSymbol(const char* symbol, void** loaded);
|
||||
const char* architectureDirectory();
|
||||
|
||||
void* mLibHandle = nullptr;
|
||||
libInit mLibInit = nullptr;
|
||||
|
||||
124
cmds/bootanimation/iot/BootParameters.cpp
Normal file
124
cmds/bootanimation/iot/BootParameters.cpp
Normal file
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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
|
||||
73
cmds/bootanimation/iot/BootParameters.h
Normal file
73
cmds/bootanimation/iot/BootParameters.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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_
|
||||
2
cmds/bootanimation/iot/bootanim_iot.rc
Normal file
2
cmds/bootanimation/iot/bootanim_iot.rc
Normal file
@@ -0,0 +1,2 @@
|
||||
on post-fs-data
|
||||
mkdir /data/misc/bootanimation 0777 root root
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
#define LOG_TAG "IotBootAnimation"
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <base/files/file_util.h>
|
||||
#include <binder/IPCThreadState.h>
|
||||
#include <binder/IServiceManager.h>
|
||||
#include <binder/ProcessState.h>
|
||||
@@ -28,28 +28,40 @@
|
||||
|
||||
#include "BootAction.h"
|
||||
#include "BootAnimationUtil.h"
|
||||
#include "BootParameters.h"
|
||||
|
||||
using namespace android;
|
||||
using android::base::ReadFileToString;
|
||||
|
||||
// Create a typedef for readability.
|
||||
typedef android::BootAnimation::Animation Animation;
|
||||
|
||||
namespace {
|
||||
|
||||
class BootActionAnimationCallbacks : public android::BootAnimation::Callbacks {public:
|
||||
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 {
|
||||
// Create and initialize BootActions if we have a boot_action.conf.
|
||||
std::string bootActionConf;
|
||||
if (ReadFileToString("/oem/app/etc/boot_action.conf", &bootActionConf)) {
|
||||
mBootAction = new BootAction();
|
||||
if (!mBootAction->init("/oem/app/lib", bootActionConf)) {
|
||||
mBootAction = NULL;
|
||||
}
|
||||
} else {
|
||||
ALOGI("No boot actions specified");
|
||||
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 {
|
||||
@@ -60,6 +72,20 @@ class BootActionAnimationCallbacks : public android::BootAnimation::Callbacks {p
|
||||
|
||||
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);
|
||||
@@ -68,6 +94,7 @@ class BootActionAnimationCallbacks : public android::BootAnimation::Callbacks {p
|
||||
};
|
||||
|
||||
private:
|
||||
std::unique_ptr<BootParameters> mBootParameters;
|
||||
sp<BootAction> mBootAction = nullptr;
|
||||
};
|
||||
|
||||
@@ -76,6 +103,9 @@ private:
|
||||
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;
|
||||
@@ -86,7 +116,8 @@ int main() {
|
||||
sp<ProcessState> proc(ProcessState::self());
|
||||
ProcessState::self()->startThreadPool();
|
||||
|
||||
sp<BootAnimation> boot = new BootAnimation(new BootActionAnimationCallbacks());
|
||||
sp<BootAnimation> boot = new BootAnimation(
|
||||
new BootActionAnimationCallbacks(std::move(bootParameters)));
|
||||
|
||||
IPCThreadState::self()->joinThreadPool();
|
||||
return 0;
|
||||
|
||||
@@ -26,18 +26,18 @@ import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.FileUtils;
|
||||
import android.os.IBinder;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.Process;
|
||||
import android.os.UserHandle;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import libcore.io.Streams;
|
||||
|
||||
import libcore.io.IoUtils;
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
|
||||
/**
|
||||
* This class is a command line utility for manipulating content. A client
|
||||
@@ -122,13 +122,14 @@ public class Content {
|
||||
+ "\n"
|
||||
+ "usage: adb shell content read --uri <URI> [--user <USER_ID>]\n"
|
||||
+ " Example:\n"
|
||||
+ " # cat default ringtone to a file, then pull to host\n"
|
||||
+ " adb shell 'content read --uri content://settings/system/ringtone >"
|
||||
+ " /mnt/sdcard/tmp.ogg' && adb pull /mnt/sdcard/tmp.ogg\n"
|
||||
+ " adb shell 'content read --uri content://settings/system/ringtone_cache' > host.ogg\n"
|
||||
+ "\n"
|
||||
+ "usage: adb shell content write --uri <URI> [--user <USER_ID>]\n"
|
||||
+ " Example:\n"
|
||||
+ " adb shell 'content write --uri content://settings/system/ringtone_cache' < host.ogg\n"
|
||||
+ "\n"
|
||||
+ "usage: adb shell content gettype --uri <URI> [--user <USER_ID>]\n"
|
||||
+ " Example:\n"
|
||||
+ " # Show the mime-type of the URI\n"
|
||||
+ " adb shell content gettype --uri content://media/internal/audio/media/\n"
|
||||
+ "\n";
|
||||
|
||||
@@ -139,6 +140,7 @@ public class Content {
|
||||
private static final String ARGUMENT_QUERY = "query";
|
||||
private static final String ARGUMENT_CALL = "call";
|
||||
private static final String ARGUMENT_READ = "read";
|
||||
private static final String ARGUMENT_WRITE = "write";
|
||||
private static final String ARGUMENT_GET_TYPE = "gettype";
|
||||
private static final String ARGUMENT_WHERE = "--where";
|
||||
private static final String ARGUMENT_BIND = "--bind";
|
||||
@@ -179,6 +181,8 @@ public class Content {
|
||||
return parseCallCommand();
|
||||
} else if (ARGUMENT_READ.equals(operation)) {
|
||||
return parseReadCommand();
|
||||
} else if (ARGUMENT_WRITE.equals(operation)) {
|
||||
return parseWriteCommand();
|
||||
} else if (ARGUMENT_GET_TYPE.equals(operation)) {
|
||||
return parseGetTypeCommand();
|
||||
} else {
|
||||
@@ -339,6 +343,25 @@ public class Content {
|
||||
return new ReadCommand(uri, userId);
|
||||
}
|
||||
|
||||
private WriteCommand parseWriteCommand() {
|
||||
Uri uri = null;
|
||||
int userId = UserHandle.USER_SYSTEM;
|
||||
for (String argument; (argument = mTokenizer.nextArg())!= null;) {
|
||||
if (ARGUMENT_URI.equals(argument)) {
|
||||
uri = Uri.parse(argumentValueRequired(argument));
|
||||
} else if (ARGUMENT_USER.equals(argument)) {
|
||||
userId = Integer.parseInt(argumentValueRequired(argument));
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported argument: " + argument);
|
||||
}
|
||||
}
|
||||
if (uri == null) {
|
||||
throw new IllegalArgumentException("Content provider URI not specified."
|
||||
+ " Did you specify --uri argument?");
|
||||
}
|
||||
return new WriteCommand(uri, userId);
|
||||
}
|
||||
|
||||
public QueryCommand parseQueryCommand() {
|
||||
Uri uri = null;
|
||||
int userId = UserHandle.USER_SYSTEM;
|
||||
@@ -535,7 +558,9 @@ public class Content {
|
||||
@Override
|
||||
public void onExecute(IContentProvider provider) throws Exception {
|
||||
Bundle result = provider.call(null, mMethod, mArg, mExtras);
|
||||
final int size = result.size(); // unpack
|
||||
if (result != null) {
|
||||
result.size(); // unpack
|
||||
}
|
||||
System.out.println("Result: " + result);
|
||||
}
|
||||
}
|
||||
@@ -559,20 +584,21 @@ public class Content {
|
||||
|
||||
@Override
|
||||
public void onExecute(IContentProvider provider) throws Exception {
|
||||
final ParcelFileDescriptor fd = provider.openFile(null, mUri, "r", null, null);
|
||||
copy(new FileInputStream(fd.getFileDescriptor()), System.out);
|
||||
try (ParcelFileDescriptor fd = provider.openFile(null, mUri, "r", null, null)) {
|
||||
FileUtils.copy(fd.getFileDescriptor(), FileDescriptor.out);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class WriteCommand extends Command {
|
||||
public WriteCommand(Uri uri, int userId) {
|
||||
super(uri, userId);
|
||||
}
|
||||
|
||||
private static void copy(InputStream is, OutputStream os) throws IOException {
|
||||
final byte[] buffer = new byte[8 * 1024];
|
||||
int read;
|
||||
try {
|
||||
while ((read = is.read(buffer)) > -1) {
|
||||
os.write(buffer, 0, read);
|
||||
}
|
||||
} finally {
|
||||
IoUtils.closeQuietly(is);
|
||||
IoUtils.closeQuietly(os);
|
||||
@Override
|
||||
public void onExecute(IContentProvider provider) throws Exception {
|
||||
try (ParcelFileDescriptor fd = provider.openFile(null, mUri, "w", null, null)) {
|
||||
FileUtils.copy(FileDescriptor.in, fd.getFileDescriptor());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,8 @@ public final class Dpm extends BaseCommand {
|
||||
private static final String COMMAND_SET_DEVICE_OWNER = "set-device-owner";
|
||||
private static final String COMMAND_SET_PROFILE_OWNER = "set-profile-owner";
|
||||
private static final String COMMAND_REMOVE_ACTIVE_ADMIN = "remove-active-admin";
|
||||
private static final String COMMAND_CLEAR_FREEZE_PERIOD_RECORD = "clear-freeze-period-record";
|
||||
private static final String COMMAND_FORCE_SECURITY_LOGS = "force-security-logs";
|
||||
|
||||
private IDevicePolicyManager mDevicePolicyManager;
|
||||
private int mUserId = UserHandle.USER_SYSTEM;
|
||||
@@ -75,7 +77,15 @@ public final class Dpm extends BaseCommand {
|
||||
"\n" +
|
||||
"dpm remove-active-admin: Disables an active admin, the admin must have declared" +
|
||||
" android:testOnly in the application in its manifest. This will also remove" +
|
||||
" device and profile owners\n");
|
||||
" device and profile owners.\n" +
|
||||
"\n" +
|
||||
"dpm " + COMMAND_CLEAR_FREEZE_PERIOD_RECORD + ": clears framework-maintained " +
|
||||
"record of past freeze periods that the device went through. For use during " +
|
||||
"feature development to prevent triggering restriction on setting freeze " +
|
||||
"periods.\n" +
|
||||
"\n" +
|
||||
"dpm " + COMMAND_FORCE_SECURITY_LOGS + ": makes all security logs available to " +
|
||||
"the DPC and triggers DeviceAdminReceiver.onSecurityLogsAvailable() if needed.");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -101,11 +111,29 @@ public final class Dpm extends BaseCommand {
|
||||
case COMMAND_REMOVE_ACTIVE_ADMIN:
|
||||
runRemoveActiveAdmin();
|
||||
break;
|
||||
case COMMAND_CLEAR_FREEZE_PERIOD_RECORD:
|
||||
runClearFreezePeriodRecord();
|
||||
break;
|
||||
case COMMAND_FORCE_SECURITY_LOGS:
|
||||
runForceSecurityLogs();
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException ("unknown command '" + command + "'");
|
||||
}
|
||||
}
|
||||
|
||||
private void runForceSecurityLogs() throws RemoteException, InterruptedException {
|
||||
while (true) {
|
||||
final long toWait = mDevicePolicyManager.forceSecurityLogs();
|
||||
if (toWait == 0) {
|
||||
break;
|
||||
}
|
||||
System.out.println("We have to wait for " + toWait + " milliseconds...");
|
||||
Thread.sleep(toWait);
|
||||
}
|
||||
System.out.println("Success");
|
||||
}
|
||||
|
||||
private void parseArgs(boolean canHaveName) {
|
||||
String opt;
|
||||
while ((opt = nextOption()) != null) {
|
||||
@@ -190,6 +218,11 @@ public final class Dpm extends BaseCommand {
|
||||
+ mComponent.toShortString() + " for user " + mUserId);
|
||||
}
|
||||
|
||||
private void runClearFreezePeriodRecord() throws RemoteException {
|
||||
mDevicePolicyManager.clearSystemUpdatePolicyFreezePeriodRecord();
|
||||
System.out.println("Success");
|
||||
}
|
||||
|
||||
private ComponentName parseComponentName(String component) {
|
||||
ComponentName cn = ComponentName.unflattenFromString(component);
|
||||
if (cn == null) {
|
||||
|
||||
@@ -101,15 +101,16 @@ Device* Device::open(int32_t id, const char* name, int32_t vid, int32_t pid,
|
||||
|
||||
struct uhid_event ev;
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.type = UHID_CREATE;
|
||||
strncpy((char*)ev.u.create.name, name, UHID_MAX_NAME_LENGTH);
|
||||
ev.u.create.rd_data = descriptor.get();
|
||||
ev.u.create.rd_size = descriptorSize;
|
||||
ev.u.create.bus = BUS_BLUETOOTH;
|
||||
ev.u.create.vendor = vid;
|
||||
ev.u.create.product = pid;
|
||||
ev.u.create.version = 0;
|
||||
ev.u.create.country = 0;
|
||||
ev.type = UHID_CREATE2;
|
||||
strncpy((char*)ev.u.create2.name, name, UHID_MAX_NAME_LENGTH);
|
||||
memcpy(&ev.u.create2.rd_data, descriptor.get(),
|
||||
descriptorSize * sizeof(ev.u.create2.rd_data[0]));
|
||||
ev.u.create2.rd_size = descriptorSize;
|
||||
ev.u.create2.bus = BUS_BLUETOOTH;
|
||||
ev.u.create2.vendor = vid;
|
||||
ev.u.create2.product = pid;
|
||||
ev.u.create2.version = 0;
|
||||
ev.u.create2.country = 0;
|
||||
|
||||
errno = 0;
|
||||
ssize_t ret = TEMP_FAILURE_RETRY(::write(fd, &ev, sizeof(ev)));
|
||||
@@ -158,9 +159,9 @@ Device::~Device() {
|
||||
void Device::sendReport(uint8_t* report, size_t reportSize) {
|
||||
struct uhid_event ev;
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.type = UHID_INPUT;
|
||||
ev.u.input.size = reportSize;
|
||||
memcpy(&ev.u.input.data, report, reportSize);
|
||||
ev.type = UHID_INPUT2;
|
||||
ev.u.input2.size = reportSize;
|
||||
memcpy(&ev.u.input2.data, report, reportSize);
|
||||
ssize_t ret = TEMP_FAILURE_RETRY(::write(mFd, &ev, sizeof(ev)));
|
||||
if (ret < 0 || ret != sizeof(ev)) {
|
||||
LOGE("Failed to send hid event: %s", strerror(errno));
|
||||
|
||||
@@ -2,15 +2,8 @@
|
||||
#
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := $(call all-subdir-java-files)
|
||||
LOCAL_MODULE := imelib
|
||||
LOCAL_MODULE_STEM := ime
|
||||
include $(BUILD_JAVA_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := ime
|
||||
LOCAL_MODULE_CLASS := EXECUTABLES
|
||||
LOCAL_SRC_FILES := ime
|
||||
LOCAL_REQUIRED_MODULES := imelib
|
||||
include $(BUILD_PREBUILT)
|
||||
|
||||
@@ -1,8 +1,2 @@
|
||||
#!/system/bin/sh
|
||||
# Script to start "pm" on the device, which has a very rudimentary
|
||||
# shell.
|
||||
#
|
||||
base=/system
|
||||
export CLASSPATH=$base/framework/ime.jar
|
||||
exec app_process $base/bin com.android.commands.ime.Ime "$@"
|
||||
|
||||
exec cmd input_method ime "$@"
|
||||
|
||||
@@ -1,248 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.commands.ime;
|
||||
|
||||
import com.android.internal.view.IInputMethodManager;
|
||||
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.util.PrintStreamPrinter;
|
||||
import android.util.Printer;
|
||||
import android.view.inputmethod.InputMethodInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public final class Ime {
|
||||
IInputMethodManager mImm;
|
||||
|
||||
private String[] mArgs;
|
||||
private int mNextArg;
|
||||
private String mCurArgData;
|
||||
|
||||
private static final String IMM_NOT_RUNNING_ERR =
|
||||
"Error: Could not access the Input Method Manager. Is the system running?";
|
||||
|
||||
public static void main(String[] args) {
|
||||
new Ime().run(args);
|
||||
}
|
||||
|
||||
public void run(String[] args) {
|
||||
if (args.length < 1) {
|
||||
showUsage();
|
||||
return;
|
||||
}
|
||||
|
||||
mImm = IInputMethodManager.Stub.asInterface(ServiceManager.getService("input_method"));
|
||||
if (mImm == null) {
|
||||
System.err.println(IMM_NOT_RUNNING_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
mArgs = args;
|
||||
String op = args[0];
|
||||
mNextArg = 1;
|
||||
|
||||
if ("list".equals(op)) {
|
||||
runList();
|
||||
return;
|
||||
}
|
||||
|
||||
if ("enable".equals(op)) {
|
||||
runSetEnabled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if ("disable".equals(op)) {
|
||||
runSetEnabled(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if ("set".equals(op)) {
|
||||
runSet();
|
||||
return;
|
||||
}
|
||||
|
||||
if (op != null) {
|
||||
System.err.println("Error: unknown command '" + op + "'");
|
||||
}
|
||||
showUsage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the list sub-command.
|
||||
*/
|
||||
private void runList() {
|
||||
String opt;
|
||||
boolean all = false;
|
||||
boolean brief = false;
|
||||
while ((opt=nextOption()) != null) {
|
||||
if (opt.equals("-a")) {
|
||||
all = true;
|
||||
} else if (opt.equals("-s")) {
|
||||
brief = true;
|
||||
} else {
|
||||
System.err.println("Error: Unknown option: " + opt);
|
||||
showUsage();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
List<InputMethodInfo> methods;
|
||||
if (!all) {
|
||||
try {
|
||||
methods = mImm.getEnabledInputMethodList();
|
||||
} catch (RemoteException e) {
|
||||
System.err.println(e.toString());
|
||||
System.err.println(IMM_NOT_RUNNING_ERR);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
methods = mImm.getInputMethodList();
|
||||
} catch (RemoteException e) {
|
||||
System.err.println(e.toString());
|
||||
System.err.println(IMM_NOT_RUNNING_ERR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (methods != null) {
|
||||
Printer pr = new PrintStreamPrinter(System.out);
|
||||
for (int i=0; i<methods.size(); i++) {
|
||||
InputMethodInfo imi = methods.get(i);
|
||||
if (brief) {
|
||||
System.out.println(imi.getId());
|
||||
} else {
|
||||
System.out.println(imi.getId() + ":");
|
||||
imi.dump(pr, " ");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void runSetEnabled(boolean state) {
|
||||
String id = nextArg();
|
||||
if (id == null) {
|
||||
System.err.println("Error: no input method ID specified");
|
||||
showUsage();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
boolean res = mImm.setInputMethodEnabled(id, state);
|
||||
if (state) {
|
||||
System.out.println("Input method " + id + ": "
|
||||
+ (res ? "already enabled" : "now enabled"));
|
||||
} else {
|
||||
System.out.println("Input method " + id + ": "
|
||||
+ (res ? "now disabled" : "already disabled"));
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
System.err.println("Error: " + e.getMessage());
|
||||
return;
|
||||
} catch (RemoteException e) {
|
||||
System.err.println(e.toString());
|
||||
System.err.println(IMM_NOT_RUNNING_ERR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void runSet() {
|
||||
String id = nextArg();
|
||||
if (id == null) {
|
||||
System.err.println("Error: no input method ID specified");
|
||||
showUsage();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
mImm.setInputMethod(null, id);
|
||||
System.out.println("Input method " + id + " selected");
|
||||
} catch (IllegalArgumentException e) {
|
||||
System.err.println("Error: " + e.getMessage());
|
||||
return;
|
||||
} catch (RemoteException e) {
|
||||
System.err.println(e.toString());
|
||||
System.err.println(IMM_NOT_RUNNING_ERR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private String nextOption() {
|
||||
if (mNextArg >= mArgs.length) {
|
||||
return null;
|
||||
}
|
||||
String arg = mArgs[mNextArg];
|
||||
if (!arg.startsWith("-")) {
|
||||
return null;
|
||||
}
|
||||
mNextArg++;
|
||||
if (arg.equals("--")) {
|
||||
return null;
|
||||
}
|
||||
if (arg.length() > 1 && arg.charAt(1) != '-') {
|
||||
if (arg.length() > 2) {
|
||||
mCurArgData = arg.substring(2);
|
||||
return arg.substring(0, 2);
|
||||
} else {
|
||||
mCurArgData = null;
|
||||
return arg;
|
||||
}
|
||||
}
|
||||
mCurArgData = null;
|
||||
return arg;
|
||||
}
|
||||
|
||||
private String nextOptionData() {
|
||||
if (mCurArgData != null) {
|
||||
return mCurArgData;
|
||||
}
|
||||
if (mNextArg >= mArgs.length) {
|
||||
return null;
|
||||
}
|
||||
String data = mArgs[mNextArg];
|
||||
mNextArg++;
|
||||
return data;
|
||||
}
|
||||
|
||||
private String nextArg() {
|
||||
if (mNextArg >= mArgs.length) {
|
||||
return null;
|
||||
}
|
||||
String arg = mArgs[mNextArg];
|
||||
mNextArg++;
|
||||
return arg;
|
||||
}
|
||||
|
||||
private static void showUsage() {
|
||||
System.err.println("usage: ime list [-a] [-s]");
|
||||
System.err.println(" ime enable ID");
|
||||
System.err.println(" ime disable ID");
|
||||
System.err.println(" ime set ID");
|
||||
System.err.println("");
|
||||
System.err.println("The list command prints all enabled input methods. Use");
|
||||
System.err.println("the -a option to see all input methods. Use");
|
||||
System.err.println("the -s option to see only a single summary line of each.");
|
||||
System.err.println("");
|
||||
System.err.println("The enable command allows the given input method ID to be used.");
|
||||
System.err.println("");
|
||||
System.err.println("The disable command disallows the given input method ID from use.");
|
||||
System.err.println("");
|
||||
System.err.println("The set command switches to the given input method ID.");
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ gen_src_dir := $(local-generated-sources-dir)
|
||||
gen := $(gen_src_dir)/incident_sections.cpp
|
||||
$(gen): $(HOST_OUT_EXECUTABLES)/incident-section-gen
|
||||
$(gen): PRIVATE_CUSTOM_TOOL = \
|
||||
$(HOST_OUT_EXECUTABLES)/incident-section-gen > $@
|
||||
$(HOST_OUT_EXECUTABLES)/incident-section-gen incident > $@
|
||||
$(gen): $(HOST_OUT_EXECUTABLES)/incident-section-gen
|
||||
$(transform-generated-source)
|
||||
LOCAL_GENERATED_SOURCES += $(gen)
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <binder/IServiceManager.h>
|
||||
#include <utils/Looper.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
@@ -93,6 +94,33 @@ StatusListener::onReportFailed()
|
||||
return Status::ok();
|
||||
}
|
||||
|
||||
// ================================================================================
|
||||
static void section_list(FILE* out) {
|
||||
IncidentSection sections[INCIDENT_SECTION_COUNT];
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
// sort the sections based on id
|
||||
while (i < INCIDENT_SECTION_COUNT) {
|
||||
IncidentSection curr = INCIDENT_SECTIONS[i];
|
||||
for (int k = 0; k < j; k++) {
|
||||
if (curr.id > sections[k].id) {
|
||||
continue;
|
||||
}
|
||||
IncidentSection tmp = curr;
|
||||
curr = sections[k];
|
||||
sections[k] = tmp;
|
||||
}
|
||||
sections[j] = curr;
|
||||
i++;
|
||||
j++;
|
||||
}
|
||||
|
||||
fprintf(out, "available sections:\n");
|
||||
for (int i = 0; i < INCIDENT_SECTION_COUNT; ++i) {
|
||||
fprintf(out, "id: %4d, name: %s\n", sections[i].id, sections[i].name);
|
||||
}
|
||||
}
|
||||
|
||||
// ================================================================================
|
||||
static IncidentSection const*
|
||||
find_section(const char* name)
|
||||
@@ -116,6 +144,26 @@ find_section(const char* name)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// ================================================================================
|
||||
static int
|
||||
get_dest(const char* arg)
|
||||
{
|
||||
if (strcmp(arg, "L") == 0
|
||||
|| strcmp(arg, "LOCAL") == 0) {
|
||||
return DEST_LOCAL;
|
||||
}
|
||||
if (strcmp(arg, "E") == 0
|
||||
|| strcmp(arg, "EXPLICIT") == 0) {
|
||||
return DEST_EXPLICIT;
|
||||
}
|
||||
if (strcmp(arg, "A") == 0
|
||||
|| strcmp(arg, "AUTO") == 0
|
||||
|| strcmp(arg, "AUTOMATIC") == 0) {
|
||||
return DEST_AUTOMATIC;
|
||||
}
|
||||
return -1; // return the default value
|
||||
}
|
||||
|
||||
// ================================================================================
|
||||
static void
|
||||
usage(FILE* out)
|
||||
@@ -127,6 +175,8 @@ usage(FILE* out)
|
||||
fprintf(out, "OPTIONS\n");
|
||||
fprintf(out, " -b (default) print the report to stdout (in proto format)\n");
|
||||
fprintf(out, " -d send the report into dropbox\n");
|
||||
fprintf(out, " -l list available sections\n");
|
||||
fprintf(out, " -p privacy spec, LOCAL, EXPLICIT or AUTOMATIC\n");
|
||||
fprintf(out, "\n");
|
||||
fprintf(out, " SECTION the field numbers of the incident report fields to include\n");
|
||||
fprintf(out, "\n");
|
||||
@@ -138,20 +188,27 @@ main(int argc, char** argv)
|
||||
Status status;
|
||||
IncidentReportArgs args;
|
||||
enum { DEST_DROPBOX, DEST_STDOUT } destination = DEST_STDOUT;
|
||||
int dest = -1; // default
|
||||
|
||||
// Parse the args
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "bhd")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "bhdlp:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'b':
|
||||
destination = DEST_STDOUT;
|
||||
break;
|
||||
case 'h':
|
||||
usage(stdout);
|
||||
return 0;
|
||||
case 'l':
|
||||
section_list(stdout);
|
||||
return 0;
|
||||
case 'b':
|
||||
destination = DEST_STDOUT;
|
||||
break;
|
||||
case 'd':
|
||||
destination = DEST_DROPBOX;
|
||||
break;
|
||||
case 'p':
|
||||
dest = get_dest(optarg);
|
||||
break;
|
||||
default:
|
||||
usage(stderr);
|
||||
return 1;
|
||||
@@ -179,8 +236,7 @@ main(int argc, char** argv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
args.setDest(dest);
|
||||
|
||||
// Start the thread pool.
|
||||
sp<ProcessState> ps(ProcessState::self());
|
||||
@@ -209,6 +265,7 @@ main(int argc, char** argv)
|
||||
|
||||
if (!status.isOk()) {
|
||||
fprintf(stderr, "reportIncident returned \"%s\"\n", status.toString8().string());
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Wait for the result and print out the data they send.
|
||||
|
||||
64
cmds/incident_helper/Android.bp
Normal file
64
cmds/incident_helper/Android.bp
Normal file
@@ -0,0 +1,64 @@
|
||||
cc_defaults {
|
||||
name: "incident_helper_defaults",
|
||||
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Werror",
|
||||
"-g",
|
||||
"-O0"
|
||||
],
|
||||
|
||||
local_include_dirs: [
|
||||
"src/",
|
||||
"src/parsers/",
|
||||
],
|
||||
|
||||
srcs: [
|
||||
"src/parsers/*.cpp",
|
||||
"src/TextParserBase.cpp",
|
||||
"src/ih_util.cpp",
|
||||
],
|
||||
|
||||
generated_headers: ["gen-platform-proto-constants"],
|
||||
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"liblog",
|
||||
"libprotoutil",
|
||||
"libutils",
|
||||
],
|
||||
}
|
||||
|
||||
cc_binary {
|
||||
name: "incident_helper",
|
||||
defaults: ["incident_helper_defaults"],
|
||||
srcs: ["src/main.cpp"],
|
||||
}
|
||||
|
||||
|
||||
cc_test {
|
||||
name: "incident_helper_test",
|
||||
test_suites: ["device-tests"],
|
||||
defaults: ["incident_helper_defaults"],
|
||||
local_include_dirs: ["src/"],
|
||||
|
||||
srcs: [
|
||||
"tests/*.cpp",
|
||||
],
|
||||
|
||||
data: [
|
||||
"testdata/*",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"libgmock",
|
||||
"libplatformprotos"
|
||||
],
|
||||
|
||||
shared_libs: [
|
||||
"libprotobuf-cpp-full"
|
||||
],
|
||||
proto: {
|
||||
type: "full",
|
||||
},
|
||||
}
|
||||
27
cmds/incident_helper/AndroidTest.xml
Normal file
27
cmds/incident_helper/AndroidTest.xml
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
<configuration description="Config for incident_helper_test">
|
||||
<target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
|
||||
<option name="cleanup" value="true" />
|
||||
<option name="push" value="incident_helper_test->/data/nativetest/incident_helper_test" />
|
||||
<option name="push" value="testdata->/data/nativetest/testdata" />
|
||||
</target_preparer>
|
||||
<option name="test-suite-tag" value="apct" />
|
||||
<test class="com.android.tradefed.testtype.GTest" >
|
||||
<option name="native-test-device-path" value="/data/nativetest" />
|
||||
<option name="module-name" value="incident_helper_test" />
|
||||
</test>
|
||||
</configuration>
|
||||
2
cmds/incident_helper/OWNERS
Normal file
2
cmds/incident_helper/OWNERS
Normal file
@@ -0,0 +1,2 @@
|
||||
jinyithu@google.com
|
||||
kwekua@google.com
|
||||
26
cmds/incident_helper/README.md
Normal file
26
cmds/incident_helper/README.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# incident_helper
|
||||
|
||||
It is an executable used to help parsing text format data to protobuf.
|
||||
|
||||
## How to build, deploy, unit test
|
||||
|
||||
For the first time, build the test and create an empty directly on device:
|
||||
|
||||
```
|
||||
root$ make -j incident_helper_test && adb shell mkdir /data/nativetest64/incident_helper_test
|
||||
```
|
||||
|
||||
Run the test on a device
|
||||
|
||||
```
|
||||
root$ mmm -j frameworks/base/cmds/incident_helper && \
|
||||
adb push $OUT/data/nativetest64/incident_helper_test/* /data/nativetest64/incident_helper_test/ && \
|
||||
adb shell /data/nativetest64/incident_helper_test/incident_helper_test 2>/dev/null
|
||||
```
|
||||
## How to adapt proto changes
|
||||
|
||||
If add a new proto file, add it in Android.bp under frameworks/base/ and make incident helper
|
||||
|
||||
```
|
||||
root$ make -j48 incident_helper
|
||||
```
|
||||
55
cmds/incident_helper/src/TextParserBase.cpp
Normal file
55
cmds/incident_helper/src/TextParserBase.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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 "incident_helper"
|
||||
|
||||
#include "TextParserBase.h"
|
||||
|
||||
#include <android-base/file.h>
|
||||
|
||||
using namespace android::base;
|
||||
|
||||
// ================================================================================
|
||||
status_t NoopParser::Parse(const int in, const int out) const
|
||||
{
|
||||
string content;
|
||||
if (!ReadFdToString(in, &content)) {
|
||||
fprintf(stderr, "[%s]Failed to read data from incidentd\n", this->name.string());
|
||||
return -1;
|
||||
}
|
||||
if (!WriteStringToFd(content, out)) {
|
||||
fprintf(stderr, "[%s]Failed to write data to incidentd\n", this->name.string());
|
||||
return -1;
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
|
||||
// ================================================================================
|
||||
status_t ReverseParser::Parse(const int in, const int out) const
|
||||
{
|
||||
string content;
|
||||
if (!ReadFdToString(in, &content)) {
|
||||
fprintf(stderr, "[%s]Failed to read data from incidentd\n", this->name.string());
|
||||
return -1;
|
||||
}
|
||||
// reverse the content
|
||||
reverse(content.begin(), content.end());
|
||||
if (!WriteStringToFd(content, out)) {
|
||||
fprintf(stderr, "[%s]Failed to write data to incidentd\n", this->name.string());
|
||||
return -1;
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
72
cmds/incident_helper/src/TextParserBase.h
Normal file
72
cmds/incident_helper/src/TextParserBase.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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 TEXT_PARSER_BASE_H
|
||||
#define TEXT_PARSER_BASE_H
|
||||
|
||||
#include <utils/Errors.h>
|
||||
#include <utils/String8.h>
|
||||
|
||||
using namespace android;
|
||||
using namespace std;
|
||||
|
||||
/**
|
||||
* Base class for text parser
|
||||
*/
|
||||
class TextParserBase {
|
||||
public:
|
||||
String8 name;
|
||||
|
||||
TextParserBase(String8 name) : name(name) {};
|
||||
virtual ~TextParserBase() {};
|
||||
|
||||
virtual status_t Parse(const int in, const int out) const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* No op parser returns what it reads
|
||||
*/
|
||||
class NoopParser : public TextParserBase {
|
||||
public:
|
||||
NoopParser() : TextParserBase(String8("NoopParser")) {};
|
||||
~NoopParser() {};
|
||||
|
||||
virtual status_t Parse(const int in, const int out) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* This parser is used for testing only, results in timeout.
|
||||
*/
|
||||
class TimeoutParser : public TextParserBase {
|
||||
public:
|
||||
TimeoutParser() : TextParserBase(String8("TimeoutParser")) {};
|
||||
~TimeoutParser() {};
|
||||
|
||||
virtual status_t Parse(const int /** in */, const int /** out */) const { while (true); };
|
||||
};
|
||||
|
||||
/**
|
||||
* This parser is used for testing only, results in reversed input text.
|
||||
*/
|
||||
class ReverseParser : public TextParserBase {
|
||||
public:
|
||||
ReverseParser() : TextParserBase(String8("ReverseParser")) {};
|
||||
~ReverseParser() {};
|
||||
|
||||
virtual status_t Parse(const int in, const int out) const;
|
||||
};
|
||||
|
||||
#endif // TEXT_PARSER_BASE_H
|
||||
458
cmds/incident_helper/src/ih_util.cpp
Normal file
458
cmds/incident_helper/src/ih_util.cpp
Normal file
@@ -0,0 +1,458 @@
|
||||
/*
|
||||
* 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 "incident_helper"
|
||||
|
||||
#include "ih_util.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <unistd.h>
|
||||
|
||||
bool isValidChar(char c) {
|
||||
uint8_t v = (uint8_t)c;
|
||||
return (v >= (uint8_t)'a' && v <= (uint8_t)'z')
|
||||
|| (v >= (uint8_t)'A' && v <= (uint8_t)'Z')
|
||||
|| (v >= (uint8_t)'0' && v <= (uint8_t)'9')
|
||||
|| (v == (uint8_t)'_');
|
||||
}
|
||||
|
||||
std::string trim(const std::string& s, const std::string& charset) {
|
||||
const auto head = s.find_first_not_of(charset);
|
||||
if (head == std::string::npos) return "";
|
||||
|
||||
const auto tail = s.find_last_not_of(charset);
|
||||
return s.substr(head, tail - head + 1);
|
||||
}
|
||||
|
||||
static inline std::string toLowerStr(const std::string& s) {
|
||||
std::string res(s);
|
||||
std::transform(res.begin(), res.end(), res.begin(), ::tolower);
|
||||
return res;
|
||||
}
|
||||
|
||||
static inline std::string trimDefault(const std::string& s) {
|
||||
return trim(s, DEFAULT_WHITESPACE);
|
||||
}
|
||||
|
||||
static inline std::string trimHeader(const std::string& s) {
|
||||
return toLowerStr(trimDefault(s));
|
||||
}
|
||||
|
||||
static inline bool isNumber(const std::string& s) {
|
||||
std::string::const_iterator it = s.begin();
|
||||
while (it != s.end() && std::isdigit(*it)) ++it;
|
||||
return !s.empty() && it == s.end();
|
||||
}
|
||||
|
||||
// This is similiar to Split in android-base/file.h, but it won't add empty string
|
||||
static void split(const std::string& line, std::vector<std::string>& words,
|
||||
const trans_func& func, const std::string& delimiters) {
|
||||
words.clear(); // clear the buffer before split
|
||||
|
||||
size_t base = 0;
|
||||
size_t found;
|
||||
while (true) {
|
||||
found = line.find_first_of(delimiters, base);
|
||||
if (found != base) {
|
||||
std::string word = (*func) (line.substr(base, found - base));
|
||||
if (!word.empty()) {
|
||||
words.push_back(word);
|
||||
}
|
||||
}
|
||||
if (found == line.npos) break;
|
||||
base = found + 1;
|
||||
}
|
||||
}
|
||||
|
||||
header_t parseHeader(const std::string& line, const std::string& delimiters) {
|
||||
header_t header;
|
||||
trans_func f = &trimHeader;
|
||||
split(line, header, f, delimiters);
|
||||
return header;
|
||||
}
|
||||
|
||||
record_t parseRecord(const std::string& line, const std::string& delimiters) {
|
||||
record_t record;
|
||||
trans_func f = &trimDefault;
|
||||
split(line, record, f, delimiters);
|
||||
return record;
|
||||
}
|
||||
|
||||
bool getColumnIndices(std::vector<int>& indices, const char** headerNames, const std::string& line) {
|
||||
indices.clear();
|
||||
|
||||
size_t lastIndex = 0;
|
||||
int i = 0;
|
||||
while (headerNames[i] != NULL) {
|
||||
std::string s = headerNames[i];
|
||||
lastIndex = line.find(s, lastIndex);
|
||||
if (lastIndex == std::string::npos) {
|
||||
fprintf(stderr, "Bad Task Header: %s\n", line.c_str());
|
||||
return false;
|
||||
}
|
||||
lastIndex += s.length();
|
||||
indices.push_back(lastIndex);
|
||||
i++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
record_t parseRecordByColumns(const std::string& line, const std::vector<int>& indices, const std::string& delimiters) {
|
||||
record_t record;
|
||||
int lastIndex = 0;
|
||||
int lastBeginning = 0;
|
||||
int lineSize = (int)line.size();
|
||||
for (std::vector<int>::const_iterator it = indices.begin(); it != indices.end(); ++it) {
|
||||
int idx = *it;
|
||||
if (idx <= lastIndex) {
|
||||
// We saved up until lastIndex last time, so we should start at
|
||||
// lastIndex + 1 this time.
|
||||
idx = lastIndex + 1;
|
||||
}
|
||||
if (idx > lineSize) {
|
||||
if (lastIndex < idx && lastIndex < lineSize) {
|
||||
// There's a little bit more for us to save, which we'll do
|
||||
// outside of the loop.
|
||||
break;
|
||||
}
|
||||
// If we're past the end of the line AND we've already saved everything up to the end.
|
||||
fprintf(stderr, "index wrong: lastIndex: %d, idx: %d, lineSize: %d\n", lastIndex, idx, lineSize);
|
||||
record.clear(); // The indices are wrong, return empty.
|
||||
return record;
|
||||
}
|
||||
while (idx < lineSize && delimiters.find(line[idx++]) == std::string::npos);
|
||||
record.push_back(trimDefault(line.substr(lastIndex, idx - lastIndex)));
|
||||
lastBeginning = lastIndex;
|
||||
lastIndex = idx;
|
||||
}
|
||||
if (lineSize - lastIndex > 0) {
|
||||
int beginning = lastIndex;
|
||||
if (record.size() == indices.size()) {
|
||||
// We've already encountered all of the columns...put whatever is
|
||||
// left in the last column.
|
||||
record.pop_back();
|
||||
beginning = lastBeginning;
|
||||
}
|
||||
record.push_back(trimDefault(line.substr(beginning, lineSize - beginning)));
|
||||
}
|
||||
return record;
|
||||
}
|
||||
|
||||
void printRecord(const record_t& record) {
|
||||
fprintf(stderr, "Record: { ");
|
||||
if (record.size() == 0) {
|
||||
fprintf(stderr, "}\n");
|
||||
return;
|
||||
}
|
||||
for(size_t i = 0; i < record.size(); ++i) {
|
||||
if(i != 0) fprintf(stderr, "\", ");
|
||||
fprintf(stderr, "\"%s", record[i].c_str());
|
||||
}
|
||||
fprintf(stderr, "\" }\n");
|
||||
}
|
||||
|
||||
bool stripPrefix(std::string* line, const char* key, bool endAtDelimiter) {
|
||||
const auto head = line->find_first_not_of(DEFAULT_WHITESPACE);
|
||||
if (head == std::string::npos) return false;
|
||||
int len = (int)line->length();
|
||||
int i = 0;
|
||||
int j = head;
|
||||
while (key[i] != '\0') {
|
||||
if (j >= len || key[i++] != line->at(j++)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (endAtDelimiter) {
|
||||
// this means if the line only have prefix or no delimiter, we still return false.
|
||||
if (j == len || isValidChar(line->at(j))) return false;
|
||||
}
|
||||
|
||||
line->assign(trimDefault(line->substr(j)));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool stripSuffix(std::string* line, const char* key, bool endAtDelimiter) {
|
||||
const auto tail = line->find_last_not_of(DEFAULT_WHITESPACE);
|
||||
if (tail == std::string::npos) return false;
|
||||
int i = 0;
|
||||
while (key[++i] != '\0'); // compute the size of the key
|
||||
int j = tail;
|
||||
while (i > 0) {
|
||||
if (j < 0 || key[--i] != line->at(j--)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (endAtDelimiter) {
|
||||
// this means if the line only have suffix or no delimiter, we still return false.
|
||||
if (j < 0 || isValidChar(line->at(j))) return false;
|
||||
}
|
||||
|
||||
line->assign(trimDefault(line->substr(0, j+1)));
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string behead(std::string* line, const char cut) {
|
||||
auto found = line->find_first_of(cut);
|
||||
if (found == std::string::npos) {
|
||||
std::string head = line->substr(0);
|
||||
line->assign("");
|
||||
return head;
|
||||
}
|
||||
std::string head = line->substr(0, found);
|
||||
while(line->at(found) == cut) found++; // trim more cut of the rest
|
||||
line->assign(line->substr(found));
|
||||
return head;
|
||||
}
|
||||
|
||||
int toInt(const std::string& s) {
|
||||
return atoi(s.c_str());
|
||||
}
|
||||
|
||||
long long toLongLong(const std::string& s) {
|
||||
return atoll(s.c_str());
|
||||
}
|
||||
|
||||
double toDouble(const std::string& s) {
|
||||
return atof(s.c_str());
|
||||
}
|
||||
|
||||
// ==============================================================================
|
||||
Reader::Reader(const int fd)
|
||||
{
|
||||
mFile = fdopen(fd, "r");
|
||||
mStatus = mFile == NULL ? "Invalid fd " + std::to_string(fd) : "";
|
||||
}
|
||||
|
||||
Reader::~Reader()
|
||||
{
|
||||
if (mFile != NULL) fclose(mFile);
|
||||
}
|
||||
|
||||
bool Reader::readLine(std::string* line) {
|
||||
if (mFile == NULL) return false;
|
||||
|
||||
char* buf = NULL;
|
||||
size_t len = 0;
|
||||
ssize_t read = getline(&buf, &len, mFile);
|
||||
if (read != -1) {
|
||||
std::string s(buf);
|
||||
line->assign(trim(s, DEFAULT_NEWLINE));
|
||||
} else if (errno == EINVAL) {
|
||||
mStatus = "Bad Argument";
|
||||
}
|
||||
free(buf);
|
||||
return read != -1;
|
||||
}
|
||||
|
||||
bool Reader::ok(std::string* error) {
|
||||
error->assign(mStatus);
|
||||
return mStatus.empty();
|
||||
}
|
||||
|
||||
// ==============================================================================
|
||||
Table::Table(const char* names[], const uint64_t ids[], const int count)
|
||||
:mEnums(),
|
||||
mEnumValuesByName()
|
||||
{
|
||||
std::map<std::string, uint64_t> fields;
|
||||
for (int i = 0; i < count; i++) {
|
||||
fields[names[i]] = ids[i];
|
||||
}
|
||||
mFields = fields;
|
||||
}
|
||||
|
||||
Table::~Table()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
Table::addEnumTypeMap(const char* field, const char* enumNames[], const int enumValues[], const int enumSize)
|
||||
{
|
||||
if (mFields.find(field) == mFields.end()) {
|
||||
fprintf(stderr, "Field '%s' not found", field);
|
||||
return;
|
||||
}
|
||||
|
||||
std::map<std::string, int> enu;
|
||||
for (int i = 0; i < enumSize; i++) {
|
||||
enu[enumNames[i]] = enumValues[i];
|
||||
}
|
||||
mEnums[field] = enu;
|
||||
}
|
||||
|
||||
void
|
||||
Table::addEnumNameToValue(const char* enumName, const int enumValue)
|
||||
{
|
||||
mEnumValuesByName[enumName] = enumValue;
|
||||
}
|
||||
|
||||
bool
|
||||
Table::insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value)
|
||||
{
|
||||
if (mFields.find(name) == mFields.end()) return false;
|
||||
|
||||
uint64_t found = mFields[name];
|
||||
record_t repeats; // used for repeated fields
|
||||
switch ((found & FIELD_COUNT_MASK) | (found & FIELD_TYPE_MASK)) {
|
||||
case FIELD_COUNT_SINGLE | FIELD_TYPE_DOUBLE:
|
||||
case FIELD_COUNT_SINGLE | FIELD_TYPE_FLOAT:
|
||||
proto->write(found, toDouble(value));
|
||||
break;
|
||||
case FIELD_COUNT_SINGLE | FIELD_TYPE_STRING:
|
||||
case FIELD_COUNT_SINGLE | FIELD_TYPE_BYTES:
|
||||
proto->write(found, value);
|
||||
break;
|
||||
case FIELD_COUNT_SINGLE | FIELD_TYPE_INT64:
|
||||
case FIELD_COUNT_SINGLE | FIELD_TYPE_SINT64:
|
||||
case FIELD_COUNT_SINGLE | FIELD_TYPE_UINT64:
|
||||
case FIELD_COUNT_SINGLE | FIELD_TYPE_FIXED64:
|
||||
case FIELD_COUNT_SINGLE | FIELD_TYPE_SFIXED64:
|
||||
proto->write(found, toLongLong(value));
|
||||
break;
|
||||
case FIELD_COUNT_SINGLE | FIELD_TYPE_BOOL:
|
||||
if (strcmp(toLowerStr(value).c_str(), "true") == 0 || strcmp(value.c_str(), "1") == 0) {
|
||||
proto->write(found, true);
|
||||
break;
|
||||
}
|
||||
if (strcmp(toLowerStr(value).c_str(), "false") == 0 || strcmp(value.c_str(), "0") == 0) {
|
||||
proto->write(found, false);
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
case FIELD_COUNT_SINGLE | FIELD_TYPE_ENUM:
|
||||
// if the field has its own enum mapping, use this, otherwise use general name to value mapping.
|
||||
if (mEnums.find(name) != mEnums.end()) {
|
||||
if (mEnums[name].find(value) != mEnums[name].end()) {
|
||||
proto->write(found, mEnums[name][value]);
|
||||
} else {
|
||||
proto->write(found, 0); // TODO: should get the default enum value (Unknown)
|
||||
}
|
||||
} else if (mEnumValuesByName.find(value) != mEnumValuesByName.end()) {
|
||||
proto->write(found, mEnumValuesByName[value]);
|
||||
} else if (isNumber(value)) {
|
||||
proto->write(found, toInt(value));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case FIELD_COUNT_SINGLE | FIELD_TYPE_INT32:
|
||||
case FIELD_COUNT_SINGLE | FIELD_TYPE_SINT32:
|
||||
case FIELD_COUNT_SINGLE | FIELD_TYPE_UINT32:
|
||||
case FIELD_COUNT_SINGLE | FIELD_TYPE_FIXED32:
|
||||
case FIELD_COUNT_SINGLE | FIELD_TYPE_SFIXED32:
|
||||
proto->write(found, toInt(value));
|
||||
break;
|
||||
// REPEATED TYPE below:
|
||||
case FIELD_COUNT_REPEATED | FIELD_TYPE_INT32:
|
||||
repeats = parseRecord(value, COMMA_DELIMITER);
|
||||
for (size_t i=0; i<repeats.size(); i++) {
|
||||
proto->write(found, toInt(repeats[i]));
|
||||
}
|
||||
break;
|
||||
case FIELD_COUNT_REPEATED | FIELD_TYPE_STRING:
|
||||
repeats = parseRecord(value, COMMA_DELIMITER);
|
||||
for (size_t i=0; i<repeats.size(); i++) {
|
||||
proto->write(found, repeats[i]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// ================================================================================
|
||||
Message::Message(Table* table)
|
||||
:mTable(table),
|
||||
mPreviousField(""),
|
||||
mTokens(),
|
||||
mSubMessages()
|
||||
{
|
||||
}
|
||||
|
||||
Message::~Message()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
Message::addSubMessage(uint64_t fieldId, Message* fieldMsg)
|
||||
{
|
||||
for (auto iter = mTable->mFields.begin(); iter != mTable->mFields.end(); iter++) {
|
||||
if (iter->second == fieldId) {
|
||||
mSubMessages[iter->first] = fieldMsg;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Message::insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value)
|
||||
{
|
||||
// If the field name can be found, it means the name is a primitive field.
|
||||
if (mTable->mFields.find(name) != mTable->mFields.end()) {
|
||||
endSession(proto);
|
||||
// The only edge case is for example ro.hardware itself is a message, so a field called "value"
|
||||
// would be defined in proto Ro::Hardware and it must be the first field.
|
||||
if (mSubMessages.find(name) != mSubMessages.end()) {
|
||||
startSession(proto, name);
|
||||
return mSubMessages[name]->insertField(proto, "value", value);
|
||||
} else {
|
||||
return mTable->insertField(proto, name, value);
|
||||
}
|
||||
}
|
||||
|
||||
// Try to find the message field which is the prefix of name, so the value would be inserted
|
||||
// recursively into the submessage.
|
||||
std::string mutableName = name;
|
||||
for (auto iter = mSubMessages.begin(); iter != mSubMessages.end(); iter++) {
|
||||
std::string fieldName = iter->first;
|
||||
std::string prefix = fieldName + "_"; // underscore is the delimiter in the name
|
||||
if (stripPrefix(&mutableName, prefix.c_str())) {
|
||||
if (mPreviousField != fieldName) {
|
||||
endSession(proto);
|
||||
startSession(proto, fieldName);
|
||||
}
|
||||
return mSubMessages[fieldName]->insertField(proto, mutableName, value);
|
||||
}
|
||||
}
|
||||
// Can't find the name in proto definition, handle it separately.
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
Message::startSession(ProtoOutputStream* proto, const std::string& name)
|
||||
{
|
||||
uint64_t fieldId = mTable->mFields[name];
|
||||
uint64_t token = proto->start(fieldId);
|
||||
mPreviousField = name;
|
||||
mTokens.push(token);
|
||||
}
|
||||
|
||||
void
|
||||
Message::endSession(ProtoOutputStream* proto)
|
||||
{
|
||||
if (mPreviousField == "") return;
|
||||
if (mSubMessages.find(mPreviousField) != mSubMessages.end()) {
|
||||
mSubMessages[mPreviousField]->endSession(proto);
|
||||
}
|
||||
proto->end(mTokens.top());
|
||||
mTokens.pop();
|
||||
mPreviousField = "";
|
||||
}
|
||||
201
cmds/incident_helper/src/ih_util.h
Normal file
201
cmds/incident_helper/src/ih_util.h
Normal file
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* 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 INCIDENT_HELPER_UTIL_H
|
||||
#define INCIDENT_HELPER_UTIL_H
|
||||
|
||||
#include <map>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <android/util/ProtoOutputStream.h>
|
||||
|
||||
using namespace android::util;
|
||||
|
||||
typedef std::vector<std::string> header_t;
|
||||
typedef std::vector<std::string> record_t;
|
||||
typedef std::string (*trans_func) (const std::string&);
|
||||
|
||||
const std::string DEFAULT_WHITESPACE = " \t";
|
||||
const std::string DEFAULT_NEWLINE = "\r\n";
|
||||
const std::string TAB_DELIMITER = "\t";
|
||||
const std::string COMMA_DELIMITER = ",";
|
||||
const std::string PIPE_DELIMITER = "|";
|
||||
const std::string PARENTHESES_DELIMITER = "()";
|
||||
|
||||
// returns true if c is a-zA-Z0-9 or underscore
|
||||
bool isValidChar(char c);
|
||||
|
||||
// trim the string with the given charset
|
||||
std::string trim(const std::string& s, const std::string& charset);
|
||||
|
||||
/**
|
||||
* When a text has a table format like this
|
||||
* line 1: HeadA HeadB HeadC
|
||||
* line 2: v1 v2 v3
|
||||
* line 3: v11 v12 v13
|
||||
*
|
||||
* We want to parse the line in structure given the delimiter.
|
||||
* parseHeader is used to parse the firse line of the table and returns a list of strings in lower case
|
||||
* parseRecord is used to parse other lines and returns a list of strings
|
||||
* empty strings are skipped
|
||||
*/
|
||||
header_t parseHeader(const std::string& line, const std::string& delimiters = DEFAULT_WHITESPACE);
|
||||
record_t parseRecord(const std::string& line, const std::string& delimiters = DEFAULT_WHITESPACE);
|
||||
|
||||
/**
|
||||
* Gets the list of end indices of each word in the line and places it in the given vector,
|
||||
* clearing out the vector beforehand. These indices can be used with parseRecordByColumns.
|
||||
* Will return false if there was a problem getting the indices. headerNames
|
||||
* must be NULL terminated.
|
||||
*/
|
||||
bool getColumnIndices(std::vector<int>& indices, const char* headerNames[], const std::string& line);
|
||||
|
||||
/**
|
||||
* When a text-format table aligns by its vertical position, it is not possible to split them by purely delimiters.
|
||||
* This function allows to parse record by its header's column position' indices, must in ascending order.
|
||||
* At the same time, it still looks at the char at index, if it doesn't belong to delimiters, moves forward to find the delimiters.
|
||||
*/
|
||||
record_t parseRecordByColumns(const std::string& line, const std::vector<int>& indices, const std::string& delimiters = DEFAULT_WHITESPACE);
|
||||
|
||||
/** Prints record_t to stderr */
|
||||
void printRecord(const record_t& record);
|
||||
|
||||
/**
|
||||
* When the line starts/ends with the given key, the function returns true
|
||||
* as well as the line argument is changed to the rest trimmed part of the original.
|
||||
* e.g. "ZRAM: 6828K physical used for 31076K in swap (524284K total swap)" becomes
|
||||
* "6828K physical used for 31076K in swap (524284K total swap)" when given key "ZRAM:",
|
||||
* otherwise the line is not changed.
|
||||
*
|
||||
* In order to prevent two values have same prefix which cause entering to incorrect conditions,
|
||||
* stripPrefix and stripSuffix can turn on a flag that requires the ending char in the line must not be a valid
|
||||
* character or digits, this feature is off by default.
|
||||
* i.e. ABC%some value, ABCD%other value
|
||||
*/
|
||||
bool stripPrefix(std::string* line, const char* key, bool endAtDelimiter = false);
|
||||
bool stripSuffix(std::string* line, const char* key, bool endAtDelimiter = false);
|
||||
|
||||
/**
|
||||
* behead the given line by the cut, return the head and reassign the line to be the rest.
|
||||
*/
|
||||
std::string behead(std::string* line, const char cut);
|
||||
|
||||
/**
|
||||
* Converts string to the desired type
|
||||
*/
|
||||
int toInt(const std::string& s);
|
||||
long long toLongLong(const std::string& s);
|
||||
double toDouble(const std::string& s);
|
||||
|
||||
/**
|
||||
* Reader class reads data from given fd in streaming fashion.
|
||||
* The buffer size is controlled by capacity parameter.
|
||||
*/
|
||||
class Reader
|
||||
{
|
||||
public:
|
||||
Reader(const int fd);
|
||||
~Reader();
|
||||
|
||||
bool readLine(std::string* line);
|
||||
bool ok(std::string* error);
|
||||
|
||||
private:
|
||||
FILE* mFile;
|
||||
std::string mStatus;
|
||||
};
|
||||
|
||||
/**
|
||||
* The Table class is constructed from two arrays generated by the given message with
|
||||
* option (stream_proto.stream_msg).enable_fields_mapping = true.
|
||||
* The names are each field's names in the message and must corresponding to the header/name of
|
||||
* the text to be parsed, and the ids are the streaming proto encoded field ids.
|
||||
*
|
||||
* This class then allows users to insert the table values to proto based on its header.
|
||||
*
|
||||
* Advance feature: if some fields in the message are enums, user must explicitly add the
|
||||
* mapping from enum name string to its enum values.
|
||||
*/
|
||||
class Message;
|
||||
class Table
|
||||
{
|
||||
friend class Message;
|
||||
public:
|
||||
Table(const char* names[], const uint64_t ids[], const int count);
|
||||
~Table();
|
||||
|
||||
// Add enum names to values for parsing purpose.
|
||||
void addEnumTypeMap(const char* field, const char* enumNames[], const int enumValues[], const int enumSize);
|
||||
|
||||
// Manually add enum names to values mapping, useful when an Enum type is used by
|
||||
// a number of fields, there must not be any enum name conflicts.
|
||||
void addEnumNameToValue(const char* enumName, const int enumValue);
|
||||
|
||||
// Based on given name, find the right field id, parse the text value and insert to proto.
|
||||
// Return false if the given name can't be found.
|
||||
bool insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value);
|
||||
private:
|
||||
std::map<std::string, uint64_t> mFields;
|
||||
std::map<std::string, std::map<std::string, int>> mEnums;
|
||||
std::map<std::string, int> mEnumValuesByName;
|
||||
};
|
||||
|
||||
/**
|
||||
* Reconstructs a typical proto message given its message Table, adds submessage fields explicitly.
|
||||
* It allows user to insert nested proto values purely by the names. See insertField for detail.
|
||||
*/
|
||||
class Message
|
||||
{
|
||||
public:
|
||||
Message(Table* table);
|
||||
~Message();
|
||||
|
||||
// Reconstructs the typical proto message by adding its message fields.
|
||||
void addSubMessage(uint64_t fieldId, Message* fieldMsg);
|
||||
|
||||
// Inserts value if the given name has the corresponding field in its message and return true.
|
||||
// It will recursively search the name in submessages and find the correct field to insert.
|
||||
// For example, when the name is dalvik_vm_heapsize, and the message's corresponding proto is:
|
||||
// message Properties {
|
||||
// message DalvikVm {
|
||||
// int32 heapsize = 1;
|
||||
// bool usejit = 2;
|
||||
// }
|
||||
// DalvikVm dalvik_vm = 1;
|
||||
// string hack_in = 2;
|
||||
// }
|
||||
// The value will be inserted into field heapsize in dalvik_vm submessage.
|
||||
//
|
||||
// Also value belongs to same submessage MUST be inserted contiguously.
|
||||
// For example, dalvik_vm_usejit must be inserted directly after dalvik_vm_heapsize, otherwise
|
||||
// if hack_in attempts to be inserted before dalvik_vm_usejit, value of usejit isn't added as expected.
|
||||
bool insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value);
|
||||
|
||||
// Starts a new message field proto session.
|
||||
void startSession(ProtoOutputStream* proto, const std::string& name);
|
||||
|
||||
// Ends the previous message field proto session.
|
||||
void endSession(ProtoOutputStream* proto);
|
||||
private:
|
||||
Table* mTable;
|
||||
std::string mPreviousField;
|
||||
std::stack<uint64_t> mTokens;
|
||||
std::map<std::string, Message*> mSubMessages;
|
||||
};
|
||||
|
||||
#endif // INCIDENT_HELPER_UTIL_H
|
||||
113
cmds/incident_helper/src/main.cpp
Normal file
113
cmds/incident_helper/src/main.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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 "incident_helper"
|
||||
|
||||
#include "parsers/BatteryTypeParser.h"
|
||||
#include "parsers/CpuFreqParser.h"
|
||||
#include "parsers/CpuInfoParser.h"
|
||||
#include "parsers/EventLogTagsParser.h"
|
||||
#include "parsers/KernelWakesParser.h"
|
||||
#include "parsers/PageTypeInfoParser.h"
|
||||
#include "parsers/ProcrankParser.h"
|
||||
#include "parsers/PsParser.h"
|
||||
#include "parsers/SystemPropertiesParser.h"
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <getopt.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
using namespace android::base;
|
||||
using namespace std;
|
||||
|
||||
static void usage(FILE* out) {
|
||||
fprintf(out, "incident_helper is not designed to run manually,");
|
||||
fprintf(out, "it reads from stdin and writes to stdout, see README.md for details.\n");
|
||||
fprintf(out, "usage: incident_helper -s SECTION\n");
|
||||
fprintf(out, "REQUIRED:\n");
|
||||
fprintf(out, " -s section id, must be positive\n");
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
static TextParserBase* selectParser(int section) {
|
||||
switch (section) {
|
||||
// IDs smaller than or equal to 0 are reserved for testing
|
||||
case -1:
|
||||
return new TimeoutParser();
|
||||
case 0:
|
||||
return new NoopParser();
|
||||
case 1: // 1 is reserved for incident header so it won't be section id
|
||||
return new ReverseParser();
|
||||
/* ========================================================================= */
|
||||
// IDs larger than 1 are section ids reserved in incident.proto
|
||||
case 1000:
|
||||
return new SystemPropertiesParser();
|
||||
case 1100:
|
||||
return new EventLogTagsParser();
|
||||
case 2000:
|
||||
return new ProcrankParser();
|
||||
case 2001:
|
||||
return new PageTypeInfoParser();
|
||||
case 2002:
|
||||
return new KernelWakesParser();
|
||||
case 2003:
|
||||
return new CpuInfoParser();
|
||||
case 2004:
|
||||
return new CpuFreqParser();
|
||||
case 2005:
|
||||
return new PsParser();
|
||||
case 2006:
|
||||
return new BatteryTypeParser();
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
int main(int argc, char** argv) {
|
||||
fprintf(stderr, "Start incident_helper...\n");
|
||||
|
||||
// Parse the args
|
||||
int opt;
|
||||
int sectionID = 0;
|
||||
while ((opt = getopt(argc, argv, "hs:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
usage(stdout);
|
||||
return 0;
|
||||
case 's':
|
||||
sectionID = atoi(optarg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "Pasring section %d...\n", sectionID);
|
||||
TextParserBase* parser = selectParser(sectionID);
|
||||
if (parser != NULL) {
|
||||
fprintf(stderr, "Running parser: %s\n", parser->name.string());
|
||||
status_t err = parser->Parse(STDIN_FILENO, STDOUT_FILENO);
|
||||
if (err != NO_ERROR) {
|
||||
fprintf(stderr, "Parse error in section %d: %s\n", sectionID, strerror(-err));
|
||||
return -1;
|
||||
}
|
||||
|
||||
delete parser;
|
||||
}
|
||||
fprintf(stderr, "Finish section %d, exiting...\n", sectionID);
|
||||
|
||||
return 0;
|
||||
}
|
||||
60
cmds/incident_helper/src/parsers/BatteryTypeParser.cpp
Normal file
60
cmds/incident_helper/src/parsers/BatteryTypeParser.cpp
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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 "incident_helper"
|
||||
|
||||
#include <android/util/ProtoOutputStream.h>
|
||||
|
||||
#include "frameworks/base/core/proto/android/os/batterytype.proto.h"
|
||||
#include "ih_util.h"
|
||||
#include "BatteryTypeParser.h"
|
||||
|
||||
using namespace android::os;
|
||||
|
||||
status_t
|
||||
BatteryTypeParser::Parse(const int in, const int out) const
|
||||
{
|
||||
Reader reader(in);
|
||||
string line;
|
||||
bool readLine = false;
|
||||
|
||||
ProtoOutputStream proto;
|
||||
|
||||
// parse line by line
|
||||
while (reader.readLine(&line)) {
|
||||
if (line.empty()) continue;
|
||||
|
||||
if (readLine) {
|
||||
fprintf(stderr, "Multiple lines in file. Unsure what to do.\n");
|
||||
break;
|
||||
}
|
||||
|
||||
proto.write(BatteryTypeProto::TYPE, line);
|
||||
|
||||
readLine = true;
|
||||
}
|
||||
|
||||
if (!reader.ok(&line)) {
|
||||
fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!proto.flush(out)) {
|
||||
fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
|
||||
return -1;
|
||||
}
|
||||
fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size());
|
||||
return NO_ERROR;
|
||||
}
|
||||
36
cmds/incident_helper/src/parsers/BatteryTypeParser.h
Normal file
36
cmds/incident_helper/src/parsers/BatteryTypeParser.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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 BATTERY_TYPE_PARSER_H
|
||||
#define BATTERY_TYPE_PARSER_H
|
||||
|
||||
#include "TextParserBase.h"
|
||||
|
||||
using namespace android;
|
||||
|
||||
/**
|
||||
* Battery type parser, parses text in file
|
||||
* /sys/class/power_supply/bms/battery_type.
|
||||
*/
|
||||
class BatteryTypeParser : public TextParserBase {
|
||||
public:
|
||||
BatteryTypeParser() : TextParserBase(String8("BatteryTypeParser")) {};
|
||||
~BatteryTypeParser() {};
|
||||
|
||||
virtual status_t Parse(const int in, const int out) const;
|
||||
};
|
||||
|
||||
#endif // BATTERY_TYPE_PARSER_H
|
||||
90
cmds/incident_helper/src/parsers/CpuFreqParser.cpp
Normal file
90
cmds/incident_helper/src/parsers/CpuFreqParser.cpp
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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 "incident_helper"
|
||||
|
||||
#include <android/util/ProtoOutputStream.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "frameworks/base/core/proto/android/os/cpufreq.proto.h"
|
||||
#include "ih_util.h"
|
||||
#include "CpuFreqParser.h"
|
||||
|
||||
using namespace android::os;
|
||||
|
||||
status_t
|
||||
CpuFreqParser::Parse(const int in, const int out) const
|
||||
{
|
||||
Reader reader(in);
|
||||
string line;
|
||||
|
||||
// parse header
|
||||
reader.readLine(&line);
|
||||
header_t header = parseHeader(line, TAB_DELIMITER);
|
||||
if (header.size() < 1) {
|
||||
fprintf(stderr, "Bad header: %s\n", line.c_str());
|
||||
return BAD_VALUE;
|
||||
}
|
||||
const int numCpus = (int)header.size() - 1;
|
||||
vector<pair<int, long long>> cpucores[numCpus];
|
||||
|
||||
// parse freq and time
|
||||
while (reader.readLine(&line)) {
|
||||
if (line.empty()) continue;
|
||||
|
||||
record_t record = parseRecord(line, TAB_DELIMITER);
|
||||
if (record.size() != header.size()) {
|
||||
fprintf(stderr, "Bad line: %s\n", line.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
int freq = toInt(record[0]);
|
||||
for (int i=0; i<numCpus; i++) {
|
||||
if (strcmp(record[i+1].c_str(), "N/A") == 0) {
|
||||
continue;
|
||||
}
|
||||
cpucores[i].push_back(make_pair(freq, toLongLong(record[i+1])));
|
||||
}
|
||||
}
|
||||
|
||||
ProtoOutputStream proto;
|
||||
|
||||
long jiffyHz = sysconf(_SC_CLK_TCK);
|
||||
proto.write(CpuFreqProto::JIFFY_HZ, (int)jiffyHz);
|
||||
|
||||
for (int i=0; i<numCpus; i++) {
|
||||
uint64_t token = proto.start(CpuFreqProto::CPU_FREQS);
|
||||
proto.write(CpuFreqProto::Stats::CPU_NAME, header[i+1]);
|
||||
for (vector<pair<int, long long>>::iterator it = cpucores[i].begin(); it != cpucores[i].end(); it++) {
|
||||
uint64_t stateToken = proto.start(CpuFreqProto::Stats::TIMES);
|
||||
proto.write(CpuFreqProto::Stats::TimeInState::STATE_KHZ, it->first);
|
||||
proto.write(CpuFreqProto::Stats::TimeInState::TIME_JIFFY, it->second);
|
||||
proto.end(stateToken);
|
||||
}
|
||||
proto.end(token);
|
||||
}
|
||||
|
||||
if (!reader.ok(&line)) {
|
||||
fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!proto.flush(out)) {
|
||||
fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
|
||||
return -1;
|
||||
}
|
||||
fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size());
|
||||
return NO_ERROR;
|
||||
}
|
||||
35
cmds/incident_helper/src/parsers/CpuFreqParser.h
Normal file
35
cmds/incident_helper/src/parsers/CpuFreqParser.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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 CPU_FREQ_PARSER_H
|
||||
#define CPU_FREQ_PARSER_H
|
||||
|
||||
#include "TextParserBase.h"
|
||||
|
||||
using namespace android;
|
||||
|
||||
/**
|
||||
* Cpu frequency parser, parses text in /sys/devices/system/cpu/cpufreq/all_time_in_state
|
||||
*/
|
||||
class CpuFreqParser : public TextParserBase {
|
||||
public:
|
||||
CpuFreqParser() : TextParserBase(String8("CpuFreqParser")) {};
|
||||
~CpuFreqParser() {};
|
||||
|
||||
virtual status_t Parse(const int in, const int out) const;
|
||||
};
|
||||
|
||||
#endif // CPU_FREQ_PARSER_H
|
||||
162
cmds/incident_helper/src/parsers/CpuInfoParser.cpp
Normal file
162
cmds/incident_helper/src/parsers/CpuInfoParser.cpp
Normal file
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* 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 "incident_helper"
|
||||
|
||||
#include <android/util/ProtoOutputStream.h>
|
||||
|
||||
#include "frameworks/base/core/proto/android/os/cpuinfo.proto.h"
|
||||
#include "ih_util.h"
|
||||
#include "CpuInfoParser.h"
|
||||
|
||||
using namespace android::os;
|
||||
|
||||
static void writeSuffixLine(ProtoOutputStream* proto, uint64_t fieldId,
|
||||
const string& line, const string& delimiter,
|
||||
const int count, const char* names[], const uint64_t ids[])
|
||||
{
|
||||
record_t record = parseRecord(line, delimiter);
|
||||
uint64_t token = proto->start(fieldId);
|
||||
for (int i=0; i<(int)record.size(); i++) {
|
||||
for (int j=0; j<count; j++) {
|
||||
if (stripSuffix(&record[i], names[j], true)) {
|
||||
proto->write(ids[j], toInt(record[i]));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
proto->end(token);
|
||||
}
|
||||
|
||||
status_t
|
||||
CpuInfoParser::Parse(const int in, const int out) const
|
||||
{
|
||||
Reader reader(in);
|
||||
string line;
|
||||
header_t header;
|
||||
vector<int> columnIndices; // task table can't be split by purely delimiter, needs column positions.
|
||||
record_t record;
|
||||
int nline = 0;
|
||||
int diff = 0;
|
||||
bool nextToSwap = false;
|
||||
bool nextToUsage = false;
|
||||
|
||||
ProtoOutputStream proto;
|
||||
Table table(CpuInfoProto::Task::_FIELD_NAMES, CpuInfoProto::Task::_FIELD_IDS, CpuInfoProto::Task::_FIELD_COUNT);
|
||||
table.addEnumTypeMap("s", CpuInfoProto::Task::_ENUM_STATUS_NAMES,
|
||||
CpuInfoProto::Task::_ENUM_STATUS_VALUES, CpuInfoProto::Task::_ENUM_STATUS_COUNT);
|
||||
table.addEnumTypeMap("pcy", CpuInfoProto::Task::_ENUM_POLICY_NAMES,
|
||||
CpuInfoProto::Task::_ENUM_POLICY_VALUES, CpuInfoProto::Task::_ENUM_POLICY_COUNT);
|
||||
|
||||
// parse line by line
|
||||
while (reader.readLine(&line)) {
|
||||
if (line.empty()) continue;
|
||||
|
||||
nline++;
|
||||
|
||||
if (stripPrefix(&line, "Tasks:")) {
|
||||
writeSuffixLine(&proto, CpuInfoProto::TASK_STATS, line, COMMA_DELIMITER,
|
||||
CpuInfoProto::TaskStats::_FIELD_COUNT,
|
||||
CpuInfoProto::TaskStats::_FIELD_NAMES,
|
||||
CpuInfoProto::TaskStats::_FIELD_IDS);
|
||||
continue;
|
||||
}
|
||||
if (stripPrefix(&line, "Mem:")) {
|
||||
writeSuffixLine(&proto, CpuInfoProto::MEM, line, COMMA_DELIMITER,
|
||||
CpuInfoProto::MemStats::_FIELD_COUNT,
|
||||
CpuInfoProto::MemStats::_FIELD_NAMES,
|
||||
CpuInfoProto::MemStats::_FIELD_IDS);
|
||||
continue;
|
||||
}
|
||||
if (stripPrefix(&line, "Swap:")) {
|
||||
writeSuffixLine(&proto, CpuInfoProto::SWAP, line, COMMA_DELIMITER,
|
||||
CpuInfoProto::MemStats::_FIELD_COUNT,
|
||||
CpuInfoProto::MemStats::_FIELD_NAMES,
|
||||
CpuInfoProto::MemStats::_FIELD_IDS);
|
||||
nextToSwap = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (nextToSwap) {
|
||||
writeSuffixLine(&proto, CpuInfoProto::CPU_USAGE, line, DEFAULT_WHITESPACE,
|
||||
CpuInfoProto::CpuUsage::_FIELD_COUNT,
|
||||
CpuInfoProto::CpuUsage::_FIELD_NAMES,
|
||||
CpuInfoProto::CpuUsage::_FIELD_IDS);
|
||||
nextToUsage = true;
|
||||
nextToSwap = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Header of tasks must be next to usage line
|
||||
if (nextToUsage) {
|
||||
// How to parse Header of Tasks:
|
||||
// PID TID USER PR NI[%CPU]S VIRT RES PCY CMD NAME
|
||||
// After parsing, header = { PID, TID, USER, PR, NI, CPU, S, VIRT, RES, PCY, CMD, NAME }
|
||||
// And columnIndices will contain end index of each word.
|
||||
header = parseHeader(line, "[ %]");
|
||||
nextToUsage = false;
|
||||
|
||||
// NAME is not in the list since we need to modify the end of the CMD index.
|
||||
const char* headerNames[] = { "PID", "TID", "USER", "PR", "NI", "CPU", "S", "VIRT", "RES", "PCY", "CMD", NULL };
|
||||
if (!getColumnIndices(columnIndices, headerNames, line)) {
|
||||
return -1;
|
||||
}
|
||||
// Need to remove the end index of CMD and use the start index of NAME because CMD values contain spaces.
|
||||
// for example: ... CMD NAME
|
||||
// ... Jit thread pool com.google.android.gms.feedback
|
||||
// If use end index of CMD, parsed result = { "Jit", "thread pool com.google.android.gms.feedback" }
|
||||
// If use start index of NAME, parsed result = { "Jit thread pool", "com.google.android.gms.feedback" }
|
||||
int endCMD = columnIndices.back();
|
||||
columnIndices.pop_back();
|
||||
columnIndices.push_back(line.find("NAME", endCMD) - 1);
|
||||
// Add NAME index to complete the column list.
|
||||
columnIndices.push_back(columnIndices.back() + 4);
|
||||
continue;
|
||||
}
|
||||
|
||||
record = parseRecordByColumns(line, columnIndices);
|
||||
diff = record.size() - header.size();
|
||||
if (diff < 0) {
|
||||
fprintf(stderr, "[%s]Line %d has %d missing fields\n%s\n", this->name.string(), nline, -diff, line.c_str());
|
||||
printRecord(record);
|
||||
continue;
|
||||
} else if (diff > 0) {
|
||||
fprintf(stderr, "[%s]Line %d has %d extra fields\n%s\n", this->name.string(), nline, diff, line.c_str());
|
||||
printRecord(record);
|
||||
continue;
|
||||
}
|
||||
|
||||
uint64_t token = proto.start(CpuInfoProto::TASKS);
|
||||
for (int i=0; i<(int)record.size(); i++) {
|
||||
if (!table.insertField(&proto, header[i], record[i])) {
|
||||
fprintf(stderr, "[%s]Line %d fails to insert field %s with value %s\n",
|
||||
this->name.string(), nline, header[i].c_str(), record[i].c_str());
|
||||
}
|
||||
}
|
||||
proto.end(token);
|
||||
}
|
||||
|
||||
if (!reader.ok(&line)) {
|
||||
fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!proto.flush(out)) {
|
||||
fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
|
||||
return -1;
|
||||
}
|
||||
fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size());
|
||||
return NO_ERROR;
|
||||
}
|
||||
36
cmds/incident_helper/src/parsers/CpuInfoParser.h
Normal file
36
cmds/incident_helper/src/parsers/CpuInfoParser.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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 CPU_INFO_PARSER_H
|
||||
#define CPU_INFO_PARSER_H
|
||||
|
||||
#include "TextParserBase.h"
|
||||
|
||||
using namespace android;
|
||||
|
||||
/**
|
||||
* Cpu info parser, parses text produced by command
|
||||
* 'top -b -n 1 -H -s 6 -o pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name'
|
||||
*/
|
||||
class CpuInfoParser : public TextParserBase {
|
||||
public:
|
||||
CpuInfoParser() : TextParserBase(String8("CpuInfoParser")) {};
|
||||
~CpuInfoParser() {};
|
||||
|
||||
virtual status_t Parse(const int in, const int out) const;
|
||||
};
|
||||
|
||||
#endif // CPU_INFO_PARSER_H
|
||||
84
cmds/incident_helper/src/parsers/EventLogTagsParser.cpp
Normal file
84
cmds/incident_helper/src/parsers/EventLogTagsParser.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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 "incident_helper"
|
||||
|
||||
#include <android/util/ProtoOutputStream.h>
|
||||
|
||||
#include "frameworks/base/core/proto/android/util/event_log_tags.proto.h"
|
||||
#include "ih_util.h"
|
||||
#include "EventLogTagsParser.h"
|
||||
|
||||
status_t
|
||||
EventLogTagsParser::Parse(const int in, const int out) const
|
||||
{
|
||||
Reader reader(in);
|
||||
string line;
|
||||
|
||||
ProtoOutputStream proto;
|
||||
|
||||
// parse line by line
|
||||
while (reader.readLine(&line)) {
|
||||
if (line.empty()) continue;
|
||||
string debug = line;
|
||||
string tagNumber = behead(&line, ' ');
|
||||
string tagName = behead(&line, ' ');
|
||||
if (tagNumber == "" || tagName == "") {
|
||||
fprintf(stderr, "Bad line, expect at least two parts: %s[%s, %s]\n",
|
||||
debug.c_str(), tagNumber.c_str(), tagName.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
uint64_t token = proto.start(EventLogTagMapProto::EVENT_LOG_TAGS);
|
||||
proto.write(EventLogTag::TAG_NUMBER, toInt(tagNumber));
|
||||
proto.write(EventLogTag::TAG_NAME, tagName);
|
||||
|
||||
record_t valueDescriptors = parseRecord(line, PARENTHESES_DELIMITER);
|
||||
for (size_t i = 0; i < valueDescriptors.size(); i++) {
|
||||
record_t valueDescriptor = parseRecord(valueDescriptors[i], PIPE_DELIMITER);
|
||||
if (valueDescriptor.size() != 2 && valueDescriptor.size() != 3) {
|
||||
// If the parts doesn't contains pipe, then skips it.
|
||||
continue;
|
||||
}
|
||||
uint64_t descriptorToken = proto.start(EventLogTag::VALUE_DESCRIPTORS);
|
||||
proto.write(EventLogTag::ValueDescriptor::NAME, valueDescriptor[0]);
|
||||
proto.write(EventLogTag::ValueDescriptor::TYPE, toInt(valueDescriptor[1]));
|
||||
if (valueDescriptor.size() == 3) {
|
||||
char c = valueDescriptor[2][0];
|
||||
int unit = 0;
|
||||
if (c < '0' || c > '9') {
|
||||
unit = (int) c;
|
||||
} else {
|
||||
unit = toInt(valueDescriptor[2]);
|
||||
}
|
||||
proto.write(EventLogTag::ValueDescriptor::UNIT, unit);
|
||||
}
|
||||
proto.end(descriptorToken);
|
||||
}
|
||||
proto.end(token);
|
||||
}
|
||||
|
||||
if (!reader.ok(&line)) {
|
||||
fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!proto.flush(out)) {
|
||||
fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
|
||||
return -1;
|
||||
}
|
||||
fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size());
|
||||
return NO_ERROR;
|
||||
}
|
||||
35
cmds/incident_helper/src/parsers/EventLogTagsParser.h
Normal file
35
cmds/incident_helper/src/parsers/EventLogTagsParser.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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 EVENT_LOG_TAGS_PARSER_H
|
||||
#define EVENT_LOG_TAGS_PARSER_H
|
||||
|
||||
#include "TextParserBase.h"
|
||||
|
||||
using namespace android;
|
||||
|
||||
/**
|
||||
* event.logtags parser, parse file in /system/etc/event-log-tags
|
||||
*/
|
||||
class EventLogTagsParser : public TextParserBase {
|
||||
public:
|
||||
EventLogTagsParser() : TextParserBase(String8("EventLogTagsParser")) {};
|
||||
~EventLogTagsParser() {};
|
||||
|
||||
virtual status_t Parse(const int in, const int out) const;
|
||||
};
|
||||
|
||||
#endif // EVENT_LOG_TAGS_PARSER_H
|
||||
83
cmds/incident_helper/src/parsers/KernelWakesParser.cpp
Normal file
83
cmds/incident_helper/src/parsers/KernelWakesParser.cpp
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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 "incident_helper"
|
||||
|
||||
#include <android/util/ProtoOutputStream.h>
|
||||
|
||||
#include "frameworks/base/core/proto/android/os/kernelwake.proto.h"
|
||||
#include "ih_util.h"
|
||||
#include "KernelWakesParser.h"
|
||||
|
||||
using namespace android::os;
|
||||
|
||||
status_t
|
||||
KernelWakesParser::Parse(const int in, const int out) const
|
||||
{
|
||||
Reader reader(in);
|
||||
string line;
|
||||
header_t header; // the header of /d/wakeup_sources
|
||||
record_t record; // retain each record
|
||||
int nline = 0;
|
||||
|
||||
ProtoOutputStream proto;
|
||||
Table table(KernelWakeSourcesProto::WakeupSource::_FIELD_NAMES,
|
||||
KernelWakeSourcesProto::WakeupSource::_FIELD_IDS,
|
||||
KernelWakeSourcesProto::WakeupSource::_FIELD_COUNT);
|
||||
|
||||
// parse line by line
|
||||
while (reader.readLine(&line)) {
|
||||
if (line.empty()) continue;
|
||||
// parse head line
|
||||
if (nline++ == 0) {
|
||||
header = parseHeader(line, TAB_DELIMITER);
|
||||
continue;
|
||||
}
|
||||
|
||||
// parse for each record, the line delimiter is \t only!
|
||||
record = parseRecord(line, TAB_DELIMITER);
|
||||
|
||||
if (record.size() < header.size()) {
|
||||
// TODO: log this to incident report!
|
||||
fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.string(), nline, line.c_str());
|
||||
continue;
|
||||
} else if (record.size() > header.size()) {
|
||||
// TODO: log this to incident report!
|
||||
fprintf(stderr, "[%s]Line %d has extra fields\n%s\n", this->name.string(), nline, line.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
uint64_t token = proto.start(KernelWakeSourcesProto::WAKEUP_SOURCES);
|
||||
for (int i=0; i<(int)record.size(); i++) {
|
||||
if (!table.insertField(&proto, header[i], record[i])) {
|
||||
fprintf(stderr, "[%s]Line %d has bad value %s of %s\n",
|
||||
this->name.string(), nline, header[i].c_str(), record[i].c_str());
|
||||
}
|
||||
}
|
||||
proto.end(token);
|
||||
}
|
||||
|
||||
if (!reader.ok(&line)) {
|
||||
fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!proto.flush(out)) {
|
||||
fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
|
||||
return -1;
|
||||
}
|
||||
fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size());
|
||||
return NO_ERROR;
|
||||
}
|
||||
33
cmds/incident_helper/src/parsers/KernelWakesParser.h
Normal file
33
cmds/incident_helper/src/parsers/KernelWakesParser.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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 KERNEL_WAKES_PARSER_H
|
||||
#define KERNEL_WAKES_PARSER_H
|
||||
|
||||
#include "TextParserBase.h"
|
||||
|
||||
/**
|
||||
* Kernel wakeup sources parser, parses text to protobuf in /d/wakeup_sources
|
||||
*/
|
||||
class KernelWakesParser : public TextParserBase {
|
||||
public:
|
||||
KernelWakesParser() : TextParserBase(String8("KernelWakeSources")) {};
|
||||
~KernelWakesParser() {};
|
||||
|
||||
virtual status_t Parse(const int in, const int out) const;
|
||||
};
|
||||
|
||||
#endif // KERNEL_WAKES_PARSER_H
|
||||
128
cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
Normal file
128
cmds/incident_helper/src/parsers/PageTypeInfoParser.cpp
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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 "incident_helper"
|
||||
|
||||
#include <android/util/ProtoOutputStream.h>
|
||||
|
||||
#include "frameworks/base/core/proto/android/os/pagetypeinfo.proto.h"
|
||||
#include "ih_util.h"
|
||||
#include "PageTypeInfoParser.h"
|
||||
|
||||
using namespace android::os;
|
||||
|
||||
status_t
|
||||
PageTypeInfoParser::Parse(const int in, const int out) const
|
||||
{
|
||||
Reader reader(in);
|
||||
string line;
|
||||
bool migrateTypeSession = false;
|
||||
int pageBlockOrder;
|
||||
header_t blockHeader;
|
||||
|
||||
ProtoOutputStream proto;
|
||||
Table table(PageTypeInfoProto::Block::_FIELD_NAMES,
|
||||
PageTypeInfoProto::Block::_FIELD_IDS,
|
||||
PageTypeInfoProto::Block::_FIELD_COUNT);
|
||||
|
||||
while (reader.readLine(&line)) {
|
||||
if (line.empty()) {
|
||||
migrateTypeSession = false;
|
||||
blockHeader.clear();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (stripPrefix(&line, "Page block order:")) {
|
||||
pageBlockOrder = toInt(line);
|
||||
proto.write(PageTypeInfoProto::PAGE_BLOCK_ORDER, pageBlockOrder);
|
||||
continue;
|
||||
}
|
||||
if (stripPrefix(&line, "Pages per block:")) {
|
||||
proto.write(PageTypeInfoProto::PAGES_PER_BLOCK, toInt(line));
|
||||
continue;
|
||||
}
|
||||
if (stripPrefix(&line, "Free pages count per migrate type at order")) {
|
||||
migrateTypeSession = true;
|
||||
continue;
|
||||
}
|
||||
if (stripPrefix(&line, "Number of blocks type")) {
|
||||
blockHeader = parseHeader(line);
|
||||
continue;
|
||||
}
|
||||
|
||||
record_t record = parseRecord(line, COMMA_DELIMITER);
|
||||
if (migrateTypeSession && record.size() == 3) {
|
||||
uint64_t token = proto.start(PageTypeInfoProto::MIGRATE_TYPES);
|
||||
// expect part 0 starts with "Node"
|
||||
if (stripPrefix(&record[0], "Node")) {
|
||||
proto.write(PageTypeInfoProto::MigrateType::NODE, toInt(record[0]));
|
||||
} else return BAD_VALUE;
|
||||
// expect part 1 starts with "zone"
|
||||
if (stripPrefix(&record[1], "zone")) {
|
||||
proto.write(PageTypeInfoProto::MigrateType::ZONE, record[1]);
|
||||
} else return BAD_VALUE;
|
||||
// expect part 2 starts with "type"
|
||||
if (stripPrefix(&record[2], "type")) {
|
||||
// expect the rest of part 2 has number of (pageBlockOrder + 2) parts
|
||||
// An example looks like:
|
||||
// header line: type 0 1 2 3 4 5 6 7 8 9 10
|
||||
// record line: Unmovable 426 279 226 1 1 1 0 0 2 2 0
|
||||
// The pageBlockOrder = 10 and it's zero-indexed. so total parts
|
||||
// are 10 + 1(zero-indexed) + 1(the type part) = 12.
|
||||
record_t pageCounts = parseRecord(record[2]);
|
||||
int pageCountsSize = pageBlockOrder + 2;
|
||||
if ((int)pageCounts.size() != pageCountsSize) return BAD_VALUE;
|
||||
|
||||
proto.write(PageTypeInfoProto::MigrateType::TYPE, pageCounts[0]);
|
||||
for (auto i=1; i<pageCountsSize; i++) {
|
||||
proto.write(PageTypeInfoProto::MigrateType::FREE_PAGES_COUNT, toInt(pageCounts[i]));
|
||||
}
|
||||
} else return BAD_VALUE;
|
||||
|
||||
proto.end(token);
|
||||
} else if (!blockHeader.empty() && record.size() == 2) {
|
||||
uint64_t token = proto.start(PageTypeInfoProto::BLOCKS);
|
||||
if (stripPrefix(&record[0], "Node")) {
|
||||
proto.write(PageTypeInfoProto::Block::NODE, toInt(record[0]));
|
||||
} else return BAD_VALUE;
|
||||
|
||||
if (stripPrefix(&record[1], "zone")) {
|
||||
record_t blockCounts = parseRecord(record[1]);
|
||||
proto.write(PageTypeInfoProto::Block::ZONE, blockCounts[0]);
|
||||
|
||||
for (size_t i=0; i<blockHeader.size(); i++) {
|
||||
if (!table.insertField(&proto, blockHeader[i], blockCounts[i+1])) {
|
||||
fprintf(stderr, "Header %s has bad data %s\n", blockHeader[i].c_str(),
|
||||
blockCounts[i+1].c_str());
|
||||
}
|
||||
}
|
||||
} else return BAD_VALUE;
|
||||
proto.end(token);
|
||||
}
|
||||
}
|
||||
|
||||
if (!reader.ok(&line)) {
|
||||
fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!proto.flush(out)) {
|
||||
fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
|
||||
return -1;
|
||||
}
|
||||
|
||||
fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size());
|
||||
return NO_ERROR;
|
||||
}
|
||||
35
cmds/incident_helper/src/parsers/PageTypeInfoParser.h
Normal file
35
cmds/incident_helper/src/parsers/PageTypeInfoParser.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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 PAGE_TYPE_INFO_PARSER_H
|
||||
#define PAGE_TYPE_INFO_PARSER_H
|
||||
|
||||
#include "TextParserBase.h"
|
||||
|
||||
using namespace android;
|
||||
|
||||
/**
|
||||
* PageTypeInfo parser, parses text to protobuf in /proc/pageinfotype
|
||||
*/
|
||||
class PageTypeInfoParser : public TextParserBase {
|
||||
public:
|
||||
PageTypeInfoParser() : TextParserBase(String8("PageTypeInfo")) {};
|
||||
~PageTypeInfoParser() {};
|
||||
|
||||
virtual status_t Parse(const int in, const int out) const;
|
||||
};
|
||||
|
||||
#endif // PAGE_TYPE_INFO_PARSER_H
|
||||
112
cmds/incident_helper/src/parsers/ProcrankParser.cpp
Normal file
112
cmds/incident_helper/src/parsers/ProcrankParser.cpp
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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 "incident_helper"
|
||||
|
||||
#include <android/util/ProtoOutputStream.h>
|
||||
|
||||
#include "frameworks/base/core/proto/android/os/procrank.proto.h"
|
||||
#include "ih_util.h"
|
||||
#include "ProcrankParser.h"
|
||||
|
||||
using namespace android::os;
|
||||
|
||||
status_t
|
||||
ProcrankParser::Parse(const int in, const int out) const
|
||||
{
|
||||
Reader reader(in);
|
||||
string line;
|
||||
header_t header; // the header of /d/wakeup_sources
|
||||
record_t record; // retain each record
|
||||
int nline = 0;
|
||||
|
||||
ProtoOutputStream proto;
|
||||
Table table(ProcrankProto::Process::_FIELD_NAMES, ProcrankProto::Process::_FIELD_IDS, ProcrankProto::Process::_FIELD_COUNT);
|
||||
string zram, ram, total;
|
||||
|
||||
// parse line by line
|
||||
while (reader.readLine(&line)) {
|
||||
if (line.empty()) continue;
|
||||
|
||||
// parse head line
|
||||
if (nline++ == 0) {
|
||||
header = parseHeader(line);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (stripPrefix(&line, "ZRAM:")) {
|
||||
zram = line;
|
||||
continue;
|
||||
}
|
||||
if (stripPrefix(&line, "RAM:")) {
|
||||
ram = line;
|
||||
continue;
|
||||
}
|
||||
|
||||
record = parseRecord(line);
|
||||
if (record.size() != header.size()) {
|
||||
if (record[record.size() - 1] == "TOTAL") { // TOTAL record
|
||||
total = line;
|
||||
} else {
|
||||
fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.string(), nline,
|
||||
line.c_str());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
uint64_t token = proto.start(ProcrankProto::PROCESSES);
|
||||
for (int i=0; i<(int)record.size(); i++) {
|
||||
if (!table.insertField(&proto, header[i], record[i])) {
|
||||
fprintf(stderr, "[%s]Line %d has bad value %s of %s\n",
|
||||
this->name.string(), nline, header[i].c_str(), record[i].c_str());
|
||||
}
|
||||
}
|
||||
proto.end(token);
|
||||
}
|
||||
|
||||
// add summary
|
||||
uint64_t token = proto.start(ProcrankProto::SUMMARY);
|
||||
if (!total.empty()) {
|
||||
record = parseRecord(total);
|
||||
uint64_t token = proto.start(ProcrankProto::Summary::TOTAL);
|
||||
for (int i=(int)record.size(); i>0; i--) {
|
||||
table.insertField(&proto, header[header.size() - i].c_str(), record[record.size() - i].c_str());
|
||||
}
|
||||
proto.end(token);
|
||||
}
|
||||
if (!zram.empty()) {
|
||||
uint64_t token = proto.start(ProcrankProto::Summary::ZRAM);
|
||||
proto.write(ProcrankProto::Summary::Zram::RAW_TEXT, zram);
|
||||
proto.end(token);
|
||||
}
|
||||
if (!ram.empty()) {
|
||||
uint64_t token = proto.start(ProcrankProto::Summary::RAM);
|
||||
proto.write(ProcrankProto::Summary::Ram::RAW_TEXT, ram);
|
||||
proto.end(token);
|
||||
}
|
||||
proto.end(token);
|
||||
|
||||
if (!reader.ok(&line)) {
|
||||
fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!proto.flush(out)) {
|
||||
fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
|
||||
return -1;
|
||||
}
|
||||
fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size());
|
||||
return NO_ERROR;
|
||||
}
|
||||
35
cmds/incident_helper/src/parsers/ProcrankParser.h
Normal file
35
cmds/incident_helper/src/parsers/ProcrankParser.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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 PROCRANK_PARSER_H
|
||||
#define PROCRANK_PARSER_H
|
||||
|
||||
#include "TextParserBase.h"
|
||||
|
||||
using namespace android;
|
||||
|
||||
/**
|
||||
* Procrank parser, parses text produced by command procrank
|
||||
*/
|
||||
class ProcrankParser : public TextParserBase {
|
||||
public:
|
||||
ProcrankParser() : TextParserBase(String8("ProcrankParser")) {};
|
||||
~ProcrankParser() {};
|
||||
|
||||
virtual status_t Parse(const int in, const int out) const;
|
||||
};
|
||||
|
||||
#endif // PROCRANK_PARSER_H
|
||||
95
cmds/incident_helper/src/parsers/PsParser.cpp
Normal file
95
cmds/incident_helper/src/parsers/PsParser.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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 "incident_helper"
|
||||
|
||||
#include <android/util/ProtoOutputStream.h>
|
||||
|
||||
#include "frameworks/base/core/proto/android/os/ps.proto.h"
|
||||
#include "ih_util.h"
|
||||
#include "PsParser.h"
|
||||
|
||||
using namespace android::os;
|
||||
|
||||
status_t PsParser::Parse(const int in, const int out) const {
|
||||
Reader reader(in);
|
||||
string line;
|
||||
header_t header; // the header of /d/wakeup_sources
|
||||
vector<int> columnIndices; // task table can't be split by purely delimiter, needs column positions.
|
||||
record_t record; // retain each record
|
||||
int nline = 0;
|
||||
int diff = 0;
|
||||
|
||||
ProtoOutputStream proto;
|
||||
Table table(PsProto::Process::_FIELD_NAMES, PsProto::Process::_FIELD_IDS, PsProto::Process::_FIELD_COUNT);
|
||||
const char* pcyNames[] = { "fg", "bg", "ta" };
|
||||
const int pcyValues[] = {PsProto::Process::POLICY_FG, PsProto::Process::POLICY_BG, PsProto::Process::POLICY_TA};
|
||||
table.addEnumTypeMap("pcy", pcyNames, pcyValues, 3);
|
||||
const char* sNames[] = { "D", "R", "S", "T", "t", "X", "Z" };
|
||||
const int sValues[] = {PsProto::Process::STATE_D, PsProto::Process::STATE_R, PsProto::Process::STATE_S, PsProto::Process::STATE_T, PsProto::Process::STATE_TRACING, PsProto::Process::STATE_X, PsProto::Process::STATE_Z};
|
||||
table.addEnumTypeMap("s", sNames, sValues, 7);
|
||||
|
||||
// Parse line by line
|
||||
while (reader.readLine(&line)) {
|
||||
if (line.empty()) continue;
|
||||
|
||||
if (nline++ == 0) {
|
||||
header = parseHeader(line, DEFAULT_WHITESPACE);
|
||||
|
||||
const char* headerNames[] = { "LABEL", "USER", "PID", "TID", "PPID", "VSZ", "RSS", "WCHAN", "ADDR", "S", "PRI", "NI", "RTPRIO", "SCH", "PCY", "TIME", "CMD", NULL };
|
||||
if (!getColumnIndices(columnIndices, headerNames, line)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
record = parseRecordByColumns(line, columnIndices);
|
||||
|
||||
diff = record.size() - header.size();
|
||||
if (diff < 0) {
|
||||
// TODO: log this to incident report!
|
||||
fprintf(stderr, "[%s]Line %d has %d missing fields\n%s\n", this->name.string(), nline, -diff, line.c_str());
|
||||
printRecord(record);
|
||||
continue;
|
||||
} else if (diff > 0) {
|
||||
// TODO: log this to incident report!
|
||||
fprintf(stderr, "[%s]Line %d has %d extra fields\n%s\n", this->name.string(), nline, diff, line.c_str());
|
||||
printRecord(record);
|
||||
continue;
|
||||
}
|
||||
|
||||
uint64_t token = proto.start(PsProto::PROCESSES);
|
||||
for (int i=0; i<(int)record.size(); i++) {
|
||||
if (!table.insertField(&proto, header[i], record[i])) {
|
||||
fprintf(stderr, "[%s]Line %d has bad value %s of %s\n",
|
||||
this->name.string(), nline, header[i].c_str(), record[i].c_str());
|
||||
}
|
||||
}
|
||||
proto.end(token);
|
||||
}
|
||||
|
||||
if (!reader.ok(&line)) {
|
||||
fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!proto.flush(out)) {
|
||||
fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
|
||||
return -1;
|
||||
}
|
||||
fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size());
|
||||
return NO_ERROR;
|
||||
}
|
||||
33
cmds/incident_helper/src/parsers/PsParser.h
Normal file
33
cmds/incident_helper/src/parsers/PsParser.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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 PS_PARSER_H
|
||||
#define PS_PARSER_H
|
||||
|
||||
#include "TextParserBase.h"
|
||||
|
||||
/**
|
||||
* PS parser, parses output of 'ps' command to protobuf.
|
||||
*/
|
||||
class PsParser : public TextParserBase {
|
||||
public:
|
||||
PsParser() : TextParserBase(String8("Ps")) {};
|
||||
~PsParser() {};
|
||||
|
||||
virtual status_t Parse(const int in, const int out) const;
|
||||
};
|
||||
|
||||
#endif // PS_PARSER_H
|
||||
227
cmds/incident_helper/src/parsers/SystemPropertiesParser.cpp
Normal file
227
cmds/incident_helper/src/parsers/SystemPropertiesParser.cpp
Normal file
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
* 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 "incident_helper"
|
||||
|
||||
#include <android/util/ProtoOutputStream.h>
|
||||
|
||||
#include "frameworks/base/core/proto/android/os/system_properties.proto.h"
|
||||
#include "ih_util.h"
|
||||
#include "SystemPropertiesParser.h"
|
||||
|
||||
using namespace android::os;
|
||||
|
||||
const string LINE_DELIMITER = "]: [";
|
||||
|
||||
// system properties' names sometimes are not valid proto field names, make the names valid.
|
||||
static string convertToFieldName(const string& name) {
|
||||
int len = (int)name.length();
|
||||
char cstr[len + 1];
|
||||
strcpy(cstr, name.c_str());
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (!isValidChar(cstr[i])) {
|
||||
cstr[i] = '_';
|
||||
}
|
||||
}
|
||||
return string(cstr);
|
||||
}
|
||||
|
||||
status_t
|
||||
SystemPropertiesParser::Parse(const int in, const int out) const
|
||||
{
|
||||
Reader reader(in);
|
||||
string line;
|
||||
string name; // the name of the property
|
||||
string value; // the string value of the property
|
||||
ProtoOutputStream proto;
|
||||
vector<pair<string, string>> extras;
|
||||
|
||||
Table sysPropTable(SystemPropertiesProto::_FIELD_NAMES,
|
||||
SystemPropertiesProto::_FIELD_IDS,
|
||||
SystemPropertiesProto::_FIELD_COUNT);
|
||||
Message sysProp(&sysPropTable);
|
||||
|
||||
Table aacDrcTable(SystemPropertiesProto::AacDrc::_FIELD_NAMES,
|
||||
SystemPropertiesProto::AacDrc::_FIELD_IDS,
|
||||
SystemPropertiesProto::AacDrc::_FIELD_COUNT);
|
||||
Message aacDrc(&aacDrcTable);
|
||||
sysProp.addSubMessage(SystemPropertiesProto::AAC_DRC, &aacDrc);
|
||||
|
||||
Table aaudioTable(SystemPropertiesProto::Aaudio::_FIELD_NAMES,
|
||||
SystemPropertiesProto::Aaudio::_FIELD_IDS,
|
||||
SystemPropertiesProto::Aaudio::_FIELD_COUNT);
|
||||
Message aaudio(&aaudioTable);
|
||||
sysProp.addSubMessage(SystemPropertiesProto::AAUDIO, &aaudio);
|
||||
|
||||
Table cameraTable(SystemPropertiesProto::Camera::_FIELD_NAMES,
|
||||
SystemPropertiesProto::Camera::_FIELD_IDS,
|
||||
SystemPropertiesProto::Camera::_FIELD_COUNT);
|
||||
Message camera(&cameraTable);
|
||||
sysProp.addSubMessage(SystemPropertiesProto::CAMERA, &camera);
|
||||
|
||||
Table dalvikVmTable(SystemPropertiesProto::DalvikVm::_FIELD_NAMES,
|
||||
SystemPropertiesProto::DalvikVm::_FIELD_IDS,
|
||||
SystemPropertiesProto::DalvikVm::_FIELD_COUNT);
|
||||
Message dalvikVm(&dalvikVmTable);
|
||||
sysProp.addSubMessage(SystemPropertiesProto::DALVIK_VM, &dalvikVm);
|
||||
|
||||
Table initSvcTable(SystemPropertiesProto::InitSvc::_FIELD_NAMES,
|
||||
SystemPropertiesProto::InitSvc::_FIELD_IDS,
|
||||
SystemPropertiesProto::InitSvc::_FIELD_COUNT);
|
||||
initSvcTable.addEnumNameToValue("running", SystemPropertiesProto::InitSvc::STATUS_RUNNING);
|
||||
initSvcTable.addEnumNameToValue("stopped", SystemPropertiesProto::InitSvc::STATUS_STOPPED);
|
||||
Message initSvc(&initSvcTable);
|
||||
sysProp.addSubMessage(SystemPropertiesProto::INIT_SVC, &initSvc);
|
||||
|
||||
Table logTable(SystemPropertiesProto::Log::_FIELD_NAMES,
|
||||
SystemPropertiesProto::Log::_FIELD_IDS,
|
||||
SystemPropertiesProto::Log::_FIELD_COUNT);
|
||||
Message logMsg(&logTable);
|
||||
sysProp.addSubMessage(SystemPropertiesProto::LOG, &logMsg);
|
||||
|
||||
Table persistTable(SystemPropertiesProto::Persist::_FIELD_NAMES,
|
||||
SystemPropertiesProto::Persist::_FIELD_IDS,
|
||||
SystemPropertiesProto::Persist::_FIELD_COUNT);
|
||||
Message persist(&persistTable);
|
||||
sysProp.addSubMessage(SystemPropertiesProto::PERSIST, &persist);
|
||||
|
||||
Table pmDexoptTable(SystemPropertiesProto::PmDexopt::_FIELD_NAMES,
|
||||
SystemPropertiesProto::PmDexopt::_FIELD_IDS,
|
||||
SystemPropertiesProto::PmDexopt::_FIELD_COUNT);
|
||||
Message pmDexopt(&pmDexoptTable);
|
||||
sysProp.addSubMessage(SystemPropertiesProto::PM_DEXOPT, &pmDexopt);
|
||||
|
||||
Table roTable(SystemPropertiesProto::Ro::_FIELD_NAMES,
|
||||
SystemPropertiesProto::Ro::_FIELD_IDS,
|
||||
SystemPropertiesProto::Ro::_FIELD_COUNT);
|
||||
Message ro(&roTable);
|
||||
|
||||
Table bootTable(SystemPropertiesProto::Ro::Boot::_FIELD_NAMES,
|
||||
SystemPropertiesProto::Ro::Boot::_FIELD_IDS,
|
||||
SystemPropertiesProto::Ro::Boot::_FIELD_COUNT);
|
||||
Message boot(&bootTable);
|
||||
ro.addSubMessage(SystemPropertiesProto::Ro::BOOT, &boot);
|
||||
|
||||
Table bootimageTable(SystemPropertiesProto::Ro::BootImage::_FIELD_NAMES,
|
||||
SystemPropertiesProto::Ro::BootImage::_FIELD_IDS,
|
||||
SystemPropertiesProto::Ro::BootImage::_FIELD_COUNT);
|
||||
Message bootimage(&bootimageTable);
|
||||
ro.addSubMessage(SystemPropertiesProto::Ro::BOOTIMAGE, &bootimage);
|
||||
|
||||
Table buildTable(SystemPropertiesProto::Ro::Build::_FIELD_NAMES,
|
||||
SystemPropertiesProto::Ro::Build::_FIELD_IDS,
|
||||
SystemPropertiesProto::Ro::Build::_FIELD_COUNT);
|
||||
Message build(&buildTable);
|
||||
|
||||
Table versionTable(SystemPropertiesProto::Ro::Build::Version::_FIELD_NAMES,
|
||||
SystemPropertiesProto::Ro::Build::Version::_FIELD_IDS,
|
||||
SystemPropertiesProto::Ro::Build::Version::_FIELD_COUNT);
|
||||
Message version(&versionTable);
|
||||
build.addSubMessage(SystemPropertiesProto::Ro::Build::VERSION, &version);
|
||||
ro.addSubMessage(SystemPropertiesProto::Ro::BUILD, &build);
|
||||
|
||||
Table configTable(SystemPropertiesProto::Ro::Config::_FIELD_NAMES,
|
||||
SystemPropertiesProto::Ro::Config::_FIELD_IDS,
|
||||
SystemPropertiesProto::Ro::Config::_FIELD_COUNT);
|
||||
Message config(&configTable);
|
||||
ro.addSubMessage(SystemPropertiesProto::Ro::CONFIG, &config);
|
||||
|
||||
Table hardwareTable(SystemPropertiesProto::Ro::Hardware::_FIELD_NAMES,
|
||||
SystemPropertiesProto::Ro::Hardware::_FIELD_IDS,
|
||||
SystemPropertiesProto::Ro::Hardware::_FIELD_COUNT);
|
||||
Message hardware(&hardwareTable);
|
||||
ro.addSubMessage(SystemPropertiesProto::Ro::HARDWARE, &hardware);
|
||||
|
||||
Table productTable(SystemPropertiesProto::Ro::Product::_FIELD_NAMES,
|
||||
SystemPropertiesProto::Ro::Product::_FIELD_IDS,
|
||||
SystemPropertiesProto::Ro::Product::_FIELD_COUNT);
|
||||
Message product(&productTable);
|
||||
|
||||
Table pVendorTable(SystemPropertiesProto::Ro::Product::Vendor::_FIELD_NAMES,
|
||||
SystemPropertiesProto::Ro::Product::Vendor::_FIELD_IDS,
|
||||
SystemPropertiesProto::Ro::Product::Vendor::_FIELD_COUNT);
|
||||
Message pVendor(&pVendorTable);
|
||||
product.addSubMessage(SystemPropertiesProto::Ro::Product::VENDOR, &pVendor);
|
||||
ro.addSubMessage(SystemPropertiesProto::Ro::PRODUCT, &product);
|
||||
|
||||
Table telephonyTable(SystemPropertiesProto::Ro::Telephony::_FIELD_NAMES,
|
||||
SystemPropertiesProto::Ro::Telephony::_FIELD_IDS,
|
||||
SystemPropertiesProto::Ro::Telephony::_FIELD_COUNT);
|
||||
Message telephony(&telephonyTable);
|
||||
ro.addSubMessage(SystemPropertiesProto::Ro::TELEPHONY, &telephony);
|
||||
|
||||
Table vendorTable(SystemPropertiesProto::Ro::Vendor::_FIELD_NAMES,
|
||||
SystemPropertiesProto::Ro::Vendor::_FIELD_IDS,
|
||||
SystemPropertiesProto::Ro::Vendor::_FIELD_COUNT);
|
||||
Message vendor(&vendorTable);
|
||||
ro.addSubMessage(SystemPropertiesProto::Ro::VENDOR, &vendor);
|
||||
|
||||
sysProp.addSubMessage(SystemPropertiesProto::RO, &ro);
|
||||
|
||||
Table sysTable(SystemPropertiesProto::Sys::_FIELD_NAMES,
|
||||
SystemPropertiesProto::Sys::_FIELD_IDS,
|
||||
SystemPropertiesProto::Sys::_FIELD_COUNT);
|
||||
Message sys(&sysTable);
|
||||
|
||||
Table usbTable(SystemPropertiesProto::Sys::Usb::_FIELD_NAMES,
|
||||
SystemPropertiesProto::Sys::Usb::_FIELD_IDS,
|
||||
SystemPropertiesProto::Sys::Usb::_FIELD_COUNT);
|
||||
Message usb(&usbTable);
|
||||
sys.addSubMessage(SystemPropertiesProto::Sys::USB, &usb);
|
||||
|
||||
sysProp.addSubMessage(SystemPropertiesProto::SYS, &sys);
|
||||
|
||||
// parse line by line
|
||||
while (reader.readLine(&line)) {
|
||||
if (line.empty()) continue;
|
||||
|
||||
line = line.substr(1, line.size() - 2); // trim []
|
||||
size_t index = line.find(LINE_DELIMITER); // split by "]: ["
|
||||
if (index == string::npos) {
|
||||
fprintf(stderr, "Bad Line %s\n", line.c_str());
|
||||
continue;
|
||||
}
|
||||
name = line.substr(0, index);
|
||||
value = trim(line.substr(index + 4), DEFAULT_WHITESPACE);
|
||||
if (value.empty()) continue;
|
||||
|
||||
// if the property name couldn't be found in proto definition or the value has mistype,
|
||||
// add to extra properties with its name and value
|
||||
if (!sysProp.insertField(&proto, convertToFieldName(name), value)) {
|
||||
extras.push_back(make_pair(name, value));
|
||||
}
|
||||
}
|
||||
// end session for the last write.
|
||||
sysProp.endSession(&proto);
|
||||
|
||||
for (auto it = extras.begin(); it != extras.end(); it++) {
|
||||
uint64_t token = proto.start(SystemPropertiesProto::EXTRA_PROPERTIES);
|
||||
proto.write(SystemPropertiesProto::Property::NAME, it->first);
|
||||
proto.write(SystemPropertiesProto::Property::VALUE, it->second);
|
||||
proto.end(token);
|
||||
}
|
||||
|
||||
if (!reader.ok(&line)) {
|
||||
fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!proto.flush(out)) {
|
||||
fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
|
||||
return -1;
|
||||
}
|
||||
fprintf(stderr, "[%s]Proto size: %zu bytes\n", this->name.string(), proto.size());
|
||||
return NO_ERROR;
|
||||
}
|
||||
35
cmds/incident_helper/src/parsers/SystemPropertiesParser.h
Normal file
35
cmds/incident_helper/src/parsers/SystemPropertiesParser.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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 SYSTEM_PROPERTIES_PARSER_H
|
||||
#define SYSTEM_PROPERTIES_PARSER_H
|
||||
|
||||
#include "TextParserBase.h"
|
||||
|
||||
using namespace android;
|
||||
|
||||
/**
|
||||
* SystemProperties parser, parses text produced by command getprop.
|
||||
*/
|
||||
class SystemPropertiesParser : public TextParserBase {
|
||||
public:
|
||||
SystemPropertiesParser() : TextParserBase(String8("SystemPropertiesParser")) {};
|
||||
~SystemPropertiesParser() {};
|
||||
|
||||
virtual status_t Parse(const int in, const int out) const;
|
||||
};
|
||||
|
||||
#endif // SYSTEM_PROPERTIES_PARSER_H
|
||||
1
cmds/incident_helper/testdata/batterytype.txt
vendored
Normal file
1
cmds/incident_helper/testdata/batterytype.txt
vendored
Normal file
@@ -0,0 +1 @@
|
||||
random_battery_type_string
|
||||
6
cmds/incident_helper/testdata/cpufreq.txt
vendored
Normal file
6
cmds/incident_helper/testdata/cpufreq.txt
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
freq cpu0 cpu1 cpu2 cpu3
|
||||
307200 23860761 23860761 23890935 23890935
|
||||
384000 83124 83124 29383 29383
|
||||
748800 N/A N/A 10547 10547
|
||||
768000 22652 22652 N/A N/A
|
||||
825600 N/A N/A 13173 13173
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user