summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp1
-rw-r--r--api/current.txt43
-rw-r--r--api/test-current.txt5
-rw-r--r--cmds/statsd/Android.bp2
-rw-r--r--cmds/statsd/Android.mk3
-rw-r--r--cmds/statsd/src/atoms.proto11
-rw-r--r--cmds/statsd/src/external/StatsPuller.h2
-rw-r--r--cmds/statsd/src/external/StatsPullerManager.h8
-rw-r--r--cmds/statsd/src/metrics/CountMetricProducer.cpp8
-rw-r--r--cmds/statsd/src/metrics/CountMetricProducer.h3
-rw-r--r--cmds/statsd/src/metrics/DurationMetricProducer.cpp7
-rw-r--r--cmds/statsd/src/metrics/DurationMetricProducer.h3
-rw-r--r--cmds/statsd/src/metrics/GaugeMetricProducer.cpp20
-rw-r--r--cmds/statsd/src/metrics/GaugeMetricProducer.h14
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.cpp42
-rw-r--r--cmds/statsd/src/metrics/ValueMetricProducer.h7
-rw-r--r--cmds/statsd/src/metrics/metrics_manager_util.cpp4
l---------cmds/statsd/src/metrics_constants/metrics_constants.proto1
-rw-r--r--cmds/statsd/src/stats_log.proto6
-rw-r--r--cmds/statsd/tests/metrics/CountMetricProducer_test.cpp31
-rw-r--r--cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp38
-rw-r--r--cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp54
-rw-r--r--cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp86
-rw-r--r--core/java/android/accessibilityservice/AccessibilityService.java104
-rw-r--r--core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl2
-rw-r--r--core/java/android/app/ActivityManagerInternal.java3
-rw-r--r--core/java/android/app/ActivityThread.java12
-rw-r--r--core/java/android/app/ApplicationErrorReport.java9
-rw-r--r--core/java/android/app/IActivityManager.aidl1
-rw-r--r--core/java/android/app/WallpaperInfo.java9
-rw-r--r--core/java/android/database/DatabaseUtils.java89
-rw-r--r--core/java/android/database/sqlite/SQLiteConnection.java8
-rw-r--r--core/java/android/database/sqlite/SQLiteQueryBuilder.java49
-rw-r--r--core/java/android/database/sqlite/SQLiteStatementBuilder.java1036
-rw-r--r--core/java/android/os/Binder.java22
-rw-r--r--core/java/android/os/Process.java6
-rw-r--r--core/java/android/os/ZygoteProcess.java10
-rw-r--r--core/java/android/preference/SeekBarVolumizer.java2
-rw-r--r--core/java/android/provider/MediaStore.java321
-rw-r--r--core/java/android/provider/Settings.java27
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java14
-rw-r--r--core/java/android/text/MeasuredParagraph.java166
-rw-r--r--core/java/android/text/NativeLineBreaker.java155
-rw-r--r--core/java/android/text/NativeMeasuredParagraph.java176
-rw-r--r--core/java/android/text/PrecomputedText.java15
-rw-r--r--core/java/android/text/StaticLayout.java468
-rw-r--r--core/java/android/view/RecordingCanvas.java2
-rw-r--r--core/java/android/view/ViewRootImpl.java28
-rw-r--r--core/java/android/view/ViewTreeObserver.java57
-rw-r--r--core/java/android/view/accessibility/AccessibilityCache.java6
-rw-r--r--core/java/android/view/autofill/AutofillManager.java10
-rw-r--r--core/java/android/webkit/WebViewClient.java60
-rw-r--r--core/java/com/android/internal/app/procstats/AssociationState.java268
-rw-r--r--core/java/com/android/internal/app/procstats/DumpUtils.java111
-rw-r--r--core/java/com/android/internal/app/procstats/ProcessState.java68
-rw-r--r--core/java/com/android/internal/app/procstats/ProcessStats.java31
-rw-r--r--core/java/com/android/internal/os/BatteryStatsImpl.java2
-rw-r--r--core/java/com/android/internal/os/BinderCallsStats.java118
-rw-r--r--core/java/com/android/internal/os/BinderInternal.java2
-rw-r--r--core/java/com/android/internal/os/Zygote.java8
-rw-r--r--core/java/com/android/internal/os/ZygoteConnection.java7
-rw-r--r--core/java/com/android/internal/util/ArrayUtils.java2
-rw-r--r--core/jni/Android.bp3
-rw-r--r--core/jni/AndroidRuntime.cpp6
-rw-r--r--core/jni/android/graphics/FontUtils.h7
-rw-r--r--core/jni/android/graphics/fonts/Font.cpp205
-rw-r--r--core/jni/android_text_LineBreaker.cpp (renamed from core/jni/android_text_StaticLayout.cpp)23
-rw-r--r--core/jni/android_text_MeasuredParagraph.cpp14
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp106
-rw-r--r--core/proto/android/providers/settings/global.proto15
-rw-r--r--core/proto/android/server/activitymanagerservice.proto4
-rw-r--r--core/res/res/values/attrs.xml6
-rw-r--r--core/res/res/values/config.xml4
-rw-r--r--core/res/res/values/public.xml2
-rw-r--r--core/res/res/values/symbols.xml4
-rw-r--r--core/tests/coretests/src/android/database/DatabaseUtilsTest.java66
-rw-r--r--core/tests/coretests/src/android/os/WorkSourceTest.java11
-rw-r--r--core/tests/coretests/src/android/provider/SettingsBackupTest.java3
-rw-r--r--core/tests/coretests/src/android/text/MeasuredParagraphTest.java13
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java21
-rw-r--r--core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java4
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java43
-rw-r--r--core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java20
-rw-r--r--graphics/java/android/graphics/BaseCanvas.java2
-rw-r--r--graphics/java/android/graphics/fonts/Font.java472
-rw-r--r--graphics/java/android/graphics/fonts/FontFileUtil.java17
-rw-r--r--libs/hwui/Android.bp1
-rw-r--r--libs/hwui/SkiaCanvasProxy.cpp486
-rw-r--r--libs/hwui/SkiaCanvasProxy.h112
-rw-r--r--libs/hwui/hwui/Canvas.h2
-rw-r--r--libs/hwui/renderthread/VulkanManager.h7
-rw-r--r--media/java/android/media/MediaPlayer2Impl.java252
-rw-r--r--media/jni/android_media_MediaDrm.cpp16
-rw-r--r--packages/SettingsLib/res/values/strings.xml3
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java14
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java14
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java2
-rw-r--r--packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml2
-rw-r--r--packages/SystemUI/res/layout/car_qs_panel.xml2
-rw-r--r--packages/SystemUI/src/com/android/systemui/BatteryMeterView.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java52
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java10
-rw-r--r--proto/Android.bp30
-rw-r--r--proto/src/metrics_constants.proto5
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java205
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java26
-rw-r--r--services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java5
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerService.java76
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java2
-rw-r--r--services/autofill/java/com/android/server/autofill/Helper.java15
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java1
-rw-r--r--services/core/java/com/android/server/InputMethodManagerService.java9
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java2
-rw-r--r--services/core/java/com/android/server/am/ActivityDisplay.java103
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java322
-rw-r--r--services/core/java/com/android/server/am/ActivityRecord.java58
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java129
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java156
-rw-r--r--services/core/java/com/android/server/am/ActivityStartInterceptor.java4
-rw-r--r--services/core/java/com/android/server/am/ActivityStarter.java20
-rw-r--r--services/core/java/com/android/server/am/ActivityTaskManagerService.java174
-rw-r--r--services/core/java/com/android/server/am/AppErrors.java18
-rw-r--r--services/core/java/com/android/server/am/AppWarnings.java25
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java2
-rw-r--r--services/core/java/com/android/server/am/CompatModeDialog.java90
-rw-r--r--services/core/java/com/android/server/am/KeyguardController.java2
-rw-r--r--services/core/java/com/android/server/am/LockTaskController.java6
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java42
-rw-r--r--services/core/java/com/android/server/am/ProcessStatsService.java13
-rw-r--r--services/core/java/com/android/server/am/RecentTasks.java2
-rw-r--r--services/core/java/com/android/server/am/RecentsAnimation.java2
-rw-r--r--services/core/java/com/android/server/am/TaskRecord.java23
-rw-r--r--services/core/java/com/android/server/am/UserController.java4
-rw-r--r--services/core/java/com/android/server/am/WindowProcessController.java43
-rw-r--r--services/core/java/com/android/server/hdmi/ArcTerminationActionFromAvr.java86
-rw-r--r--services/core/java/com/android/server/hdmi/Constants.java7
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java46
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java20
-rw-r--r--services/core/java/com/android/server/job/controllers/IdleController.java166
-rw-r--r--services/core/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java139
-rw-r--r--services/core/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java175
-rw-r--r--services/core/java/com/android/server/job/controllers/idle/IdlenessListener.java32
-rw-r--r--services/core/java/com/android/server/job/controllers/idle/IdlenessTracker.java46
-rw-r--r--services/core/java/com/android/server/pm/InstantAppRegistry.java28
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerShellCommand.java5
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java14
-rw-r--r--services/core/java/com/android/server/trust/TrustManagerService.java16
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java4
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java7
-rw-r--r--services/core/java/com/android/server/wm/AppWindowToken.java52
-rw-r--r--services/core/java/com/android/server/wm/InputManagerCallback.java19
-rw-r--r--services/core/java/com/android/server/wm/TaskWindowContainerController.java6
-rw-r--r--services/core/java/com/android/server/wm/WindowAnimationSpec.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java45
-rw-r--r--services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java149
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java93
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java50
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java385
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java124
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java38
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java40
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java132
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/AppWindowTokenAnimationTests.java118
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java14
-rw-r--r--services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java5
-rw-r--r--tests/Internal/res/xml/livewallpaper.xml3
-rw-r--r--tests/Internal/src/android/app/WallpaperInfoTest.java4
-rw-r--r--tests/SystemMemoryTest/Android.mk17
-rw-r--r--tests/SystemMemoryTest/README.txt21
-rw-r--r--tests/SystemMemoryTest/device/Android.mk24
-rw-r--r--tests/SystemMemoryTest/device/AndroidManifest.xml30
-rw-r--r--tests/SystemMemoryTest/device/src/com/android/tests/sysmem/device/Cujs.java53
-rw-r--r--tests/SystemMemoryTest/host/Android.mk23
-rw-r--r--tests/SystemMemoryTest/host/AndroidTest.xml25
-rw-r--r--tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Cujs.java43
-rw-r--r--tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/MemoryTest.java79
-rw-r--r--tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Metrics.java148
-rw-r--r--tests/net/java/android/net/ip/IpClientTest.java30
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java16
-rw-r--r--wifi/java/android/net/wifi/ITrafficStateCallback.aidl34
-rw-r--r--wifi/java/android/net/wifi/IWifiManager.aidl5
-rw-r--r--wifi/java/android/net/wifi/WifiManager.java129
-rw-r--r--wifi/tests/src/android/net/wifi/WifiManagerTest.java94
194 files changed, 6214 insertions, 4286 deletions
diff --git a/Android.bp b/Android.bp
index 1f82b01fa6ad..21b84040ac0c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -577,6 +577,7 @@ java_library {
"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/ITrafficStateCallback.aidl",
"wifi/java/android/net/wifi/IWifiManager.aidl",
"wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl",
"wifi/java/android/net/wifi/aware/IWifiAwareEventCallback.aidl",
diff --git a/api/current.txt b/api/current.txt
index abf1224473a7..7f4fae542037 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -272,6 +272,7 @@ package android {
field public static final int allowBackup = 16843392; // 0x1010280
field public static final int allowClearUserData = 16842757; // 0x1010005
field public static final int allowEmbedded = 16843765; // 0x10103f5
+ field public static final int allowForceDark = 16844171; // 0x101058b
field public static final int allowParallelSyncs = 16843570; // 0x1010332
field public static final int allowSingleTap = 16843353; // 0x1010259
field public static final int allowTaskReparenting = 16843268; // 0x1010204
@@ -1294,6 +1295,7 @@ package android {
field public static final int summaryColumn = 16843426; // 0x10102a2
field public static final int summaryOff = 16843248; // 0x10101f0
field public static final int summaryOn = 16843247; // 0x10101ef
+ field public static final int supportsAmbientMode = 16844172; // 0x101058c
field public static final int supportsAssist = 16844016; // 0x10104f0
field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1
field public static final int supportsLocalInteraction = 16844047; // 0x101050f
@@ -2818,6 +2820,7 @@ package android.accessibilityservice {
field public static final java.lang.String SERVICE_META_DATA = "android.accessibilityservice";
field public static final int SHOW_MODE_AUTO = 0; // 0x0
field public static final int SHOW_MODE_HIDDEN = 1; // 0x1
+ field public static final int SHOW_MODE_WITH_HARD_KEYBOARD = 2; // 0x2
}
public static abstract class AccessibilityService.GestureResultCallback {
@@ -6279,6 +6282,7 @@ package android.app {
method public android.graphics.drawable.Drawable loadIcon(android.content.pm.PackageManager);
method public java.lang.CharSequence loadLabel(android.content.pm.PackageManager);
method public android.graphics.drawable.Drawable loadThumbnail(android.content.pm.PackageManager);
+ method public boolean supportsAmbientMode();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.app.WallpaperInfo> CREATOR;
}
@@ -12673,12 +12677,14 @@ package android.database.sqlite {
method public static void appendColumns(java.lang.StringBuilder, java.lang.String[]);
method public void appendWhere(java.lang.CharSequence);
method public void appendWhereEscapeString(java.lang.String);
+ method public void appendWhereStandalone(java.lang.CharSequence);
method public java.lang.String buildQuery(java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
method public deprecated java.lang.String buildQuery(java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String);
method public static java.lang.String buildQueryString(boolean, java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
method public java.lang.String buildUnionQuery(java.lang.String[], java.lang.String, java.lang.String);
method public java.lang.String buildUnionSubQuery(java.lang.String, java.lang.String[], java.util.Set<java.lang.String>, int, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
method public deprecated java.lang.String buildUnionSubQuery(java.lang.String, java.lang.String[], java.util.Set<java.lang.String>, int, java.lang.String, java.lang.String, java.lang.String[], java.lang.String, java.lang.String);
+ method public int delete(android.database.sqlite.SQLiteDatabase, java.lang.String, java.lang.String[]);
method public java.lang.String getTables();
method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String);
method public android.database.Cursor query(android.database.sqlite.SQLiteDatabase, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String);
@@ -12688,6 +12694,7 @@ package android.database.sqlite {
method public void setProjectionMap(java.util.Map<java.lang.String, java.lang.String>);
method public void setStrict(boolean);
method public void setTables(java.lang.String);
+ method public int update(android.database.sqlite.SQLiteDatabase, android.content.ContentValues, java.lang.String, java.lang.String[]);
}
public class SQLiteReadOnlyDatabaseException extends android.database.sqlite.SQLiteException {
@@ -15173,6 +15180,37 @@ package android.graphics.drawable.shapes {
package android.graphics.fonts {
+ public class Font {
+ method public android.graphics.fonts.FontVariationAxis[] getAxes();
+ method public int getTtcIndex();
+ method public int getWeight();
+ method public boolean isItalic();
+ field public static final int FONT_WEIGHT_BLACK = 900; // 0x384
+ field public static final int FONT_WEIGHT_BOLD = 700; // 0x2bc
+ field public static final int FONT_WEIGHT_EXTRA_BOLD = 800; // 0x320
+ field public static final int FONT_WEIGHT_EXTRA_LIGHT = 200; // 0xc8
+ field public static final int FONT_WEIGHT_LIGHT = 300; // 0x12c
+ field public static final int FONT_WEIGHT_MEDIUM = 500; // 0x1f4
+ field public static final int FONT_WEIGHT_NORMAL = 400; // 0x190
+ field public static final int FONT_WEIGHT_SEMI_BOLD = 600; // 0x258
+ field public static final int FONT_WEIGHT_THIN = 100; // 0x64
+ }
+
+ public static class Font.Builder {
+ ctor public Font.Builder(java.nio.ByteBuffer);
+ ctor public Font.Builder(java.io.File) throws java.io.IOException;
+ ctor public Font.Builder(java.io.FileDescriptor) throws java.io.IOException;
+ ctor public Font.Builder(java.io.FileDescriptor, long, long) throws java.io.IOException;
+ ctor public Font.Builder(android.content.res.AssetManager, java.lang.String) throws java.io.IOException;
+ ctor public Font.Builder(android.content.res.Resources, int) throws java.io.IOException;
+ method public android.graphics.fonts.Font build();
+ method public android.graphics.fonts.Font.Builder setFontVariationSettings(java.lang.String);
+ method public android.graphics.fonts.Font.Builder setFontVariationSettings(android.graphics.fonts.FontVariationAxis[]);
+ method public android.graphics.fonts.Font.Builder setItalic(boolean);
+ method public android.graphics.fonts.Font.Builder setTtcIndex(int);
+ method public android.graphics.fonts.Font.Builder setWeight(int);
+ }
+
public final class FontVariationAxis {
ctor public FontVariationAxis(java.lang.String, float);
method public static android.graphics.fonts.FontVariationAxis[] fromFontVariationSettings(java.lang.String);
@@ -32169,6 +32207,7 @@ package android.os {
public class Binder implements android.os.IBinder {
ctor public Binder();
+ ctor public Binder(java.lang.String);
method public void attachInterface(android.os.IInterface, java.lang.String);
method public static final long clearCallingIdentity();
method public void dump(java.io.FileDescriptor, java.lang.String[]);
@@ -39815,9 +39854,11 @@ package android.service.wallpaper {
method public int getDesiredMinimumHeight();
method public int getDesiredMinimumWidth();
method public android.view.SurfaceHolder getSurfaceHolder();
+ method public boolean isInAmbientMode();
method public boolean isPreview();
method public boolean isVisible();
method public void notifyColorsChanged();
+ method public void onAmbientModeChanged(boolean, boolean);
method public void onApplyWindowInsets(android.view.WindowInsets);
method public android.os.Bundle onCommand(java.lang.String, int, int, int, android.os.Bundle, boolean);
method public android.app.WallpaperColors onComputeColors();
@@ -48819,6 +48860,7 @@ package android.view {
method public void dispatchOnGlobalLayout();
method public boolean dispatchOnPreDraw();
method public boolean isAlive();
+ method public void registerFrameCommitCallback(java.lang.Runnable);
method public deprecated void removeGlobalOnLayoutListener(android.view.ViewTreeObserver.OnGlobalLayoutListener);
method public void removeOnDrawListener(android.view.ViewTreeObserver.OnDrawListener);
method public void removeOnGlobalFocusChangeListener(android.view.ViewTreeObserver.OnGlobalFocusChangeListener);
@@ -48828,6 +48870,7 @@ package android.view {
method public void removeOnTouchModeChangeListener(android.view.ViewTreeObserver.OnTouchModeChangeListener);
method public void removeOnWindowAttachListener(android.view.ViewTreeObserver.OnWindowAttachListener);
method public void removeOnWindowFocusChangeListener(android.view.ViewTreeObserver.OnWindowFocusChangeListener);
+ method public boolean unregisterFrameCommitCallback(java.lang.Runnable);
}
public static abstract interface ViewTreeObserver.OnDrawListener {
diff --git a/api/test-current.txt b/api/test-current.txt
index 5bd6dc8773a5..462d22e2e9b3 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1467,6 +1467,11 @@ package android.view {
method public static int getLongPressTooltipHideTimeout();
}
+ public final class ViewTreeObserver {
+ method public void registerFrameCommitCallback(java.lang.Runnable);
+ method public boolean unregisterFrameCommitCallback(java.lang.Runnable);
+ }
+
public static class WindowManager.LayoutParams extends android.view.ViewGroup.LayoutParams implements android.os.Parcelable {
field public static final int ACCESSIBILITY_TITLE_CHANGED = 33554432; // 0x2000000
field public static final int PRIVATE_FLAG_NO_MOVE_ANIMATION = 64; // 0x40
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 14af5b96ffac..b5660995fa36 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -25,7 +25,6 @@ cc_library_host_shared {
],
shared_libs: [
- "libmetricprotos",
"libplatformprotos",
],
@@ -38,7 +37,6 @@ cc_library_host_shared {
},
export_shared_lib_headers: [
- "libmetricprotos",
"libplatformprotos",
]
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 49ea6d56d4fe..61c185f7686d 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -232,7 +232,6 @@ LOCAL_SRC_FILES := \
LOCAL_STATIC_LIBRARIES := \
$(statsd_common_static_libraries) \
libgmock \
- libmetricprotos \
libplatformprotos
LOCAL_PROTOC_OPTIMIZE_TYPE := full
@@ -253,7 +252,6 @@ include $(CLEAR_VARS)
LOCAL_MODULE := statsdprotolite
LOCAL_SRC_FILES := \
- src/metrics_constants/metrics_constants.proto \
src/stats_log.proto \
src/statsd_config.proto \
src/atoms.proto
@@ -317,7 +315,6 @@ LOCAL_AIDL_INCLUDES := $(statsd_common_aidl_includes)
LOCAL_STATIC_LIBRARIES := \
$(statsd_common_static_libraries) \
- libmetricprotos \
libplatformprotos
LOCAL_SHARED_LIBRARIES := $(statsd_common_shared_libraries) \
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index bf033a7f3826..203a8e9b3b0b 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -29,7 +29,6 @@ import "frameworks/base/core/proto/android/server/enums.proto";
import "frameworks/base/core/proto/android/telecomm/enums.proto";
import "frameworks/base/core/proto/android/telephony/enums.proto";
import "frameworks/base/core/proto/android/view/enums.proto";
-import "frameworks/base/proto/src/metrics_constants.proto";
/**
* The master atom class. This message defines all of the available
@@ -2160,11 +2159,11 @@ message BinderCallsExceptions {
/**
* An atom for generic metrics logging. Available from Android Q.
- * One has to add an enum to frameworks/base/proto/src/metrics_constants.proto
- * to extend another metric.
*/
message GenericAtom {
- // Type of event. Previously it only indicated visual elements but now it
- // is expanded to describe any type of event.
- optional com_android_internal_logging.MetricsEvent.View view = 1;
+ // The uid of the application that sent this custom atom.
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // An event_id indicates the type of event.
+ optional int32 event_id = 2;
}
diff --git a/cmds/statsd/src/external/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h
index caac677ee215..22cb2f5c2175 100644
--- a/cmds/statsd/src/external/StatsPuller.h
+++ b/cmds/statsd/src/external/StatsPuller.h
@@ -37,6 +37,8 @@ public:
virtual ~StatsPuller() {}
+ // Pulls the data. The returned data will have elapsedTimeNs set as timeNs
+ // and will have wallClockTimeNs set as current wall clock time.
bool Pull(const int64_t timeNs, std::vector<std::shared_ptr<LogEvent>>* data);
// Clear cache immediately
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index 45efc4a8bea0..bbf5d9dc69db 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -53,9 +53,12 @@ public:
virtual ~StatsPullerManager() {
}
+ // Registers a receiver for tagId. It will be pulled on the nextPullTimeNs
+ // and then every intervalNs thereafter.
virtual void RegisterReceiver(int tagId, wp<PullDataReceiver> receiver, int64_t nextPullTimeNs,
int64_t intervalNs);
+ // Stop listening on a tagId.
virtual void UnRegisterReceiver(int tagId, wp<PullDataReceiver> receiver);
// Verify if we know how to pull for this matcher
@@ -63,11 +66,16 @@ public:
void OnAlarmFired(const int64_t timeNs);
+ // Use respective puller to pull the data. The returned data will have
+ // elapsedTimeNs set as timeNs and will have wallClockTimeNs set as current
+ // wall clock time.
virtual bool Pull(const int tagId, const int64_t timeNs,
vector<std::shared_ptr<LogEvent>>* data);
+ // Clear pull data cache immediately.
int ForceClearPullerCache();
+ // Clear pull data cache if it is beyond respective cool down time.
int ClearPullerCacheIfNecessary(int64_t timestampNs);
void SetStatsCompanionService(sp<IStatsCompanionService> statsCompanionService);
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index a894782db9f7..bd94800a327d 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -66,8 +66,8 @@ const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6;
CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric& metric,
const int conditionIndex,
const sp<ConditionWizard>& wizard,
- const int64_t startTimeNs)
- : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard) {
+ const int64_t timeBaseNs, const int64_t startTimeNs)
+ : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard) {
if (metric.has_bucket()) {
mBucketSizeNs =
TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000;
@@ -100,6 +100,10 @@ CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric
mConditionSliced = (metric.links().size() > 0) || (mDimensionsInCondition.size() > 0);
+ flushIfNeededLocked(startTimeNs);
+ // Adjust start for partial bucket
+ mCurrentBucketStartTimeNs = startTimeNs;
+
VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
(long long)mBucketSizeNs, (long long)mTimeBaseNs);
}
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index 520d5de2b33f..39d4ae2f36a5 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -42,7 +42,7 @@ class CountMetricProducer : public MetricProducer {
public:
CountMetricProducer(const ConfigKey& key, const CountMetric& countMetric,
const int conditionIndex, const sp<ConditionWizard>& wizard,
- const int64_t startTimeNs);
+ const int64_t timeBaseNs, const int64_t startTimeNs);
virtual ~CountMetricProducer();
@@ -98,6 +98,7 @@ private:
FRIEND_TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced);
FRIEND_TEST(CountMetricProducerTest, TestEventWithAppUpgrade);
FRIEND_TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket);
+ FRIEND_TEST(CountMetricProducerTest, TestFirstBucket);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index a19eb0b66bc7..9d9e5be9e165 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -68,8 +68,8 @@ DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const Durat
const bool nesting,
const sp<ConditionWizard>& wizard,
const FieldMatcher& internalDimensions,
- const int64_t startTimeNs)
- : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard),
+ const int64_t timeBaseNs, const int64_t startTimeNs)
+ : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard),
mAggregationType(metric.aggregation_type()),
mStartIndex(startIndex),
mStopIndex(stopIndex),
@@ -128,6 +128,9 @@ DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const Durat
mMetric2ConditionLinks.begin()->conditionFields);
}
}
+ flushIfNeededLocked(startTimeNs);
+ // Adjust start for partial bucket
+ mCurrentBucketStartTimeNs = startTimeNs;
VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
(long long)mBucketSizeNs, (long long)mTimeBaseNs);
}
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index c496b12df935..12addb8727f1 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -42,7 +42,7 @@ public:
const int conditionIndex, const size_t startIndex,
const size_t stopIndex, const size_t stopAllIndex, const bool nesting,
const sp<ConditionWizard>& wizard,
- const FieldMatcher& internalDimensions, const int64_t startTimeNs);
+ const FieldMatcher& internalDimensions, const int64_t timeBaseNs, const int64_t startTimeNs);
virtual ~DurationMetricProducer();
@@ -141,6 +141,7 @@ private:
FRIEND_TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade);
FRIEND_TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgradeInNextBucket);
FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicates);
+ FRIEND_TEST(DurationMetricTrackerTest, TestFirstBucket);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index a77941017c2d..fbe0b2193556 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -76,6 +76,7 @@ GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric
: MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard),
mPullerManager(pullerManager),
mPullTagId(pullTagId),
+ mIsPulled(pullTagId != -1),
mMinBucketSizeNs(metric.min_bucket_size_nanos()),
mDimensionSoftLimit(StatsdStats::kAtomDimensionKeySizeLimitMap.find(pullTagId) !=
StatsdStats::kAtomDimensionKeySizeLimitMap.end()
@@ -125,11 +126,17 @@ GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric
flushIfNeededLocked(startTimeNs);
// Kicks off the puller immediately.
- if (mPullTagId != -1 && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
+ if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
mPullerManager->RegisterReceiver(mPullTagId, this, getCurrentBucketEndTimeNs(),
mBucketSizeNs);
}
+ // Adjust start for partial bucket
+ mCurrentBucketStartTimeNs = startTimeNs;
+ if (mIsPulled) {
+ pullLocked(startTimeNs);
+ }
+
VLOG("Gauge metric %lld created. bucket size %lld start_time: %lld sliced %d",
(long long)metric.id(), (long long)mBucketSizeNs, (long long)mTimeBaseNs,
mConditionSliced);
@@ -137,7 +144,7 @@ GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric
GaugeMetricProducer::~GaugeMetricProducer() {
VLOG("~GaugeMetricProducer() called");
- if (mPullTagId != -1 && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
+ if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
mPullerManager->UnRegisterReceiver(mPullTagId, this);
}
}
@@ -323,13 +330,11 @@ void GaugeMetricProducer::pullLocked(const int64_t timestampNs) {
if (!triggerPuller) {
return;
}
-
vector<std::shared_ptr<LogEvent>> allData;
if (!mPullerManager->Pull(mPullTagId, timestampNs, &allData)) {
- ALOGE("Gauge Stats puller failed for tag: %d", mPullTagId);
+ ALOGE("Gauge Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs);
return;
}
-
for (const auto& data : allData) {
onMatchedLogEventLocked(0, *data);
}
@@ -340,8 +345,7 @@ void GaugeMetricProducer::onConditionChangedLocked(const bool conditionMet,
VLOG("GaugeMetric %lld onConditionChanged", (long long)mMetricId);
flushIfNeededLocked(eventTimeNs);
mCondition = conditionMet;
-
- if (mPullTagId != -1) {
+ if (mIsPulled) {
pullLocked(eventTimeNs);
} // else: Push mode. No need to proactively pull the gauge data.
}
@@ -354,7 +358,7 @@ void GaugeMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition
// If the condition is sliced, mCondition is true if any of the dimensions is true. And we will
// pull for every dimension.
mCondition = overallCondition;
- if (mPullTagId != -1) {
+ if (mIsPulled) {
pullLocked(eventTimeNs);
} // else: Push mode. No need to proactively pull the gauge data.
}
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 6984aa2d145d..cc65440c9572 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -77,7 +77,7 @@ public:
}
flushCurrentBucketLocked(eventTimeNs);
mCurrentBucketStartTimeNs = eventTimeNs;
- if (mPullTagId != -1) {
+ if (mIsPulled) {
pullLocked(eventTimeNs);
}
};
@@ -121,6 +121,9 @@ private:
// tagId for pulled data. -1 if this is not pulled
const int mPullTagId;
+ // if this is pulled metric
+ const bool mIsPulled;
+
// Save the past buckets and we can clear when the StatsLogReport is dumped.
std::unordered_map<MetricDimensionKey, std::vector<GaugeBucket>> mPastBuckets;
@@ -159,12 +162,13 @@ private:
const size_t mGaugeAtomsPerDimensionLimit;
- FRIEND_TEST(GaugeMetricProducerTest, TestWithCondition);
- FRIEND_TEST(GaugeMetricProducerTest, TestWithSlicedCondition);
- FRIEND_TEST(GaugeMetricProducerTest, TestNoCondition);
+ FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition);
+ FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition);
+ FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition);
FRIEND_TEST(GaugeMetricProducerTest, TestPushedEventsWithUpgrade);
FRIEND_TEST(GaugeMetricProducerTest, TestPulledWithUpgrade);
- FRIEND_TEST(GaugeMetricProducerTest, TestAnomalyDetection);
+ FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection);
+ FRIEND_TEST(GaugeMetricProducerTest, TestFirstBucket);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index c6f7bb42e5d5..16447e850cf5 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -64,8 +64,8 @@ const int FIELD_ID_BUCKET_INFO = 3;
const int FIELD_ID_DIMENSION_LEAF_IN_WHAT = 4;
const int FIELD_ID_DIMENSION_LEAF_IN_CONDITION = 5;
// for ValueBucketInfo
-const int FIELD_ID_VALUE_LONG = 3;
-const int FIELD_ID_VALUE_DOUBLE = 7;
+const int FIELD_ID_VALUE_LONG = 7;
+const int FIELD_ID_VALUE_DOUBLE = 8;
const int FIELD_ID_BUCKET_NUM = 4;
const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 5;
const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6;
@@ -74,7 +74,7 @@ const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6;
ValueMetricProducer::ValueMetricProducer(const ConfigKey& key, const ValueMetric& metric,
const int conditionIndex,
const sp<ConditionWizard>& wizard, const int pullTagId,
- const int64_t timeBaseNs, const int64_t startTimestampNs,
+ const int64_t timeBaseNs, const int64_t startTimeNs,
const sp<StatsPullerManager>& pullerManager)
: MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard),
mPullerManager(pullerManager),
@@ -127,13 +127,20 @@ ValueMetricProducer::ValueMetricProducer(const ConfigKey& key, const ValueMetric
mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what()) ||
HasPositionALL(metric.dimensions_in_condition());
- flushIfNeededLocked(startTimestampNs);
+ flushIfNeededLocked(startTimeNs);
// Kicks off the puller immediately.
if (mIsPulled) {
- mPullerManager->RegisterReceiver(mPullTagId, this,
- mCurrentBucketStartTimeNs + mBucketSizeNs, mBucketSizeNs);
+ mPullerManager->RegisterReceiver(mPullTagId, this, getCurrentBucketEndTimeNs(),
+ mBucketSizeNs);
}
+ // TODO: Only do this for partial buckets like first bucket. All other buckets should use
+ // flushIfNeeded to adjust start and end to bucket boundaries.
+ // Adjust start for partial bucket
+ mCurrentBucketStartTimeNs = startTimeNs;
+ if (mIsPulled) {
+ pullLocked(startTimeNs);
+ }
VLOG("value metric %lld created. bucket size %lld start_time: %lld",
(long long)metric.id(), (long long)mBucketSizeNs, (long long)mTimeBaseNs);
}
@@ -280,16 +287,19 @@ void ValueMetricProducer::onConditionChangedLocked(const bool condition,
flushIfNeededLocked(eventTimeNs);
if (mIsPulled) {
- vector<shared_ptr<LogEvent>> allData;
- if (mPullerManager->Pull(mPullTagId, eventTimeNs, &allData)) {
- if (allData.size() == 0) {
- return;
- }
- for (const auto& data : allData) {
- onMatchedLogEventLocked(0, *data);
- }
+ pullLocked(eventTimeNs);
+ }
+}
+
+void ValueMetricProducer::pullLocked(const int64_t timestampNs) {
+ vector<std::shared_ptr<LogEvent>> allData;
+ if (mPullerManager->Pull(mPullTagId, timestampNs, &allData)) {
+ if (allData.size() == 0) {
+ return;
+ }
+ for (const auto& data : allData) {
+ onMatchedLogEventLocked(0, *data);
}
- return;
}
}
@@ -306,12 +316,14 @@ void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEven
int64_t eventTime = mTimeBaseNs +
((realEventTime - mTimeBaseNs) / mBucketSizeNs) * mBucketSizeNs;
+ // close the end of the bucket
mCondition = false;
for (const auto& data : allData) {
data->setElapsedTimestampNs(eventTime - 1);
onMatchedLogEventLocked(0, *data);
}
+ // start a new bucket
mCondition = true;
for (const auto& data : allData) {
data->setElapsedTimestampNs(eventTime);
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 188e3de6a289..b2f0b6ff4d78 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -55,7 +55,7 @@ public:
const int64_t version) override {
std::lock_guard<std::mutex> lock(mMutex);
- if (mPullTagId != -1 && (mCondition == true || mConditionTrackerIndex < 0) ) {
+ if (mIsPulled && (mCondition == true || mConditionTrackerIndex < 0)) {
vector<shared_ptr<LogEvent>> allData;
mPullerManager->Pull(mPullTagId, eventTimeNs, &allData);
if (allData.size() == 0) {
@@ -159,6 +159,8 @@ private:
// Util function to check whether the specified dimension hits the guardrail.
bool hitGuardRailLocked(const MetricDimensionKey& newKey);
+ void pullLocked(const int64_t timestampNs);
+
static const size_t kBucketSize = sizeof(ValueBucket{});
const size_t mDimensionSoftLimit;
@@ -171,7 +173,7 @@ private:
const Type mValueType;
- FRIEND_TEST(ValueMetricProducerTest, TestNonDimensionalEvents);
+ FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsNoCondition);
FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset);
FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset);
FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition);
@@ -190,6 +192,7 @@ private:
FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateAvg);
FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateSum);
FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateSumSliced);
+ FRIEND_TEST(ValueMetricProducerTest, TestFirstBucket);
};
} // namespace statsd
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index e03edb3000ca..ff48d0239059 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -312,7 +312,7 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t
}
sp<MetricProducer> countProducer =
- new CountMetricProducer(key, metric, conditionIndex, wizard, timeBaseTimeNs);
+ new CountMetricProducer(key, metric, conditionIndex, wizard, timeBaseTimeNs, currentTimeNs);
allMetricProducers.push_back(countProducer);
}
@@ -382,7 +382,7 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t
sp<MetricProducer> durationMetric = new DurationMetricProducer(
key, metric, conditionIndex, trackerIndices[0], trackerIndices[1],
- trackerIndices[2], nesting, wizard, internalDimensions, timeBaseTimeNs);
+ trackerIndices[2], nesting, wizard, internalDimensions, timeBaseTimeNs, currentTimeNs);
allMetricProducers.push_back(durationMetric);
}
diff --git a/cmds/statsd/src/metrics_constants/metrics_constants.proto b/cmds/statsd/src/metrics_constants/metrics_constants.proto
deleted file mode 120000
index 8366db767fe8..000000000000
--- a/cmds/statsd/src/metrics_constants/metrics_constants.proto
+++ /dev/null
@@ -1 +0,0 @@
-../../../../proto/src/metrics_constants.proto \ No newline at end of file
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index cfd62690dd1c..db7e680f1d37 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -106,10 +106,12 @@ message ValueBucketInfo {
optional int64 end_bucket_elapsed_nanos = 2;
+ optional int64 value = 3 [deprecated = true];
+
oneof values {
- int64 value_long = 3;
+ int64 value_long = 7;
- double value_double = 7;
+ double value_double = 8;
}
optional int64 bucket_num = 4;
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index 9a8919e98f6d..67c704eb87fd 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -37,6 +37,19 @@ namespace statsd {
const ConfigKey kConfigKey(0, 12345);
+TEST(CountMetricProducerTest, TestFirstBucket) {
+ CountMetric metric;
+ metric.set_id(1);
+ metric.set_bucket(ONE_MINUTE);
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+ CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+ 5, 600 * NS_PER_SEC + NS_PER_SEC/2);
+ EXPECT_EQ(600500000000, countProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(10, countProducer.mCurrentBucketNum);
+ EXPECT_EQ(660000000005, countProducer.getCurrentBucketEndTimeNs());
+}
+
TEST(CountMetricProducerTest, TestNonDimensionalEvents) {
int64_t bucketStartTimeNs = 10000000000;
int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
@@ -56,8 +69,7 @@ TEST(CountMetricProducerTest, TestNonDimensionalEvents) {
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
- bucketStartTimeNs);
- countProducer.setBucketSize(60 * NS_PER_SEC);
+ bucketStartTimeNs, bucketStartTimeNs);
// 2 events in bucket 1.
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
@@ -119,8 +131,7 @@ TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition) {
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
- CountMetricProducer countProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs);
- countProducer.setBucketSize(60 * NS_PER_SEC);
+ CountMetricProducer countProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs, bucketStartTimeNs);
countProducer.onConditionChanged(true, bucketStartTimeNs);
countProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
@@ -181,8 +192,7 @@ TEST(CountMetricProducerTest, TestEventsWithSlicedCondition) {
EXPECT_CALL(*wizard, query(_, key2, _, _, _, _)).WillOnce(Return(ConditionState::kTrue));
CountMetricProducer countProducer(kConfigKey, metric, 1 /*condition tracker index*/, wizard,
- bucketStartTimeNs);
- countProducer.setBucketSize(60 * NS_PER_SEC);
+ bucketStartTimeNs, bucketStartTimeNs);
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
countProducer.flushIfNeededLocked(bucketStartTimeNs + 1);
@@ -221,8 +231,7 @@ TEST(CountMetricProducerTest, TestEventWithAppUpgrade) {
event1.init();
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, wizard,
- bucketStartTimeNs);
- countProducer.setBucketSize(60 * NS_PER_SEC);
+ bucketStartTimeNs, bucketStartTimeNs);
sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor);
EXPECT_TRUE(anomalyTracker != nullptr);
@@ -280,8 +289,7 @@ TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket) {
event1.init();
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, wizard,
- bucketStartTimeNs);
- countProducer.setBucketSize(60 * NS_PER_SEC);
+ bucketStartTimeNs, bucketStartTimeNs);
// Bucket is flushed yet.
countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
@@ -337,8 +345,7 @@ TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) {
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
- bucketStartTimeNs);
- countProducer.setBucketSize(60 * NS_PER_SEC);
+ bucketStartTimeNs, bucketStartTimeNs);
sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor);
diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
index 7ef8c5bd6a1b..b54096441d3f 100644
--- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
@@ -39,6 +39,23 @@ namespace statsd {
const ConfigKey kConfigKey(0, 12345);
+TEST(DurationMetricTrackerTest, TestFirstBucket) {
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ DurationMetric metric;
+ metric.set_id(1);
+ metric.set_bucket(ONE_MINUTE);
+ metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
+
+ FieldMatcher dimensions;
+ DurationMetricProducer durationProducer(
+ kConfigKey, metric, -1 /*no condition*/, 1 /* start index */, 2 /* stop index */,
+ 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, 5, 600 * NS_PER_SEC + NS_PER_SEC/2);
+
+ EXPECT_EQ(600500000000, durationProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(10, durationProducer.mCurrentBucketNum);
+ EXPECT_EQ(660000000005, durationProducer.getCurrentBucketEndTimeNs());
+}
+
TEST(DurationMetricTrackerTest, TestNoCondition) {
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
int64_t bucketStartTimeNs = 10000000000;
@@ -58,8 +75,7 @@ TEST(DurationMetricTrackerTest, TestNoCondition) {
FieldMatcher dimensions;
DurationMetricProducer durationProducer(
kConfigKey, metric, -1 /*no condition*/, 1 /* start index */, 2 /* stop index */,
- 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
- durationProducer.setBucketSize(60 * NS_PER_SEC);
+ 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
@@ -100,8 +116,7 @@ TEST(DurationMetricTrackerTest, TestNonSlicedCondition) {
FieldMatcher dimensions;
DurationMetricProducer durationProducer(
kConfigKey, metric, 0 /* condition index */, 1 /* start index */, 2 /* stop index */,
- 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
- durationProducer.setBucketSize(60 * NS_PER_SEC);
+ 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
EXPECT_FALSE(durationProducer.mCondition);
EXPECT_FALSE(durationProducer.isConditionSliced());
@@ -151,8 +166,7 @@ TEST(DurationMetricTrackerTest, TestSumDurationWithUpgrade) {
FieldMatcher dimensions;
DurationMetricProducer durationProducer(
kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
- 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
- durationProducer.setBucketSize(60 * NS_PER_SEC);
+ 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
LogEvent start_event(tagId, startTimeNs);
start_event.init();
@@ -206,8 +220,7 @@ TEST(DurationMetricTrackerTest, TestSumDurationWithUpgradeInFollowingBucket) {
FieldMatcher dimensions;
DurationMetricProducer durationProducer(
kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
- 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
- durationProducer.setBucketSize(60 * NS_PER_SEC);
+ 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
LogEvent start_event(tagId, startTimeNs);
start_event.init();
@@ -261,8 +274,7 @@ TEST(DurationMetricTrackerTest, TestSumDurationAnomalyWithUpgrade) {
FieldMatcher dimensions;
DurationMetricProducer durationProducer(
kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
- 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
- durationProducer.setBucketSize(60 * NS_PER_SEC);
+ 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
sp<AnomalyTracker> anomalyTracker = durationProducer.addAnomalyTracker(alert, alarmMonitor);
EXPECT_TRUE(anomalyTracker != nullptr);
@@ -300,8 +312,7 @@ TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgrade) {
FieldMatcher dimensions;
DurationMetricProducer durationProducer(
kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
- 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
- durationProducer.setBucketSize(60 * NS_PER_SEC);
+ 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
LogEvent start_event(tagId, startTimeNs);
start_event.init();
@@ -348,8 +359,7 @@ TEST(DurationMetricTrackerTest, TestMaxDurationWithUpgradeInNextBucket) {
FieldMatcher dimensions;
DurationMetricProducer durationProducer(
kConfigKey, metric, -1 /* no condition */, 1 /* start index */, 2 /* stop index */,
- 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
- durationProducer.setBucketSize(60 * NS_PER_SEC);
+ 3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
LogEvent start_event(tagId, startTimeNs);
start_event.init();
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index 19c9f775465b..2fda858db7d7 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -47,7 +47,33 @@ const int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs;
const int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
-TEST(GaugeMetricProducerTest, TestNoCondition) {
+/*
+ * Tests that the first bucket works correctly
+ */
+TEST(GaugeMetricProducerTest, TestFirstBucket) {
+ GaugeMetric metric;
+ metric.set_id(metricId);
+ metric.set_bucket(ONE_MINUTE);
+ metric.mutable_gauge_fields_filter()->set_include_all(false);
+ auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
+ gaugeFieldMatcher->set_field(tagId);
+ gaugeFieldMatcher->add_child()->set_field(1);
+ gaugeFieldMatcher->add_child()->set_field(3);
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+ // statsd started long ago.
+ // The metric starts in the middle of the bucket
+ GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+ -1, 5, 600 * NS_PER_SEC + NS_PER_SEC/2, pullerManager);
+
+ EXPECT_EQ(600500000000, gaugeProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(10, gaugeProducer.mCurrentBucketNum);
+ EXPECT_EQ(660000000005, gaugeProducer.getCurrentBucketEndTimeNs());
+}
+
+TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition) {
GaugeMetric metric;
metric.set_id(metricId);
metric.set_bucket(ONE_MINUTE);
@@ -62,6 +88,16 @@ TEST(GaugeMetricProducerTest, TestNoCondition) {
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ .WillOnce(Invoke([](int tagId, int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+ event->write(3);
+ event->init();
+ data->push_back(event);
+ return true;
+ }));
GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
@@ -83,7 +119,9 @@ TEST(GaugeMetricProducerTest, TestNoCondition) {
EXPECT_EQ(10, it->mValue.int_value);
it++;
EXPECT_EQ(11, it->mValue.int_value);
- EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size());
+ EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
+ EXPECT_EQ(3, gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms
+ .front().mFields->begin()->mValue.int_value);
allData.clear();
std::shared_ptr<LogEvent> event2 = std::make_shared<LogEvent>(tagId, bucket3StartTimeNs + 10);
@@ -102,7 +140,7 @@ TEST(GaugeMetricProducerTest, TestNoCondition) {
EXPECT_EQ(25, it->mValue.int_value);
// One dimension.
EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
- EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size());
it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin();
EXPECT_EQ(INT, it->mValue.getType());
EXPECT_EQ(10L, it->mValue.int_value);
@@ -114,7 +152,7 @@ TEST(GaugeMetricProducerTest, TestNoCondition) {
EXPECT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size());
// One dimension.
EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
- EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(3UL, gaugeProducer.mPastBuckets.begin()->second.size());
it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin();
EXPECT_EQ(INT, it->mValue.getType());
EXPECT_EQ(24L, it->mValue.int_value);
@@ -210,6 +248,7 @@ TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) {
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ .WillOnce(Return(false))
.WillOnce(Invoke([](int tagId, int64_t timeNs,
vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
@@ -263,7 +302,7 @@ TEST(GaugeMetricProducerTest, TestPulledWithUpgrade) {
->mValue.int_value);
}
-TEST(GaugeMetricProducerTest, TestWithCondition) {
+TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition) {
GaugeMetric metric;
metric.set_id(metricId);
metric.set_bucket(ONE_MINUTE);
@@ -333,7 +372,7 @@ TEST(GaugeMetricProducerTest, TestWithCondition) {
->mValue.int_value);
}
-TEST(GaugeMetricProducerTest, TestWithSlicedCondition) {
+TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition) {
const int conditionTag = 65;
GaugeMetric metric;
metric.set_id(1111111);
@@ -409,13 +448,14 @@ TEST(GaugeMetricProducerTest, TestWithSlicedCondition) {
EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
}
-TEST(GaugeMetricProducerTest, TestAnomalyDetection) {
+TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection) {
sp<AlarmMonitor> alarmMonitor;
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, Pull(tagId, _, _)).WillOnce(Return(false));
GaugeMetric metric;
metric.set_id(metricId);
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 3559a7c2dd60..57aab971eaaa 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -50,9 +50,32 @@ const int64_t bucket6StartTimeNs = bucketStartTimeNs + 5 * bucketSizeNs;
const int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
/*
+ * Tests that the first bucket works correctly
+ */
+TEST(ValueMetricProducerTest, TestFirstBucket) {
+ ValueMetric metric;
+ metric.set_id(metricId);
+ metric.set_bucket(ONE_MINUTE);
+ metric.mutable_value_field()->set_field(tagId);
+ metric.mutable_value_field()->add_child()->set_field(2);
+
+ sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+ sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+
+ // statsd started long ago.
+ // The metric starts in the middle of the bucket
+ ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+ -1, 5, 600 * NS_PER_SEC + NS_PER_SEC/2, pullerManager);
+
+ EXPECT_EQ(600500000000, valueProducer.mCurrentBucketStartTimeNs);
+ EXPECT_EQ(10, valueProducer.mCurrentBucketNum);
+ EXPECT_EQ(660000000005, valueProducer.getCurrentBucketEndTimeNs());
+}
+
+/*
* Tests pulled atoms with no conditions
*/
-TEST(ValueMetricProducerTest, TestNonDimensionalEvents) {
+TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) {
ValueMetric metric;
metric.set_id(metricId);
metric.set_bucket(ONE_MINUTE);
@@ -63,10 +86,20 @@ TEST(ValueMetricProducerTest, TestNonDimensionalEvents) {
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ .WillOnce(Invoke([](int tagId, int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+ event->write(tagId);
+ event->write(3);
+ event->init();
+ data->push_back(event);
+ return true;
+ }));
ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
vector<shared_ptr<LogEvent>> allData;
allData.clear();
@@ -85,7 +118,8 @@ TEST(ValueMetricProducerTest, TestNonDimensionalEvents) {
EXPECT_EQ(true, curInterval.startUpdated);
EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(11, curInterval.start.long_value);
- EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
+ EXPECT_EQ(8, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
allData.clear();
event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
@@ -99,9 +133,9 @@ TEST(ValueMetricProducerTest, TestNonDimensionalEvents) {
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
// tartUpdated:false sum:12
EXPECT_EQ(true, curInterval.startUpdated);
- EXPECT_EQ(0, curInterval.value.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
- EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size());
EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
allData.clear();
@@ -115,9 +149,9 @@ TEST(ValueMetricProducerTest, TestNonDimensionalEvents) {
curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
// startUpdated:false sum:12
EXPECT_EQ(true, curInterval.startUpdated);
- EXPECT_EQ(0, curInterval.value.long_value);
+ EXPECT_EQ(false, curInterval.hasValue);
EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
- EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size());
+ EXPECT_EQ(3UL, valueProducer.mPastBuckets.begin()->second.size());
EXPECT_EQ(13, valueProducer.mPastBuckets.begin()->second.back().mValueLong);
}
@@ -136,10 +170,10 @@ TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset) {
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, Pull(tagId, _, _)).WillOnce(Return(false));
ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
vector<shared_ptr<LogEvent>> allData;
allData.clear();
@@ -205,10 +239,10 @@ TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) {
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, Pull(tagId, _, _)).WillOnce(Return(false));
ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
vector<shared_ptr<LogEvent>> allData;
allData.clear();
@@ -275,6 +309,17 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) {
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ // should not take effect
+ .WillOnce(Invoke([](int tagId, int64_t timeNs,
+ vector<std::shared_ptr<LogEvent>>* data) {
+ data->clear();
+ shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+ event->write(tagId);
+ event->write(3);
+ event->init();
+ data->push_back(event);
+ return true;
+ }))
.WillOnce(Invoke([](int tagId, int64_t timeNs,
vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
@@ -298,7 +343,6 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) {
ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
// has one slice
@@ -349,7 +393,6 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade) {
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event1->write(1);
@@ -392,6 +435,7 @@ TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade) {
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ .WillOnce(Return(false))
.WillOnce(Invoke([](int tagId, int64_t timeNs,
vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
@@ -404,7 +448,6 @@ TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade) {
}));
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
vector<shared_ptr<LogEvent>> allData;
allData.clear();
@@ -447,6 +490,7 @@ TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse) {
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ .WillOnce(Return(false))
.WillOnce(Invoke([](int tagId, int64_t timeNs,
vector<std::shared_ptr<LogEvent>>* data) {
data->clear();
@@ -469,7 +513,6 @@ TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse) {
}));
ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
valueProducer.onConditionChanged(true, bucketStartTimeNs + 1);
valueProducer.onConditionChanged(false, bucket2StartTimeNs-100);
@@ -496,7 +539,6 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) {
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event1->write(1);
@@ -538,7 +580,6 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) {
ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, -1, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event1->write(1);
@@ -612,7 +653,6 @@ TEST(ValueMetricProducerTest, TestAnomalyDetection) {
ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-1 /*not pulled*/, bucketStartTimeNs, bucketStartTimeNs,
pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
sp<AnomalyTracker> anomalyTracker = valueProducer.addAnomalyTracker(alert, alarmMonitor);
@@ -687,10 +727,10 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) {
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return());
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
+ EXPECT_CALL(*pullerManager, Pull(tagId, _, _)).WillOnce(Return(false));
ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
vector<shared_ptr<LogEvent>> allData;
// pull 1
@@ -770,6 +810,7 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) {
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ .WillOnce(Return(false))
// condition becomes true
.WillOnce(Invoke([](int tagId, int64_t timeNs,
vector<std::shared_ptr<LogEvent>>* data) {
@@ -795,7 +836,6 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) {
ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
// has one slice
@@ -849,6 +889,7 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) {
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ .WillOnce(Return(false))
// condition becomes true
.WillOnce(Invoke([](int tagId, int64_t timeNs,
vector<std::shared_ptr<LogEvent>>* data) {
@@ -885,7 +926,6 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) {
ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
// has one slice
@@ -947,6 +987,7 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition3) {
EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
EXPECT_CALL(*pullerManager, Pull(tagId, _, _))
+ .WillOnce(Return(false))
// condition becomes true
.WillOnce(Invoke([](int tagId, int64_t timeNs,
vector<std::shared_ptr<LogEvent>>* data) {
@@ -972,7 +1013,6 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition3) {
ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
// has one slice
@@ -1022,7 +1062,6 @@ TEST(ValueMetricProducerTest, TestPushedAggregateMin) {
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event1->write(1);
@@ -1065,7 +1104,6 @@ TEST(ValueMetricProducerTest, TestPushedAggregateMax) {
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event1->write(1);
@@ -1108,7 +1146,6 @@ TEST(ValueMetricProducerTest, TestPushedAggregateAvg) {
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event1->write(1);
@@ -1154,7 +1191,6 @@ TEST(ValueMetricProducerTest, TestPushedAggregateSum) {
ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
event1->write(1);
@@ -1218,14 +1254,12 @@ TEST(ValueMetricProducerTest, TestPushedAggregateSumSliced) {
sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
EXPECT_CALL(*wizard, query(_, key1, _, _, _, _)).WillOnce(Return(ConditionState::kFalse));
-
EXPECT_CALL(*wizard, query(_, key2, _, _, _, _)).WillOnce(Return(ConditionState::kTrue));
sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, -1, bucketStartTimeNs,
bucketStartTimeNs, pullerManager);
- valueProducer.setBucketSize(60 * NS_PER_SEC);
valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 829a94446937..300a530970c9 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -31,7 +31,6 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
-import android.provider.Settings;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
@@ -398,13 +397,49 @@ public abstract class AccessibilityService extends Service {
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = { "SHOW_MODE_" }, value = {
SHOW_MODE_AUTO,
- SHOW_MODE_HIDDEN
+ SHOW_MODE_HIDDEN,
+ SHOW_MODE_WITH_HARD_KEYBOARD
})
public @interface SoftKeyboardShowMode {}
+ /**
+ * Allow the system to control when the soft keyboard is shown.
+ * @see SoftKeyboardController
+ */
public static final int SHOW_MODE_AUTO = 0;
+
+ /**
+ * Never show the soft keyboard.
+ * @see SoftKeyboardController
+ */
public static final int SHOW_MODE_HIDDEN = 1;
+ /**
+ * Allow the soft keyboard to be shown, even if a hard keyboard is connected
+ * @see SoftKeyboardController
+ */
+ public static final int SHOW_MODE_WITH_HARD_KEYBOARD = 2;
+
+ /**
+ * Mask used to cover the show modes supported in public API
+ * @hide
+ */
+ public static final int SHOW_MODE_MASK = 0x03;
+
+ /**
+ * Bit used to hold the old value of the hard IME setting to restore when a service is shut
+ * down.
+ * @hide
+ */
+ public static final int SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE = 0x20000000;
+
+ /**
+ * Bit for show mode setting to indicate that the user has overridden the hard keyboard
+ * behavior.
+ * @hide
+ */
+ public static final int SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN = 0x40000000;
+
private int mConnectionId = AccessibilityInteractionClient.NO_ID;
private AccessibilityServiceInfo mInfo;
@@ -1147,7 +1182,27 @@ public abstract class AccessibilityService extends Service {
}
/**
- * Used to control and query the soft keyboard show mode.
+ * Used to control, query, and listen for changes to the soft keyboard show mode.
+ * <p>
+ * Accessibility services may request to override the decisions normally made about whether or
+ * not the soft keyboard is shown.
+ * <p>
+ * If multiple services make conflicting requests, the last request is honored. A service may
+ * register a listener to find out if the mode has changed under it.
+ * <p>
+ * If the user takes action to override the behavior behavior requested by an accessibility
+ * service, the user's request takes precendence, the show mode will be reset to
+ * {@link AccessibilityService#SHOW_MODE_AUTO}, and services will no longer be able to control
+ * that aspect of the soft keyboard's behavior.
+ * <p>
+ * Note: Because soft keyboards are independent apps, the framework does not have total control
+ * over their behavior. They may choose to show themselves, or not, without regard to requests
+ * made here. So the framework will make a best effort to deliver the behavior requested, but
+ * cannot guarantee success.
+ *
+ * @see AccessibilityService#SHOW_MODE_AUTO
+ * @see AccessibilityService#SHOW_MODE_HIDDEN
+ * @see AccessibilityService#SHOW_MODE_WITH_HARD_KEYBOARD
*/
public static final class SoftKeyboardController {
private final AccessibilityService mService;
@@ -1217,7 +1272,8 @@ public abstract class AccessibilityService extends Service {
* @param listener the listener to remove, must be non-null
* @return {@code true} if the listener was removed, {@code false} otherwise
*/
- public boolean removeOnShowModeChangedListener(@NonNull OnShowModeChangedListener listener) {
+ public boolean removeOnShowModeChangedListener(
+ @NonNull OnShowModeChangedListener listener) {
if (mListeners == null) {
return false;
}
@@ -1289,32 +1345,32 @@ public abstract class AccessibilityService extends Service {
}
/**
- * Returns the show mode of the soft keyboard. The default show mode is
- * {@code SHOW_MODE_AUTO}, where the soft keyboard is shown when a text input field is
- * focused. An AccessibilityService can also request the show mode
- * {@code SHOW_MODE_HIDDEN}, where the soft keyboard is never shown.
+ * Returns the show mode of the soft keyboard.
*
* @return the current soft keyboard show mode
+ *
+ * @see AccessibilityService#SHOW_MODE_AUTO
+ * @see AccessibilityService#SHOW_MODE_HIDDEN
+ * @see AccessibilityService#SHOW_MODE_WITH_HARD_KEYBOARD
*/
@SoftKeyboardShowMode
public int getShowMode() {
- try {
- return Settings.Secure.getInt(mService.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE);
- } catch (Settings.SettingNotFoundException e) {
- Log.v(LOG_TAG, "Failed to obtain the soft keyboard mode", e);
- // The settings hasn't been changed yet, so it's value is null. Return the default.
- return 0;
- }
+ final IAccessibilityServiceConnection connection =
+ AccessibilityInteractionClient.getInstance().getConnection(
+ mService.mConnectionId);
+ if (connection != null) {
+ try {
+ return connection.getSoftKeyboardShowMode();
+ } catch (RemoteException re) {
+ Log.w(LOG_TAG, "Failed to set soft keyboard behavior", re);
+ re.rethrowFromSystemServer();
+ }
+ }
+ return SHOW_MODE_AUTO;
}
/**
- * Sets the soft keyboard show mode. The default show mode is
- * {@code SHOW_MODE_AUTO}, where the soft keyboard is shown when a text input field is
- * focused. An AccessibilityService can also request the show mode
- * {@code SHOW_MODE_HIDDEN}, where the soft keyboard is never shown. The
- * The lastto this method will be honored, regardless of any previous calls (including those
- * made by other AccessibilityServices).
+ * Sets the soft keyboard show mode.
* <p>
* <strong>Note:</strong> If the service is not yet connected (e.g.
* {@link AccessibilityService#onServiceConnected()} has not yet been called) or the
@@ -1322,6 +1378,10 @@ public abstract class AccessibilityService extends Service {
*
* @param showMode the new show mode for the soft keyboard
* @return {@code true} on success
+ *
+ * @see AccessibilityService#SHOW_MODE_AUTO
+ * @see AccessibilityService#SHOW_MODE_HIDDEN
+ * @see AccessibilityService#SHOW_MODE_WITH_HARD_KEYBOARD
*/
public boolean setShowMode(@SoftKeyboardShowMode int showMode) {
final IAccessibilityServiceConnection connection =
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 037aeb058f15..276131f9d0d6 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -87,6 +87,8 @@ interface IAccessibilityServiceConnection {
boolean setSoftKeyboardShowMode(int showMode);
+ int getSoftKeyboardShowMode();
+
void setSoftKeyboardCallbackEnabled(boolean enabled);
boolean isAccessibilityButtonAvailable();
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index c8959788cf50..2baae92bc6a8 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -228,4 +228,7 @@ public abstract class ActivityManagerInternal {
public abstract boolean isCurrentProfile(int userId);
public abstract boolean hasStartedUserState(int userId);
public abstract void finishUserSwitch(Object uss);
+
+ /** Schedule the execution of all pending app GCs. */
+ public abstract void scheduleAppGcs();
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 151e9a5a4908..2a3fc04b2451 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -259,6 +259,7 @@ public final class ActivityThread extends ClientTransactionHandler {
final H mH = new H();
final Executor mExecutor = new HandlerExecutor(mH);
final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
+ /** The activities to be truly destroyed (not include relaunch). */
final Map<IBinder, ClientTransactionItem> mActivitiesToBeDestroyed =
Collections.synchronizedMap(new ArrayMap<IBinder, ClientTransactionItem>());
// List of new activities (via ActivityRecord.nextIdle) that should
@@ -3078,6 +3079,10 @@ public final class ActivityThread extends ClientTransactionHandler {
}
private void reportSizeConfigurations(ActivityClientRecord r) {
+ if (mActivitiesToBeDestroyed.containsKey(r.token)) {
+ // Size configurations of a destroyed activity is meaningless.
+ return;
+ }
Configuration[] configurations = r.activity.getResources().getSizeConfigurations();
if (configurations == null) {
return;
@@ -3826,6 +3831,13 @@ public final class ActivityThread extends ClientTransactionHandler {
// We didn't actually resume the activity, so skipping any follow-up actions.
return;
}
+ if (mActivitiesToBeDestroyed.containsKey(token)) {
+ // Although the activity is resumed, it is going to be destroyed. So the following
+ // UI operations are unnecessary and also prevents exception because its token may
+ // be gone that window manager cannot recognize it. All necessary cleanup actions
+ // performed below will be done while handling destruction.
+ return;
+ }
final Activity a = r.activity;
diff --git a/core/java/android/app/ApplicationErrorReport.java b/core/java/android/app/ApplicationErrorReport.java
index e6452619b508..8874554f928c 100644
--- a/core/java/android/app/ApplicationErrorReport.java
+++ b/core/java/android/app/ApplicationErrorReport.java
@@ -29,6 +29,7 @@ import android.os.SystemProperties;
import android.provider.Settings;
import android.util.Printer;
import android.util.Slog;
+
import com.android.internal.util.FastPrintWriter;
import java.io.PrintWriter;
@@ -333,6 +334,12 @@ public class ApplicationErrorReport implements Parcelable {
public String stackTrace;
/**
+ * Crash tag for some context.
+ * @hide
+ */
+ public String crashTag;
+
+ /**
* Create an uninitialized instance of CrashInfo.
*/
public CrashInfo() {
@@ -416,6 +423,7 @@ public class ApplicationErrorReport implements Parcelable {
throwMethodName = in.readString();
throwLineNumber = in.readInt();
stackTrace = in.readString();
+ crashTag = in.readString();
}
/**
@@ -430,6 +438,7 @@ public class ApplicationErrorReport implements Parcelable {
dest.writeString(throwMethodName);
dest.writeInt(throwLineNumber);
dest.writeString(stackTrace);
+ dest.writeString(crashTag);
int total = dest.dataPosition()-start;
if (Binder.CHECK_PARCEL_SIZE && total > 20*1024) {
Slog.d("Error", "ERR: exClass=" + exceptionClassName);
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 285f83b9457b..16360b3bf5a8 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -325,7 +325,6 @@ interface IActivityManager {
*/
void requestWifiBugReport(in String shareTitle, in String shareDescription);
- long inputDispatchingTimedOut(int pid, boolean aboveSystem, in String reason);
void clearPendingBackup();
Intent getIntentForIntentSender(in IIntentSender sender);
// This is not public because you need to be very careful in how you
diff --git a/core/java/android/app/WallpaperInfo.java b/core/java/android/app/WallpaperInfo.java
index 35a17892eec3..9873a8152b3f 100644
--- a/core/java/android/app/WallpaperInfo.java
+++ b/core/java/android/app/WallpaperInfo.java
@@ -315,12 +315,13 @@ public final class WallpaperInfo implements Parcelable {
}
/**
- * Returns whether a wallpaper was optimized or not for ambient mode.
+ * Returns whether a wallpaper was optimized or not for ambient mode and can be drawn in there.
*
- * @return {@code true} if wallpaper can draw in ambient mode.
- * @hide
+ * @see WallpaperService.Engine#onAmbientModeChanged(boolean, boolean)
+ * @see WallpaperService.Engine#isInAmbientMode()
+ * @return {@code true} if wallpaper can draw when in ambient mode.
*/
- public boolean getSupportsAmbientMode() {
+ public boolean supportsAmbientMode() {
return mSupportsAmbientMode;
}
diff --git a/core/java/android/database/DatabaseUtils.java b/core/java/android/database/DatabaseUtils.java
index 3d019f07cb84..7a8ab60c502d 100644
--- a/core/java/android/database/DatabaseUtils.java
+++ b/core/java/android/database/DatabaseUtils.java
@@ -16,6 +16,7 @@
package android.database;
+import android.annotation.Nullable;
import android.content.ContentValues;
import android.content.Context;
import android.content.OperationApplicationException;
@@ -34,6 +35,8 @@ import android.os.ParcelFileDescriptor;
import android.text.TextUtils;
import android.util.Log;
+import com.android.internal.util.ArrayUtils;
+
import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.text.Collator;
@@ -216,6 +219,92 @@ public class DatabaseUtils {
}
/**
+ * Bind the given selection with the given selection arguments.
+ * <p>
+ * Internally assumes that '?' is only ever used for arguments, and doesn't
+ * appear as a literal or escaped value.
+ * <p>
+ * This method is typically useful for trusted code that needs to cook up a
+ * fully-bound selection.
+ *
+ * @hide
+ */
+ public static @Nullable String bindSelection(@Nullable String selection,
+ @Nullable Object... selectionArgs) {
+ if (selection == null) return null;
+ // If no arguments provided, so we can't bind anything
+ if (ArrayUtils.isEmpty(selectionArgs)) return selection;
+ // If no bindings requested, so we can shortcut
+ if (selection.indexOf('?') == -1) return selection;
+
+ // Track the chars immediately before and after each bind request, to
+ // decide if it needs additional whitespace added
+ char before = ' ';
+ char after = ' ';
+
+ int argIndex = 0;
+ final int len = selection.length();
+ final StringBuilder res = new StringBuilder(len);
+ for (int i = 0; i < len; ) {
+ char c = selection.charAt(i++);
+ if (c == '?') {
+ // Assume this bind request is guarded until we find a specific
+ // trailing character below
+ after = ' ';
+
+ // Sniff forward to see if the selection is requesting a
+ // specific argument index
+ int start = i;
+ for (; i < len; i++) {
+ c = selection.charAt(i);
+ if (c < '0' || c > '9') {
+ after = c;
+ break;
+ }
+ }
+ if (start != i) {
+ argIndex = Integer.parseInt(selection.substring(start, i)) - 1;
+ }
+
+ // Manually bind the argument into the selection, adding
+ // whitespace when needed for clarity
+ final Object arg = selectionArgs[argIndex++];
+ if (before != ' ' && before != '=') res.append(' ');
+ switch (DatabaseUtils.getTypeOfObject(arg)) {
+ case Cursor.FIELD_TYPE_NULL:
+ res.append("NULL");
+ break;
+ case Cursor.FIELD_TYPE_INTEGER:
+ res.append(((Number) arg).longValue());
+ break;
+ case Cursor.FIELD_TYPE_FLOAT:
+ res.append(((Number) arg).doubleValue());
+ break;
+ case Cursor.FIELD_TYPE_BLOB:
+ throw new IllegalArgumentException("Blobs not supported");
+ case Cursor.FIELD_TYPE_STRING:
+ default:
+ if (arg instanceof Boolean) {
+ // Provide compatibility with legacy applications which may pass
+ // Boolean values in bind args.
+ res.append(((Boolean) arg).booleanValue() ? 1 : 0);
+ } else {
+ res.append('\'');
+ res.append(arg.toString());
+ res.append('\'');
+ }
+ break;
+ }
+ if (after != ' ') res.append(' ');
+ } else {
+ res.append(c);
+ before = c;
+ }
+ }
+ return res.toString();
+ }
+
+ /**
* Returns data type of the given object's value.
*<p>
* Returned values are
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index 101fb821f4ce..df6447a7e51e 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -34,10 +34,10 @@ import dalvik.system.CloseGuard;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Date;
import java.util.Map;
-
/**
* Represents a SQLite database connection.
* Each connection wraps an instance of a native <code>sqlite3</code> object.
@@ -90,6 +90,8 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
private static final String TAG = "SQLiteConnection";
private static final boolean DEBUG = false;
+ public static volatile boolean sLocalDebug = false;
+
private static final String[] EMPTY_STRING_ARRAY = new String[0];
private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
@@ -991,6 +993,10 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
}
private void bindArguments(PreparedStatement statement, Object[] bindArgs) {
+ if (sLocalDebug) {
+ Log.v(TAG, statement.mSql + " with args " + Arrays.toString(bindArgs));
+ }
+
final int count = bindArgs != null ? bindArgs.length : 0;
if (count != statement.mNumParameters) {
throw new SQLiteBindOrColumnIndexOutOfRangeException(
diff --git a/core/java/android/database/sqlite/SQLiteQueryBuilder.java b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
index 2d45b14146fc..06560f2e4887 100644
--- a/core/java/android/database/sqlite/SQLiteQueryBuilder.java
+++ b/core/java/android/database/sqlite/SQLiteQueryBuilder.java
@@ -29,7 +29,7 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
-import com.android.internal.util.ArrayUtils;
+import libcore.util.EmptyArray;
import java.util.Arrays;
import java.util.Iterator;
@@ -43,8 +43,7 @@ import java.util.regex.Pattern;
* This is a convenience class that helps build SQL queries to be sent to
* {@link SQLiteDatabase} objects.
*/
-public class SQLiteQueryBuilder
-{
+public class SQLiteQueryBuilder {
private static final String TAG = "SQLiteQueryBuilder";
private static final Pattern sLimitPattern =
Pattern.compile("\\s*\\d+\\s*(,\\s*\\d+\\s*)?");
@@ -100,7 +99,7 @@ public class SQLiteQueryBuilder
*
* @param inWhere the chunk of text to append to the WHERE clause.
*/
- public void appendWhere(CharSequence inWhere) {
+ public void appendWhere(@NonNull CharSequence inWhere) {
if (mWhereClause == null) {
mWhereClause = new StringBuilder(inWhere.length() + 16);
}
@@ -117,7 +116,7 @@ public class SQLiteQueryBuilder
* @param inWhere the chunk of text to append to the WHERE clause. it will be escaped
* to avoid SQL injection attacks
*/
- public void appendWhereEscapeString(String inWhere) {
+ public void appendWhereEscapeString(@NonNull String inWhere) {
if (mWhereClause == null) {
mWhereClause = new StringBuilder(inWhere.length() + 16);
}
@@ -125,6 +124,27 @@ public class SQLiteQueryBuilder
}
/**
+ * Add a standalone chunk to the {@code WHERE} clause of this query.
+ * <p>
+ * This method differs from {@link #appendWhere(CharSequence)} in that it
+ * automatically appends {@code AND} to any existing {@code WHERE} clause
+ * already under construction before appending the given standalone
+ * expression wrapped in parentheses.
+ *
+ * @param inWhere the standalone expression to append to the {@code WHERE}
+ * clause. It will be wrapped in parentheses when it's appended.
+ */
+ public void appendWhereStandalone(@NonNull CharSequence inWhere) {
+ if (mWhereClause == null) {
+ mWhereClause = new StringBuilder(inWhere.length() + 16);
+ }
+ if (mWhereClause.length() > 0) {
+ mWhereClause.append(" AND ");
+ }
+ mWhereClause.append('(').append(inWhere).append(')');
+ }
+
+ /**
* Sets the projection map for the query. The projection map maps
* from column names that the caller passes into query to database
* column names. This is useful for renaming columns as well as
@@ -436,7 +456,6 @@ public class SQLiteQueryBuilder
* that they appear in the selection. The values will be bound
* as Strings.
* @return the number of rows updated
- * @hide
*/
public int update(@NonNull SQLiteDatabase db, @NonNull ContentValues values,
@Nullable String selection, @Nullable String[] selectionArgs) {
@@ -471,14 +490,19 @@ public class SQLiteQueryBuilder
sql = unwrappedSql;
}
+ if (selectionArgs == null) {
+ selectionArgs = EmptyArray.STRING;
+ }
final ArrayMap<String, Object> rawValues = values.getValues();
- final String[] updateArgs = new String[rawValues.size()];
- for (int i = 0; i < updateArgs.length; i++) {
- final Object arg = rawValues.valueAt(i);
- updateArgs[i] = (arg != null) ? arg.toString() : null;
+ final int valuesLength = rawValues.size();
+ final Object[] sqlArgs = new Object[valuesLength + selectionArgs.length];
+ for (int i = 0; i < sqlArgs.length; i++) {
+ if (i < valuesLength) {
+ sqlArgs[i] = rawValues.valueAt(i);
+ } else {
+ sqlArgs[i] = selectionArgs[i - valuesLength];
+ }
}
-
- final String[] sqlArgs = ArrayUtils.concat(String.class, updateArgs, selectionArgs);
if (Log.isLoggable(TAG, Log.DEBUG)) {
if (Build.IS_DEBUGGABLE) {
Log.d(TAG, sql + " with args " + Arrays.toString(sqlArgs));
@@ -502,7 +526,6 @@ public class SQLiteQueryBuilder
* that they appear in the selection. The values will be bound
* as Strings.
* @return the number of rows deleted
- * @hide
*/
public int delete(@NonNull SQLiteDatabase db, @Nullable String selection,
@Nullable String[] selectionArgs) {
diff --git a/core/java/android/database/sqlite/SQLiteStatementBuilder.java b/core/java/android/database/sqlite/SQLiteStatementBuilder.java
deleted file mode 100644
index e2efb2f8c39b..000000000000
--- a/core/java/android/database/sqlite/SQLiteStatementBuilder.java
+++ /dev/null
@@ -1,1036 +0,0 @@
-/*
- * Copyright (C) 2006 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.sqlite;
-
-import static android.content.ContentResolver.QUERY_ARG_SQL_GROUP_BY;
-import static android.content.ContentResolver.QUERY_ARG_SQL_HAVING;
-import static android.content.ContentResolver.QUERY_ARG_SQL_LIMIT;
-import static android.content.ContentResolver.QUERY_ARG_SQL_SELECTION;
-import static android.content.ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS;
-import static android.content.ContentResolver.QUERY_ARG_SQL_SORT_ORDER;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.CancellationSignal;
-import android.os.OperationCanceledException;
-import android.provider.BaseColumns;
-import android.text.TextUtils;
-import android.util.ArrayMap;
-import android.util.Log;
-
-import com.android.internal.util.ArrayUtils;
-
-import dalvik.system.VMRuntime;
-
-import libcore.util.EmptyArray;
-
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Objects;
-import java.util.Set;
-import java.util.regex.Pattern;
-
-/**
- * This is a convenience class that helps build SQL queries to be sent to
- * {@link SQLiteDatabase} objects.
- * @hide
- */
-public class SQLiteStatementBuilder {
- private static final String TAG = "SQLiteStatementBuilder";
- private static final Pattern sLimitPattern =
- Pattern.compile("\\s*\\d+\\s*(,\\s*\\d+\\s*)?");
-
- private Map<String, String> mProjectionMap = null;
- private String mTables = "";
- private StringBuilder mWhereClause = null; // lazily created
- private String[] mWhereArgs = EmptyArray.STRING;
- private boolean mDistinct;
- private SQLiteDatabase.CursorFactory mFactory;
- private boolean mStrict;
-
- public SQLiteStatementBuilder() {
- mDistinct = false;
- mFactory = null;
- }
-
- /**
- * Mark the query as DISTINCT.
- *
- * @param distinct if true the query is DISTINCT, otherwise it isn't
- */
- public void setDistinct(boolean distinct) {
- mDistinct = distinct;
- }
-
- /**
- * Returns the list of tables being queried
- *
- * @return the list of tables being queried
- */
- public String getTables() {
- return mTables;
- }
-
- /**
- * Sets the list of tables to query. Multiple tables can be specified to perform a join.
- * For example:
- * setTables("foo, bar")
- * setTables("foo LEFT OUTER JOIN bar ON (foo.id = bar.foo_id)")
- *
- * @param inTables the list of tables to query on
- */
- public void setTables(String inTables) {
- mTables = inTables;
- }
-
- /** {@hide} */
- public @Nullable String getWhere() {
- return (mWhereClause != null) ? mWhereClause.toString() : null;
- }
-
- /** {@hide} */
- public String[] getWhereArgs() {
- return mWhereArgs;
- }
-
- /**
- * Append a chunk to the {@code WHERE} clause of the query. All chunks
- * appended are surrounded by parenthesis and {@code AND}ed with the
- * selection passed to {@link #query}. The final {@code WHERE} clause looks
- * like:
- *
- * <pre>
- * WHERE (&lt;append chunk 1>&lt;append chunk2>) AND (&lt;query() selection parameter>)
- * </pre>
- *
- * @param inWhere the chunk of text to append to the {@code WHERE} clause.
- */
- public void appendWhere(@NonNull CharSequence inWhere) {
- appendWhere(inWhere, EmptyArray.STRING);
- }
-
- /**
- * Append a chunk to the {@code WHERE} clause of the query. All chunks
- * appended are surrounded by parenthesis and {@code AND}ed with the
- * selection passed to {@link #query}. The final {@code WHERE} clause looks
- * like:
- *
- * <pre>
- * WHERE (&lt;append chunk 1>&lt;append chunk2>) AND (&lt;query() selection parameter>)
- * </pre>
- *
- * @param inWhere the chunk of text to append to the {@code WHERE} clause.
- * @param inWhereArgs list of arguments to be bound to any '?' occurrences
- * in the where clause.
- */
- public void appendWhere(@NonNull CharSequence inWhere, String... inWhereArgs) {
- if (mWhereClause == null) {
- mWhereClause = new StringBuilder(inWhere.length() + 16);
- }
- mWhereClause.append(inWhere);
- mWhereArgs = ArrayUtils.concat(String.class, mWhereArgs, inWhereArgs);
- }
-
- /**
- * Append a standalone expression to the {@code WHERE} clause of this query.
- * <p>
- * This method differs from {@link #appendWhere(CharSequence)} in that it
- * automatically appends {@code AND} to any existing {@code WHERE} clause
- * already under construction before appending the given standalone
- * expression.
- *
- * @param inWhere the standalone expression to append to the {@code WHERE}
- * clause. It will be wrapped in parentheses when it's appended.
- */
- public void appendWhereExpression(@NonNull CharSequence inWhere) {
- appendWhereExpression(inWhere, EmptyArray.STRING);
- }
-
- /**
- * Append a standalone expression to the {@code WHERE} clause of this query.
- * <p>
- * This method differs from {@link #appendWhere(CharSequence)} in that it
- * automatically appends {@code AND} to any existing {@code WHERE} clause
- * already under construction before appending the given standalone
- * expression.
- *
- * @param inWhere the standalone expression to append to the {@code WHERE}
- * clause. It will be wrapped in parentheses when it's appended.
- * @param inWhereArgs list of arguments to be bound to any '?' occurrences
- * in the standalone expression.
- */
- public void appendWhereExpression(@NonNull CharSequence inWhere, String... inWhereArgs) {
- if (mWhereClause == null) {
- mWhereClause = new StringBuilder(inWhere.length() + 16);
- }
- if (mWhereClause.length() > 0) {
- mWhereClause.append(" AND ");
- }
- mWhereClause.append('(').append(inWhere).append(')');
- mWhereArgs = ArrayUtils.concat(String.class, mWhereArgs, inWhereArgs);
- }
-
- /**
- * Append a chunk to the {@code WHERE} clause of the query. All chunks
- * appended are surrounded by parenthesis and {@code AND}ed with the
- * selection passed to {@link #query}. The final {@code WHERE} clause looks
- * like this:
- *
- * <pre>
- * WHERE (&lt;append chunk 1>&lt;append chunk2>) AND (&lt;query() selection parameter>)
- * </pre>
- *
- * @param inWhere the chunk of text to append to the {@code WHERE} clause.
- * It will be escaped to avoid SQL injection attacks.
- */
- public void appendWhereEscapeString(@NonNull String inWhere) {
- appendWhereEscapeString(inWhere, EmptyArray.STRING);
- }
-
- /**
- * Append a chunk to the {@code WHERE} clause of the query. All chunks
- * appended are surrounded by parenthesis and {@code AND}ed with the
- * selection passed to {@link #query}. The final {@code WHERE} clause looks
- * like this:
- *
- * <pre>
- * WHERE (&lt;append chunk 1>&lt;append chunk2>) AND (&lt;query() selection parameter>)
- * </pre>
- *
- * @param inWhere the chunk of text to append to the {@code WHERE} clause.
- * It will be escaped to avoid SQL injection attacks.
- * @param inWhereArgs list of arguments to be bound to any '?' occurrences
- * in the where clause.
- */
- public void appendWhereEscapeString(@NonNull String inWhere, String... inWhereArgs) {
- if (mWhereClause == null) {
- mWhereClause = new StringBuilder(inWhere.length() + 16);
- }
- DatabaseUtils.appendEscapedSQLString(mWhereClause, inWhere);
- mWhereArgs = ArrayUtils.concat(String.class, mWhereArgs, inWhereArgs);
- }
-
- /**
- * Sets the projection map for the query. The projection map maps
- * from column names that the caller passes into query to database
- * column names. This is useful for renaming columns as well as
- * disambiguating column names when doing joins. For example you
- * could map "name" to "people.name". If a projection map is set
- * it must contain all column names the user may request, even if
- * the key and value are the same.
- *
- * @param columnMap maps from the user column names to the database column names
- */
- public void setProjectionMap(Map<String, String> columnMap) {
- mProjectionMap = columnMap;
- }
-
- /**
- * Sets the cursor factory to be used for the query. You can use
- * one factory for all queries on a database but it is normally
- * easier to specify the factory when doing this query.
- *
- * @param factory the factory to use.
- */
- public void setCursorFactory(SQLiteDatabase.CursorFactory factory) {
- mFactory = factory;
- }
-
- /**
- * When set, the selection is verified against malicious arguments.
- * When using this class to create a statement using
- * {@link #buildQueryString(boolean, String, String[], String, String, String, String, String)},
- * non-numeric limits will raise an exception. If a projection map is specified, fields
- * not in that map will be ignored.
- * If this class is used to execute the statement directly using
- * {@link #query(SQLiteDatabase, String[], String, String[], String, String, String)}
- * or
- * {@link #query(SQLiteDatabase, String[], String, String[], String, String, String, String)},
- * additionally also parenthesis escaping selection are caught.
- *
- * To summarize: To get maximum protection against malicious third party apps (for example
- * content provider consumers), make sure to do the following:
- * <ul>
- * <li>Set this value to true</li>
- * <li>Use a projection map</li>
- * <li>Use one of the query overloads instead of getting the statement as a sql string</li>
- * </ul>
- * By default, this value is false.
- */
- public void setStrict(boolean strict) {
- mStrict = strict;
- }
-
- /**
- * Build an SQL query string from the given clauses.
- *
- * @param distinct true if you want each row to be unique, false otherwise.
- * @param tables The table names to compile the query against.
- * @param columns A list of which columns to return. Passing null will
- * return all columns, which is discouraged to prevent reading
- * data from storage that isn't going to be used.
- * @param where A filter declaring which rows to return, formatted as an SQL
- * WHERE clause (excluding the WHERE itself). Passing null will
- * return all rows for the given URL.
- * @param groupBy A filter declaring how to group rows, formatted as an SQL
- * GROUP BY clause (excluding the GROUP BY itself). Passing null
- * will cause the rows to not be grouped.
- * @param having A filter declare which row groups to include in the cursor,
- * if row grouping is being used, formatted as an SQL HAVING
- * clause (excluding the HAVING itself). Passing null will cause
- * all row groups to be included, and is required when row
- * grouping is not being used.
- * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
- * (excluding the ORDER BY itself). Passing null will use the
- * default sort order, which may be unordered.
- * @param limit Limits the number of rows returned by the query,
- * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
- * @return the SQL query string
- */
- public static String buildQueryString(
- boolean distinct, String tables, String[] columns, String where,
- String groupBy, String having, String orderBy, String limit) {
- if (TextUtils.isEmpty(groupBy) && !TextUtils.isEmpty(having)) {
- throw new IllegalArgumentException(
- "HAVING clauses are only permitted when using a groupBy clause");
- }
- if (!TextUtils.isEmpty(limit) && !sLimitPattern.matcher(limit).matches()) {
- throw new IllegalArgumentException("invalid LIMIT clauses:" + limit);
- }
-
- StringBuilder query = new StringBuilder(120);
-
- query.append("SELECT ");
- if (distinct) {
- query.append("DISTINCT ");
- }
- if (columns != null && columns.length != 0) {
- appendColumns(query, columns);
- } else {
- query.append("* ");
- }
- query.append("FROM ");
- query.append(tables);
- appendClause(query, " WHERE ", where);
- appendClause(query, " GROUP BY ", groupBy);
- appendClause(query, " HAVING ", having);
- appendClause(query, " ORDER BY ", orderBy);
- appendClause(query, " LIMIT ", limit);
-
- return query.toString();
- }
-
- private static void appendClause(StringBuilder s, String name, String clause) {
- if (!TextUtils.isEmpty(clause)) {
- s.append(name);
- s.append(clause);
- }
- }
-
- /**
- * Add the names that are non-null in columns to s, separating
- * them with commas.
- */
- public static void appendColumns(StringBuilder s, String[] columns) {
- int n = columns.length;
-
- for (int i = 0; i < n; i++) {
- String column = columns[i];
-
- if (column != null) {
- if (i > 0) {
- s.append(", ");
- }
- s.append(column);
- }
- }
- s.append(' ');
- }
-
- /**
- * Perform a query by combining all current settings and the
- * information passed into this method.
- *
- * @param db the database to query on
- * @param projection A list of which columns to return. Passing
- * null will return all columns, which is discouraged to prevent
- * reading data from storage that isn't going to be used.
- * @param selection A filter declaring which rows to return,
- * formatted as an SQL WHERE clause (excluding the WHERE
- * itself). Passing null will return all rows for the given URL.
- * @param selectionArgs You may include ?s in selection, which
- * will be replaced by the values from selectionArgs, in order
- * that they appear in the selection. The values will be bound
- * as Strings.
- * @param groupBy A filter declaring how to group rows, formatted
- * as an SQL GROUP BY clause (excluding the GROUP BY
- * itself). Passing null will cause the rows to not be grouped.
- * @param having A filter declare which row groups to include in
- * the cursor, if row grouping is being used, formatted as an
- * SQL HAVING clause (excluding the HAVING itself). Passing
- * null will cause all row groups to be included, and is
- * required when row grouping is not being used.
- * @param sortOrder How to order the rows, formatted as an SQL
- * ORDER BY clause (excluding the ORDER BY itself). Passing null
- * will use the default sort order, which may be unordered.
- * @return a cursor over the result set
- * @see android.content.ContentResolver#query(android.net.Uri, String[],
- * String, String[], String)
- */
- public @Nullable Cursor query(@NonNull SQLiteDatabase db,
- @Nullable String[] projection,
- @Nullable String selection,
- @Nullable String[] selectionArgs,
- @Nullable String groupBy,
- @Nullable String having,
- @Nullable String sortOrder) {
- return query(db, projection, selection, selectionArgs, groupBy, having, sortOrder,
- null /* limit */, null /* cancellationSignal */);
- }
-
- /**
- * Perform a query by combining all current settings and the
- * information passed into this method.
- *
- * @param db the database to query on
- * @param projection A list of which columns to return. Passing
- * null will return all columns, which is discouraged to prevent
- * reading data from storage that isn't going to be used.
- * @param selection A filter declaring which rows to return,
- * formatted as an SQL WHERE clause (excluding the WHERE
- * itself). Passing null will return all rows for the given URL.
- * @param selectionArgs You may include ?s in selection, which
- * will be replaced by the values from selectionArgs, in order
- * that they appear in the selection. The values will be bound
- * as Strings.
- * @param groupBy A filter declaring how to group rows, formatted
- * as an SQL GROUP BY clause (excluding the GROUP BY
- * itself). Passing null will cause the rows to not be grouped.
- * @param having A filter declare which row groups to include in
- * the cursor, if row grouping is being used, formatted as an
- * SQL HAVING clause (excluding the HAVING itself). Passing
- * null will cause all row groups to be included, and is
- * required when row grouping is not being used.
- * @param sortOrder How to order the rows, formatted as an SQL
- * ORDER BY clause (excluding the ORDER BY itself). Passing null
- * will use the default sort order, which may be unordered.
- * @param limit Limits the number of rows returned by the query,
- * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
- * @return a cursor over the result set
- * @see android.content.ContentResolver#query(android.net.Uri, String[],
- * String, String[], String)
- */
- public @Nullable Cursor query(@NonNull SQLiteDatabase db,
- @Nullable String[] projection,
- @Nullable String selection,
- @Nullable String[] selectionArgs,
- @Nullable String groupBy,
- @Nullable String having,
- @Nullable String sortOrder,
- @Nullable String limit) {
- return query(db, projection, selection, selectionArgs,
- groupBy, having, sortOrder, limit, null);
- }
-
- /**
- * Perform a query by combining all current settings and the
- * information passed into this method.
- *
- * @param db the database to query on
- * @param projection A list of which columns to return. Passing
- * null will return all columns, which is discouraged to prevent
- * reading data from storage that isn't going to be used.
- * @param selection A filter declaring which rows to return,
- * formatted as an SQL WHERE clause (excluding the WHERE
- * itself). Passing null will return all rows for the given URL.
- * @param selectionArgs You may include ?s in selection, which
- * will be replaced by the values from selectionArgs, in order
- * that they appear in the selection. The values will be bound
- * as Strings.
- * @param sortOrder How to order the rows, formatted as an SQL
- * ORDER BY clause (excluding the ORDER BY itself). Passing null
- * will use the default sort order, which may be unordered.
- * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
- * If the operation is canceled, then {@link OperationCanceledException} will be thrown
- * when the query is executed.
- * @return a cursor over the result set
- * @see android.content.ContentResolver#query(android.net.Uri, String[],
- * String, String[], String)
- */
- public @Nullable Cursor query(@NonNull SQLiteDatabase db,
- @Nullable String[] projection,
- @Nullable String selection,
- @Nullable String[] selectionArgs,
- @Nullable String sortOrder,
- @Nullable CancellationSignal cancellationSignal) {
- return query(db, projection, selection, selectionArgs, null, null, sortOrder, null,
- cancellationSignal);
- }
-
- /**
- * Perform a query by combining all current settings and the
- * information passed into this method.
- *
- * @param db the database to query on
- * @param projection A list of which columns to return. Passing
- * null will return all columns, which is discouraged to prevent
- * reading data from storage that isn't going to be used.
- * @param selection A filter declaring which rows to return,
- * formatted as an SQL WHERE clause (excluding the WHERE
- * itself). Passing null will return all rows for the given URL.
- * @param selectionArgs You may include ?s in selection, which
- * will be replaced by the values from selectionArgs, in order
- * that they appear in the selection. The values will be bound
- * as Strings.
- * @param groupBy A filter declaring how to group rows, formatted
- * as an SQL GROUP BY clause (excluding the GROUP BY
- * itself). Passing null will cause the rows to not be grouped.
- * @param having A filter declare which row groups to include in
- * the cursor, if row grouping is being used, formatted as an
- * SQL HAVING clause (excluding the HAVING itself). Passing
- * null will cause all row groups to be included, and is
- * required when row grouping is not being used.
- * @param sortOrder How to order the rows, formatted as an SQL
- * ORDER BY clause (excluding the ORDER BY itself). Passing null
- * will use the default sort order, which may be unordered.
- * @param limit Limits the number of rows returned by the query,
- * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
- * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
- * If the operation is canceled, then {@link OperationCanceledException} will be thrown
- * when the query is executed.
- * @return a cursor over the result set
- * @see android.content.ContentResolver#query(android.net.Uri, String[],
- * String, String[], String)
- */
- public @Nullable Cursor query(@NonNull SQLiteDatabase db,
- @Nullable String[] projection,
- @Nullable String selection,
- @Nullable String[] selectionArgs,
- @Nullable String groupBy,
- @Nullable String having,
- @Nullable String sortOrder,
- @Nullable String limit,
- @Nullable CancellationSignal cancellationSignal) {
- final Bundle queryArgs = new Bundle();
- maybePutString(queryArgs, QUERY_ARG_SQL_SELECTION, selection);
- maybePutStringArray(queryArgs, QUERY_ARG_SQL_SELECTION_ARGS, selectionArgs);
- maybePutString(queryArgs, QUERY_ARG_SQL_GROUP_BY, groupBy);
- maybePutString(queryArgs, QUERY_ARG_SQL_HAVING, having);
- maybePutString(queryArgs, QUERY_ARG_SQL_SORT_ORDER, sortOrder);
- maybePutString(queryArgs, QUERY_ARG_SQL_LIMIT, limit);
- return query(db, projection, queryArgs, cancellationSignal);
- }
-
- /**
- * Perform a query by combining all current settings and the information
- * passed into this method.
- *
- * @param db the database to query on
- * @param projection A list of which columns to return. Passing null will
- * return all columns, which is discouraged to prevent reading
- * data from storage that isn't going to be used.
- * @param queryArgs A collection of arguments for the query, defined using
- * keys such as {@link ContentResolver#QUERY_ARG_SQL_SELECTION}
- * and {@link ContentResolver#QUERY_ARG_SQL_SELECTION_ARGS}.
- * @param cancellationSignal A signal to cancel the operation in progress,
- * or null if none. If the operation is canceled, then
- * {@link OperationCanceledException} will be thrown when the
- * query is executed.
- * @return a cursor over the result set
- */
- public Cursor query(@NonNull SQLiteDatabase db,
- @Nullable String[] projection,
- @Nullable Bundle queryArgs,
- @Nullable CancellationSignal cancellationSignal) {
- Objects.requireNonNull(db, "No database defined");
-
- if (VMRuntime.getRuntime().getTargetSdkVersion() >= Build.VERSION_CODES.Q) {
- Objects.requireNonNull(mTables, "No tables defined");
- } else if (mTables == null) {
- return null;
- }
-
- if (queryArgs == null) {
- queryArgs = Bundle.EMPTY;
- }
-
- // Final SQL that we will execute
- final String sql;
-
- final String unwrappedSql = buildQuery(projection,
- queryArgs.getString(QUERY_ARG_SQL_SELECTION),
- queryArgs.getString(QUERY_ARG_SQL_GROUP_BY),
- queryArgs.getString(QUERY_ARG_SQL_HAVING),
- queryArgs.getString(QUERY_ARG_SQL_SORT_ORDER),
- queryArgs.getString(QUERY_ARG_SQL_LIMIT));
-
- if (mStrict) {
- // Validate the user-supplied selection to detect syntactic anomalies
- // in the selection string that could indicate a SQL injection attempt.
- // The idea is to ensure that the selection clause is a valid SQL expression
- // by compiling it twice: once wrapped in parentheses and once as
- // originally specified. An attacker cannot create an expression that
- // would escape the SQL expression while maintaining balanced parentheses
- // in both the wrapped and original forms.
-
- // NOTE: The ordering of the below operations is important; we must
- // execute the wrapped query to ensure the untrusted clause has been
- // fully isolated.
-
- // TODO: decode SORT ORDER and LIMIT clauses, since they can contain
- // "expr" inside that need to be validated
-
- final String wrappedSql = buildQuery(projection,
- wrap(queryArgs.getString(QUERY_ARG_SQL_SELECTION)),
- queryArgs.getString(QUERY_ARG_SQL_GROUP_BY),
- queryArgs.getString(QUERY_ARG_SQL_HAVING),
- queryArgs.getString(QUERY_ARG_SQL_SORT_ORDER),
- queryArgs.getString(QUERY_ARG_SQL_LIMIT));
-
- // Validate the unwrapped query
- db.validateSql(unwrappedSql, cancellationSignal);
-
- // Execute wrapped query for extra protection
- sql = wrappedSql;
- } else {
- // Execute unwrapped query
- sql = unwrappedSql;
- }
-
- final String[] sqlArgs = ArrayUtils.concat(String.class,
- queryArgs.getStringArray(QUERY_ARG_SQL_SELECTION_ARGS), mWhereArgs);
-
- if (Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, sql + " with args " + Arrays.toString(sqlArgs));
- }
-
- return db.rawQueryWithFactory(
- mFactory, sql, sqlArgs,
- SQLiteDatabase.findEditTable(mTables),
- cancellationSignal); // will throw if query is invalid
- }
-
- /**
- * Perform an update by combining all current settings and the
- * information passed into this method.
- *
- * @param db the database to update on
- * @param selection A filter declaring which rows to return,
- * formatted as an SQL WHERE clause (excluding the WHERE
- * itself). Passing null will return all rows for the given URL.
- * @param selectionArgs You may include ?s in selection, which
- * will be replaced by the values from selectionArgs, in order
- * that they appear in the selection. The values will be bound
- * as Strings.
- * @return the number of rows updated
- */
- public int update(@NonNull SQLiteDatabase db, @NonNull ContentValues values,
- @Nullable String selection, @Nullable String[] selectionArgs) {
- Objects.requireNonNull(mTables, "No tables defined");
- Objects.requireNonNull(db, "No database defined");
- Objects.requireNonNull(values, "No values defined");
-
- if (mStrict) {
- // Validate the user-supplied selection to detect syntactic anomalies
- // in the selection string that could indicate a SQL injection attempt.
- // The idea is to ensure that the selection clause is a valid SQL expression
- // by compiling it twice: once wrapped in parentheses and once as
- // originally specified. An attacker cannot create an expression that
- // would escape the SQL expression while maintaining balanced parentheses
- // in both the wrapped and original forms.
- final String sql = buildUpdate(values, wrap(selection));
- db.validateSql(sql, null); // will throw if query is invalid
- }
-
- final ArrayMap<String, Object> rawValues = values.getValues();
- final String[] updateArgs = new String[rawValues.size()];
- for (int i = 0; i < updateArgs.length; i++) {
- final Object arg = rawValues.valueAt(i);
- updateArgs[i] = (arg != null) ? arg.toString() : null;
- }
-
- final String sql = buildUpdate(values, selection);
- final String[] sqlArgs = ArrayUtils.concat(String.class, updateArgs,
- ArrayUtils.concat(String.class, selectionArgs, mWhereArgs));
-
- if (Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, sql + " with args " + Arrays.toString(sqlArgs));
- }
-
- return db.executeSql(sql, sqlArgs);
- }
-
- /**
- * Perform a delete by combining all current settings and the
- * information passed into this method.
- *
- * @param db the database to delete on
- * @param selection A filter declaring which rows to return,
- * formatted as an SQL WHERE clause (excluding the WHERE
- * itself). Passing null will return all rows for the given URL.
- * @param selectionArgs You may include ?s in selection, which
- * will be replaced by the values from selectionArgs, in order
- * that they appear in the selection. The values will be bound
- * as Strings.
- * @return the number of rows deleted
- */
- public int delete(@NonNull SQLiteDatabase db, @Nullable String selection,
- @Nullable String[] selectionArgs) {
- Objects.requireNonNull(mTables, "No tables defined");
- Objects.requireNonNull(db, "No database defined");
-
- if (mStrict) {
- // Validate the user-supplied selection to detect syntactic anomalies
- // in the selection string that could indicate a SQL injection attempt.
- // The idea is to ensure that the selection clause is a valid SQL expression
- // by compiling it twice: once wrapped in parentheses and once as
- // originally specified. An attacker cannot create an expression that
- // would escape the SQL expression while maintaining balanced parentheses
- // in both the wrapped and original forms.
- final String sql = buildDelete(wrap(selection));
- db.validateSql(sql, null); // will throw if query is invalid
- }
-
- final String sql = buildDelete(selection);
- final String[] sqlArgs = ArrayUtils.concat(String.class, selectionArgs, mWhereArgs);
-
- if (Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, sql + " with args " + Arrays.toString(sqlArgs));
- }
-
- return db.executeSql(sql, sqlArgs);
- }
-
- /**
- * Construct a SELECT statement suitable for use in a group of
- * SELECT statements that will be joined through UNION operators
- * in buildUnionQuery.
- *
- * @param projectionIn A list of which columns to return. Passing
- * null will return all columns, which is discouraged to
- * prevent reading data from storage that isn't going to be
- * used.
- * @param selection A filter declaring which rows to return,
- * formatted as an SQL WHERE clause (excluding the WHERE
- * itself). Passing null will return all rows for the given
- * URL.
- * @param groupBy A filter declaring how to group rows, formatted
- * as an SQL GROUP BY clause (excluding the GROUP BY itself).
- * Passing null will cause the rows to not be grouped.
- * @param having A filter declare which row groups to include in
- * the cursor, if row grouping is being used, formatted as an
- * SQL HAVING clause (excluding the HAVING itself). Passing
- * null will cause all row groups to be included, and is
- * required when row grouping is not being used.
- * @param sortOrder How to order the rows, formatted as an SQL
- * ORDER BY clause (excluding the ORDER BY itself). Passing null
- * will use the default sort order, which may be unordered.
- * @param limit Limits the number of rows returned by the query,
- * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
- * @return the resulting SQL SELECT statement
- */
- public String buildQuery(
- String[] projectionIn, String selection, String groupBy,
- String having, String sortOrder, String limit) {
- String[] projection = computeProjection(projectionIn);
- String where = computeWhere(selection);
-
- return buildQueryString(
- mDistinct, mTables, projection, where,
- groupBy, having, sortOrder, limit);
- }
-
- /**
- * @deprecated This method's signature is misleading since no SQL parameter
- * substitution is carried out. The selection arguments parameter does not get
- * used at all. To avoid confusion, call
- * {@link #buildQuery(String[], String, String, String, String, String)} instead.
- */
- @Deprecated
- public String buildQuery(
- String[] projectionIn, String selection, String[] selectionArgs,
- String groupBy, String having, String sortOrder, String limit) {
- return buildQuery(projectionIn, selection, groupBy, having, sortOrder, limit);
- }
-
- /** {@hide} */
- public String buildUpdate(ContentValues values, String selection) {
- if (values == null || values.isEmpty()) {
- throw new IllegalArgumentException("Empty values");
- }
-
- StringBuilder sql = new StringBuilder(120);
- sql.append("UPDATE ");
- sql.append(mTables);
- sql.append(" SET ");
-
- final ArrayMap<String, Object> rawValues = values.getValues();
- for (int i = 0; i < rawValues.size(); i++) {
- if (i > 0) {
- sql.append(',');
- }
- sql.append(rawValues.keyAt(i));
- sql.append("=?");
- }
-
- final String where = computeWhere(selection);
- appendClause(sql, " WHERE ", where);
- return sql.toString();
- }
-
- /** {@hide} */
- public String buildDelete(String selection) {
- StringBuilder sql = new StringBuilder(120);
- sql.append("DELETE FROM ");
- sql.append(mTables);
-
- final String where = computeWhere(selection);
- appendClause(sql, " WHERE ", where);
- return sql.toString();
- }
-
- /**
- * Construct a SELECT statement suitable for use in a group of
- * SELECT statements that will be joined through UNION operators
- * in buildUnionQuery.
- *
- * @param typeDiscriminatorColumn the name of the result column
- * whose cells will contain the name of the table from which
- * each row was drawn.
- * @param unionColumns the names of the columns to appear in the
- * result. This may include columns that do not appear in the
- * table this SELECT is querying (i.e. mTables), but that do
- * appear in one of the other tables in the UNION query that we
- * are constructing.
- * @param columnsPresentInTable a Set of the names of the columns
- * that appear in this table (i.e. in the table whose name is
- * mTables). Since columns in unionColumns include columns that
- * appear only in other tables, we use this array to distinguish
- * which ones actually are present. Other columns will have
- * NULL values for results from this subquery.
- * @param computedColumnsOffset all columns in unionColumns before
- * this index are included under the assumption that they're
- * computed and therefore won't appear in columnsPresentInTable,
- * e.g. "date * 1000 as normalized_date"
- * @param typeDiscriminatorValue the value used for the
- * type-discriminator column in this subquery
- * @param selection A filter declaring which rows to return,
- * formatted as an SQL WHERE clause (excluding the WHERE
- * itself). Passing null will return all rows for the given
- * URL.
- * @param groupBy A filter declaring how to group rows, formatted
- * as an SQL GROUP BY clause (excluding the GROUP BY itself).
- * Passing null will cause the rows to not be grouped.
- * @param having A filter declare which row groups to include in
- * the cursor, if row grouping is being used, formatted as an
- * SQL HAVING clause (excluding the HAVING itself). Passing
- * null will cause all row groups to be included, and is
- * required when row grouping is not being used.
- * @return the resulting SQL SELECT statement
- */
- public String buildUnionSubQuery(
- String typeDiscriminatorColumn,
- String[] unionColumns,
- Set<String> columnsPresentInTable,
- int computedColumnsOffset,
- String typeDiscriminatorValue,
- String selection,
- String groupBy,
- String having) {
- int unionColumnsCount = unionColumns.length;
- String[] projectionIn = new String[unionColumnsCount];
-
- for (int i = 0; i < unionColumnsCount; i++) {
- String unionColumn = unionColumns[i];
-
- if (unionColumn.equals(typeDiscriminatorColumn)) {
- projectionIn[i] = "'" + typeDiscriminatorValue + "' AS "
- + typeDiscriminatorColumn;
- } else if (i <= computedColumnsOffset
- || columnsPresentInTable.contains(unionColumn)) {
- projectionIn[i] = unionColumn;
- } else {
- projectionIn[i] = "NULL AS " + unionColumn;
- }
- }
- return buildQuery(
- projectionIn, selection, groupBy, having,
- null /* sortOrder */,
- null /* limit */);
- }
-
- /**
- * @deprecated This method's signature is misleading since no SQL parameter
- * substitution is carried out. The selection arguments parameter does not get
- * used at all. To avoid confusion, call
- * {@link #buildUnionSubQuery}
- * instead.
- */
- @Deprecated
- public String buildUnionSubQuery(
- String typeDiscriminatorColumn,
- String[] unionColumns,
- Set<String> columnsPresentInTable,
- int computedColumnsOffset,
- String typeDiscriminatorValue,
- String selection,
- String[] selectionArgs,
- String groupBy,
- String having) {
- return buildUnionSubQuery(
- typeDiscriminatorColumn, unionColumns, columnsPresentInTable,
- computedColumnsOffset, typeDiscriminatorValue, selection,
- groupBy, having);
- }
-
- /**
- * Given a set of subqueries, all of which are SELECT statements,
- * construct a query that returns the union of what those
- * subqueries return.
- * @param subQueries an array of SQL SELECT statements, all of
- * which must have the same columns as the same positions in
- * their results
- * @param sortOrder How to order the rows, formatted as an SQL
- * ORDER BY clause (excluding the ORDER BY itself). Passing
- * null will use the default sort order, which may be unordered.
- * @param limit The limit clause, which applies to the entire union result set
- *
- * @return the resulting SQL SELECT statement
- */
- public String buildUnionQuery(String[] subQueries, String sortOrder, String limit) {
- StringBuilder query = new StringBuilder(128);
- int subQueryCount = subQueries.length;
- String unionOperator = mDistinct ? " UNION " : " UNION ALL ";
-
- for (int i = 0; i < subQueryCount; i++) {
- if (i > 0) {
- query.append(unionOperator);
- }
- query.append(subQueries[i]);
- }
- appendClause(query, " ORDER BY ", sortOrder);
- appendClause(query, " LIMIT ", limit);
- return query.toString();
- }
-
- private @Nullable String[] computeProjection(@Nullable String[] projectionIn) {
- if (projectionIn != null && projectionIn.length > 0) {
- if (mProjectionMap != null) {
- String[] projection = new String[projectionIn.length];
- int length = projectionIn.length;
-
- for (int i = 0; i < length; i++) {
- String userColumn = projectionIn[i];
- String column = mProjectionMap.get(userColumn);
-
- if (column != null) {
- projection[i] = column;
- continue;
- }
-
- if (!mStrict &&
- ( userColumn.contains(" AS ") || userColumn.contains(" as "))) {
- /* A column alias already exist */
- projection[i] = userColumn;
- continue;
- }
-
- throw new IllegalArgumentException("Invalid column "
- + projectionIn[i] + " from tables " + mTables);
- }
- return projection;
- } else {
- return projectionIn;
- }
- } else if (mProjectionMap != null) {
- // Return all columns in projection map.
- Set<Entry<String, String>> entrySet = mProjectionMap.entrySet();
- String[] projection = new String[entrySet.size()];
- Iterator<Entry<String, String>> entryIter = entrySet.iterator();
- int i = 0;
-
- while (entryIter.hasNext()) {
- Entry<String, String> entry = entryIter.next();
-
- // Don't include the _count column when people ask for no projection.
- if (entry.getKey().equals(BaseColumns._COUNT)) {
- continue;
- }
- projection[i++] = entry.getValue();
- }
- return projection;
- }
- return null;
- }
-
- private @NonNull String computeWhere(@Nullable String selection) {
- final boolean hasUser = selection != null && selection.length() > 0;
- final boolean hasInternal = mWhereClause != null && mWhereClause.length() > 0;
-
- if (hasUser || hasInternal) {
- final StringBuilder where = new StringBuilder();
- if (hasUser) {
- where.append('(').append(selection).append(')');
- }
- if (hasUser && hasInternal) {
- where.append(" AND ");
- }
- if (hasInternal) {
- where.append('(').append(mWhereClause.toString()).append(')');
- }
- return where.toString();
- } else {
- return null;
- }
- }
-
- /**
- * Wrap given argument in parenthesis, unless it's {@code null} or
- * {@code ()}, in which case return it verbatim.
- */
- private @Nullable String wrap(@Nullable String arg) {
- if (arg == null) {
- return null;
- } else if (arg.equals("")) {
- return arg;
- } else {
- return "(" + arg + ")";
- }
- }
-
- private static void maybePutString(@NonNull Bundle bundle, @NonNull String key,
- @Nullable String value) {
- if (value != null) {
- bundle.putString(key, value);
- }
- }
-
- private static void maybePutStringArray(@NonNull Bundle bundle, @NonNull String key,
- @Nullable String[] value) {
- if (value != null) {
- bundle.putStringArray(key, value);
- }
- }
-}
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 662d13023cd1..b9d900730b69 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -406,9 +406,28 @@ public class Binder implements IBinder {
public static final native void blockUntilThreadAvailable();
/**
- * Default constructor initializes the object.
+ * Default constructor just initializes the object.
+ *
+ * If you're creating a Binder token (a Binder object without an attached interface),
+ * you should use {@link #Binder(String)} instead.
*/
public Binder() {
+ this(null);
+ }
+
+ /**
+ * Constructor for creating a raw Binder object (token) along with a descriptor.
+ *
+ * The descriptor of binder objects usually specifies the interface they are implementing.
+ * In case of binder tokens, no interface is implemented, and the descriptor can be used
+ * as a sort of tag to help identify the binder token. This will help identify remote
+ * references to these objects more easily when debugging.
+ *
+ * @param descriptor Used to identify the creator of this token, for example the class name.
+ * Instead of creating multiple tokens with the same descriptor, consider adding a suffix to
+ * help identify them.
+ */
+ public Binder(@Nullable String descriptor) {
mObject = getNativeBBinderHolder();
NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mObject);
@@ -420,6 +439,7 @@ public class Binder implements IBinder {
klass.getCanonicalName());
}
}
+ mDescriptor = descriptor;
}
/**
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 7ce7c9237815..7caf0b103534 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -492,10 +492,11 @@ public class Process {
String instructionSet,
String appDataDir,
String invokeWith,
+ String packageName,
String[] zygoteArgs) {
return zygoteProcess.start(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
- abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
+ abi, instructionSet, appDataDir, invokeWith, packageName, zygoteArgs);
}
/** @hide */
@@ -509,10 +510,11 @@ public class Process {
String instructionSet,
String appDataDir,
String invokeWith,
+ String packageName,
String[] zygoteArgs) {
return WebViewZygote.getProcess().start(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
- abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
+ abi, instructionSet, appDataDir, invokeWith, packageName, zygoteArgs);
}
/**
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 021e72f7a082..067e8493717b 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -227,12 +227,13 @@ public class ZygoteProcess {
String instructionSet,
String appDataDir,
String invokeWith,
+ String packageName,
String[] zygoteArgs) {
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, false /* startChildZygote */,
- zygoteArgs);
+ packageName, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
@@ -366,6 +367,7 @@ public class ZygoteProcess {
String appDataDir,
String invokeWith,
boolean startChildZygote,
+ String packageName,
String[] extraArgs)
throws ZygoteStartFailedEx {
ArrayList<String> argsForZygote = new ArrayList<String>();
@@ -426,6 +428,10 @@ public class ZygoteProcess {
argsForZygote.add("--start-child-zygote");
}
+ if (packageName != null) {
+ argsForZygote.add("--package-name=" + packageName);
+ }
+
argsForZygote.add(processClass);
if (extraArgs != null) {
@@ -733,7 +739,7 @@ public class ZygoteProcess {
result = startViaZygote(processClass, niceName, uid, gid,
gids, runtimeFlags, 0 /* mountExternal */, 0 /* targetSdkVersion */, seInfo,
abi, instructionSet, null /* appDataDir */, null /* invokeWith */,
- true /* startChildZygote */, extraArgs);
+ true /* startChildZygote */, null /* packageName */, extraArgs);
} catch (ZygoteStartFailedEx ex) {
throw new RuntimeException("Starting child-zygote through Zygote failed", ex);
}
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index 0ed252667533..2a094bb2ed1c 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -277,7 +277,7 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
mHandler.sendEmptyMessage(MSG_INIT_SAMPLE);
mVolumeObserver = new Observer(mHandler);
mContext.getContentResolver().registerContentObserver(
- System.getUriFor(System.VOLUME_SETTINGS[mStreamType]),
+ System.getUriFor(System.VOLUME_SETTINGS_INT[mStreamType]),
false, mVolumeObserver);
mReceiver.setListening(true);
}
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 4b45e32d2dc5..9c3a40995c77 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -16,8 +16,11 @@
package android.provider;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.content.ClipData;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentUris;
@@ -25,22 +28,23 @@ import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.UriPermission;
+import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.database.DatabaseUtils;
-import android.database.sqlite.SQLiteException;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
-import android.graphics.Matrix;
-import android.media.MiniThumbFile;
-import android.media.ThumbnailUtils;
+import android.graphics.Point;
import android.net.Uri;
import android.os.Bundle;
+import android.os.CancellationSignal;
import android.os.Environment;
-import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.service.media.CameraPrewarmService;
+import android.util.ArrayMap;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+
import libcore.io.IoUtils;
import java.io.File;
@@ -49,7 +53,6 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.util.Arrays;
import java.util.List;
/**
@@ -611,178 +614,79 @@ public final class MediaStore {
}
}
+ /** @hide */
+ public static class ThumbnailConstants {
+ public static final int MINI_KIND = 1;
+ public static final int FULL_SCREEN_KIND = 2;
+ public static final int MICRO_KIND = 3;
+
+ public static final Point MINI_SIZE = new Point(512, 384);
+ public static final Point MICRO_SIZE = new Point(96, 96);
+ }
+
/**
* This class is used internally by Images.Thumbnails and Video.Thumbnails, it's not intended
* to be accessed elsewhere.
*/
private static class InternalThumbnails implements BaseColumns {
- private static final int MINI_KIND = 1;
- private static final int FULL_SCREEN_KIND = 2;
- private static final int MICRO_KIND = 3;
- private static final String[] PROJECTION = new String[] {_ID, MediaColumns.DATA};
- static final int DEFAULT_GROUP_ID = 0;
- private static final Object sThumbBufLock = new Object();
- private static byte[] sThumbBuf;
-
- private static Bitmap getMiniThumbFromFile(
- Cursor c, Uri baseUri, ContentResolver cr, BitmapFactory.Options options) {
- Bitmap bitmap = null;
- Uri thumbUri = null;
- try {
- long thumbId = c.getLong(0);
- String filePath = c.getString(1);
- thumbUri = ContentUris.withAppendedId(baseUri, thumbId);
- ParcelFileDescriptor pfdInput = cr.openFileDescriptor(thumbUri, "r");
- bitmap = BitmapFactory.decodeFileDescriptor(
- pfdInput.getFileDescriptor(), null, options);
- pfdInput.close();
- } catch (FileNotFoundException ex) {
- Log.e(TAG, "couldn't open thumbnail " + thumbUri + "; " + ex);
- } catch (IOException ex) {
- Log.e(TAG, "couldn't open thumbnail " + thumbUri + "; " + ex);
- } catch (OutOfMemoryError ex) {
- Log.e(TAG, "failed to allocate memory for thumbnail "
- + thumbUri + "; " + ex);
- }
- return bitmap;
- }
-
/**
- * This method cancels the thumbnail request so clients waiting for getThumbnail will be
- * interrupted and return immediately. Only the original process which made the getThumbnail
- * requests can cancel their own requests.
- *
- * @param cr ContentResolver
- * @param origId original image or video id. use -1 to cancel all requests.
- * @param groupId the same groupId used in getThumbnail
- * @param baseUri the base URI of requested thumbnails
+ * Currently outstanding thumbnail requests that can be cancelled.
*/
- static void cancelThumbnailRequest(ContentResolver cr, long origId, Uri baseUri,
- long groupId) {
- Uri cancelUri = baseUri.buildUpon().appendQueryParameter("cancel", "1")
- .appendQueryParameter("orig_id", String.valueOf(origId))
- .appendQueryParameter("group_id", String.valueOf(groupId)).build();
- Cursor c = null;
- try {
- c = cr.query(cancelUri, PROJECTION, null, null, null);
- }
- finally {
- if (c != null) c.close();
- }
- }
+ @GuardedBy("sPending")
+ private static ArrayMap<Uri, CancellationSignal> sPending = new ArrayMap<>();
/**
- * This method ensure thumbnails associated with origId are generated and decode the byte
- * stream from database (MICRO_KIND) or file (MINI_KIND).
- *
- * Special optimization has been done to avoid further IPC communication for MICRO_KIND
- * thumbnails.
+ * Make a blocking request to obtain the given thumbnail, generating it
+ * if needed.
*
- * @param cr ContentResolver
- * @param origId original image or video id
- * @param kind could be MINI_KIND or MICRO_KIND
- * @param options this is only used for MINI_KIND when decoding the Bitmap
- * @param baseUri the base URI of requested thumbnails
- * @param groupId the id of group to which this request belongs
- * @return Bitmap bitmap of specified thumbnail kind
+ * @see #cancelThumbnail(ContentResolver, Uri)
*/
- static Bitmap getThumbnail(ContentResolver cr, long origId, long groupId, int kind,
- BitmapFactory.Options options, Uri baseUri, boolean isVideo) {
- Bitmap bitmap = null;
- // Log.v(TAG, "getThumbnail: origId="+origId+", kind="+kind+", isVideo="+isVideo);
- // If the magic is non-zero, we simply return thumbnail if it does exist.
- // querying MediaProvider and simply return thumbnail.
- MiniThumbFile thumbFile = MiniThumbFile.instance(
- isVideo ? Video.Media.EXTERNAL_CONTENT_URI : Images.Media.EXTERNAL_CONTENT_URI);
- Cursor c = null;
- try {
- long magic = thumbFile.getMagic(origId);
- if (magic != 0) {
- if (kind == MICRO_KIND) {
- synchronized (sThumbBufLock) {
- if (sThumbBuf == null) {
- sThumbBuf = new byte[MiniThumbFile.BYTES_PER_MINTHUMB];
- }
- if (thumbFile.getMiniThumbFromFile(origId, sThumbBuf) != null) {
- bitmap = BitmapFactory.decodeByteArray(sThumbBuf, 0, sThumbBuf.length);
- if (bitmap == null) {
- Log.w(TAG, "couldn't decode byte array.");
- }
- }
- }
- return bitmap;
- } else if (kind == MINI_KIND) {
- String column = isVideo ? "video_id=" : "image_id=";
- c = cr.query(baseUri, PROJECTION, column + origId, null, null);
- if (c != null && c.moveToFirst()) {
- bitmap = getMiniThumbFromFile(c, baseUri, cr, options);
- if (bitmap != null) {
- return bitmap;
- }
- }
- }
+ static @Nullable Bitmap getThumbnail(@NonNull ContentResolver cr, @NonNull Uri uri,
+ int kind, @Nullable BitmapFactory.Options opts) {
+ final Bundle openOpts = new Bundle();
+ if (kind == ThumbnailConstants.MICRO_KIND) {
+ openOpts.putParcelable(ContentResolver.EXTRA_SIZE, ThumbnailConstants.MICRO_SIZE);
+ } else if (kind == ThumbnailConstants.MINI_KIND) {
+ openOpts.putParcelable(ContentResolver.EXTRA_SIZE, ThumbnailConstants.MINI_SIZE);
+ } else {
+ throw new IllegalArgumentException("Unsupported kind: " + kind);
+ }
+
+ CancellationSignal signal = null;
+ synchronized (sPending) {
+ signal = sPending.get(uri);
+ if (signal == null) {
+ signal = new CancellationSignal();
+ sPending.put(uri, signal);
}
+ }
- Uri blockingUri = baseUri.buildUpon().appendQueryParameter("blocking", "1")
- .appendQueryParameter("orig_id", String.valueOf(origId))
- .appendQueryParameter("group_id", String.valueOf(groupId)).build();
- if (c != null) c.close();
- c = cr.query(blockingUri, PROJECTION, null, null, null);
- // This happens when original image/video doesn't exist.
- if (c == null) return null;
-
- // Assuming thumbnail has been generated, at least original image exists.
- if (kind == MICRO_KIND) {
- synchronized (sThumbBufLock) {
- if (sThumbBuf == null) {
- sThumbBuf = new byte[MiniThumbFile.BYTES_PER_MINTHUMB];
- }
- Arrays.fill(sThumbBuf, (byte)0);
- if (thumbFile.getMiniThumbFromFile(origId, sThumbBuf) != null) {
- bitmap = BitmapFactory.decodeByteArray(sThumbBuf, 0, sThumbBuf.length);
- if (bitmap == null) {
- Log.w(TAG, "couldn't decode byte array.");
- }
- }
- }
- } else if (kind == MINI_KIND) {
- if (c.moveToFirst()) {
- bitmap = getMiniThumbFromFile(c, baseUri, cr, options);
- }
- } else {
- throw new IllegalArgumentException("Unsupported kind: " + kind);
+ try (AssetFileDescriptor afd = cr.openTypedAssetFileDescriptor(uri,
+ "image/*", openOpts, signal)) {
+ return BitmapFactory.decodeFileDescriptor(afd.getFileDescriptor(), null, opts);
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to obtain thumbnail for " + uri, e);
+ return null;
+ } finally {
+ synchronized (sPending) {
+ sPending.remove(uri);
}
+ }
+ }
- // We probably run out of space, so create the thumbnail in memory.
- if (bitmap == null) {
- Log.v(TAG, "Create the thumbnail in memory: origId=" + origId
- + ", kind=" + kind + ", isVideo="+isVideo);
- Uri uri = Uri.parse(
- baseUri.buildUpon().appendPath(String.valueOf(origId))
- .toString().replaceFirst("thumbnails", "media"));
- if (c != null) c.close();
- c = cr.query(uri, PROJECTION, null, null, null);
- if (c == null || !c.moveToFirst()) {
- return null;
- }
- String filePath = c.getString(1);
- if (filePath != null) {
- if (isVideo) {
- bitmap = ThumbnailUtils.createVideoThumbnail(filePath, kind);
- } else {
- bitmap = ThumbnailUtils.createImageThumbnail(filePath, kind);
- }
- }
+ /**
+ * This method cancels the thumbnail request so clients waiting for
+ * {@link #getThumbnail} will be interrupted and return immediately.
+ * Only the original process which made the request can cancel their own
+ * requests.
+ */
+ static void cancelThumbnail(@NonNull ContentResolver cr, @NonNull Uri uri) {
+ synchronized (sPending) {
+ final CancellationSignal signal = sPending.get(uri);
+ if (signal != null) {
+ signal.cancel();
}
- } catch (SQLiteException ex) {
- Log.w(TAG, ex);
- } finally {
- if (c != null) c.close();
- // To avoid file descriptor leak in application process.
- thumbFile.deactivate();
- thumbFile = null;
}
- return bitmap;
}
}
@@ -916,48 +820,6 @@ public final class MediaStore {
}
}
- private static final Bitmap StoreThumbnail(
- ContentResolver cr,
- Bitmap source,
- long id,
- float width, float height,
- int kind) {
- // create the matrix to scale it
- Matrix matrix = new Matrix();
-
- float scaleX = width / source.getWidth();
- float scaleY = height / source.getHeight();
-
- matrix.setScale(scaleX, scaleY);
-
- Bitmap thumb = Bitmap.createBitmap(source, 0, 0,
- source.getWidth(),
- source.getHeight(), matrix,
- true);
-
- ContentValues values = new ContentValues(4);
- values.put(Images.Thumbnails.KIND, kind);
- values.put(Images.Thumbnails.IMAGE_ID, (int)id);
- values.put(Images.Thumbnails.HEIGHT, thumb.getHeight());
- values.put(Images.Thumbnails.WIDTH, thumb.getWidth());
-
- Uri url = cr.insert(Images.Thumbnails.EXTERNAL_CONTENT_URI, values);
-
- try {
- OutputStream thumbOut = cr.openOutputStream(url);
-
- thumb.compress(Bitmap.CompressFormat.JPEG, 100, thumbOut);
- thumbOut.close();
- return thumb;
- }
- catch (FileNotFoundException ex) {
- return null;
- }
- catch (IOException ex) {
- return null;
- }
- }
-
/**
* Insert an image and create a thumbnail for it.
*
@@ -990,12 +852,9 @@ public final class MediaStore {
}
long id = ContentUris.parseId(url);
- // Wait until MINI_KIND thumbnail is generated.
- Bitmap miniThumb = Images.Thumbnails.getThumbnail(cr, id,
- Images.Thumbnails.MINI_KIND, null);
- // This is for backward compatibility.
- Bitmap microThumb = StoreThumbnail(cr, miniThumb, id, 50F, 50F,
- Images.Thumbnails.MICRO_KIND);
+ // Block until we've generated common thumbnails
+ Images.Thumbnails.getThumbnail(cr, id, Images.Thumbnails.MINI_KIND, null);
+ Images.Thumbnails.getThumbnail(cr, id, Images.Thumbnails.MICRO_KIND, null);
} else {
Log.e(TAG, "Failed to create thumbnail, removing original");
cr.delete(url, null, null);
@@ -1085,8 +944,9 @@ public final class MediaStore {
* @param origId original image id
*/
public static void cancelThumbnailRequest(ContentResolver cr, long origId) {
- InternalThumbnails.cancelThumbnailRequest(cr, origId, EXTERNAL_CONTENT_URI,
- InternalThumbnails.DEFAULT_GROUP_ID);
+ final Uri uri = ContentUris.withAppendedId(
+ Images.Media.EXTERNAL_CONTENT_URI, origId);
+ InternalThumbnails.cancelThumbnail(cr, uri);
}
/**
@@ -1102,9 +962,9 @@ public final class MediaStore {
*/
public static Bitmap getThumbnail(ContentResolver cr, long origId, int kind,
BitmapFactory.Options options) {
- return InternalThumbnails.getThumbnail(cr, origId,
- InternalThumbnails.DEFAULT_GROUP_ID, kind, options,
- EXTERNAL_CONTENT_URI, false);
+ final Uri uri = ContentUris.withAppendedId(
+ Images.Media.EXTERNAL_CONTENT_URI, origId);
+ return InternalThumbnails.getThumbnail(cr, uri, kind, options);
}
/**
@@ -1117,7 +977,7 @@ public final class MediaStore {
* @param groupId the same groupId used in getThumbnail.
*/
public static void cancelThumbnailRequest(ContentResolver cr, long origId, long groupId) {
- InternalThumbnails.cancelThumbnailRequest(cr, origId, EXTERNAL_CONTENT_URI, groupId);
+ cancelThumbnailRequest(cr, origId);
}
/**
@@ -1134,8 +994,7 @@ public final class MediaStore {
*/
public static Bitmap getThumbnail(ContentResolver cr, long origId, long groupId,
int kind, BitmapFactory.Options options) {
- return InternalThumbnails.getThumbnail(cr, origId, groupId, kind, options,
- EXTERNAL_CONTENT_URI, false);
+ return getThumbnail(cr, origId, kind, options);
}
/**
@@ -1193,9 +1052,9 @@ public final class MediaStore {
*/
public static final String KIND = "kind";
- public static final int MINI_KIND = 1;
- public static final int FULL_SCREEN_KIND = 2;
- public static final int MICRO_KIND = 3;
+ public static final int MINI_KIND = ThumbnailConstants.MINI_KIND;
+ public static final int FULL_SCREEN_KIND = ThumbnailConstants.FULL_SCREEN_KIND;
+ public static final int MICRO_KIND = ThumbnailConstants.MICRO_KIND;
/**
* The blob raw data of thumbnail
* <P>Type: DATA STREAM</P>
@@ -2155,8 +2014,9 @@ public final class MediaStore {
* @param origId original video id
*/
public static void cancelThumbnailRequest(ContentResolver cr, long origId) {
- InternalThumbnails.cancelThumbnailRequest(cr, origId, EXTERNAL_CONTENT_URI,
- InternalThumbnails.DEFAULT_GROUP_ID);
+ final Uri uri = ContentUris.withAppendedId(
+ Video.Media.EXTERNAL_CONTENT_URI, origId);
+ InternalThumbnails.cancelThumbnail(cr, uri);
}
/**
@@ -2172,9 +2032,9 @@ public final class MediaStore {
*/
public static Bitmap getThumbnail(ContentResolver cr, long origId, int kind,
BitmapFactory.Options options) {
- return InternalThumbnails.getThumbnail(cr, origId,
- InternalThumbnails.DEFAULT_GROUP_ID, kind, options,
- EXTERNAL_CONTENT_URI, true);
+ final Uri uri = ContentUris.withAppendedId(
+ Video.Media.EXTERNAL_CONTENT_URI, origId);
+ return InternalThumbnails.getThumbnail(cr, uri, kind, options);
}
/**
@@ -2191,8 +2051,7 @@ public final class MediaStore {
*/
public static Bitmap getThumbnail(ContentResolver cr, long origId, long groupId,
int kind, BitmapFactory.Options options) {
- return InternalThumbnails.getThumbnail(cr, origId, groupId, kind, options,
- EXTERNAL_CONTENT_URI, true);
+ return getThumbnail(cr, origId, kind, options);
}
/**
@@ -2205,7 +2064,7 @@ public final class MediaStore {
* @param groupId the same groupId used in getThumbnail.
*/
public static void cancelThumbnailRequest(ContentResolver cr, long origId, long groupId) {
- InternalThumbnails.cancelThumbnailRequest(cr, origId, EXTERNAL_CONTENT_URI, groupId);
+ cancelThumbnailRequest(cr, origId);
}
/**
@@ -2263,9 +2122,9 @@ public final class MediaStore {
*/
public static final String KIND = "kind";
- public static final int MINI_KIND = 1;
- public static final int FULL_SCREEN_KIND = 2;
- public static final int MICRO_KIND = 3;
+ public static final int MINI_KIND = ThumbnailConstants.MINI_KIND;
+ public static final int FULL_SCREEN_KIND = ThumbnailConstants.FULL_SCREEN_KIND;
+ public static final int MICRO_KIND = ThumbnailConstants.MICRO_KIND;
/**
* The width of the thumbnal
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 848138714e98..ebd90bdc2414 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -12067,6 +12067,33 @@ public final class Settings {
"autofill_compat_mode_allowed_packages";
/**
+ * Level of autofill logging.
+ *
+ * <p>Valid values are
+ * {@link android.view.autofill.AutofillManager#NO_LOGGING},
+ * {@link android.view.autofill.AutofillManager#FLAG_ADD_CLIENT_DEBUG}, or
+ * {@link android.view.autofill.AutofillManager#FLAG_ADD_CLIENT_VERBOSE}.
+ *
+ * @hide
+ */
+ public static final String AUTOFILL_LOGGING_LEVEL = "autofill_logging_level";
+
+ /**
+ * Maximum number of partitions that can be allowed in an autofill session.
+ *
+ * @hide
+ */
+ public static final String AUTOFILL_MAX_PARTITIONS_SIZE = "autofill_max_partitions_size";
+
+ /**
+ * Maximum number of visible datasets in the Autofill dataset picker UI, or {@code 0} to use
+ * the default value from resources.
+ *
+ * @hide
+ */
+ public static final String AUTOFILL_MAX_VISIBLE_DATASETS = "autofill_max_visible_datasets";
+
+ /**
* Exemptions to the hidden API blacklist.
*
* @hide
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index c48b6d12e948..29f73b55bb40 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -21,6 +21,7 @@ import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.app.Service;
import android.app.WallpaperColors;
+import android.app.WallpaperInfo;
import android.app.WallpaperManager;
import android.content.Context;
import android.content.Intent;
@@ -436,8 +437,7 @@ public abstract class WallpaperService extends Service {
/**
* Returns true if this engine is running in ambient mode -- that is,
- * it is being shown in low power mode, in always on display.
- * @hide
+ * it is being shown in low power mode, on always on display.
*/
public boolean isInAmbientMode() {
return mIsInAmbientMode;
@@ -563,10 +563,12 @@ public abstract class WallpaperService extends Service {
* Called when the device enters or exits ambient mode.
*
* @param inAmbientMode {@code true} if in ambient mode.
- * @param animated {@code true} if you'll have te opportunity of animating your transition
- * {@code false} when the screen will blank and the wallpaper should be
- * set to ambient mode immediately.
- * @hide
+ * @param animated {@code true} if you'll have the opportunity of animating your transition
+ * {@code false} when the wallpaper should present its ambient version
+ * immediately.
+ *
+ * @see #isInAmbientMode()
+ * @see WallpaperInfo#supportsAmbientMode()
*/
public void onAmbientModeChanged(boolean inAmbientMode, boolean animated) {
}
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
index c2c3182c2abd..9bf8cd20f441 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -30,10 +30,6 @@ import android.text.style.MetricAffectingSpan;
import android.text.style.ReplacementSpan;
import android.util.Pools.SynchronizedPool;
-import dalvik.annotation.optimization.CriticalNative;
-
-import libcore.util.NativeAllocationRegistry;
-
import java.util.Arrays;
/**
@@ -62,9 +58,6 @@ import java.util.Arrays;
public class MeasuredParagraph {
private static final char OBJECT_REPLACEMENT_CHARACTER = '\uFFFC';
- private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
- MeasuredParagraph.class.getClassLoader(), nGetReleaseFunc(), 1024);
-
private MeasuredParagraph() {} // Use build static functions instead.
private static final SynchronizedPool<MeasuredParagraph> sPool = new SynchronizedPool<>(1);
@@ -128,24 +121,7 @@ public class MeasuredParagraph {
private @Nullable IntArray mFontMetrics = new IntArray(4 * 4);
// The native MeasuredParagraph.
- // See getNativePtr comments.
- // Do not modify these members directly. Use bindNativeObject/unbindNativeObject instead.
- private /* Maybe Zero */ long mNativePtr = 0;
- private @Nullable Runnable mNativeObjectCleaner;
-
- // Associate the native object to this Java object.
- private void bindNativeObject(/* Non Zero*/ long nativePtr) {
- mNativePtr = nativePtr;
- mNativeObjectCleaner = sRegistry.registerNativeAllocation(this, nativePtr);
- }
-
- // Decouple the native object from this Java object and release the native object.
- private void unbindNativeObject() {
- if (mNativePtr != 0) {
- mNativeObjectCleaner.run();
- mNativePtr = 0;
- }
- }
+ private @Nullable NativeMeasuredParagraph mNativeMeasuredParagraph;
// Following two objects are for avoiding object allocation.
private @NonNull TextPaint mCachedPaint = new TextPaint();
@@ -173,7 +149,7 @@ public class MeasuredParagraph {
mWidths.clear();
mFontMetrics.clear();
mSpanEndCache.clear();
- unbindNativeObject();
+ mNativeMeasuredParagraph = null;
}
/**
@@ -267,10 +243,10 @@ public class MeasuredParagraph {
* Returns the native ptr of the MeasuredParagraph.
*
* This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
- * Returns 0 in other cases.
+ * Returns null in other cases.
*/
- public /* Maybe Zero */ long getNativePtr() {
- return mNativePtr;
+ public NativeMeasuredParagraph getNativeMeasuredParagraph() {
+ return mNativeMeasuredParagraph;
}
/**
@@ -283,7 +259,7 @@ public class MeasuredParagraph {
* @param end the exclusive end offset of the target region in the text
*/
public float getWidth(int start, int end) {
- if (mNativePtr == 0) {
+ if (mNativeMeasuredParagraph == null) {
// We have result in Java.
final float[] widths = mWidths.getRawArray();
float r = 0.0f;
@@ -293,7 +269,7 @@ public class MeasuredParagraph {
return r;
} else {
// We have result in native.
- return nGetWidth(mNativePtr, start, end);
+ return mNativeMeasuredParagraph.getWidth(start, end);
}
}
@@ -305,7 +281,16 @@ public class MeasuredParagraph {
*/
public void getBounds(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
@NonNull Rect bounds) {
- nGetBounds(mNativePtr, mCopiedBuffer, start, end, bounds);
+ mNativeMeasuredParagraph.getBounds(mCopiedBuffer, start, end, bounds);
+ }
+
+ /**
+ * Returns a width of the character at the offset.
+ *
+ * This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
+ */
+ public float getCharWidthAt(@IntRange(from = 0) int offset) {
+ return mNativeMeasuredParagraph.getCharWidthAt(offset);
}
/**
@@ -364,7 +349,7 @@ public class MeasuredParagraph {
if (mt.mSpanned == null) {
// No style change by MetricsAffectingSpan. Just measure all text.
mt.applyMetricsAffectingSpan(
- paint, null /* spans */, start, end, 0 /* native static layout ptr */);
+ paint, null /* spans */, start, end, null /* native builder ptr */);
} else {
// There may be a MetricsAffectingSpan. Split into span transitions and apply styles.
int spanEnd;
@@ -374,7 +359,7 @@ public class MeasuredParagraph {
MetricAffectingSpan.class);
spans = TextUtils.removeEmptySpans(spans, mt.mSpanned, MetricAffectingSpan.class);
mt.applyMetricsAffectingSpan(
- paint, spans, spanStart, spanEnd, 0 /* native static layout ptr */);
+ paint, spans, spanStart, spanEnd, null /* native builder ptr */);
}
}
return mt;
@@ -406,25 +391,16 @@ public class MeasuredParagraph {
@Nullable MeasuredParagraph recycle) {
final MeasuredParagraph mt = recycle == null ? obtain() : recycle;
mt.resetAndAnalyzeBidi(text, start, end, textDir);
+ final NativeMeasuredParagraph.Builder builder = new NativeMeasuredParagraph.Builder();
if (mt.mTextLength == 0) {
// Need to build empty native measured text for StaticLayout.
// TODO: Stop creating empty measured text for empty lines.
- long nativeBuilderPtr = nInitBuilder();
- try {
- mt.bindNativeObject(
- nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer,
- computeHyphenation, computeLayout));
- } finally {
- nFreeBuilder(nativeBuilderPtr);
- }
- return mt;
- }
-
- long nativeBuilderPtr = nInitBuilder();
- try {
+ mt.mNativeMeasuredParagraph = builder.build(mt.mCopiedBuffer, computeHyphenation,
+ computeLayout);
+ } else {
if (mt.mSpanned == null) {
// No style change by MetricsAffectingSpan. Just measure all text.
- mt.applyMetricsAffectingSpan(paint, null /* spans */, start, end, nativeBuilderPtr);
+ mt.applyMetricsAffectingSpan(paint, null /* spans */, start, end, builder);
mt.mSpanEndCache.append(end);
} else {
// There may be a MetricsAffectingSpan. Split into span transitions and apply
@@ -437,15 +413,12 @@ public class MeasuredParagraph {
MetricAffectingSpan.class);
spans = TextUtils.removeEmptySpans(spans, mt.mSpanned,
MetricAffectingSpan.class);
- mt.applyMetricsAffectingSpan(paint, spans, spanStart, spanEnd,
- nativeBuilderPtr);
+ mt.applyMetricsAffectingSpan(paint, spans, spanStart, spanEnd, builder);
mt.mSpanEndCache.append(spanEnd);
}
}
- mt.bindNativeObject(nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer,
- computeHyphenation, computeLayout));
- } finally {
- nFreeBuilder(nativeBuilderPtr);
+ mt.mNativeMeasuredParagraph = builder.build(mt.mCopiedBuffer, computeHyphenation,
+ computeLayout);
}
return mt;
@@ -517,13 +490,13 @@ public class MeasuredParagraph {
private void applyReplacementRun(@NonNull ReplacementSpan replacement,
@IntRange(from = 0) int start, // inclusive, in copied buffer
@IntRange(from = 0) int end, // exclusive, in copied buffer
- /* Maybe Zero */ long nativeBuilderPtr) {
+ @Nullable NativeMeasuredParagraph.Builder builder) {
// Use original text. Shouldn't matter.
// TODO: passing uninitizlied FontMetrics to developers. Do we need to keep this for
// backward compatibility? or Should we initialize them for getFontMetricsInt?
final float width = replacement.getSize(
mCachedPaint, mSpanned, start + mTextStart, end + mTextStart, mCachedFm);
- if (nativeBuilderPtr == 0) {
+ if (builder == null) {
// Assigns all width to the first character. This is the same behavior as minikin.
mWidths.set(start, width);
if (end > start + 1) {
@@ -531,24 +504,22 @@ public class MeasuredParagraph {
}
mWholeWidth += width;
} else {
- nAddReplacementRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), start, end,
- width);
+ builder.addReplacementRun(mCachedPaint, start, end, width);
}
}
private void applyStyleRun(@IntRange(from = 0) int start, // inclusive, in copied buffer
@IntRange(from = 0) int end, // exclusive, in copied buffer
- /* Maybe Zero */ long nativeBuilderPtr) {
+ @Nullable NativeMeasuredParagraph.Builder builder) {
if (mLtrWithoutBidi) {
// If the whole text is LTR direction, just apply whole region.
- if (nativeBuilderPtr == 0) {
+ if (builder == null) {
mWholeWidth += mCachedPaint.getTextRunAdvances(
mCopiedBuffer, start, end - start, start, end - start, false /* isRtl */,
mWidths.getRawArray(), start);
} else {
- nAddStyleRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), start, end,
- false /* isRtl */);
+ builder.addStyleRun(mCachedPaint, start, end, false /* isRtl */);
}
} else {
// If there is multiple bidi levels, split into individual bidi level and apply style.
@@ -558,14 +529,13 @@ public class MeasuredParagraph {
for (int levelStart = start, levelEnd = start + 1;; ++levelEnd) {
if (levelEnd == end || mLevels.get(levelEnd) != level) { // transition point
final boolean isRtl = (level & 0x1) != 0;
- if (nativeBuilderPtr == 0) {
+ if (builder == null) {
final int levelLength = levelEnd - levelStart;
mWholeWidth += mCachedPaint.getTextRunAdvances(
mCopiedBuffer, levelStart, levelLength, levelStart, levelLength,
isRtl, mWidths.getRawArray(), levelStart);
} else {
- nAddStyleRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), levelStart,
- levelEnd, isRtl);
+ builder.addStyleRun(mCachedPaint, levelStart, levelEnd, isRtl);
}
if (levelEnd == end) {
break;
@@ -582,12 +552,12 @@ public class MeasuredParagraph {
@Nullable MetricAffectingSpan[] spans,
@IntRange(from = 0) int start, // inclusive, in original text buffer
@IntRange(from = 0) int end, // exclusive, in original text buffer
- /* Maybe Zero */ long nativeBuilderPtr) {
+ @Nullable NativeMeasuredParagraph.Builder builder) {
mCachedPaint.set(paint);
// XXX paint should not have a baseline shift, but...
mCachedPaint.baselineShift = 0;
- final boolean needFontMetrics = nativeBuilderPtr != 0;
+ final boolean needFontMetrics = builder != null;
if (needFontMetrics && mCachedFm == null) {
mCachedFm = new Paint.FontMetricsInt();
@@ -610,15 +580,14 @@ public class MeasuredParagraph {
final int startInCopiedBuffer = start - mTextStart;
final int endInCopiedBuffer = end - mTextStart;
- if (nativeBuilderPtr != 0) {
+ if (builder != null) {
mCachedPaint.getFontMetricsInt(mCachedFm);
}
if (replacement != null) {
- applyReplacementRun(replacement, startInCopiedBuffer, endInCopiedBuffer,
- nativeBuilderPtr);
+ applyReplacementRun(replacement, startInCopiedBuffer, endInCopiedBuffer, builder);
} else {
- applyStyleRun(startInCopiedBuffer, endInCopiedBuffer, nativeBuilderPtr);
+ applyStyleRun(startInCopiedBuffer, endInCopiedBuffer, builder);
}
if (needFontMetrics) {
@@ -689,59 +658,6 @@ public class MeasuredParagraph {
* This only works if the MeasuredParagraph is computed with buildForStaticLayout.
*/
public @IntRange(from = 0) int getMemoryUsage() {
- return nGetMemoryUsage(mNativePtr);
+ return mNativeMeasuredParagraph.getMemoryUsage();
}
-
- private static native /* Non Zero */ long nInitBuilder();
-
- /**
- * Apply style to make native measured text.
- *
- * @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
- * @param paintPtr The native paint pointer to be applied.
- * @param start The start offset in the copied buffer.
- * @param end The end offset in the copied buffer.
- * @param isRtl True if the text is RTL.
- */
- private static native void nAddStyleRun(/* Non Zero */ long nativeBuilderPtr,
- /* Non Zero */ long paintPtr,
- @IntRange(from = 0) int start,
- @IntRange(from = 0) int end,
- boolean isRtl);
-
- /**
- * Apply ReplacementRun to make native measured text.
- *
- * @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
- * @param paintPtr The native paint pointer to be applied.
- * @param start The start offset in the copied buffer.
- * @param end The end offset in the copied buffer.
- * @param width The width of the replacement.
- */
- private static native void nAddReplacementRun(/* Non Zero */ long nativeBuilderPtr,
- /* Non Zero */ long paintPtr,
- @IntRange(from = 0) int start,
- @IntRange(from = 0) int end,
- @FloatRange(from = 0) float width);
-
- private static native long nBuildNativeMeasuredParagraph(/* Non Zero */ long nativeBuilderPtr,
- @NonNull char[] text,
- boolean computeHyphenation,
- boolean computeLayout);
-
- private static native void nFreeBuilder(/* Non Zero */ long nativeBuilderPtr);
-
- @CriticalNative
- private static native float nGetWidth(/* Non Zero */ long nativePtr,
- @IntRange(from = 0) int start,
- @IntRange(from = 0) int end);
-
- @CriticalNative
- private static native /* Non Zero */ long nGetReleaseFunc();
-
- @CriticalNative
- private static native int nGetMemoryUsage(/* Non Zero */ long nativePtr);
-
- private static native void nGetBounds(long nativePtr, char[] buf, int start, int end,
- Rect rect);
}
diff --git a/core/java/android/text/NativeLineBreaker.java b/core/java/android/text/NativeLineBreaker.java
new file mode 100644
index 000000000000..a31b3361d970
--- /dev/null
+++ b/core/java/android/text/NativeLineBreaker.java
@@ -0,0 +1,155 @@
+/*
+ * 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.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
+
+import libcore.util.NativeAllocationRegistry;
+
+/**
+ * A native implementation of the line breaker.
+ * TODO: Consider to make this class public.
+ * @hide
+ */
+public class NativeLineBreaker {
+
+ /**
+ * A result object of a line breaking
+ */
+ public static class LineBreaks {
+ public int breakCount;
+ private static final int INITIAL_SIZE = 16;
+ public int[] breaks = new int[INITIAL_SIZE];
+ public float[] widths = new float[INITIAL_SIZE];
+ public float[] ascents = new float[INITIAL_SIZE];
+ public float[] descents = new float[INITIAL_SIZE];
+ public int[] flags = new int[INITIAL_SIZE];
+ // breaks, widths, and flags should all have the same length
+ }
+
+ private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+ NativeLineBreaker.class.getClassLoader(), nGetReleaseFunc(), 64);
+
+ private final long mNativePtr;
+
+ /**
+ * A constructor of NativeLineBreaker
+ */
+ public NativeLineBreaker(@Layout.BreakStrategy int breakStrategy,
+ @Layout.HyphenationFrequency int hyphenationFrequency,
+ boolean justify, @Nullable int[] indents) {
+ mNativePtr = nInit(breakStrategy, hyphenationFrequency, justify, indents);
+ sRegistry.registerNativeAllocation(this, mNativePtr);
+ }
+
+ /**
+ * Break text into lines
+ *
+ * @param chars an array of characters
+ * @param measuredPara a result of the text measurement
+ * @param length a length of the target text from the begining
+ * @param firstWidth a width of the first width of the line in this paragraph
+ * @param firstWidthLineCount a number of lines that has the length of the firstWidth
+ * @param restWidth a width of the rest of the lines.
+ * @param variableTabStops an array of tab stop widths
+ * @param defaultTabStop a width of the tab stop
+ * @param indentsOffset an offset of the indents to be used.
+ * @param out output buffer
+ * @return a number of the lines
+ */
+ @NonNull public int computeLineBreaks(
+ @NonNull char[] chars,
+ @NonNull NativeMeasuredParagraph measuredPara,
+ @IntRange(from = 0) int length,
+ @FloatRange(from = 0.0f) float firstWidth,
+ @IntRange(from = 0) int firstWidthLineCount,
+ @FloatRange(from = 0.0f) float restWidth,
+ @Nullable int[] variableTabStops,
+ int defaultTabStop,
+ @IntRange(from = 0) int indentsOffset,
+ @NonNull LineBreaks out) {
+ return nComputeLineBreaks(
+ mNativePtr,
+
+ // Inputs
+ chars,
+ measuredPara.getNativePtr(),
+ length,
+ firstWidth,
+ firstWidthLineCount,
+ restWidth,
+ variableTabStops,
+ defaultTabStop,
+ indentsOffset,
+
+ // Outputs
+ out,
+ out.breaks.length,
+ out.breaks,
+ out.widths,
+ out.ascents,
+ out.descents,
+ out.flags);
+
+ }
+
+ @FastNative
+ private static native long nInit(
+ @Layout.BreakStrategy int breakStrategy,
+ @Layout.HyphenationFrequency int hyphenationFrequency,
+ boolean isJustified,
+ @Nullable int[] indents);
+
+ @CriticalNative
+ private static native long nGetReleaseFunc();
+
+ // populates LineBreaks and returns the number of breaks found
+ //
+ // the arrays inside the LineBreaks objects are passed in as well
+ // to reduce the number of JNI calls in the common case where the
+ // arrays do not have to be resized
+ // The individual character widths will be returned in charWidths. The length of
+ // charWidths must be at least the length of the text.
+ private static native int nComputeLineBreaks(
+ /* non zero */ long nativePtr,
+
+ // Inputs
+ @NonNull char[] text,
+ /* Non Zero */ long measuredTextPtr,
+ @IntRange(from = 0) int length,
+ @FloatRange(from = 0.0f) float firstWidth,
+ @IntRange(from = 0) int firstWidthLineCount,
+ @FloatRange(from = 0.0f) float restWidth,
+ @Nullable int[] variableTabStops,
+ int defaultTabStop,
+ @IntRange(from = 0) int indentsOffset,
+
+ // Outputs
+ @NonNull LineBreaks recycle,
+ @IntRange(from = 0) int recycleLength,
+ @NonNull int[] recycleBreaks,
+ @NonNull float[] recycleWidths,
+ @NonNull float[] recycleAscents,
+ @NonNull float[] recycleDescents,
+ @NonNull int[] recycleFlags);
+}
diff --git a/core/java/android/text/NativeMeasuredParagraph.java b/core/java/android/text/NativeMeasuredParagraph.java
new file mode 100644
index 000000000000..d03674f86109
--- /dev/null
+++ b/core/java/android/text/NativeMeasuredParagraph.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.text;
+
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.graphics.Paint;
+import android.graphics.Rect;
+
+import dalvik.annotation.optimization.CriticalNative;
+
+import libcore.util.NativeAllocationRegistry;
+
+/**
+ * A native implementation of measured paragraph.
+ * TODO: Consider to make this class public.
+ * @hide
+ */
+public class NativeMeasuredParagraph {
+ private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+ NativeMeasuredParagraph.class.getClassLoader(), nGetReleaseFunc(), 1024);
+
+ private long mNativePtr;
+
+ // Use builder instead.
+ private NativeMeasuredParagraph(long ptr) {
+ mNativePtr = ptr;
+ }
+
+ /**
+ * Returns a width of the given region
+ */
+ public float getWidth(int start, int end) {
+ return nGetWidth(mNativePtr, start, end);
+ }
+
+ /**
+ * Returns a memory usage of the native object.
+ */
+ public int getMemoryUsage() {
+ return nGetMemoryUsage(mNativePtr);
+ }
+
+ /**
+ * Fills the boundary box of the given region
+ */
+ public void getBounds(char[] buf, int start, int end, Rect rect) {
+ nGetBounds(mNativePtr, buf, start, end, rect);
+ }
+
+ /**
+ * Returns the width of the character at the given offset
+ */
+ public float getCharWidthAt(int offset) {
+ return nGetCharWidthAt(mNativePtr, offset);
+ }
+
+ /**
+ * Returns a native pointer of the underlying native object.
+ */
+ public long getNativePtr() {
+ return mNativePtr;
+ }
+
+ @CriticalNative
+ private static native float nGetWidth(/* Non Zero */ long nativePtr,
+ @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end);
+
+ @CriticalNative
+ private static native /* Non Zero */ long nGetReleaseFunc();
+
+ @CriticalNative
+ private static native int nGetMemoryUsage(/* Non Zero */ long nativePtr);
+
+ private static native void nGetBounds(long nativePtr, char[] buf, int start, int end,
+ Rect rect);
+
+ @CriticalNative
+ private static native float nGetCharWidthAt(long nativePtr, int offset);
+
+ /**
+ * A builder for the NativeMeasuredParagraph
+ */
+ public static class Builder {
+ private final long mNativePtr;
+
+ public Builder() {
+ mNativePtr = nInitBuilder();
+ }
+
+ /**
+ * Apply styles to given range
+ */
+ public void addStyleRun(@NonNull Paint paint, int start, int end, boolean isRtl) {
+ nAddStyleRun(mNativePtr, paint.getNativeInstance(), start, end, isRtl);
+ }
+
+ /**
+ * Tells native that the given range is replaced with the object of given width.
+ */
+ public void addReplacementRun(@NonNull Paint paint, int start, int end, float width) {
+ nAddReplacementRun(mNativePtr, paint.getNativeInstance(), start, end, width);
+ }
+
+ /**
+ * Build the NativeMeasuredParagraph
+ */
+ public NativeMeasuredParagraph build(char[] text, boolean computeHyphenation,
+ boolean computeLayout) {
+ try {
+ long ptr = nBuildNativeMeasuredParagraph(mNativePtr, text, computeHyphenation,
+ computeLayout);
+ NativeMeasuredParagraph res = new NativeMeasuredParagraph(ptr);
+ sRegistry.registerNativeAllocation(res, ptr);
+ return res;
+ } finally {
+ nFreeBuilder(mNativePtr);
+ }
+ }
+
+ private static native /* Non Zero */ long nInitBuilder();
+
+ /**
+ * Apply style to make native measured text.
+ *
+ * @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
+ * @param paintPtr The native paint pointer to be applied.
+ * @param start The start offset in the copied buffer.
+ * @param end The end offset in the copied buffer.
+ * @param isRtl True if the text is RTL.
+ */
+ private static native void nAddStyleRun(/* Non Zero */ long nativeBuilderPtr,
+ /* Non Zero */ long paintPtr,
+ @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end,
+ boolean isRtl);
+ /**
+ * Apply ReplacementRun to make native measured text.
+ *
+ * @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
+ * @param paintPtr The native paint pointer to be applied.
+ * @param start The start offset in the copied buffer.
+ * @param end The end offset in the copied buffer.
+ * @param width The width of the replacement.
+ */
+ private static native void nAddReplacementRun(/* Non Zero */ long nativeBuilderPtr,
+ /* Non Zero */ long paintPtr,
+ @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end,
+ @FloatRange(from = 0) float width);
+
+ private static native long nBuildNativeMeasuredParagraph(
+ /* Non Zero */ long nativeBuilderPtr,
+ @NonNull char[] text,
+ boolean computeHyphenation,
+ boolean computeLayout);
+
+ private static native void nFreeBuilder(/* Non Zero */ long nativeBuilderPtr);
+ }
+}
diff --git a/core/java/android/text/PrecomputedText.java b/core/java/android/text/PrecomputedText.java
index 027ead30c9ae..b7ea0122cb06 100644
--- a/core/java/android/text/PrecomputedText.java
+++ b/core/java/android/text/PrecomputedText.java
@@ -518,6 +518,21 @@ public class PrecomputedText implements Spannable {
}
/**
+ * Returns a width of a character at offset
+ *
+ * @param offset an offset of the text.
+ * @return a width of the character.
+ * @hide
+ */
+ public float getCharWidthAt(@IntRange(from = 0) int offset) {
+ Preconditions.checkArgument(0 <= offset && offset < mText.length(), "invalid offset");
+ final int paraIndex = findParaIndex(offset);
+ final int paraStart = getParagraphStart(paraIndex);
+ final int paraEnd = getParagraphEnd(paraIndex);
+ return getMeasuredParagraph(paraIndex).getCharWidthAt(offset - paraStart);
+ }
+
+ /**
* Returns the size of native PrecomputedText memory usage.
*
* Note that this is not guaranteed to be accurate. Must be used only for testing purposes.
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 4b78aa2332d5..6dad23815bef 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -21,7 +21,6 @@ import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Paint;
-import android.text.AutoGrowArray.FloatArray;
import android.text.style.LeadingMarginSpan;
import android.text.style.LeadingMarginSpan.LeadingMarginSpan2;
import android.text.style.LineHeightSpan;
@@ -32,9 +31,6 @@ import android.util.Pools.SynchronizedPool;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.GrowingArrayUtils;
-import dalvik.annotation.optimization.CriticalNative;
-import dalvik.annotation.optimization.FastNative;
-
import java.util.Arrays;
/**
@@ -57,7 +53,7 @@ public class StaticLayout extends Layout {
*
* - Create MeasuredParagraph by MeasuredParagraph.buildForStaticLayout which measures in
* native.
- * - Run nComputeLineBreaks() to obtain line breaks for the paragraph.
+ * - Run NativeLineBreaker.computeLineBreaks() to obtain line breaks for the paragraph.
*
* After all paragraphs, call finish() to release expensive buffers.
*/
@@ -586,8 +582,7 @@ public class StaticLayout extends Layout {
float ellipsizedWidth = b.mEllipsizedWidth;
TextUtils.TruncateAt ellipsize = b.mEllipsize;
final boolean addLastLineSpacing = b.mAddLastLineLineSpacing;
- LineBreaks lineBreaks = new LineBreaks(); // TODO: move to builder to avoid allocation costs
- FloatArray widths = new FloatArray();
+ NativeLineBreaker.LineBreaks lineBreaks = new NativeLineBreaker.LineBreaks();
mLineCount = 0;
mEllipsized = false;
@@ -615,7 +610,7 @@ public class StaticLayout extends Layout {
indents = null;
}
- final long nativePtr = nInit(
+ final NativeLineBreaker lineBreaker = new NativeLineBreaker(
b.mBreakStrategy, b.mHyphenationFrequency,
// TODO: Support more justification mode, e.g. letter spacing, stretching.
b.mJustificationMode != Layout.JUSTIFICATION_MODE_NONE,
@@ -639,243 +634,219 @@ public class StaticLayout extends Layout {
bufEnd, false /* computeLayout */);
}
- try {
- for (int paraIndex = 0; paraIndex < paragraphInfo.length; paraIndex++) {
- final int paraStart = paraIndex == 0
- ? bufStart : paragraphInfo[paraIndex - 1].paragraphEnd;
- final int paraEnd = paragraphInfo[paraIndex].paragraphEnd;
-
- int firstWidthLineCount = 1;
- int firstWidth = outerWidth;
- int restWidth = outerWidth;
-
- LineHeightSpan[] chooseHt = null;
-
- if (spanned != null) {
- LeadingMarginSpan[] sp = getParagraphSpans(spanned, paraStart, paraEnd,
- LeadingMarginSpan.class);
- for (int i = 0; i < sp.length; i++) {
- LeadingMarginSpan lms = sp[i];
- firstWidth -= sp[i].getLeadingMargin(true);
- restWidth -= sp[i].getLeadingMargin(false);
-
- // LeadingMarginSpan2 is odd. The count affects all
- // leading margin spans, not just this particular one
- if (lms instanceof LeadingMarginSpan2) {
- LeadingMarginSpan2 lms2 = (LeadingMarginSpan2) lms;
- firstWidthLineCount = Math.max(firstWidthLineCount,
- lms2.getLeadingMarginLineCount());
- }
+ for (int paraIndex = 0; paraIndex < paragraphInfo.length; paraIndex++) {
+ final int paraStart = paraIndex == 0
+ ? bufStart : paragraphInfo[paraIndex - 1].paragraphEnd;
+ final int paraEnd = paragraphInfo[paraIndex].paragraphEnd;
+
+ int firstWidthLineCount = 1;
+ int firstWidth = outerWidth;
+ int restWidth = outerWidth;
+
+ LineHeightSpan[] chooseHt = null;
+ if (spanned != null) {
+ LeadingMarginSpan[] sp = getParagraphSpans(spanned, paraStart, paraEnd,
+ LeadingMarginSpan.class);
+ for (int i = 0; i < sp.length; i++) {
+ LeadingMarginSpan lms = sp[i];
+ firstWidth -= sp[i].getLeadingMargin(true);
+ restWidth -= sp[i].getLeadingMargin(false);
+
+ // LeadingMarginSpan2 is odd. The count affects all
+ // leading margin spans, not just this particular one
+ if (lms instanceof LeadingMarginSpan2) {
+ LeadingMarginSpan2 lms2 = (LeadingMarginSpan2) lms;
+ firstWidthLineCount = Math.max(firstWidthLineCount,
+ lms2.getLeadingMarginLineCount());
}
+ }
- chooseHt = getParagraphSpans(spanned, paraStart, paraEnd, LineHeightSpan.class);
+ chooseHt = getParagraphSpans(spanned, paraStart, paraEnd, LineHeightSpan.class);
- if (chooseHt.length == 0) {
- chooseHt = null; // So that out() would not assume it has any contents
- } else {
- if (chooseHtv == null || chooseHtv.length < chooseHt.length) {
- chooseHtv = ArrayUtils.newUnpaddedIntArray(chooseHt.length);
- }
+ if (chooseHt.length == 0) {
+ chooseHt = null; // So that out() would not assume it has any contents
+ } else {
+ if (chooseHtv == null || chooseHtv.length < chooseHt.length) {
+ chooseHtv = ArrayUtils.newUnpaddedIntArray(chooseHt.length);
+ }
- for (int i = 0; i < chooseHt.length; i++) {
- int o = spanned.getSpanStart(chooseHt[i]);
+ for (int i = 0; i < chooseHt.length; i++) {
+ int o = spanned.getSpanStart(chooseHt[i]);
- if (o < paraStart) {
- // starts in this layout, before the
- // current paragraph
+ if (o < paraStart) {
+ // starts in this layout, before the
+ // current paragraph
- chooseHtv[i] = getLineTop(getLineForOffset(o));
- } else {
- // starts in this paragraph
+ chooseHtv[i] = getLineTop(getLineForOffset(o));
+ } else {
+ // starts in this paragraph
- chooseHtv[i] = v;
- }
+ chooseHtv[i] = v;
}
}
}
-
- // tab stop locations
- int[] variableTabStops = null;
- if (spanned != null) {
- TabStopSpan[] spans = getParagraphSpans(spanned, paraStart,
- paraEnd, TabStopSpan.class);
- if (spans.length > 0) {
- int[] stops = new int[spans.length];
- for (int i = 0; i < spans.length; i++) {
- stops[i] = spans[i].getTabStop();
- }
- Arrays.sort(stops, 0, stops.length);
- variableTabStops = stops;
+ }
+ // tab stop locations
+ int[] variableTabStops = null;
+ if (spanned != null) {
+ TabStopSpan[] spans = getParagraphSpans(spanned, paraStart,
+ paraEnd, TabStopSpan.class);
+ if (spans.length > 0) {
+ int[] stops = new int[spans.length];
+ for (int i = 0; i < spans.length; i++) {
+ stops[i] = spans[i].getTabStop();
}
+ Arrays.sort(stops, 0, stops.length);
+ variableTabStops = stops;
}
+ }
- final MeasuredParagraph measuredPara = paragraphInfo[paraIndex].measured;
- final char[] chs = measuredPara.getChars();
- final int[] spanEndCache = measuredPara.getSpanEndCache().getRawArray();
- final int[] fmCache = measuredPara.getFontMetrics().getRawArray();
- // TODO: Stop keeping duplicated width copy in native and Java.
- widths.resize(chs.length);
-
- // measurement has to be done before performing line breaking
- // but we don't want to recompute fontmetrics or span ranges the
- // second time, so we cache those and then use those stored values
-
- int breakCount = nComputeLineBreaks(
- nativePtr,
-
- // Inputs
- chs,
- measuredPara.getNativePtr(),
- paraEnd - paraStart,
- firstWidth,
- firstWidthLineCount,
- restWidth,
- variableTabStops,
- TAB_INCREMENT,
- mLineCount,
-
- // Outputs
- lineBreaks,
- lineBreaks.breaks.length,
- lineBreaks.breaks,
- lineBreaks.widths,
- lineBreaks.ascents,
- lineBreaks.descents,
- lineBreaks.flags,
- widths.getRawArray());
-
- final int[] breaks = lineBreaks.breaks;
- final float[] lineWidths = lineBreaks.widths;
- final float[] ascents = lineBreaks.ascents;
- final float[] descents = lineBreaks.descents;
- final int[] flags = lineBreaks.flags;
-
- final int remainingLineCount = mMaximumVisibleLineCount - mLineCount;
- final boolean ellipsisMayBeApplied = ellipsize != null
- && (ellipsize == TextUtils.TruncateAt.END
- || (mMaximumVisibleLineCount == 1
- && ellipsize != TextUtils.TruncateAt.MARQUEE));
- if (0 < remainingLineCount && remainingLineCount < breakCount
- && ellipsisMayBeApplied) {
- // Calculate width and flag.
- float width = 0;
- int flag = 0; // XXX May need to also have starting hyphen edit
- for (int i = remainingLineCount - 1; i < breakCount; i++) {
- if (i == breakCount - 1) {
- width += lineWidths[i];
- } else {
- for (int j = (i == 0 ? 0 : breaks[i - 1]); j < breaks[i]; j++) {
- width += widths.get(j);
- }
+ final MeasuredParagraph measuredPara = paragraphInfo[paraIndex].measured;
+ final char[] chs = measuredPara.getChars();
+ final int[] spanEndCache = measuredPara.getSpanEndCache().getRawArray();
+ final int[] fmCache = measuredPara.getFontMetrics().getRawArray();
+ int breakCount = lineBreaker.computeLineBreaks(
+ measuredPara.getChars(),
+ measuredPara.getNativeMeasuredParagraph(),
+ paraEnd - paraStart,
+ firstWidth,
+ firstWidthLineCount,
+ restWidth,
+ variableTabStops,
+ TAB_INCREMENT,
+ mLineCount,
+ lineBreaks);
+
+ final int[] breaks = lineBreaks.breaks;
+ final float[] lineWidths = lineBreaks.widths;
+ final float[] ascents = lineBreaks.ascents;
+ final float[] descents = lineBreaks.descents;
+ final int[] flags = lineBreaks.flags;
+
+ final int remainingLineCount = mMaximumVisibleLineCount - mLineCount;
+ final boolean ellipsisMayBeApplied = ellipsize != null
+ && (ellipsize == TextUtils.TruncateAt.END
+ || (mMaximumVisibleLineCount == 1
+ && ellipsize != TextUtils.TruncateAt.MARQUEE));
+ if (0 < remainingLineCount && remainingLineCount < breakCount
+ && ellipsisMayBeApplied) {
+ // Calculate width
+ float width = 0;
+ int flag = 0; // XXX May need to also have starting hyphen edit
+ for (int i = remainingLineCount - 1; i < breakCount; i++) {
+ if (i == breakCount - 1) {
+ width += lineWidths[i];
+ } else {
+ for (int j = (i == 0 ? 0 : breaks[i - 1]); j < breaks[i]; j++) {
+ width += measuredPara.getCharWidthAt(j - paraStart);
}
- flag |= flags[i] & TAB_MASK;
}
- // Treat the last line and overflowed lines as a single line.
- breaks[remainingLineCount - 1] = breaks[breakCount - 1];
- lineWidths[remainingLineCount - 1] = width;
- flags[remainingLineCount - 1] = flag;
+ flag |= flags[i] & TAB_MASK;
+ }
+ // Treat the last line and overflowed lines as a single line.
+ breaks[remainingLineCount - 1] = breaks[breakCount - 1];
+ lineWidths[remainingLineCount - 1] = width;
+ flags[remainingLineCount - 1] = flag;
+
+ breakCount = remainingLineCount;
+ }
+
+ // here is the offset of the starting character of the line we are currently
+ // measuring
+ int here = paraStart;
+
+ int fmTop = 0, fmBottom = 0, fmAscent = 0, fmDescent = 0;
+ int fmCacheIndex = 0;
+ int spanEndCacheIndex = 0;
+ int breakIndex = 0;
+ for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) {
+ // retrieve end of span
+ spanEnd = spanEndCache[spanEndCacheIndex++];
+
+ // retrieve cached metrics, order matches above
+ fm.top = fmCache[fmCacheIndex * 4 + 0];
+ fm.bottom = fmCache[fmCacheIndex * 4 + 1];
+ fm.ascent = fmCache[fmCacheIndex * 4 + 2];
+ fm.descent = fmCache[fmCacheIndex * 4 + 3];
+ fmCacheIndex++;
+
+ if (fm.top < fmTop) {
+ fmTop = fm.top;
+ }
+ if (fm.ascent < fmAscent) {
+ fmAscent = fm.ascent;
+ }
+ if (fm.descent > fmDescent) {
+ fmDescent = fm.descent;
+ }
+ if (fm.bottom > fmBottom) {
+ fmBottom = fm.bottom;
+ }
- breakCount = remainingLineCount;
+ // skip breaks ending before current span range
+ while (breakIndex < breakCount && paraStart + breaks[breakIndex] < spanStart) {
+ breakIndex++;
}
- // here is the offset of the starting character of the line we are currently
- // measuring
- int here = paraStart;
-
- int fmTop = 0, fmBottom = 0, fmAscent = 0, fmDescent = 0;
- int fmCacheIndex = 0;
- int spanEndCacheIndex = 0;
- int breakIndex = 0;
- for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) {
- // retrieve end of span
- spanEnd = spanEndCache[spanEndCacheIndex++];
-
- // retrieve cached metrics, order matches above
- fm.top = fmCache[fmCacheIndex * 4 + 0];
- fm.bottom = fmCache[fmCacheIndex * 4 + 1];
- fm.ascent = fmCache[fmCacheIndex * 4 + 2];
- fm.descent = fmCache[fmCacheIndex * 4 + 3];
- fmCacheIndex++;
-
- if (fm.top < fmTop) {
+ while (breakIndex < breakCount && paraStart + breaks[breakIndex] <= spanEnd) {
+ int endPos = paraStart + breaks[breakIndex];
+
+ boolean moreChars = (endPos < bufEnd);
+
+ final int ascent = fallbackLineSpacing
+ ? Math.min(fmAscent, Math.round(ascents[breakIndex]))
+ : fmAscent;
+ final int descent = fallbackLineSpacing
+ ? Math.max(fmDescent, Math.round(descents[breakIndex]))
+ : fmDescent;
+
+ v = out(source, here, endPos,
+ ascent, descent, fmTop, fmBottom,
+ v, spacingmult, spacingadd, chooseHt, chooseHtv, fm,
+ flags[breakIndex], needMultiply, measuredPara, bufEnd,
+ includepad, trackpad, addLastLineSpacing, chs,
+ paraStart, ellipsize, ellipsizedWidth, lineWidths[breakIndex],
+ paint, moreChars);
+
+ if (endPos < spanEnd) {
+ // preserve metrics for current span
fmTop = fm.top;
- }
- if (fm.ascent < fmAscent) {
+ fmBottom = fm.bottom;
fmAscent = fm.ascent;
- }
- if (fm.descent > fmDescent) {
fmDescent = fm.descent;
+ } else {
+ fmTop = fmBottom = fmAscent = fmDescent = 0;
}
- if (fm.bottom > fmBottom) {
- fmBottom = fm.bottom;
- }
-
- // skip breaks ending before current span range
- while (breakIndex < breakCount && paraStart + breaks[breakIndex] < spanStart) {
- breakIndex++;
- }
-
- while (breakIndex < breakCount && paraStart + breaks[breakIndex] <= spanEnd) {
- int endPos = paraStart + breaks[breakIndex];
-
- boolean moreChars = (endPos < bufEnd);
-
- final int ascent = fallbackLineSpacing
- ? Math.min(fmAscent, Math.round(ascents[breakIndex]))
- : fmAscent;
- final int descent = fallbackLineSpacing
- ? Math.max(fmDescent, Math.round(descents[breakIndex]))
- : fmDescent;
- v = out(source, here, endPos,
- ascent, descent, fmTop, fmBottom,
- v, spacingmult, spacingadd, chooseHt, chooseHtv, fm,
- flags[breakIndex], needMultiply, measuredPara, bufEnd,
- includepad, trackpad, addLastLineSpacing, chs, widths.getRawArray(),
- paraStart, ellipsize, ellipsizedWidth, lineWidths[breakIndex],
- paint, moreChars);
-
- if (endPos < spanEnd) {
- // preserve metrics for current span
- fmTop = fm.top;
- fmBottom = fm.bottom;
- fmAscent = fm.ascent;
- fmDescent = fm.descent;
- } else {
- fmTop = fmBottom = fmAscent = fmDescent = 0;
- }
- here = endPos;
- breakIndex++;
+ here = endPos;
+ breakIndex++;
- if (mLineCount >= mMaximumVisibleLineCount && mEllipsized) {
- return;
- }
+ if (mLineCount >= mMaximumVisibleLineCount && mEllipsized) {
+ return;
}
}
-
- if (paraEnd == bufEnd) {
- break;
- }
}
- if ((bufEnd == bufStart || source.charAt(bufEnd - 1) == CHAR_NEW_LINE)
- && mLineCount < mMaximumVisibleLineCount) {
- final MeasuredParagraph measuredPara =
- MeasuredParagraph.buildForBidi(source, bufEnd, bufEnd, textDir, null);
- paint.getFontMetricsInt(fm);
- v = out(source,
- bufEnd, bufEnd, fm.ascent, fm.descent,
- fm.top, fm.bottom,
- v,
- spacingmult, spacingadd, null,
- null, fm, 0,
- needMultiply, measuredPara, bufEnd,
- includepad, trackpad, addLastLineSpacing, null,
- null, bufStart, ellipsize,
- ellipsizedWidth, 0, paint, false);
+ if (paraEnd == bufEnd) {
+ break;
}
- } finally {
- nFinish(nativePtr);
+ }
+
+ if ((bufEnd == bufStart || source.charAt(bufEnd - 1) == CHAR_NEW_LINE)
+ && mLineCount < mMaximumVisibleLineCount) {
+ final MeasuredParagraph measuredPara =
+ MeasuredParagraph.buildForBidi(source, bufEnd, bufEnd, textDir, null);
+ paint.getFontMetricsInt(fm);
+ v = out(source,
+ bufEnd, bufEnd, fm.ascent, fm.descent,
+ fm.top, fm.bottom,
+ v,
+ spacingmult, spacingadd, null,
+ null, fm, 0,
+ needMultiply, measuredPara, bufEnd,
+ includepad, trackpad, addLastLineSpacing, null,
+ bufStart, ellipsize,
+ ellipsizedWidth, 0, paint, false);
}
}
@@ -884,7 +855,7 @@ public class StaticLayout extends Layout {
final LineHeightSpan[] chooseHt, final int[] chooseHtv, final Paint.FontMetricsInt fm,
final int flags, final boolean needMultiply, @NonNull final MeasuredParagraph measured,
final int bufEnd, final boolean includePad, final boolean trackPad,
- final boolean addLastLineLineSpacing, final char[] chs, final float[] widths,
+ final boolean addLastLineLineSpacing, final char[] chs,
final int widthStart, final TextUtils.TruncateAt ellipsize, final float ellipsisWidth,
final float textWidth, final TextPaint paint, final boolean moreChars) {
final int j = mLineCount;
@@ -942,7 +913,7 @@ public class StaticLayout extends Layout {
(!firstLine && (currentLineIsTheLastVisibleOne || !moreChars) &&
ellipsize == TextUtils.TruncateAt.END);
if (doEllipsis) {
- calculateEllipsis(start, end, widths, widthStart,
+ calculateEllipsis(start, end, measured, widthStart,
ellipsisWidth, ellipsize, j,
textWidth, paint, forceEllipsis);
}
@@ -1026,7 +997,7 @@ public class StaticLayout extends Layout {
}
private void calculateEllipsis(int lineStart, int lineEnd,
- float[] widths, int widthStart,
+ MeasuredParagraph measured, int widthStart,
float avail, TextUtils.TruncateAt where,
int line, float textWidth, TextPaint paint,
boolean forceEllipsis) {
@@ -1050,9 +1021,10 @@ public class StaticLayout extends Layout {
int i;
for (i = len; i > 0; i--) {
- float w = widths[i - 1 + lineStart - widthStart];
+ float w = measured.getCharWidthAt(i - 1 + lineStart - widthStart);
if (w + sum + ellipsisWidth > avail) {
- while (i < len && widths[i + lineStart - widthStart] == 0.0f) {
+ while (i < len
+ && measured.getCharWidthAt(i + lineStart - widthStart) == 0.0f) {
i++;
}
break;
@@ -1074,7 +1046,7 @@ public class StaticLayout extends Layout {
int i;
for (i = 0; i < len; i++) {
- float w = widths[i + lineStart - widthStart];
+ float w = measured.getCharWidthAt(i + lineStart - widthStart);
if (w + sum + ellipsisWidth > avail) {
break;
@@ -1097,10 +1069,12 @@ public class StaticLayout extends Layout {
float ravail = (avail - ellipsisWidth) / 2;
for (right = len; right > 0; right--) {
- float w = widths[right - 1 + lineStart - widthStart];
+ float w = measured.getCharWidthAt(right - 1 + lineStart - widthStart);
if (w + rsum > ravail) {
- while (right < len && widths[right + lineStart - widthStart] == 0.0f) {
+ while (right < len
+ && measured.getCharWidthAt(right + lineStart - widthStart)
+ == 0.0f) {
right++;
}
break;
@@ -1110,7 +1084,7 @@ public class StaticLayout extends Layout {
float lavail = avail - ellipsisWidth - rsum;
for (left = 0; left < right; left++) {
- float w = widths[left + lineStart - widthStart];
+ float w = measured.getCharWidthAt(left + lineStart - widthStart);
if (w + lsum > lavail) {
break;
@@ -1306,47 +1280,6 @@ public class StaticLayout extends Layout {
? mMaxLineHeight : super.getHeight();
}
- @FastNative
- private static native long nInit(
- @BreakStrategy int breakStrategy,
- @HyphenationFrequency int hyphenationFrequency,
- boolean isJustified,
- @Nullable int[] indents);
-
- @CriticalNative
- private static native void nFinish(long nativePtr);
-
- // populates LineBreaks and returns the number of breaks found
- //
- // the arrays inside the LineBreaks objects are passed in as well
- // to reduce the number of JNI calls in the common case where the
- // arrays do not have to be resized
- // The individual character widths will be returned in charWidths. The length of charWidths must
- // be at least the length of the text.
- private static native int nComputeLineBreaks(
- /* non zero */ long nativePtr,
-
- // Inputs
- @NonNull char[] text,
- /* Non Zero */ long measuredTextPtr,
- @IntRange(from = 0) int length,
- @FloatRange(from = 0.0f) float firstWidth,
- @IntRange(from = 0) int firstWidthLineCount,
- @FloatRange(from = 0.0f) float restWidth,
- @Nullable int[] variableTabStops,
- int defaultTabStop,
- @IntRange(from = 0) int indentsOffset,
-
- // Outputs
- @NonNull LineBreaks recycle,
- @IntRange(from = 0) int recycleLength,
- @NonNull int[] recycleBreaks,
- @NonNull float[] recycleWidths,
- @NonNull float[] recycleAscents,
- @NonNull float[] recycleDescents,
- @NonNull int[] recycleFlags,
- @NonNull float[] charWidths);
-
private int mLineCount;
private int mTopPadding, mBottomPadding;
private int mColumns;
@@ -1396,8 +1329,7 @@ public class StaticLayout extends Layout {
private static final int DEFAULT_MAX_LINE_HEIGHT = -1;
- // This is used to return three arrays from a single JNI call when
- // performing line breaking
+ // Unused, here because of gray list private API accesses.
/*package*/ static class LineBreaks {
private static final int INITIAL_SIZE = 16;
public int[] breaks = new int[INITIAL_SIZE];
diff --git a/core/java/android/view/RecordingCanvas.java b/core/java/android/view/RecordingCanvas.java
index 18cc10f5b2f2..74fa9e875076 100644
--- a/core/java/android/view/RecordingCanvas.java
+++ b/core/java/android/view/RecordingCanvas.java
@@ -515,7 +515,7 @@ public class RecordingCanvas extends Canvas {
contextStart - paraStart,
contextEnd - contextStart,
x, y, isRtl, paint.getNativeInstance(),
- mp.getNativePtr());
+ mp.getNativeMeasuredParagraph().getNativePtr());
return;
}
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 240f3c009841..97a2d795edaa 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -3097,13 +3097,27 @@ public final class ViewRootImpl implements ViewParent,
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
boolean usingAsyncReport = false;
- if (mReportNextDraw && mAttachInfo.mThreadedRenderer != null
- && mAttachInfo.mThreadedRenderer.isEnabled()) {
- usingAsyncReport = true;
- mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> {
- // TODO: Use the frame number
- pendingDrawFinished();
- });
+ if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) {
+ ArrayList<Runnable> commitCallbacks = mAttachInfo.mTreeObserver
+ .captureFrameCommitCallbacks();
+ if (mReportNextDraw) {
+ usingAsyncReport = true;
+ mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> {
+ // TODO: Use the frame number
+ pendingDrawFinished();
+ if (commitCallbacks != null) {
+ for (int i = 0; i < commitCallbacks.size(); i++) {
+ commitCallbacks.get(i).run();
+ }
+ }
+ });
+ } else if (commitCallbacks != null && commitCallbacks.size() > 0) {
+ mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> {
+ for (int i = 0; i < commitCallbacks.size(); i++) {
+ commitCallbacks.get(i).run();
+ }
+ });
+ }
}
try {
diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java
index 0973d0aa9b90..efc18072e547 100644
--- a/core/java/android/view/ViewTreeObserver.java
+++ b/core/java/android/view/ViewTreeObserver.java
@@ -16,6 +16,9 @@
package android.view;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.Region;
@@ -56,6 +59,9 @@ public final class ViewTreeObserver {
private ArrayList<OnDrawListener> mOnDrawListeners;
private static boolean sIllegalOnDrawModificationIsFatal;
+ // These listeners are one-shot
+ private ArrayList<Runnable> mOnFrameCommitListeners;
+
/** Remains false until #dispatchOnWindowShown() is called. If a listener registers after
* that the listener will be immediately called. */
private boolean mWindowShown;
@@ -393,6 +399,14 @@ public final class ViewTreeObserver {
}
}
+ if (observer.mOnFrameCommitListeners != null) {
+ if (mOnFrameCommitListeners != null) {
+ mOnFrameCommitListeners.addAll(observer.captureFrameCommitCallbacks());
+ } else {
+ mOnFrameCommitListeners = observer.captureFrameCommitCallbacks();
+ }
+ }
+
if (observer.mOnTouchModeChangeListeners != null) {
if (mOnTouchModeChangeListeners != null) {
mOnTouchModeChangeListeners.addAll(observer.mOnTouchModeChangeListeners);
@@ -713,6 +727,49 @@ public final class ViewTreeObserver {
}
/**
+ * Adds a frame commit callback. This callback will be invoked when the current rendering
+ * content has been rendered into a frame and submitted to the swap chain. The frame may
+ * not currently be visible on the display when this is invoked, but it has been submitted.
+ * This callback is useful in combination with {@link PixelCopy} to capture the current
+ * rendered content of the UI reliably.
+ *
+ * Note: Only works with hardware rendering. Does nothing otherwise.
+ *
+ * @param callback The callback to invoke when the frame is committed.
+ */
+ @TestApi
+ public void registerFrameCommitCallback(@NonNull Runnable callback) {
+ checkIsAlive();
+ if (mOnFrameCommitListeners == null) {
+ mOnFrameCommitListeners = new ArrayList<>();
+ }
+ mOnFrameCommitListeners.add(callback);
+ }
+
+ @Nullable ArrayList<Runnable> captureFrameCommitCallbacks() {
+ ArrayList<Runnable> ret = mOnFrameCommitListeners;
+ mOnFrameCommitListeners = null;
+ return ret;
+ }
+
+ /**
+ * Attempts to remove the given callback from the list of pending frame complete callbacks.
+ *
+ * @param callback The callback to remove
+ * @return Whether or not the callback was removed. If this returns true the callback will
+ * not be invoked. If false is returned then the callback was either never added
+ * or may already be pending execution and was unable to be removed
+ */
+ @TestApi
+ public boolean unregisterFrameCommitCallback(@NonNull Runnable callback) {
+ checkIsAlive();
+ if (mOnFrameCommitListeners == null) {
+ return false;
+ }
+ return mOnFrameCommitListeners.remove(callback);
+ }
+
+ /**
* Register a callback to be invoked when a view has been scrolled.
*
* @param listener The callback to add
diff --git a/core/java/android/view/accessibility/AccessibilityCache.java b/core/java/android/view/accessibility/AccessibilityCache.java
index 0e1e379d610a..b4d9c53aedb8 100644
--- a/core/java/android/view/accessibility/AccessibilityCache.java
+++ b/core/java/android/view/accessibility/AccessibilityCache.java
@@ -336,7 +336,13 @@ public class AccessibilityCache {
AccessibilityNodeInfo clone = AccessibilityNodeInfo.obtain(info);
nodes.put(sourceId, clone);
if (clone.isAccessibilityFocused()) {
+ if (mAccessibilityFocus != AccessibilityNodeInfo.UNDEFINED_ITEM_ID
+ && mAccessibilityFocus != sourceId) {
+ refreshCachedNodeLocked(windowId, mAccessibilityFocus);
+ }
mAccessibilityFocus = sourceId;
+ } else if (mAccessibilityFocus == sourceId) {
+ mAccessibilityFocus = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
}
if (clone.isFocused()) {
mInputFocus = sourceId;
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 41daf9e11370..b5c736480215 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -34,6 +34,7 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.Rect;
import android.metrics.LogMaker;
+import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcelable;
@@ -202,10 +203,17 @@ public final class AutofillManager {
/** @hide */ public static final int ACTION_VIEW_EXITED = 3;
/** @hide */ public static final int ACTION_VALUE_CHANGED = 4;
-
+ /** @hide */ public static final int NO_LOGGING = 0;
/** @hide */ public static final int FLAG_ADD_CLIENT_ENABLED = 0x1;
/** @hide */ public static final int FLAG_ADD_CLIENT_DEBUG = 0x2;
/** @hide */ public static final int FLAG_ADD_CLIENT_VERBOSE = 0x4;
+ /** @hide */
+ public static final int DEFAULT_LOGGING_LEVEL = Build.IS_DEBUGGABLE
+ ? AutofillManager.FLAG_ADD_CLIENT_DEBUG
+ : AutofillManager.NO_LOGGING;
+
+ /** @hide */
+ public static final int DEFAULT_MAX_PARTITIONS_SIZE = 10;
/** Which bits in an authentication id are used for the dataset id */
private static final int AUTHENTICATION_ID_DATASET_ID_MASK = 0xFFFF;
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index f686b6645655..bdd7a0900213 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -31,18 +31,25 @@ import java.lang.annotation.RetentionPolicy;
public class WebViewClient {
/**
- * Give the host application a chance to take over the control when a new
- * url is about to be loaded in the current WebView. If WebViewClient is not
- * provided, by default WebView will ask Activity Manager to choose the
- * proper handler for the url. If WebViewClient is provided, return {@code true}
- * means the host application handles the url, while return {@code false} means the
- * current WebView handles the url.
- * This method is not called for requests using the POST "method".
+ * Give the host application a chance to take control when a URL is about to be loaded in the
+ * current WebView. If a WebViewClient is not provided, by default WebView will ask Activity
+ * Manager to choose the proper handler for the URL. If a WebViewClient is provided, returning
+ * {@code true} causes the current WebView to abort loading the URL, while returning
+ * {@code false} causes the WebView to continue loading the URL as usual.
+ *
+ * <p class="note"><b>Note:</b> Do not call {@link WebView#loadUrl(String)} with the same
+ * URL and then return {@code true}. This unnecessarily cancels the current load and starts a
+ * new load with the same URL. The correct way to continue loading a given URL is to simply
+ * return {@code false}, without calling {@link WebView#loadUrl(String)}.
+ *
+ * <p class="note"><b>Note:</b> This method is not called for POST requests.
+ *
+ * <p class="note"><b>Note:</b> This method may be called for subframes and with non-HTTP(S)
+ * schemes; calling {@link WebView#loadUrl(String)} with such a URL will fail.
*
* @param view The WebView that is initiating the callback.
- * @param url The url to be loaded.
- * @return {@code true} if the host application wants to leave the current WebView
- * and handle the url itself, otherwise return {@code false}.
+ * @param url The URL to be loaded.
+ * @return {@code true} to cancel the current load, otherwise return {@code false}.
* @deprecated Use {@link #shouldOverrideUrlLoading(WebView, WebResourceRequest)
* shouldOverrideUrlLoading(WebView, WebResourceRequest)} instead.
*/
@@ -52,26 +59,25 @@ public class WebViewClient {
}
/**
- * Give the host application a chance to take over the control when a new
- * url is about to be loaded in the current WebView. If WebViewClient is not
- * provided, by default WebView will ask Activity Manager to choose the
- * proper handler for the url. If WebViewClient is provided, return {@code true}
- * means the host application handles the url, while return {@code false} means the
- * current WebView handles the url.
- *
- * <p>Notes:
- * <ul>
- * <li>This method is not called for requests using the POST &quot;method&quot;.</li>
- * <li>This method is also called for subframes with non-http schemes, thus it is
- * strongly disadvised to unconditionally call {@link WebView#loadUrl(String)}
- * with the request's url from inside the method and then return {@code true},
- * as this will make WebView to attempt loading a non-http url, and thus fail.</li>
- * </ul>
+ * Give the host application a chance to take control when a URL is about to be loaded in the
+ * current WebView. If a WebViewClient is not provided, by default WebView will ask Activity
+ * Manager to choose the proper handler for the URL. If a WebViewClient is provided, returning
+ * {@code true} causes the current WebView to abort loading the URL, while returning
+ * {@code false} causes the WebView to continue loading the URL as usual.
+ *
+ * <p class="note"><b>Note:</b> Do not call {@link WebView#loadUrl(String)} with the request's
+ * URL and then return {@code true}. This unnecessarily cancels the current load and starts a
+ * new load with the same URL. The correct way to continue loading a given URL is to simply
+ * return {@code false}, without calling {@link WebView#loadUrl(String)}.
+ *
+ * <p class="note"><b>Note:</b> This method is not called for POST requests.
+ *
+ * <p class="note"><b>Note:</b> This method may be called for subframes and with non-HTTP(S)
+ * schemes; calling {@link WebView#loadUrl(String)} with such a URL will fail.
*
* @param view The WebView that is initiating the callback.
* @param request Object containing the details of the request.
- * @return {@code true} if the host application wants to leave the current WebView
- * and handle the url itself, otherwise return {@code false}.
+ * @return {@code true} to cancel the current load, otherwise return {@code false}.
*/
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
return shouldOverrideUrlLoading(view, request.getUrl().toString());
diff --git a/core/java/com/android/internal/app/procstats/AssociationState.java b/core/java/com/android/internal/app/procstats/AssociationState.java
index e5d6556e1218..f63c43f6614f 100644
--- a/core/java/com/android/internal/app/procstats/AssociationState.java
+++ b/core/java/com/android/internal/app/procstats/AssociationState.java
@@ -36,7 +36,6 @@ public final class AssociationState {
private final ProcessStats.PackageState mPackageState;
private final String mProcessName;
private final String mName;
- private final DurationsTable mDurations;
public final class SourceState {
final SourceKey mKey;
@@ -49,8 +48,10 @@ public final class AssociationState {
long mDuration;
long mTrackingUptime;
int mActiveCount;
+ int mActiveProcState = ProcessStats.STATE_NOTHING;
long mActiveStartUptime;
long mActiveDuration;
+ DurationsTable mDurations;
SourceState(SourceKey key) {
mKey = key;
@@ -77,13 +78,15 @@ public final class AssociationState {
mProcState = procState;
}
if (procState < ProcessStats.STATE_HOME) {
+ // If the proc state has become better than cached, then we want to
+ // start tracking it to count when it is actually active. If it drops
+ // down to cached, we will clean it up when we later evaluate all currently
+ // tracked associations in ProcessStats.updateTrackingAssociationsLocked().
if (!mInTrackingList) {
mInTrackingList = true;
mTrackingUptime = now;
mProcessStats.mTrackingAssociations.add(this);
}
- } else {
- stopTracking(now);
}
}
@@ -102,6 +105,22 @@ public final class AssociationState {
mActiveStartUptime = now;
mActiveCount++;
}
+ if (mActiveProcState != mProcState) {
+ if (mActiveProcState != ProcessStats.STATE_NOTHING) {
+ // Currently active proc state changed, need to store the duration
+ // so far and switch tracking to the new proc state.
+ final long duration = mActiveDuration + now - mActiveStartUptime;
+ if (duration != 0) {
+ if (mDurations == null) {
+ makeDurations();
+ }
+ mDurations.addDuration(mActiveProcState, duration);
+ mActiveDuration = 0;
+ }
+ mActiveStartUptime = now;
+ }
+ mActiveProcState = mProcState;
+ }
} else {
Slog.wtf(TAG, "startActive while not tracking: " + this);
}
@@ -112,15 +131,25 @@ public final class AssociationState {
if (!mInTrackingList) {
Slog.wtf(TAG, "stopActive while not tracking: " + this);
}
- mActiveDuration += now - mActiveStartUptime;
+ final long duration = mActiveDuration + now - mActiveStartUptime;
+ if (mDurations != null) {
+ mDurations.addDuration(mActiveProcState, duration);
+ } else {
+ mActiveDuration = duration;
+ }
mActiveStartUptime = 0;
}
}
+ void makeDurations() {
+ mDurations = new DurationsTable(mProcessStats.mTableData);
+ }
+
void stopTracking(long now) {
stopActive(now);
if (mInTrackingList) {
mInTrackingList = false;
+ mProcState = ProcessStats.STATE_NOTHING;
// Do a manual search for where to remove, since these objects will typically
// be towards the end of the array.
final ArrayList<SourceState> list = mProcessStats.mTrackingAssociations;
@@ -207,7 +236,6 @@ public final class AssociationState {
mPackageState = packageState;
mName = name;
mProcessName = processName;
- mDurations = new DurationsTable(processStats.mTableData);
mProc = proc;
}
@@ -254,7 +282,6 @@ public final class AssociationState {
}
public void add(AssociationState other) {
- mDurations.addDurations(other.mDurations);
for (int isrc = other.mSources.size() - 1; isrc >= 0; isrc--) {
final SourceKey key = other.mSources.keyAt(isrc);
final SourceState otherSrc = other.mSources.valueAt(isrc);
@@ -266,7 +293,48 @@ public final class AssociationState {
mySrc.mCount += otherSrc.mCount;
mySrc.mDuration += otherSrc.mDuration;
mySrc.mActiveCount += otherSrc.mActiveCount;
- mySrc.mActiveDuration += otherSrc.mActiveDuration;
+ if (otherSrc.mActiveDuration != 0 || otherSrc.mDurations != null) {
+ // Only need to do anything if the other one has some duration data.
+ if (mySrc.mDurations != null) {
+ // If the target already has multiple durations, just add in whatever
+ // we have in the other.
+ if (otherSrc.mDurations != null) {
+ mySrc.mDurations.addDurations(otherSrc.mDurations);
+ } else {
+ mySrc.mDurations.addDuration(otherSrc.mActiveProcState,
+ otherSrc.mActiveDuration);
+ }
+ } else if (otherSrc.mDurations != null) {
+ // The other one has multiple durations, but we don't. Expand to
+ // multiple durations and copy over.
+ mySrc.makeDurations();
+ mySrc.mDurations.addDurations(otherSrc.mDurations);
+ if (mySrc.mActiveDuration != 0) {
+ mySrc.mDurations.addDuration(mySrc.mActiveProcState, mySrc.mActiveDuration);
+ mySrc.mActiveDuration = 0;
+ mySrc.mActiveProcState = ProcessStats.STATE_NOTHING;
+ }
+ } else if (mySrc.mActiveDuration != 0) {
+ // Both have a single inline duration... we can either add them together,
+ // or need to expand to multiple durations.
+ if (mySrc.mActiveProcState == otherSrc.mActiveProcState) {
+ mySrc.mDuration += otherSrc.mDuration;
+ } else {
+ // The two have durations with different proc states, need to turn
+ // in to multiple durations.
+ mySrc.makeDurations();
+ mySrc.mDurations.addDuration(mySrc.mActiveProcState, mySrc.mActiveDuration);
+ mySrc.mDurations.addDuration(otherSrc.mActiveProcState,
+ otherSrc.mActiveDuration);
+ mySrc.mActiveDuration = 0;
+ mySrc.mActiveProcState = ProcessStats.STATE_NOTHING;
+ }
+ } else {
+ // The other one has a duration, and we know the target doesn't. Copy over.
+ mySrc.mActiveProcState = otherSrc.mActiveProcState;
+ mySrc.mActiveDuration = otherSrc.mActiveDuration;
+ }
+ }
}
}
@@ -275,7 +343,6 @@ public final class AssociationState {
}
public void resetSafely(long now) {
- mDurations.resetTable();
if (!isInUse()) {
mSources.clear();
} else {
@@ -293,6 +360,7 @@ public final class AssociationState {
src.mActiveCount = 0;
}
src.mActiveDuration = 0;
+ src.mDurations = null;
} else {
mSources.removeAt(isrc);
}
@@ -301,7 +369,6 @@ public final class AssociationState {
}
public void writeToParcel(ProcessStats stats, Parcel out, long nowUptime) {
- mDurations.writeToParcel(out);
final int NSRC = mSources.size();
out.writeInt(NSRC);
for (int isrc = 0; isrc < NSRC; isrc++) {
@@ -312,7 +379,14 @@ public final class AssociationState {
out.writeInt(src.mCount);
out.writeLong(src.mDuration);
out.writeInt(src.mActiveCount);
- out.writeLong(src.mActiveDuration);
+ if (src.mDurations != null) {
+ out.writeInt(1);
+ src.mDurations.writeToParcel(out);
+ } else {
+ out.writeInt(0);
+ out.writeInt(src.mActiveProcState);
+ out.writeLong(src.mActiveDuration);
+ }
}
}
@@ -321,9 +395,6 @@ public final class AssociationState {
* caused it to fail.
*/
public String readFromParcel(ProcessStats stats, Parcel in, int parcelVersion) {
- if (!mDurations.readFromParcel(in)) {
- return "Duration table corrupt";
- }
final int NSRC = in.readInt();
if (NSRC < 0 || NSRC > 100000) {
return "Association with bad src count: " + NSRC;
@@ -336,7 +407,15 @@ public final class AssociationState {
src.mCount = in.readInt();
src.mDuration = in.readLong();
src.mActiveCount = in.readInt();
- src.mActiveDuration = in.readLong();
+ if (in.readInt() != 0) {
+ src.makeDurations();
+ if (!src.mDurations.readFromParcel(in)) {
+ return "Duration table corrupt: " + key + " <- " + src;
+ }
+ } else {
+ src.mActiveProcState = in.readInt();
+ src.mActiveDuration = in.readLong();
+ }
mSources.put(key, src);
}
return null;
@@ -351,7 +430,12 @@ public final class AssociationState {
src.mStartUptime = nowUptime;
}
if (src.mActiveStartUptime > 0) {
- src.mActiveDuration += nowUptime - src.mActiveStartUptime;
+ final long duration = src.mActiveDuration + nowUptime - src.mActiveStartUptime;
+ if (src.mDurations != null) {
+ src.mDurations.addDuration(src.mActiveProcState, duration);
+ } else {
+ src.mActiveDuration = duration;
+ }
src.mActiveStartUptime = nowUptime;
}
}
@@ -359,7 +443,7 @@ public final class AssociationState {
}
public void dumpStats(PrintWriter pw, String prefix, String prefixInner, String headerPrefix,
- long now, long totalTime, boolean dumpSummary, boolean dumpAll) {
+ long now, long totalTime, boolean dumpDetails, boolean dumpAll) {
if (dumpAll) {
pw.print(prefix);
pw.print("mNumActive=");
@@ -376,18 +460,18 @@ public final class AssociationState {
UserHandle.formatUid(pw, key.mUid);
pw.println(":");
pw.print(prefixInner);
- pw.print(" Count ");
+ pw.print(" Total count ");
pw.print(src.mCount);
long duration = src.mDuration;
if (src.mNesting > 0) {
duration += now - src.mStartUptime;
}
if (dumpAll) {
- pw.print(" / Duration ");
+ pw.print(": Duration ");
TimeUtils.formatDuration(duration, pw);
pw.print(" / ");
} else {
- pw.print(" / time ");
+ pw.print(": time ");
}
DumpUtils.printPercent(pw, (double)duration/(double)totalTime);
if (src.mNesting > 0) {
@@ -401,30 +485,120 @@ public final class AssociationState {
pw.print(")");
}
pw.println();
- if (src.mActiveCount > 0) {
+ if (src.mActiveCount > 0 || src.mDurations != null || src.mActiveDuration != 0
+ || src.mActiveStartUptime != 0) {
pw.print(prefixInner);
pw.print(" Active count ");
pw.print(src.mActiveCount);
- duration = src.mActiveDuration;
- if (src.mActiveStartUptime > 0) {
- duration += now - src.mActiveStartUptime;
- }
- if (dumpAll) {
- pw.print(" / Duration ");
- TimeUtils.formatDuration(duration, pw);
- pw.print(" / ");
+ if (dumpDetails) {
+ if (dumpAll) {
+ pw.print(src.mDurations != null ? " (multi-field)" : " (inline)");
+ }
+ pw.println(":");
+ dumpTime(pw, prefixInner, src, totalTime, now, dumpDetails, dumpAll);
} else {
- pw.print(" / time ");
+ pw.print(": ");
+ dumpActiveDurationSummary(pw, src, totalTime, now, dumpAll);
+ pw.println();
}
- DumpUtils.printPercent(pw, (double)duration/(double)totalTime);
- if (src.mActiveStartUptime > 0) {
- pw.print(" (running)");
+ }
+ if (dumpAll) {
+ if (src.mInTrackingList) {
+ pw.print(prefixInner);
+ pw.print(" mInTrackingList=");
+ pw.println(src.mInTrackingList);
+ }
+ if (src.mProcState != ProcessStats.STATE_NOTHING) {
+ pw.print(prefixInner);
+ pw.print(" mProcState=");
+ pw.print(DumpUtils.STATE_NAMES[src.mProcState]);
+ pw.print(" mProcStateSeq=");
+ pw.println(src.mProcStateSeq);
}
- pw.println();
}
}
}
+ void dumpActiveDurationSummary(PrintWriter pw, final SourceState src, long totalTime,
+ long now, boolean dumpAll) {
+ long duration = dumpTime(null, null, src, totalTime, now, false, false);
+ final boolean isRunning = duration < 0;
+ if (isRunning) {
+ duration = -duration;
+ }
+ if (dumpAll) {
+ pw.print("Duration ");
+ TimeUtils.formatDuration(duration, pw);
+ pw.print(" / ");
+ } else {
+ pw.print("time ");
+ }
+ DumpUtils.printPercent(pw, (double) duration / (double) totalTime);
+ if (src.mActiveStartUptime > 0) {
+ pw.print(" (running)");
+ }
+ pw.println();
+ }
+
+ long dumpTime(PrintWriter pw, String prefix, final SourceState src, long overallTime, long now,
+ boolean dumpDetails, boolean dumpAll) {
+ long totalTime = 0;
+ boolean isRunning = false;
+ for (int iprocstate = 0; iprocstate < ProcessStats.STATE_COUNT; iprocstate++) {
+ long time;
+ if (src.mDurations != null) {
+ time = src.mDurations.getValueForId((byte)iprocstate);
+ } else {
+ time = src.mActiveProcState == iprocstate ? src.mDuration : 0;
+ }
+ final String running;
+ if (src.mActiveStartUptime != 0 && src.mActiveProcState == iprocstate) {
+ running = " (running)";
+ isRunning = true;
+ time += now - src.mActiveStartUptime;
+ } else {
+ running = null;
+ }
+ if (time != 0) {
+ if (pw != null) {
+ pw.print(prefix);
+ pw.print(" ");
+ pw.print(DumpUtils.STATE_LABELS[iprocstate]);
+ pw.print(": ");
+ if (dumpAll) {
+ pw.print("Duration ");
+ TimeUtils.formatDuration(time, pw);
+ pw.print(" / ");
+ } else {
+ pw.print("time ");
+ }
+ DumpUtils.printPercent(pw, (double) time / (double) overallTime);
+ if (running != null) {
+ pw.print(running);
+ }
+ pw.println();
+ }
+ totalTime += time;
+ }
+ }
+ if (totalTime != 0 && pw != null) {
+ pw.print(prefix);
+ pw.print(" ");
+ pw.print(DumpUtils.STATE_LABEL_TOTAL);
+ pw.print(": ");
+ if (dumpAll) {
+ pw.print("Duration ");
+ TimeUtils.formatDuration(totalTime, pw);
+ pw.print(" / ");
+ } else {
+ pw.print("time ");
+ }
+ DumpUtils.printPercent(pw, (double) totalTime / (double) overallTime);
+ pw.println();
+ }
+ return isRunning ? -totalTime : totalTime;
+ }
+
public void dumpTimesCheckin(PrintWriter pw, String pkgName, int uid, long vers,
String associationName, long now) {
final int NSRC = mSources.size();
@@ -454,12 +628,30 @@ public final class AssociationState {
pw.print(duration);
pw.print(",");
pw.print(src.mActiveCount);
- duration = src.mActiveDuration;
- if (src.mActiveStartUptime > 0) {
- duration += now - src.mActiveStartUptime;
+ final long timeNow = src.mActiveStartUptime != 0 ? (now-src.mActiveStartUptime) : 0;
+ if (src.mDurations != null) {
+ final int N = src.mDurations.getKeyCount();
+ for (int i=0; i<N; i++) {
+ final int dkey = src.mDurations.getKeyAt(i);
+ duration = src.mDurations.getValue(dkey);
+ if (dkey == src.mActiveProcState) {
+ duration += timeNow;
+ }
+ final int procState = SparseMappingTable.getIdFromKey(dkey);
+ pw.print(",");
+ DumpUtils.printArrayEntry(pw, DumpUtils.STATE_TAGS, procState, 1);
+ pw.print(':');
+ pw.print(duration);
+ }
+ } else {
+ duration = src.mActiveDuration + timeNow;
+ if (duration != 0) {
+ pw.print(",");
+ DumpUtils.printArrayEntry(pw, DumpUtils.STATE_TAGS, src.mActiveProcState, 1);
+ pw.print(':');
+ pw.print(duration);
+ }
}
- pw.print(",");
- pw.print(duration);
pw.println();
}
}
diff --git a/core/java/com/android/internal/app/procstats/DumpUtils.java b/core/java/com/android/internal/app/procstats/DumpUtils.java
index 06b6552ca59f..e6073e58b53c 100644
--- a/core/java/com/android/internal/app/procstats/DumpUtils.java
+++ b/core/java/com/android/internal/app/procstats/DumpUtils.java
@@ -48,6 +48,9 @@ import java.util.Objects;
*/
public final class DumpUtils {
public static final String[] STATE_NAMES;
+ public static final String[] STATE_LABELS;
+ public static final String STATE_LABEL_TOTAL;
+ public static final String STATE_LABEL_CACHED;
public static final String[] STATE_NAMES_CSV;
static final String[] STATE_TAGS;
static final int[] STATE_PROTO_ENUMS;
@@ -55,52 +58,70 @@ public final class DumpUtils {
// Make the mapping easy to update.
static {
STATE_NAMES = new String[STATE_COUNT];
- STATE_NAMES[STATE_PERSISTENT] = "Persist";
- STATE_NAMES[STATE_TOP] = "Top";
- STATE_NAMES[STATE_IMPORTANT_FOREGROUND] = "ImpFg";
- STATE_NAMES[STATE_IMPORTANT_BACKGROUND] = "ImpBg";
- STATE_NAMES[STATE_BACKUP] = "Backup";
- STATE_NAMES[STATE_SERVICE] = "Service";
- STATE_NAMES[STATE_SERVICE_RESTARTING] = "ServRst";
- STATE_NAMES[STATE_RECEIVER] = "Receivr";
- STATE_NAMES[STATE_HEAVY_WEIGHT] = "HeavyWt";
- STATE_NAMES[STATE_HOME] = "Home";
- STATE_NAMES[STATE_LAST_ACTIVITY] = "LastAct";
- STATE_NAMES[STATE_CACHED_ACTIVITY] = "CchAct";
- STATE_NAMES[STATE_CACHED_ACTIVITY_CLIENT] = "CchCAct";
- STATE_NAMES[STATE_CACHED_EMPTY] = "CchEmty";
+ STATE_NAMES[STATE_PERSISTENT] = "Persist";
+ STATE_NAMES[STATE_TOP] = "Top";
+ STATE_NAMES[STATE_IMPORTANT_FOREGROUND] = "ImpFg";
+ STATE_NAMES[STATE_IMPORTANT_BACKGROUND] = "ImpBg";
+ STATE_NAMES[STATE_BACKUP] = "Backup";
+ STATE_NAMES[STATE_SERVICE] = "Service";
+ STATE_NAMES[STATE_SERVICE_RESTARTING] = "ServRst";
+ STATE_NAMES[STATE_RECEIVER] = "Receivr";
+ STATE_NAMES[STATE_HEAVY_WEIGHT] = "HeavyWt";
+ STATE_NAMES[STATE_HOME] = "Home";
+ STATE_NAMES[STATE_LAST_ACTIVITY] = "LastAct";
+ STATE_NAMES[STATE_CACHED_ACTIVITY] = "CchAct";
+ STATE_NAMES[STATE_CACHED_ACTIVITY_CLIENT] = "CchCAct";
+ STATE_NAMES[STATE_CACHED_EMPTY] = "CchEmty";
+
+ STATE_LABELS = new String[STATE_COUNT];
+ STATE_LABELS[STATE_PERSISTENT] = "Persistent";
+ STATE_LABELS[STATE_TOP] = " Top";
+ STATE_LABELS[STATE_IMPORTANT_FOREGROUND] = " Imp Fg";
+ STATE_LABELS[STATE_IMPORTANT_BACKGROUND] = " Imp Bg";
+ STATE_LABELS[STATE_BACKUP] = " Backup";
+ STATE_LABELS[STATE_SERVICE] = " Service";
+ STATE_LABELS[STATE_SERVICE_RESTARTING] = "Service Rs";
+ STATE_LABELS[STATE_RECEIVER] = " Receiver";
+ STATE_LABELS[STATE_HEAVY_WEIGHT] = " Heavy Wgt";
+ STATE_LABELS[STATE_HOME] = " (Home)";
+ STATE_LABELS[STATE_LAST_ACTIVITY] = "(Last Act)";
+ STATE_LABELS[STATE_CACHED_ACTIVITY] = " (Cch Act)";
+ STATE_LABELS[STATE_CACHED_ACTIVITY_CLIENT] = "(Cch CAct)";
+ STATE_LABELS[STATE_CACHED_EMPTY] = "(Cch Emty)";
+ STATE_LABEL_CACHED = " (Cached)";
+ STATE_LABEL_TOTAL = " TOTAL";
STATE_NAMES_CSV = new String[STATE_COUNT];
- STATE_NAMES_CSV[STATE_PERSISTENT] = "pers";
- STATE_NAMES_CSV[STATE_TOP] = "top";
- STATE_NAMES_CSV[STATE_IMPORTANT_FOREGROUND] = "impfg";
- STATE_NAMES_CSV[STATE_IMPORTANT_BACKGROUND] = "impbg";
- STATE_NAMES_CSV[STATE_BACKUP] = "backup";
- STATE_NAMES_CSV[STATE_SERVICE] = "service";
- STATE_NAMES_CSV[STATE_SERVICE_RESTARTING] = "service-rs";
- STATE_NAMES_CSV[STATE_RECEIVER] = "receiver";
- STATE_NAMES_CSV[STATE_HEAVY_WEIGHT] = "heavy";
- STATE_NAMES_CSV[STATE_HOME] = "home";
- STATE_NAMES_CSV[STATE_LAST_ACTIVITY] = "lastact";
- STATE_NAMES_CSV[STATE_CACHED_ACTIVITY] = "cch-activity";
- STATE_NAMES_CSV[STATE_CACHED_ACTIVITY_CLIENT] = "cch-aclient";
- STATE_NAMES_CSV[STATE_CACHED_EMPTY] = "cch-empty";
+ STATE_NAMES_CSV[STATE_PERSISTENT] = "pers";
+ STATE_NAMES_CSV[STATE_TOP] = "top";
+ STATE_NAMES_CSV[STATE_IMPORTANT_FOREGROUND] = "impfg";
+ STATE_NAMES_CSV[STATE_IMPORTANT_BACKGROUND] = "impbg";
+ STATE_NAMES_CSV[STATE_BACKUP] = "backup";
+ STATE_NAMES_CSV[STATE_SERVICE] = "service";
+ STATE_NAMES_CSV[STATE_SERVICE_RESTARTING] = "service-rs";
+ STATE_NAMES_CSV[STATE_RECEIVER] = "receiver";
+ STATE_NAMES_CSV[STATE_HEAVY_WEIGHT] = "heavy";
+ STATE_NAMES_CSV[STATE_HOME] = "home";
+ STATE_NAMES_CSV[STATE_LAST_ACTIVITY] = "lastact";
+ STATE_NAMES_CSV[STATE_CACHED_ACTIVITY] = "cch-activity";
+ STATE_NAMES_CSV[STATE_CACHED_ACTIVITY_CLIENT] = "cch-aclient";
+ STATE_NAMES_CSV[STATE_CACHED_EMPTY] = "cch-empty";
STATE_TAGS = new String[STATE_COUNT];
- STATE_TAGS[STATE_PERSISTENT] = "p";
- STATE_TAGS[STATE_TOP] = "t";
- STATE_TAGS[STATE_IMPORTANT_FOREGROUND] = "f";
- STATE_TAGS[STATE_IMPORTANT_BACKGROUND] = "b";
- STATE_TAGS[STATE_BACKUP] = "u";
- STATE_TAGS[STATE_SERVICE] = "s";
- STATE_TAGS[STATE_SERVICE_RESTARTING] = "x";
- STATE_TAGS[STATE_RECEIVER] = "r";
- STATE_TAGS[STATE_HEAVY_WEIGHT] = "w";
- STATE_TAGS[STATE_HOME] = "h";
- STATE_TAGS[STATE_LAST_ACTIVITY] = "l";
- STATE_TAGS[STATE_CACHED_ACTIVITY] = "a";
- STATE_TAGS[STATE_CACHED_ACTIVITY_CLIENT] = "c";
- STATE_TAGS[STATE_CACHED_EMPTY] = "e";
+ STATE_TAGS[STATE_PERSISTENT] = "p";
+ STATE_TAGS[STATE_TOP] = "t";
+ STATE_TAGS[STATE_IMPORTANT_FOREGROUND] = "f";
+ STATE_TAGS[STATE_IMPORTANT_BACKGROUND] = "b";
+ STATE_TAGS[STATE_BACKUP] = "u";
+ STATE_TAGS[STATE_SERVICE] = "s";
+ STATE_TAGS[STATE_SERVICE_RESTARTING] = "x";
+ STATE_TAGS[STATE_RECEIVER] = "r";
+ STATE_TAGS[STATE_HEAVY_WEIGHT] = "w";
+ STATE_TAGS[STATE_HOME] = "h";
+ STATE_TAGS[STATE_LAST_ACTIVITY] = "l";
+ STATE_TAGS[STATE_CACHED_ACTIVITY] = "a";
+ STATE_TAGS[STATE_CACHED_ACTIVITY_CLIENT] = "c";
+ STATE_TAGS[STATE_CACHED_EMPTY] = "e";
STATE_PROTO_ENUMS = new int[STATE_COUNT];
STATE_PROTO_ENUMS[STATE_PERSISTENT] = ProcessStatsProto.State.PERSISTENT;
@@ -166,7 +187,7 @@ public final class DumpUtils {
pw.print("SOff/");
break;
case ADJ_SCREEN_ON:
- pw.print("SOn /");
+ pw.print(" SOn/");
break;
default:
pw.print("????/");
@@ -201,11 +222,11 @@ public final class DumpUtils {
if (sep != 0) pw.print(sep);
break;
case ADJ_MEM_FACTOR_MODERATE:
- pw.print("Mod ");
+ pw.print(" Mod");
if (sep != 0) pw.print(sep);
break;
case ADJ_MEM_FACTOR_LOW:
- pw.print("Low ");
+ pw.print(" Low");
if (sep != 0) pw.print(sep);
break;
case ADJ_MEM_FACTOR_CRITICAL:
diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java
index ad42288c25bb..9685f752285d 100644
--- a/core/java/com/android/internal/app/procstats/ProcessState.java
+++ b/core/java/com/android/internal/app/procstats/ProcessState.java
@@ -787,35 +787,36 @@ public final class ProcessState {
pw.print(" / v");
pw.print(mVersion);
pw.println(":");
- dumpProcessSummaryDetails(pw, prefix, " TOTAL: ", screenStates, memStates,
- procStates, now, totalTime, true);
- dumpProcessSummaryDetails(pw, prefix, " Persistent: ", screenStates, memStates,
- new int[] { STATE_PERSISTENT }, now, totalTime, true);
- dumpProcessSummaryDetails(pw, prefix, " Top: ", screenStates, memStates,
- new int[] {STATE_TOP}, now, totalTime, true);
- dumpProcessSummaryDetails(pw, prefix, " Imp Fg: ", screenStates, memStates,
- new int[] { STATE_IMPORTANT_FOREGROUND }, now, totalTime, true);
- dumpProcessSummaryDetails(pw, prefix, " Imp Bg: ", screenStates, memStates,
- new int[] {STATE_IMPORTANT_BACKGROUND}, now, totalTime, true);
- dumpProcessSummaryDetails(pw, prefix, " Backup: ", screenStates, memStates,
- new int[] {STATE_BACKUP}, now, totalTime, true);
- dumpProcessSummaryDetails(pw, prefix, " Heavy Wgt: ", screenStates, memStates,
- new int[] {STATE_HEAVY_WEIGHT}, now, totalTime, true);
- dumpProcessSummaryDetails(pw, prefix, " Service: ", screenStates, memStates,
- new int[] {STATE_SERVICE}, now, totalTime, true);
- dumpProcessSummaryDetails(pw, prefix, " Service Rs: ", screenStates, memStates,
- new int[] {STATE_SERVICE_RESTARTING}, now, totalTime, true);
- dumpProcessSummaryDetails(pw, prefix, " Receiver: ", screenStates, memStates,
- new int[] {STATE_RECEIVER}, now, totalTime, true);
- dumpProcessSummaryDetails(pw, prefix, " Heavy: ", screenStates, memStates,
- new int[] {STATE_HOME}, now, totalTime, true);
- dumpProcessSummaryDetails(pw, prefix, " (Home): ", screenStates, memStates,
- new int[] {STATE_HOME}, now, totalTime, true);
- dumpProcessSummaryDetails(pw, prefix, " (Last Act): ", screenStates, memStates,
- new int[] {STATE_LAST_ACTIVITY}, now, totalTime, true);
- dumpProcessSummaryDetails(pw, prefix, " (Cached): ", screenStates, memStates,
- new int[] {STATE_CACHED_ACTIVITY, STATE_CACHED_ACTIVITY_CLIENT,
- STATE_CACHED_EMPTY}, now, totalTime, true);
+ dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABEL_TOTAL,
+ screenStates, memStates, procStates, now, totalTime, true);
+ dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_PERSISTENT],
+ screenStates, memStates, new int[] { STATE_PERSISTENT }, now, totalTime, true);
+ dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_TOP],
+ screenStates, memStates, new int[] {STATE_TOP}, now, totalTime, true);
+ dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_IMPORTANT_FOREGROUND],
+ screenStates, memStates, new int[] { STATE_IMPORTANT_FOREGROUND }, now, totalTime,
+ true);
+ dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_IMPORTANT_BACKGROUND],
+ screenStates, memStates, new int[] {STATE_IMPORTANT_BACKGROUND}, now, totalTime,
+ true);
+ dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_BACKUP],
+ screenStates, memStates, new int[] {STATE_BACKUP}, now, totalTime, true);
+ dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_SERVICE],
+ screenStates, memStates, new int[] {STATE_SERVICE}, now, totalTime, true);
+ dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_SERVICE_RESTARTING],
+ screenStates, memStates, new int[] {STATE_SERVICE_RESTARTING}, now, totalTime,
+ true);
+ dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_RECEIVER],
+ screenStates, memStates, new int[] {STATE_RECEIVER}, now, totalTime, true);
+ dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_HEAVY_WEIGHT],
+ screenStates, memStates, new int[] {STATE_HEAVY_WEIGHT}, now, totalTime, true);
+ dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_HOME],
+ screenStates, memStates, new int[] {STATE_HOME}, now, totalTime, true);
+ dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABELS[STATE_LAST_ACTIVITY],
+ screenStates, memStates, new int[] {STATE_LAST_ACTIVITY}, now, totalTime, true);
+ dumpProcessSummaryDetails(pw, prefix, DumpUtils.STATE_LABEL_CACHED,
+ screenStates, memStates, new int[] {STATE_CACHED_ACTIVITY,
+ STATE_CACHED_ACTIVITY_CLIENT, STATE_CACHED_EMPTY}, now, totalTime, true);
}
public void dumpProcessState(PrintWriter pw, String prefix,
@@ -846,7 +847,7 @@ public final class ProcessState {
printedMem != imem ? imem : STATE_NOTHING, '/');
printedMem = imem;
}
- pw.print(DumpUtils.STATE_NAMES[procStates[ip]]); pw.print(": ");
+ pw.print(DumpUtils.STATE_LABELS[procStates[ip]]); pw.print(": ");
TimeUtils.formatDuration(time, pw); pw.println(running);
totalTime += time;
}
@@ -861,7 +862,8 @@ public final class ProcessState {
if (memStates.length > 1) {
DumpUtils.printMemLabel(pw, STATE_NOTHING, '/');
}
- pw.print("TOTAL : ");
+ pw.print(DumpUtils.STATE_LABEL_TOTAL);
+ pw.print(": ");
TimeUtils.formatDuration(totalTime, pw);
pw.println();
}
@@ -899,7 +901,7 @@ public final class ProcessState {
printedMem != imem ? imem : STATE_NOTHING, '/');
printedMem = imem;
}
- pw.print(DumpUtils.STATE_NAMES[procStates[ip]]); pw.print(": ");
+ pw.print(DumpUtils.STATE_LABELS[procStates[ip]]); pw.print(": ");
pw.print(count);
pw.print(" samples ");
DebugUtils.printSizeValue(pw, getPssMinimum(bucket) * 1024);
@@ -950,7 +952,9 @@ public final class ProcessState {
pw.print(prefix);
}
if (label != null) {
+ pw.print(" ");
pw.print(label);
+ pw.print(": ");
}
totals.print(pw, totalTime, full);
if (prefix != null) {
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index 15f140e2ea2d..12b16d040594 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -158,7 +158,7 @@ public final class ProcessStats implements Parcelable {
};
// Current version of the parcel format.
- private static final int PARCEL_VERSION = 32;
+ private static final int PARCEL_VERSION = 33;
// In-memory Parcel magic number, used to detect attempts to unmarshall bad data
private static final int MAGIC = 0x50535454;
@@ -1380,9 +1380,13 @@ public final class ProcessStats implements Parcelable {
final int NUM = mTrackingAssociations.size();
for (int i = NUM - 1; i >= 0; i--) {
final AssociationState.SourceState act = mTrackingAssociations.get(i);
- if (act.mProcStateSeq != curSeq) {
+ if (act.mProcStateSeq != curSeq || act.mProcState >= ProcessStats.STATE_HOME) {
+ // If this association did not get touched the last time we computed
+ // process states, or its state ended up down in cached, then we no
+ // longer have a reason to track it at all.
+ act.stopActive(now);
act.mInTrackingList = false;
- act.mProcState = STATE_NOTHING;
+ act.mProcState = ProcessStats.STATE_NOTHING;
mTrackingAssociations.remove(i);
} else {
final ProcessState proc = act.getAssociationState().getProcess();
@@ -1407,7 +1411,7 @@ public final class ProcessStats implements Parcelable {
}
public void dumpLocked(PrintWriter pw, String reqPackage, long now, boolean dumpSummary,
- boolean dumpAll, boolean activeOnly) {
+ boolean dumpDetails, boolean dumpAll, boolean activeOnly) {
long totalTime = DumpUtils.dumpSingleTime(null, null, mMemFactorDurations, mMemFactor,
mStartTime, now);
boolean sepNeeded = false;
@@ -1538,7 +1542,7 @@ public final class ProcessStats implements Parcelable {
pw.println(":");
pw.print(" Process: "); pw.println(asc.getProcessName());
asc.dumpStats(pw, " ", " ", " ",
- now, totalTime, dumpSummary, dumpAll);
+ now, totalTime, dumpDetails, dumpAll);
}
}
}
@@ -1632,21 +1636,8 @@ public final class ProcessStats implements Parcelable {
if (src.mActiveCount > 0) {
pw.print(" Active count ");
pw.print(src.mActiveCount);
- long duration = src.mActiveDuration;
- if (src.mActiveStartUptime > 0) {
- duration += now - src.mActiveStartUptime;
- }
- if (dumpAll) {
- pw.print(" / Duration ");
- TimeUtils.formatDuration(duration, pw);
- pw.print(" / ");
- } else {
- pw.print(" / time ");
- }
- DumpUtils.printPercent(pw, (double)duration/(double)totalTime);
- if (src.mActiveStartUptime > 0) {
- pw.print(" (running)");
- }
+ pw.print(": ");
+ asc.dumpActiveDurationSummary(pw, src, totalTime, now, dumpAll);
pw.println();
}
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index af50420208b3..76f9a8d5f8e8 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -13181,7 +13181,7 @@ public class BatteryStatsImpl extends BatteryStats {
public static final String KEY_MAX_HISTORY_FILES = "max_history_files";
public static final String KEY_MAX_HISTORY_BUFFER_KB = "max_history_buffer_kb";
- private static final boolean DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE = true;
+ private static final boolean DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE = false;
private static final boolean DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME = true;
private static final long DEFAULT_PROC_STATE_CPU_TIMES_READ_DELAY_MS = 5_000;
private static final long DEFAULT_KERNEL_UID_READERS_THROTTLE_TIME = 10_000;
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index dc302058aa80..f88a902b43b6 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -34,6 +34,7 @@ import com.android.internal.util.Preconditions;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
@@ -76,16 +77,17 @@ public class BinderCallsStats implements BinderInternal.Observer {
@Override
public CallSession callStarted(Binder binder, int code) {
- return callStarted(binder.getClass().getName(), code, binder.getTransactionName(code));
+ return callStarted(binder.getClass(), code, binder.getTransactionName(code));
}
- private CallSession callStarted(String className, int code, @Nullable String methodName) {
+ private CallSession callStarted(Class<? extends Binder> binderClass, int code,
+ @Nullable String methodName) {
CallSession s = mCallSessionsPool.poll();
if (s == null) {
s = new CallSession();
}
- s.className = className;
+ s.binderClass = binderClass;
s.transactionCode = code;
s.methodName = methodName;
s.exceptionThrown = false;
@@ -117,22 +119,30 @@ public class BinderCallsStats implements BinderInternal.Observer {
}
private void processCallEnded(CallSession s, int parcelRequestSize, int parcelReplySize) {
+ // Non-negative time signals we need to record data for this call.
+ final boolean recordCall = s.cpuTimeStarted >= 0;
+ final long duration;
+ final long latencyDuration;
+ if (recordCall) {
+ duration = getThreadTimeMicro() - s.cpuTimeStarted;
+ latencyDuration = getElapsedRealtimeMicro() - s.timeStarted;
+ } else {
+ duration = 0;
+ latencyDuration = 0;
+ }
+ final int callingUid = getCallingUid();
+
synchronized (mLock) {
- final int callingUid = getCallingUid();
UidEntry uidEntry = mUidEntries.get(callingUid);
if (uidEntry == null) {
uidEntry = new UidEntry(callingUid);
mUidEntries.put(callingUid, uidEntry);
}
uidEntry.callCount++;
- CallStat callStat = uidEntry.getOrCreate(s.className, s.transactionCode);
+ CallStat callStat = uidEntry.getOrCreate(s.binderClass, s.transactionCode);
callStat.callCount++;
- // Non-negative time signals we need to record data for this call.
- final boolean recordCall = s.cpuTimeStarted >= 0;
if (recordCall) {
- final long duration = getThreadTimeMicro() - s.cpuTimeStarted;
- final long latencyDuration = getElapsedRealtimeMicro() - s.timeStarted;
uidEntry.cpuTimeMicros += duration;
uidEntry.recordedCallCount++;
@@ -175,6 +185,9 @@ public class BinderCallsStats implements BinderInternal.Observer {
}
}
+ /**
+ * This method is expensive to call.
+ */
public ArrayList<ExportedCallStat> getExportedCallStats() {
// We do not collect all the data if detailed tracking is off.
if (!mDetailedTracking) {
@@ -189,7 +202,7 @@ public class BinderCallsStats implements BinderInternal.Observer {
for (CallStat stat : entry.getCallStatsList()) {
ExportedCallStat exported = new ExportedCallStat();
exported.uid = entry.uid;
- exported.className = stat.className;
+ exported.className = stat.binderClass.getName();
exported.methodName = stat.methodName == null
? String.valueOf(stat.transactionCode) : stat.methodName;
exported.cpuTimeMicros = stat.cpuTimeMicros;
@@ -250,23 +263,22 @@ public class BinderCallsStats implements BinderInternal.Observer {
+ "latency_time_micros, max_latency_time_micros, exception_count, "
+ "max_request_size_bytes, max_reply_size_bytes, recorded_call_count, "
+ "call_count):");
- for (UidEntry uidEntry : topEntries) {
- for (CallStat e : uidEntry.getCallStatsList()) {
- sb.setLength(0);
- sb.append(" ")
- .append(uidToString(uidEntry.uid, appIdToPkgNameMap))
- .append(',').append(e)
- .append(',').append(e.cpuTimeMicros)
- .append(',').append(e.maxCpuTimeMicros)
- .append(',').append(e.latencyMicros)
- .append(',').append(e.maxLatencyMicros)
- .append(',').append(mDetailedTracking ? e.exceptionCount : '_')
- .append(',').append(mDetailedTracking ? e.maxRequestSizeBytes : '_')
- .append(',').append(mDetailedTracking ? e.maxReplySizeBytes : '_')
- .append(',').append(e.recordedCallCount)
- .append(',').append(e.callCount);
- pw.println(sb);
- }
+ for (ExportedCallStat e : sortByCpuDesc(getExportedCallStats())) {
+ sb.setLength(0);
+ sb.append(" ")
+ .append(uidToString(e.uid, appIdToPkgNameMap))
+ .append(',').append(e.className)
+ .append('#').append(e.methodName)
+ .append(',').append(e.cpuTimeMicros)
+ .append(',').append(e.maxCpuTimeMicros)
+ .append(',').append(e.latencyMicros)
+ .append(',').append(e.maxLatencyMicros)
+ .append(',').append(mDetailedTracking ? e.exceptionCount : '_')
+ .append(',').append(mDetailedTracking ? e.maxRequestSizeBytes : '_')
+ .append(',').append(mDetailedTracking ? e.maxReplySizeBytes : '_')
+ .append(',').append(e.recordedCallCount)
+ .append(',').append(e.callCount);
+ pw.println(sb);
}
pw.println();
pw.println("Per-UID Summary " + datasetSizeDesc
@@ -372,7 +384,7 @@ public class BinderCallsStats implements BinderInternal.Observer {
@VisibleForTesting
public static class CallStat {
- public String className;
+ public Class<? extends Binder> binderClass;
public int transactionCode;
// Method name might be null when we cannot resolve the transaction code. For instance, if
// the binder was not generated by AIDL.
@@ -397,23 +409,15 @@ public class BinderCallsStats implements BinderInternal.Observer {
public long maxReplySizeBytes;
public long exceptionCount;
- CallStat() {
- }
-
- CallStat(String className, int transactionCode) {
- this.className = className;
+ CallStat(Class<? extends Binder> binderClass, int transactionCode) {
+ this.binderClass = binderClass;
this.transactionCode = transactionCode;
}
-
- @Override
- public String toString() {
- return className + "#" + (methodName == null ? transactionCode : methodName);
- }
}
/** Key used to store CallStat object in a Map. */
public static class CallStatKey {
- public String className;
+ public Class<? extends Binder> binderClass;
public int transactionCode;
@Override
@@ -424,12 +428,12 @@ public class BinderCallsStats implements BinderInternal.Observer {
CallStatKey key = (CallStatKey) o;
return transactionCode == key.transactionCode
- && (className.equals(key.className));
+ && (binderClass.equals(key.binderClass));
}
@Override
public int hashCode() {
- int result = className.hashCode();
+ int result = binderClass.hashCode();
result = 31 * result + transactionCode;
return result;
}
@@ -457,16 +461,16 @@ public class BinderCallsStats implements BinderInternal.Observer {
private Map<CallStatKey, CallStat> mCallStats = new ArrayMap<>();
private CallStatKey mTempKey = new CallStatKey();
- CallStat getOrCreate(String className, int transactionCode) {
+ CallStat getOrCreate(Class<? extends Binder> binderClass, int transactionCode) {
// Use a global temporary key to avoid creating new objects for every lookup.
- mTempKey.className = className;
+ mTempKey.binderClass = binderClass;
mTempKey.transactionCode = transactionCode;
CallStat mapCallStat = mCallStats.get(mTempKey);
// Only create CallStat if it's a new entry, otherwise update existing instance
if (mapCallStat == null) {
- mapCallStat = new CallStat(className, transactionCode);
+ mapCallStat = new CallStat(binderClass, transactionCode);
CallStatKey key = new CallStatKey();
- key.className = className;
+ key.binderClass = binderClass;
key.transactionCode = transactionCode;
mCallStats.put(key, mapCallStat);
}
@@ -476,17 +480,8 @@ public class BinderCallsStats implements BinderInternal.Observer {
/**
* Returns list of calls sorted by CPU time
*/
- public List<CallStat> getCallStatsList() {
- List<CallStat> callStats = new ArrayList<>(mCallStats.values());
- callStats.sort((o1, o2) -> {
- if (o1.cpuTimeMicros < o2.cpuTimeMicros) {
- return 1;
- } else if (o1.cpuTimeMicros > o2.cpuTimeMicros) {
- return -1;
- }
- return 0;
- });
- return callStats;
+ public Collection<CallStat> getCallStatsList() {
+ return mCallStats.values();
}
@Override
@@ -545,4 +540,15 @@ public class BinderCallsStats implements BinderInternal.Observer {
return result;
}
+ private List<ExportedCallStat> sortByCpuDesc(List<ExportedCallStat> callStats) {
+ callStats.sort((o1, o2) -> {
+ if (o1.cpuTimeMicros < o2.cpuTimeMicros) {
+ return 1;
+ } else if (o1.cpuTimeMicros > o2.cpuTimeMicros) {
+ return -1;
+ }
+ return 0;
+ });
+ return callStats;
+ }
}
diff --git a/core/java/com/android/internal/os/BinderInternal.java b/core/java/com/android/internal/os/BinderInternal.java
index 4b93c8644f64..af12521f0079 100644
--- a/core/java/com/android/internal/os/BinderInternal.java
+++ b/core/java/com/android/internal/os/BinderInternal.java
@@ -75,7 +75,7 @@ public class BinderInternal {
*/
public static class CallSession {
// Binder interface descriptor.
- public String className;
+ public Class<? extends Binder> binderClass;
// Binder transaction code.
public int transactionCode;
// Binder transaction method name.
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 4ee950aa2beb..413f89da571e 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -132,13 +132,14 @@ public final class Zygote {
*/
public static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
- int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir) {
+ int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
+ String packageName) {
VM_HOOKS.preFork();
// Resets nice priority for zygote process.
resetNicePriority();
int pid = nativeForkAndSpecialize(
uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
- fdsToIgnore, startChildZygote, instructionSet, appDataDir);
+ fdsToIgnore, startChildZygote, instructionSet, appDataDir, packageName);
// Enable tracing as soon as possible for the child process.
if (pid == 0) {
Trace.setTracingEnabled(true, runtimeFlags);
@@ -152,7 +153,8 @@ public final class Zygote {
native private static int nativeForkAndSpecialize(int uid, int gid, int[] gids,int runtimeFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
- int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir);
+ int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
+ String packageName);
/**
* Called to do any initialization before starting an application.
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 12761b9274e3..b9c717f03749 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -239,7 +239,7 @@ class ZygoteConnection {
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
parsedArgs.runtimeFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.startChildZygote,
- parsedArgs.instructionSet, parsedArgs.appDataDir);
+ parsedArgs.instructionSet, parsedArgs.appDataDir, parsedArgs.packageName);
try {
if (pid == 0) {
@@ -426,6 +426,9 @@ class ZygoteConnection {
/** from --invoke-with */
String invokeWith;
+ /** from --package-name */
+ String packageName;
+
/**
* Any args after and including the first non-option arg
* (or after a '--')
@@ -674,6 +677,8 @@ class ZygoteConnection {
"Invalid log sampling rate: " + rateStr, nfe);
}
expectRuntimeArgs = false;
+ } else if (arg.startsWith("--package-name=")) {
+ packageName = arg.substring(arg.indexOf('=') + 1);
} else {
break;
}
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index c3d33ca84ee1..4b662670f5e7 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -309,7 +309,7 @@ public class ArrayUtils {
}
@SuppressWarnings("unchecked")
- public static @NonNull <T> T[] concat(Class<T> kind, @Nullable T[] a, @Nullable T[] b) {
+ public static @NonNull <T> T[] concatElements(Class<T> kind, @Nullable T[] a, @Nullable T[] b) {
final int an = (a != null) ? a.length : 0;
final int bn = (b != null) ? b.length : 0;
if (an == 0 && bn == 0) {
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index b675698a461b..6856e2942f21 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -84,8 +84,8 @@ cc_library_shared {
"android_view_VelocityTracker.cpp",
"android_text_AndroidCharacter.cpp",
"android_text_Hyphenator.cpp",
+ "android_text_LineBreaker.cpp",
"android_text_MeasuredParagraph.cpp",
- "android_text_StaticLayout.cpp",
"android_os_Debug.cpp",
"android_os_GraphicsEnvironment.cpp",
"android_os_HidlSupport.cpp",
@@ -154,6 +154,7 @@ cc_library_shared {
"android/graphics/Typeface.cpp",
"android/graphics/Utils.cpp",
"android/graphics/YuvToJpegEncoder.cpp",
+ "android/graphics/fonts/Font.cpp",
"android/graphics/pdf/PdfDocument.cpp",
"android/graphics/pdf/PdfEditor.cpp",
"android/graphics/pdf/PdfRenderer.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index bd2a8def2618..1820888f9c0a 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -140,6 +140,7 @@ extern int register_android_graphics_Region(JNIEnv* env);
extern int register_android_graphics_SurfaceTexture(JNIEnv* env);
extern int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env);
extern int register_android_graphics_drawable_VectorDrawable(JNIEnv* env);
+extern int register_android_graphics_fonts_Font(JNIEnv* env);
extern int register_android_graphics_pdf_PdfDocument(JNIEnv* env);
extern int register_android_graphics_pdf_PdfEditor(JNIEnv* env);
extern int register_android_graphics_pdf_PdfRenderer(JNIEnv* env);
@@ -183,7 +184,7 @@ extern int register_android_net_NetworkUtils(JNIEnv* env);
extern int register_android_text_AndroidCharacter(JNIEnv *env);
extern int register_android_text_Hyphenator(JNIEnv *env);
extern int register_android_text_MeasuredParagraph(JNIEnv* env);
-extern int register_android_text_StaticLayout(JNIEnv *env);
+extern int register_android_text_LineBreaker(JNIEnv *env);
extern int register_android_opengl_classes(JNIEnv *env);
extern int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env);
extern int register_android_server_NetworkManagementSocketTagger(JNIEnv* env);
@@ -1334,7 +1335,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_text_AndroidCharacter),
REG_JNI(register_android_text_Hyphenator),
REG_JNI(register_android_text_MeasuredParagraph),
- REG_JNI(register_android_text_StaticLayout),
+ REG_JNI(register_android_text_LineBreaker),
REG_JNI(register_android_view_InputDevice),
REG_JNI(register_android_view_KeyCharacterMap),
REG_JNI(register_android_os_Process),
@@ -1406,6 +1407,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_graphics_YuvImage),
REG_JNI(register_android_graphics_drawable_AnimatedVectorDrawable),
REG_JNI(register_android_graphics_drawable_VectorDrawable),
+ REG_JNI(register_android_graphics_fonts_Font),
REG_JNI(register_android_graphics_pdf_PdfDocument),
REG_JNI(register_android_graphics_pdf_PdfEditor),
REG_JNI(register_android_graphics_pdf_PdfRenderer),
diff --git a/core/jni/android/graphics/FontUtils.h b/core/jni/android/graphics/FontUtils.h
index 9eaaa4964b66..9f6462e67050 100644
--- a/core/jni/android/graphics/FontUtils.h
+++ b/core/jni/android/graphics/FontUtils.h
@@ -20,6 +20,8 @@
#include <jni.h>
#include <memory>
+#include <minikin/Font.h>
+
namespace minikin {
class FontFamily;
} // namespace minikin
@@ -31,6 +33,11 @@ struct FontFamilyWrapper {
std::shared_ptr<minikin::FontFamily> family;
};
+struct FontWrapper {
+ FontWrapper(minikin::Font&& font) : font(std::move(font)) {}
+ minikin::Font font;
+};
+
// Utility wrapper for java.util.List
class ListHelper {
public:
diff --git a/core/jni/android/graphics/fonts/Font.cpp b/core/jni/android/graphics/fonts/Font.cpp
new file mode 100644
index 000000000000..2d1d7a0c4aea
--- /dev/null
+++ b/core/jni/android/graphics/fonts/Font.cpp
@@ -0,0 +1,205 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "Minikin"
+
+#include <nativehelper/JNIHelp.h>
+#include <core_jni_helpers.h>
+
+#include "SkData.h"
+#include "SkFontMgr.h"
+#include "SkRefCnt.h"
+#include "SkTypeface.h"
+#include "GraphicsJNI.h"
+#include <nativehelper/ScopedUtfChars.h>
+#include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/android_util_AssetManager.h>
+#include <androidfw/AssetManager2.h>
+#include "Utils.h"
+#include "FontUtils.h"
+
+#include <hwui/MinikinSkia.h>
+#include <hwui/Typeface.h>
+#include <utils/FatVector.h>
+#include <minikin/FontFamily.h>
+
+#include <memory>
+
+namespace android {
+
+struct NativeFontBuilder {
+ std::vector<minikin::FontVariation> axes;
+};
+
+static inline NativeFontBuilder* toBuilder(jlong ptr) {
+ return reinterpret_cast<NativeFontBuilder*>(ptr);
+}
+
+static inline Asset* toAsset(jlong ptr) {
+ return reinterpret_cast<Asset*>(ptr);
+}
+
+static void releaseAsset(jlong asset) {
+ delete toAsset(asset);
+}
+
+static void releaseFont(jlong font) {
+ delete reinterpret_cast<FontWrapper*>(font);
+}
+
+static void release_global_ref(const void* /*data*/, void* context) {
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ if (env == nullptr) {
+ JavaVMAttachArgs args;
+ args.version = JNI_VERSION_1_4;
+ args.name = "release_font_data";
+ args.group = nullptr;
+ jint result = AndroidRuntime::getJavaVM()->AttachCurrentThread(&env, &args);
+ if (result != JNI_OK) {
+ ALOGE("failed to attach to thread to release global ref.");
+ return;
+ }
+ }
+
+ jobject obj = reinterpret_cast<jobject>(context);
+ env->DeleteGlobalRef(obj);
+}
+
+// Regular JNI
+static jlong Font_Builder_getNativeAsset(
+ JNIEnv* env, jobject clazz, jobject assetMgr, jstring path, jboolean isAsset, jint cookie) {
+ NPE_CHECK_RETURN_ZERO(env, assetMgr);
+ NPE_CHECK_RETURN_ZERO(env, path);
+
+ Guarded<AssetManager2>* mgr = AssetManagerForJavaObject(env, assetMgr);
+ if (mgr == nullptr) {
+ return 0;
+ }
+
+ ScopedUtfChars str(env, path);
+ if (str.c_str() == nullptr) {
+ return 0;
+ }
+
+ std::unique_ptr<Asset> asset;
+ {
+ ScopedLock<AssetManager2> locked_mgr(*mgr);
+ if (isAsset) {
+ asset = locked_mgr->Open(str.c_str(), Asset::ACCESS_BUFFER);
+ } else if (cookie > 0) {
+ // Valid java cookies are 1-based, but AssetManager cookies are 0-based.
+ asset = locked_mgr->OpenNonAsset(str.c_str(), static_cast<ApkAssetsCookie>(cookie - 1),
+ Asset::ACCESS_BUFFER);
+ } else {
+ asset = locked_mgr->OpenNonAsset(str.c_str(), Asset::ACCESS_BUFFER);
+ }
+ }
+
+ return reinterpret_cast<jlong>(asset.release());
+}
+
+// Regular JNI
+static jobject Font_Builder_getAssetBuffer(JNIEnv* env, jobject clazz, jlong nativeAsset) {
+ Asset* asset = toAsset(nativeAsset);
+ return env->NewDirectByteBuffer(const_cast<void*>(asset->getBuffer(false)), asset->getLength());
+}
+
+// CriticalNative
+static jlong Font_Builder_getReleaseNativeAssetFunc() {
+ return reinterpret_cast<jlong>(&releaseAsset);
+}
+
+// Regular JNI
+static jlong Font_Builder_initBuilder(JNIEnv*, jobject) {
+ return reinterpret_cast<jlong>(new NativeFontBuilder());
+}
+
+// Critical Native
+static void Font_Builder_addAxis(jlong builderPtr, jint tag, jfloat value) {
+ toBuilder(builderPtr)->axes.emplace_back(static_cast<minikin::AxisTag>(tag), value);
+}
+
+// Regular JNI
+static jlong Font_Builder_build(JNIEnv* env, jobject clazz, jlong builderPtr, jobject buffer,
+ jint weight, jboolean italic, jint ttcIndex) {
+ NPE_CHECK_RETURN_ZERO(env, buffer);
+ std::unique_ptr<NativeFontBuilder> builder(toBuilder(builderPtr));
+ const void* fontPtr = env->GetDirectBufferAddress(buffer);
+ if (fontPtr == nullptr) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", "Not a direct buffer");
+ return 0;
+ }
+ jlong fontSize = env->GetDirectBufferCapacity(buffer);
+ if (fontSize <= 0) {
+ jniThrowException(env, "java/lang/IllegalArgumentException",
+ "buffer size must not be zero or negative");
+ return 0;
+ }
+ jobject fontRef = MakeGlobalRefOrDie(env, buffer);
+ sk_sp<SkData> data(SkData::MakeWithProc(fontPtr, fontSize,
+ release_global_ref, reinterpret_cast<void*>(fontRef)));
+
+ uirenderer::FatVector<SkFontArguments::Axis, 2> skiaAxes;
+ for (const auto& axis : builder->axes) {
+ skiaAxes.emplace_back(SkFontArguments::Axis{axis.axisTag, axis.value});
+ }
+
+ std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(std::move(data)));
+
+ SkFontArguments params;
+ params.setCollectionIndex(ttcIndex);
+ params.setAxes(skiaAxes.data(), skiaAxes.size());
+
+ sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
+ sk_sp<SkTypeface> face(fm->makeFromStream(std::move(fontData), params));
+ if (face == nullptr) {
+ jniThrowException(env, "java/lang/IllegalArgumentException",
+ "Failed to create internal object. maybe invalid font data.");
+ return 0;
+ }
+ std::shared_ptr<minikin::MinikinFont> minikinFont =
+ std::make_shared<MinikinFontSkia>(std::move(face), fontPtr, fontSize, ttcIndex,
+ builder->axes);
+ minikin::Font font = minikin::Font::Builder(minikinFont).setWeight(weight)
+ .setSlant(static_cast<minikin::FontStyle::Slant>(italic)).build();
+ return reinterpret_cast<jlong>(new FontWrapper(std::move(font)));
+}
+
+// Critical Native
+static jlong Font_Builder_getReleaseNativeFont() {
+ return reinterpret_cast<jlong>(releaseFont);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const JNINativeMethod gFontBuilderMethods[] = {
+ { "nInitBuilder", "()J", (void*) Font_Builder_initBuilder },
+ { "nAddAxis", "(JIF)V", (void*) Font_Builder_addAxis },
+ { "nBuild", "(JLjava/nio/ByteBuffer;IZI)J", (void*) Font_Builder_build },
+ { "nGetReleaseNativeFont", "()J", (void*) Font_Builder_getReleaseNativeFont },
+
+ { "nGetNativeAsset", "(Landroid/content/res/AssetManager;Ljava/lang/String;ZI)J",
+ (void*) Font_Builder_getNativeAsset },
+ { "nGetAssetBuffer", "(J)Ljava/nio/ByteBuffer;", (void*) Font_Builder_getAssetBuffer },
+ { "nGetReleaseNativeAssetFunc", "()J", (void*) Font_Builder_getReleaseNativeAssetFunc },
+};
+
+int register_android_graphics_fonts_Font(JNIEnv* env) {
+ return RegisterMethodsOrDie(env, "android/graphics/fonts/Font$Builder", gFontBuilderMethods,
+ NELEM(gFontBuilderMethods));
+}
+
+}
diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_LineBreaker.cpp
index fec5b6995646..dac108ef5497 100644
--- a/core/jni/android_text_StaticLayout.cpp
+++ b/core/jni/android_text_LineBreaker.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "StaticLayout"
+#define LOG_TAG "LineBreaker"
#include "unicode/locid.h"
#include "unicode/brkiter.h"
@@ -76,11 +76,15 @@ static jlong nInit(JNIEnv* env, jclass /* unused */,
jintArrayToFloatVector(env, indents)));
}
-// CriticalNative
static void nFinish(jlong nativePtr) {
delete toNative(nativePtr);
}
+// CriticalNative
+static jlong nGetReleaseFunc() {
+ return reinterpret_cast<jlong>(nFinish);
+}
+
static void recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks,
jfloatArray recycleWidths, jfloatArray recycleAscents,
jfloatArray recycleDescents, jintArray recycleFlags,
@@ -144,9 +148,6 @@ static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr,
recycleCopy(env, recycle, recycleBreaks, recycleWidths, recycleAscents, recycleDescents,
recycleFlags, recycleLength, result);
- env->SetFloatArrayRegion(charWidths, 0, measuredText->widths.size(),
- measuredText->widths.data());
-
return static_cast<jint>(result.breakPoints.size());
}
@@ -160,7 +161,7 @@ static const JNINativeMethod gMethods[] = {
")J", (void*) nInit},
// Critical Natives
- {"nFinish", "(J)V", (void*) nFinish},
+ {"nGetReleaseFunc", "()J", (void*) nGetReleaseFunc},
// Regular JNI
{"nComputeLineBreaks", "("
@@ -178,21 +179,20 @@ static const JNINativeMethod gMethods[] = {
"I" // indentsOffset
// Outputs
- "Landroid/text/StaticLayout$LineBreaks;" // recycle
+ "Landroid/text/NativeLineBreaker$LineBreaks;" // recycle
"I" // recycleLength
"[I" // recycleBreaks
"[F" // recycleWidths
"[F" // recycleAscents
"[F" // recycleDescents
"[I" // recycleFlags
- "[F" // charWidths
")I", (void*) nComputeLineBreaks}
};
-int register_android_text_StaticLayout(JNIEnv* env)
+int register_android_text_LineBreaker(JNIEnv* env)
{
gLineBreaks_class = MakeGlobalRefOrDie(env,
- FindClassOrDie(env, "android/text/StaticLayout$LineBreaks"));
+ FindClassOrDie(env, "android/text/NativeLineBreaker$LineBreaks"));
gLineBreaks_fieldID.breaks = GetFieldIDOrDie(env, gLineBreaks_class, "breaks", "[I");
gLineBreaks_fieldID.widths = GetFieldIDOrDie(env, gLineBreaks_class, "widths", "[F");
@@ -200,7 +200,8 @@ int register_android_text_StaticLayout(JNIEnv* env)
gLineBreaks_fieldID.descents = GetFieldIDOrDie(env, gLineBreaks_class, "descents", "[F");
gLineBreaks_fieldID.flags = GetFieldIDOrDie(env, gLineBreaks_class, "flags", "[I");
- return RegisterMethodsOrDie(env, "android/text/StaticLayout", gMethods, NELEM(gMethods));
+ return RegisterMethodsOrDie(env, "android/text/NativeLineBreaker",
+ gMethods, NELEM(gMethods));
}
}
diff --git a/core/jni/android_text_MeasuredParagraph.cpp b/core/jni/android_text_MeasuredParagraph.cpp
index 9eb6f8d4189a..18f509c7ec60 100644
--- a/core/jni/android_text_MeasuredParagraph.cpp
+++ b/core/jni/android_text_MeasuredParagraph.cpp
@@ -109,6 +109,10 @@ static jfloat nGetWidth(jlong ptr, jint start, jint end) {
return r;
}
+static jfloat nGetCharWidthAt(jlong ptr, jint offset) {
+ return toMeasuredParagraph(ptr)->widths[offset];
+}
+
// Regular JNI
static void nGetBounds(JNIEnv* env, jobject, jlong ptr, jcharArray javaText, jint start, jint end,
jobject bounds) {
@@ -138,23 +142,29 @@ static jint nGetMemoryUsage(jlong ptr) {
return static_cast<jint>(toMeasuredParagraph(ptr)->getMemoryUsage());
}
-static const JNINativeMethod gMethods[] = {
+static const JNINativeMethod gMTBuilderMethods[] = {
// MeasuredParagraphBuilder native functions.
{"nInitBuilder", "()J", (void*) nInitBuilder},
{"nAddStyleRun", "(JJIIZ)V", (void*) nAddStyleRun},
{"nAddReplacementRun", "(JJIIF)V", (void*) nAddReplacementRun},
{"nBuildNativeMeasuredParagraph", "(J[CZZ)J", (void*) nBuildNativeMeasuredParagraph},
{"nFreeBuilder", "(J)V", (void*) nFreeBuilder},
+};
+static const JNINativeMethod gMTMethods[] = {
// MeasuredParagraph native functions.
{"nGetWidth", "(JII)F", (void*) nGetWidth}, // Critical Natives
{"nGetBounds", "(J[CIILandroid/graphics/Rect;)V", (void*) nGetBounds}, // Regular JNI
{"nGetReleaseFunc", "()J", (void*) nGetReleaseFunc}, // Critical Natives
{"nGetMemoryUsage", "(J)I", (void*) nGetMemoryUsage}, // Critical Native
+ {"nGetCharWidthAt", "(JI)F", (void*) nGetCharWidthAt}, // Critical Native
};
int register_android_text_MeasuredParagraph(JNIEnv* env) {
- return RegisterMethodsOrDie(env, "android/text/MeasuredParagraph", gMethods, NELEM(gMethods));
+ return RegisterMethodsOrDie(env, "android/text/NativeMeasuredParagraph",
+ gMTMethods, NELEM(gMTMethods))
+ + RegisterMethodsOrDie(env, "android/text/NativeMeasuredParagraph$Builder",
+ gMTBuilderMethods, NELEM(gMTBuilderMethods));
}
}
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 0022cf889098..738069216b2c 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -70,6 +70,7 @@
namespace {
using android::String8;
+using android::base::StringAppendF;
using android::base::StringPrintf;
using android::base::WriteStringToFile;
using android::base::GetBoolProperty;
@@ -79,6 +80,7 @@ using android::base::GetBoolProperty;
static pid_t gSystemServerPid = 0;
+static const char kIsolatedStorage[] = "persist.sys.isolated_storage";
static const char kZygoteClassName[] = "com/android/internal/os/Zygote";
static jclass gZygoteClass;
static jmethodID gCallPostForkChildHooks;
@@ -379,10 +381,32 @@ static int UnmountTree(const char* path) {
return 0;
}
+static bool createPkgSandbox(uid_t uid, const char* package_name, std::string& pkg_sandbox_dir,
+ std::string* error_msg) {
+ // Create /mnt/user/0/package/<package-name>
+ userid_t user_id = multiuser_get_user_id(uid);
+ StringAppendF(&pkg_sandbox_dir, "/%d", user_id);
+ if (fs_prepare_dir(pkg_sandbox_dir.c_str(), 0700, AID_ROOT, AID_ROOT) != 0) {
+ *error_msg = CREATE_ERROR("fs_prepare_dir failed on %s", pkg_sandbox_dir.c_str());
+ return false;
+ }
+ StringAppendF(&pkg_sandbox_dir, "/package");
+ if (fs_prepare_dir(pkg_sandbox_dir.c_str(), 0700, AID_ROOT, AID_ROOT) != 0) {
+ *error_msg = CREATE_ERROR("fs_prepare_dir failed on %s", pkg_sandbox_dir.c_str());
+ return false;
+ }
+ StringAppendF(&pkg_sandbox_dir, "/%s", package_name);
+ if (fs_prepare_dir(pkg_sandbox_dir.c_str(), 0755, uid, uid) != 0) {
+ *error_msg = CREATE_ERROR("fs_prepare_dir failed on %s", pkg_sandbox_dir.c_str());
+ return false;
+ }
+ return true;
+}
+
// Create a private mount namespace and bind mount appropriate emulated
// storage for the given user.
static bool MountEmulatedStorage(uid_t uid, jint mount_mode,
- bool force_mount_namespace, std::string* error_msg) {
+ bool force_mount_namespace, std::string* error_msg, const char* package_name) {
// See storage config details at http://source.android.com/tech/storage/
String8 storageSource;
@@ -408,27 +432,44 @@ static bool MountEmulatedStorage(uid_t uid, jint mount_mode,
return true;
}
- if (TEMP_FAILURE_RETRY(mount(storageSource.string(), "/storage",
- NULL, MS_BIND | MS_REC | MS_SLAVE, NULL)) == -1) {
- *error_msg = CREATE_ERROR("Failed to mount %s to /storage: %s",
- storageSource.string(),
- strerror(errno));
- return false;
- }
+ if (GetBoolProperty(kIsolatedStorage, false)) {
+ if (package_name == nullptr) {
+ return true;
+ }
- // Mount user-specific symlink helper into place
- userid_t user_id = multiuser_get_user_id(uid);
- const String8 userSource(String8::format("/mnt/user/%d", user_id));
- if (fs_prepare_dir(userSource.string(), 0751, 0, 0) == -1) {
- *error_msg = CREATE_ERROR("fs_prepare_dir failed on %s", userSource.string());
- return false;
- }
- if (TEMP_FAILURE_RETRY(mount(userSource.string(), "/storage/self",
- NULL, MS_BIND, NULL)) == -1) {
- *error_msg = CREATE_ERROR("Failed to mount %s to /storage/self: %s",
- userSource.string(),
- strerror(errno));
- return false;
+ std::string pkgSandboxDir("/mnt/user");
+ if (!createPkgSandbox(uid, package_name, pkgSandboxDir, error_msg)) {
+ return false;
+ }
+ if (TEMP_FAILURE_RETRY(mount(pkgSandboxDir.c_str(), "/storage",
+ nullptr, MS_BIND | MS_REC | MS_SLAVE, nullptr)) == -1) {
+ *error_msg = CREATE_ERROR("Failed to mount %s to /storage: %s",
+ pkgSandboxDir.c_str(), strerror(errno));
+ return false;
+ }
+ } else {
+ if (TEMP_FAILURE_RETRY(mount(storageSource.string(), "/storage",
+ NULL, MS_BIND | MS_REC | MS_SLAVE, NULL)) == -1) {
+ *error_msg = CREATE_ERROR("Failed to mount %s to /storage: %s",
+ storageSource.string(),
+ strerror(errno));
+ return false;
+ }
+
+ // Mount user-specific symlink helper into place
+ userid_t user_id = multiuser_get_user_id(uid);
+ const String8 userSource(String8::format("/mnt/user/%d", user_id));
+ if (fs_prepare_dir(userSource.string(), 0751, 0, 0) == -1) {
+ *error_msg = CREATE_ERROR("fs_prepare_dir failed on %s", userSource.string());
+ return false;
+ }
+ if (TEMP_FAILURE_RETRY(mount(userSource.string(), "/storage/self",
+ NULL, MS_BIND, NULL)) == -1) {
+ *error_msg = CREATE_ERROR("Failed to mount %s to /storage/self: %s",
+ userSource.string(),
+ strerror(errno));
+ return false;
+ }
}
return true;
@@ -544,7 +585,7 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGi
jlong permittedCapabilities, jlong effectiveCapabilities,
jint mount_external, jstring java_se_info, jstring java_se_name,
bool is_system_server, bool is_child_zygote, jstring instructionSet,
- jstring dataDir) {
+ jstring dataDir, jstring packageName) {
std::string error_msg;
auto fail_fn = [env, java_se_name, is_system_server](const std::string& msg)
@@ -594,7 +635,18 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGi
ALOGW("Native bridge will not be used because dataDir == NULL.");
}
- if (!MountEmulatedStorage(uid, mount_external, use_native_bridge, &error_msg)) {
+ ScopedUtfChars* package_name = nullptr;
+ const char* package_name_c_str = nullptr;
+ if (packageName != nullptr) {
+ package_name = new ScopedUtfChars(env, packageName);
+ package_name_c_str = package_name->c_str();
+ } else if (is_system_server) {
+ package_name_c_str = "android";
+ }
+ bool success = MountEmulatedStorage(uid, mount_external, use_native_bridge, &error_msg,
+ package_name_c_str);
+ delete package_name;
+ if (!success) {
ALOGW("Failed to mount emulated storage: %s (%s)", error_msg.c_str(), strerror(errno));
if (errno == ENOTCONN || errno == EROFS) {
// When device is actively encrypting, we get ENOTCONN here
@@ -858,7 +910,7 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
jint runtime_flags, jobjectArray rlimits,
jint mount_external, jstring se_info, jstring se_name,
jintArray fdsToClose, jintArray fdsToIgnore, jboolean is_child_zygote,
- jstring instructionSet, jstring appDataDir) {
+ jstring instructionSet, jstring appDataDir, jstring packageName) {
jlong capabilities = 0;
// Grant CAP_WAKE_ALARM to the Bluetooth process.
@@ -911,7 +963,7 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
capabilities, capabilities,
mount_external, se_info, se_name, false,
- is_child_zygote == JNI_TRUE, instructionSet, appDataDir);
+ is_child_zygote == JNI_TRUE, instructionSet, appDataDir, packageName);
}
return pid;
}
@@ -925,7 +977,7 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer(
SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
permittedCapabilities, effectiveCapabilities,
MOUNT_EXTERNAL_DEFAULT, NULL, NULL, true,
- false, NULL, NULL);
+ false, NULL, NULL, nullptr);
} else if (pid > 0) {
// The zygote process checks whether the child process has died or not.
ALOGI("System server process %d has been created", pid);
@@ -1006,7 +1058,7 @@ static const JNINativeMethod gMethods[] = {
{ "nativeSecurityInit", "()V",
(void *) com_android_internal_os_Zygote_nativeSecurityInit },
{ "nativeForkAndSpecialize",
- "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;)I",
+ "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
(void *) com_android_internal_os_Zygote_nativeForkAndSpecialize },
{ "nativeForkSystemServer", "(II[II[[IJJ)I",
(void *) com_android_internal_os_Zygote_nativeForkSystemServer },
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index 04e5658be5a0..ed7316a43156 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -97,7 +97,18 @@ message GlobalSettingsProto {
}
optional Auto auto = 16;
- optional SettingProto autofill_compat_mode_allowed_packages = 17 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ reserved 17; // Used to be autofill_compat_mode_allowed_packages
+
+ message Autofill {
+ option (android.msg_privacy).dest = DEST_EXPLICIT;
+
+ optional SettingProto compat_mode_allowed_packages = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto logging_level = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto max_partitions_size = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ optional SettingProto max_visible_datasets = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ }
+ optional Autofill autofill = 140;
+
optional SettingProto backup_agent_timeout_parameters = 18;
message Battery {
@@ -943,5 +954,5 @@ message GlobalSettingsProto {
// Please insert fields in alphabetical order and group them into messages
// if possible (to avoid reaching the method limit).
- // Next tag = 140;
+ // Next tag = 141;
}
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index ebf751c861bf..52c76ccaa639 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -62,6 +62,8 @@ message ActivityStackSupervisorProto {
optional .com.android.server.wm.ConfigurationContainerProto configuration_container = 1;
repeated ActivityDisplayProto displays = 2;
optional KeyguardControllerProto keyguard_controller = 3;
+ // TODO(b/111541062): Focused stack and resumed activity are now per-display. Topmost instances
+ // can be obtained from top display and these fields can be removed.
optional int32 focused_stack_id = 4;
optional .com.android.server.wm.IdentifierProto resumed_activity = 5;
// Whether or not the home activity is the recents activity. This is needed for the CTS tests to
@@ -77,6 +79,8 @@ message ActivityDisplayProto {
optional .com.android.server.wm.ConfigurationContainerProto configuration_container = 1;
optional int32 id = 2;
repeated ActivityStackProto stacks = 3;
+ optional int32 focused_stack_id = 4;
+ optional .com.android.server.wm.IdentifierProto resumed_activity = 5;
}
message ActivityStackProto {
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 44eea30cf09f..2f710bf4dd29 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3113,6 +3113,9 @@
the alpha channel of the outlineAmbientShadowColor (typically opaque), and the
{@link android.R.attr#ambientShadowAlpha} theme attribute. -->
<attr name="outlineAmbientShadowColor" format="color" />
+
+ <!-- Whether to allow the rendering system to force this View to render as light-on-dark. -->
+ <attr name="allowForceDark" format="boolean" />
</declare-styleable>
<!-- Attributes that can be assigned to a tag for a particular View. -->
@@ -7863,8 +7866,7 @@
wallpaper. -->
<attr name="showMetadataInPreview" format="boolean" />
- <!-- Wallpapers optimized and capable of drawing in ambient mode will return true.
- @hide -->
+ <!-- Wallpapers optimized and capable of drawing in ambient mode will return true. -->
<attr name="supportsAmbientMode" format="boolean" />
</declare-styleable>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 3053fa11f80a..5944108e8c10 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -593,10 +593,6 @@
<integer translatable="false" name="config_wifi_framework_wifi_score_entry_rssi_threshold_24GHz">-80</integer>
<integer translatable="false" name="config_wifi_framework_wifi_score_low_rssi_threshold_24GHz">-73</integer>
<integer translatable="false" name="config_wifi_framework_wifi_score_good_rssi_threshold_24GHz">-60</integer>
- <integer translatable="false" name="config_wifi_framework_wifi_score_bad_link_speed_24">6</integer>
- <integer translatable="false" name="config_wifi_framework_wifi_score_bad_link_speed_5">12</integer>
- <integer translatable="false" name="config_wifi_framework_wifi_score_good_link_speed_24">24</integer>
- <integer translatable="false" name="config_wifi_framework_wifi_score_good_link_speed_5">36</integer>
<!-- Integer delay in milliseconds before shutting down soft AP when there
are no connected devices. Framework will enforce a minimum limit on
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 618997159c80..e25bb79cee7b 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2907,6 +2907,8 @@
<public name="opticalInsetTop" />
<public name="opticalInsetRight" />
<public name="opticalInsetBottom" />
+ <public name="allowForceDark" />
+ <public name="supportsAmbientMode" />
</public-group>
<public-group type="style" first-id="0x010302e2">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 1c66b2b8ca41..86952d638fee 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -394,10 +394,6 @@
<java-symbol type="integer" name="config_wifi_framework_wifi_score_entry_rssi_threshold_5GHz" />
<java-symbol type="integer" name="config_wifi_framework_wifi_score_low_rssi_threshold_5GHz" />
<java-symbol type="integer" name="config_wifi_framework_wifi_score_good_rssi_threshold_5GHz" />
- <java-symbol type="integer" name="config_wifi_framework_wifi_score_bad_link_speed_24" />
- <java-symbol type="integer" name="config_wifi_framework_wifi_score_bad_link_speed_5" />
- <java-symbol type="integer" name="config_wifi_framework_wifi_score_good_link_speed_24" />
- <java-symbol type="integer" name="config_wifi_framework_wifi_score_good_link_speed_5" />
<java-symbol type="integer" name="config_wifi_framework_scan_result_rssi_level_patchup_value" />
<java-symbol type="integer" name="config_wifi_framework_current_network_boost" />
<java-symbol type="string" name="config_wifi_random_mac_oui" />
diff --git a/core/tests/coretests/src/android/database/DatabaseUtilsTest.java b/core/tests/coretests/src/android/database/DatabaseUtilsTest.java
new file mode 100644
index 000000000000..7c206d7eecf7
--- /dev/null
+++ b/core/tests/coretests/src/android/database/DatabaseUtilsTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.database;
+
+import static android.database.DatabaseUtils.bindSelection;
+
+import static org.junit.Assert.assertEquals;
+
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class DatabaseUtilsTest {
+ private static final Object[] ARGS = { "baz", 4, null };
+
+ @Test
+ public void testBindSelection_none() throws Exception {
+ assertEquals(null,
+ bindSelection(null, ARGS));
+ assertEquals("",
+ bindSelection("", ARGS));
+ assertEquals("foo=bar",
+ bindSelection("foo=bar", ARGS));
+ }
+
+ @Test
+ public void testBindSelection_normal() throws Exception {
+ assertEquals("foo='baz'",
+ bindSelection("foo=?", ARGS));
+ assertEquals("foo='baz' AND bar=4",
+ bindSelection("foo=? AND bar=?", ARGS));
+ assertEquals("foo='baz' AND bar=4 AND meow=NULL",
+ bindSelection("foo=? AND bar=? AND meow=?", ARGS));
+ }
+
+ @Test
+ public void testBindSelection_whitespace() throws Exception {
+ assertEquals("BETWEEN 5 AND 10",
+ bindSelection("BETWEEN? AND ?", 5, 10));
+ assertEquals("IN 'foo'",
+ bindSelection("IN?", "foo"));
+ }
+
+ @Test
+ public void testBindSelection_indexed() throws Exception {
+ assertEquals("foo=10 AND bar=11 AND meow=1",
+ bindSelection("foo=?10 AND bar=? AND meow=?1",
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12));
+ }
+}
diff --git a/core/tests/coretests/src/android/os/WorkSourceTest.java b/core/tests/coretests/src/android/os/WorkSourceTest.java
index 952a64dbeaaa..425ab8962307 100644
--- a/core/tests/coretests/src/android/os/WorkSourceTest.java
+++ b/core/tests/coretests/src/android/os/WorkSourceTest.java
@@ -378,6 +378,17 @@ public class WorkSourceTest extends TestCase {
assertEquals(75, ws1.getWorkChains().get(0).getAttributionUid());
}
+ public void testRemove_fromSameWorkSource() {
+ WorkSource ws1 = new WorkSource(50, "foo");
+ WorkSource ws2 = ws1;
+ ws2.add(ws1);
+ assertTrue(ws2.remove(ws1));
+
+ assertEquals(0, ws1.size());
+ assertEquals(50, ws1.get(0));
+ assertEquals("foo", ws1.getName(0));
+ }
+
public void testTransferWorkChains() {
WorkSource ws1 = new WorkSource();
WorkChain wc1 = ws1.createWorkChain().addNode(100, "tag");
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 60e512cb2c1c..37dec134b86e 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -119,6 +119,9 @@ public class SettingsBackupTest {
Settings.Global.ASSISTED_GPS_ENABLED,
Settings.Global.AUDIO_SAFE_VOLUME_STATE,
Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES,
+ Settings.Global.AUTOFILL_LOGGING_LEVEL,
+ Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE,
+ Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS,
Settings.Global.BATTERY_DISCHARGE_DURATION_THRESHOLD,
Settings.Global.BATTERY_DISCHARGE_THRESHOLD,
Settings.Global.BATTERY_SAVER_DEVICE_SPECIFIC_CONSTANTS,
diff --git a/core/tests/coretests/src/android/text/MeasuredParagraphTest.java b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
index 6f1d47d2dac2..f3d6013b8ee3 100644
--- a/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
+++ b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java
@@ -19,6 +19,7 @@ package android.text;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import android.content.Context;
import android.graphics.Typeface;
@@ -72,7 +73,7 @@ public class MeasuredParagraphTest {
assertEquals(0, mt.getWidths().size());
assertEquals(0, mt.getSpanEndCache().size());
assertEquals(0, mt.getFontMetrics().size());
- assertEquals(0, mt.getNativePtr());
+ assertNull(mt.getNativeMeasuredParagraph());
// Recycle it
MeasuredParagraph mt2 = MeasuredParagraph.buildForBidi("_VVV_", 1, 4, RTL, mt);
@@ -84,7 +85,7 @@ public class MeasuredParagraphTest {
assertEquals(0, mt2.getWidths().size());
assertEquals(0, mt2.getSpanEndCache().size());
assertEquals(0, mt2.getFontMetrics().size());
- assertEquals(0, mt2.getNativePtr());
+ assertNull(mt.getNativeMeasuredParagraph());
mt2.recycle();
}
@@ -106,7 +107,7 @@ public class MeasuredParagraphTest {
assertEquals(10, mt.getWidths().get(2), 0);
assertEquals(0, mt.getSpanEndCache().size());
assertEquals(0, mt.getFontMetrics().size());
- assertEquals(0, mt.getNativePtr());
+ assertNull(mt.getNativeMeasuredParagraph());
// Recycle it
MeasuredParagraph mt2 =
@@ -123,7 +124,7 @@ public class MeasuredParagraphTest {
assertEquals(5, mt2.getWidths().get(2), 0);
assertEquals(0, mt2.getSpanEndCache().size());
assertEquals(0, mt2.getFontMetrics().size());
- assertEquals(0, mt2.getNativePtr());
+ assertNull(mt.getNativeMeasuredParagraph());
mt2.recycle();
}
@@ -143,7 +144,7 @@ public class MeasuredParagraphTest {
assertEquals(1, mt.getSpanEndCache().size());
assertEquals(3, mt.getSpanEndCache().get(0));
assertNotEquals(0, mt.getFontMetrics().size());
- assertNotEquals(0, mt.getNativePtr());
+ assertNotNull(mt.getNativeMeasuredParagraph());
// Recycle it
MeasuredParagraph mt2 =
@@ -158,7 +159,7 @@ public class MeasuredParagraphTest {
assertEquals(1, mt2.getSpanEndCache().size());
assertEquals(4, mt2.getSpanEndCache().get(0));
assertNotEquals(0, mt2.getFontMetrics().size());
- assertNotEquals(0, mt2.getNativePtr());
+ assertNotNull(mt.getNativeMeasuredParagraph());
mt2.recycle();
}
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
index 993378d8a4d0..ec80d20e3179 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java
@@ -543,6 +543,27 @@ public class AccessibilityCacheTest {
}
}
+ @Test
+ public void addA11yFocusNodeBeforeFocusClearedEvent_previousA11yFocusNodeGetsRefreshed() {
+ AccessibilityNodeInfo nodeInfo1 = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
+ nodeInfo1.setAccessibilityFocused(true);
+ mAccessibilityCache.add(nodeInfo1);
+ AccessibilityNodeInfo nodeInfo2 = getNodeWithA11yAndWindowId(OTHER_VIEW_ID, WINDOW_ID_1);
+ nodeInfo2.setAccessibilityFocused(true);
+ mAccessibilityCache.add(nodeInfo2);
+ AccessibilityEvent event = AccessibilityEvent.obtain(
+ AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
+ event.setSource(getMockViewWithA11yAndWindowIds(SINGLE_VIEW_ID, WINDOW_ID_1));
+ mAccessibilityCache.onAccessibilityEvent(event);
+ event.recycle();
+ try {
+ verify(mAccessibilityNodeRefresher).refreshNode(nodeInfo1, true);
+ } finally {
+ nodeInfo1.recycle();
+ nodeInfo2.recycle();
+ }
+ }
+
private void assertNodeIsRefreshedWithEventType(int eventType, int contentChangeTypes) {
AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
mAccessibilityCache.add(nodeInfo);
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
index 44b1f088111a..0dd768530603 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
@@ -119,6 +119,10 @@ public class AccessibilityServiceConnectionImpl extends IAccessibilityServiceCon
return false;
}
+ public int getSoftKeyboardShowMode() {
+ return 0;
+ }
+
public void setSoftKeyboardCallbackEnabled(boolean enabled) {}
public boolean isAccessibilityButtonAvailable() {
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
index 20dc87205b2d..c0b0a3800e11 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java
@@ -31,6 +31,7 @@ import org.junit.runner.RunWith;
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@@ -62,11 +63,11 @@ public class BinderCallsStatsTest {
assertEquals(1, uidEntries.size());
BinderCallsStats.UidEntry uidEntry = uidEntries.get(TEST_UID);
Assert.assertNotNull(uidEntry);
- List<BinderCallsStats.CallStat> callStatsList = uidEntry.getCallStatsList();
+ List<BinderCallsStats.CallStat> callStatsList = new ArrayList(uidEntry.getCallStatsList());
assertEquals(1, uidEntry.callCount);
assertEquals(1, uidEntry.recordedCallCount);
assertEquals(10, uidEntry.cpuTimeMicros);
- assertEquals(binder.getClass().getName(), callStatsList.get(0).className);
+ assertEquals(binder.getClass(), callStatsList.get(0).binderClass);
assertEquals(1, callStatsList.get(0).transactionCode);
// CPU usage is sampled, should not be tracked here.
@@ -85,7 +86,7 @@ public class BinderCallsStatsTest {
assertEquals(3, uidEntry.callCount);
assertEquals(1, uidEntry.recordedCallCount);
// Still sampled even for another API.
- callStatsList = uidEntry.getCallStatsList();
+ callStatsList = new ArrayList(uidEntry.getCallStatsList());
assertEquals(2, callStatsList.size());
}
@@ -105,12 +106,12 @@ public class BinderCallsStatsTest {
Assert.assertNotNull(uidEntry);
assertEquals(1, uidEntry.callCount);
assertEquals(10, uidEntry.cpuTimeMicros);
- assertEquals(1, uidEntry.getCallStatsList().size());
+ assertEquals(1, new ArrayList(uidEntry.getCallStatsList()).size());
- List<BinderCallsStats.CallStat> callStatsList = uidEntry.getCallStatsList();
+ List<BinderCallsStats.CallStat> callStatsList = new ArrayList(uidEntry.getCallStatsList());
assertEquals(1, callStatsList.get(0).callCount);
assertEquals(10, callStatsList.get(0).cpuTimeMicros);
- assertEquals(binder.getClass().getName(), callStatsList.get(0).className);
+ assertEquals(binder.getClass(), callStatsList.get(0).binderClass);
assertEquals(1, callStatsList.get(0).transactionCode);
callSession = bcs.callStarted(binder, 1);
@@ -120,7 +121,7 @@ public class BinderCallsStatsTest {
uidEntry = bcs.getUidEntries().get(TEST_UID);
assertEquals(2, uidEntry.callCount);
assertEquals(30, uidEntry.cpuTimeMicros);
- callStatsList = uidEntry.getCallStatsList();
+ callStatsList = new ArrayList(uidEntry.getCallStatsList());
assertEquals(1, callStatsList.size());
callSession = bcs.callStarted(binder, 2);
@@ -131,7 +132,7 @@ public class BinderCallsStatsTest {
// This is the first transaction of a new type, so the real CPU time will be measured
assertEquals(80, uidEntry.cpuTimeMicros);
- callStatsList = uidEntry.getCallStatsList();
+ callStatsList = new ArrayList(uidEntry.getCallStatsList());
assertEquals(2, callStatsList.size());
}
@@ -182,7 +183,7 @@ public class BinderCallsStatsTest {
assertEquals(3, uidEntry.callCount);
assertEquals(60 /* 10 + 50 */, uidEntry.cpuTimeMicros);
- List<BinderCallsStats.CallStat> callStatsList = uidEntry.getCallStatsList();
+ List<BinderCallsStats.CallStat> callStatsList = new ArrayList(uidEntry.getCallStatsList());
assertEquals(1, callStatsList.size());
BinderCallsStats.CallStat callStats = callStatsList.get(0);
assertEquals(3, callStats.callCount);
@@ -216,7 +217,7 @@ public class BinderCallsStatsTest {
assertEquals(1, uidEntry.recordedCallCount);
assertEquals(10, uidEntry.cpuTimeMicros);
- List<BinderCallsStats.CallStat> callStatsList = uidEntry.getCallStatsList();
+ List<BinderCallsStats.CallStat> callStatsList = new ArrayList(uidEntry.getCallStatsList());
assertEquals(2, callStatsList.size());
BinderCallsStats.CallStat callStats = callStatsList.get(0);
@@ -248,7 +249,7 @@ public class BinderCallsStatsTest {
bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
List<BinderCallsStats.CallStat> callStatsList =
- bcs.getUidEntries().get(TEST_UID).getCallStatsList();
+ new ArrayList(bcs.getUidEntries().get(TEST_UID).getCallStatsList());
assertEquals(1, callStatsList.get(0).transactionCode);
assertEquals("resolved", callStatsList.get(0).methodName);
}
@@ -263,7 +264,7 @@ public class BinderCallsStatsTest {
bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
List<BinderCallsStats.CallStat> callStatsList =
- bcs.getUidEntries().get(TEST_UID).getCallStatsList();
+ new ArrayList(bcs.getUidEntries().get(TEST_UID).getCallStatsList());
assertEquals(REQUEST_SIZE, callStatsList.get(0).maxRequestSizeBytes);
assertEquals(REPLY_SIZE, callStatsList.get(0).maxReplySizeBytes);
@@ -283,7 +284,7 @@ public class BinderCallsStatsTest {
bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
List<BinderCallsStats.CallStat> callStatsList =
- bcs.getUidEntries().get(TEST_UID).getCallStatsList();
+ new ArrayList(bcs.getUidEntries().get(TEST_UID).getCallStatsList());
assertEquals(50, callStatsList.get(0).maxCpuTimeMicros);
}
@@ -302,7 +303,7 @@ public class BinderCallsStatsTest {
bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE);
List<BinderCallsStats.CallStat> callStatsList =
- bcs.getUidEntries().get(TEST_UID).getCallStatsList();
+ new ArrayList(bcs.getUidEntries().get(TEST_UID).getCallStatsList());
assertEquals(5, callStatsList.get(0).maxLatencyMicros);
}
@@ -388,6 +389,20 @@ public class BinderCallsStatsTest {
assertEquals(0, stat.exceptionCount);
}
+ @Test
+ public void testGetExportedStatsWithoutCalls() {
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ Binder binder = new Binder();
+ assertEquals(0, bcs.getExportedCallStats().size());
+ }
+
+ @Test
+ public void testGetExportedExceptionsWithoutCalls() {
+ TestBinderCallsStats bcs = new TestBinderCallsStats();
+ Binder binder = new Binder();
+ assertEquals(0, bcs.getExceptionCounts().size());
+ }
+
static class TestBinderCallsStats extends BinderCallsStats {
int callingUid = TEST_UID;
long time = 1234;
diff --git a/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java
index 6464ad3e9709..39bb84a20d7a 100644
--- a/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/ArrayUtilsTest.java
@@ -16,6 +16,8 @@
package com.android.internal.util;
+import static com.android.internal.util.ArrayUtils.concatElements;
+
import static org.junit.Assert.assertArrayEquals;
import junit.framework.TestCase;
@@ -156,23 +158,23 @@ public class ArrayUtilsTest extends TestCase {
public void testConcatEmpty() throws Exception {
assertArrayEquals(new Long[] {},
- ArrayUtils.concat(Long.class, null, null));
+ concatElements(Long.class, null, null));
assertArrayEquals(new Long[] {},
- ArrayUtils.concat(Long.class, new Long[] {}, null));
+ concatElements(Long.class, new Long[] {}, null));
assertArrayEquals(new Long[] {},
- ArrayUtils.concat(Long.class, null, new Long[] {}));
+ concatElements(Long.class, null, new Long[] {}));
assertArrayEquals(new Long[] {},
- ArrayUtils.concat(Long.class, new Long[] {}, new Long[] {}));
+ concatElements(Long.class, new Long[] {}, new Long[] {}));
}
- public void testConcat() throws Exception {
+ public void testconcatElements() throws Exception {
assertArrayEquals(new Long[] { 1L },
- ArrayUtils.concat(Long.class, new Long[] { 1L }, new Long[] {}));
+ concatElements(Long.class, new Long[] { 1L }, new Long[] {}));
assertArrayEquals(new Long[] { 1L },
- ArrayUtils.concat(Long.class, new Long[] {}, new Long[] { 1L }));
+ concatElements(Long.class, new Long[] {}, new Long[] { 1L }));
assertArrayEquals(new Long[] { 1L, 2L },
- ArrayUtils.concat(Long.class, new Long[] { 1L }, new Long[] { 2L }));
+ concatElements(Long.class, new Long[] { 1L }, new Long[] { 2L }));
assertArrayEquals(new Long[] { 1L, 2L, 3L, 4L },
- ArrayUtils.concat(Long.class, new Long[] { 1L, 2L }, new Long[] { 3L, 4L }));
+ concatElements(Long.class, new Long[] { 1L, 2L }, new Long[] { 3L, 4L }));
}
}
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index 97130f166eb2..ffe6abc13aae 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -501,7 +501,7 @@ public abstract class BaseCanvas {
contextStart - paraStart,
contextEnd - contextStart,
x, y, isRtl, paint.getNativeInstance(),
- mp.getNativePtr());
+ mp.getNativeMeasuredParagraph().getNativePtr());
return;
}
}
diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java
new file mode 100644
index 000000000000..9da61db94780
--- /dev/null
+++ b/graphics/java/android/graphics/fonts/Font.java
@@ -0,0 +1,472 @@
+/*
+ * 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.graphics.fonts;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.util.TypedValue;
+
+import com.android.internal.util.Preconditions;
+
+import dalvik.annotation.optimization.CriticalNative;
+
+import libcore.util.NativeAllocationRegistry;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+
+/**
+ * A font class can be used for creating FontFamily.
+ */
+public class Font {
+ private static final String TAG = "Font";
+
+ private static final int NOT_SPECIFIED = -1;
+ private static final int STYLE_ITALIC = 1;
+ private static final int STYLE_NORMAL = 0;
+
+ /**
+ * A font weight value for the thin weight
+ */
+ public static final int FONT_WEIGHT_THIN = 100;
+
+ /**
+ * A font weight value for the extra-light weight
+ */
+ public static final int FONT_WEIGHT_EXTRA_LIGHT = 200;
+
+ /**
+ * A font weight value for the light weight
+ */
+ public static final int FONT_WEIGHT_LIGHT = 300;
+
+ /**
+ * A font weight value for the normal weight
+ */
+ public static final int FONT_WEIGHT_NORMAL = 400;
+
+ /**
+ * A font weight value for the medium weight
+ */
+ public static final int FONT_WEIGHT_MEDIUM = 500;
+
+ /**
+ * A font weight value for the semi-bold weight
+ */
+ public static final int FONT_WEIGHT_SEMI_BOLD = 600;
+
+ /**
+ * A font weight value for the bold weight.
+ */
+ public static final int FONT_WEIGHT_BOLD = 700;
+
+ /**
+ * A font weight value for the extra-bold weight
+ */
+ public static final int FONT_WEIGHT_EXTRA_BOLD = 800;
+
+ /**
+ * A font weight value for the black weight
+ */
+ public static final int FONT_WEIGHT_BLACK = 900;
+
+ /**
+ * A builder class for creating new Font.
+ */
+ public static class Builder {
+ private static final NativeAllocationRegistry sAssetByteBufferRegistroy =
+ new NativeAllocationRegistry(ByteBuffer.class.getClassLoader(),
+ nGetReleaseNativeAssetFunc(), 64);
+
+ private static final NativeAllocationRegistry sFontRegistory =
+ new NativeAllocationRegistry(Font.class.getClassLoader(),
+ nGetReleaseNativeFont(), 64);
+
+ private @Nullable ByteBuffer mBuffer;
+ private @IntRange(from = -1, to = 1000) int mWeight = NOT_SPECIFIED;
+ private @IntRange(from = -1, to = 1) int mItalic = NOT_SPECIFIED;
+ private @IntRange(from = 0) int mTtcIndex = 0;
+ private @Nullable FontVariationAxis[] mAxes = null;
+
+ /**
+ * Constructs a builder with a byte buffer.
+ *
+ * Note that only direct buffer can be used as the source of font data.
+ *
+ * @see ByteBuffer#allocateDirect(int)
+ * @param buffer a byte buffer of a font data
+ */
+ public Builder(@NonNull ByteBuffer buffer) {
+ Preconditions.checkNotNull(buffer, "buffer can not be null");
+ if (!buffer.isDirect()) {
+ throw new IllegalArgumentException(
+ "Only direct buffer can be used as the source of font data.");
+ }
+ mBuffer = buffer;
+ }
+
+ /**
+ * Constructs a builder with a file path.
+ *
+ * @param path a file path to the font file
+ */
+ public Builder(@NonNull File path) throws IOException {
+ Preconditions.checkNotNull(path, "path can not be null");
+ try (FileInputStream fis = new FileInputStream(path)) {
+ final FileChannel fc = fis.getChannel();
+ mBuffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
+ }
+ }
+
+ /**
+ * Constructs a builder with a file descriptor.
+ *
+ * @param fd a file descriptor
+ */
+ public Builder(@NonNull FileDescriptor fd) throws IOException {
+ this(fd, 0, -1);
+ }
+
+ /**
+ * Constructs a builder with a file descriptor.
+ *
+ * @param fd a file descriptor
+ * @param offset an offset to of the font data in the file
+ * @param size a size of the font data. If -1 is passed, use until end of the file.
+ */
+ public Builder(@NonNull FileDescriptor fd, @IntRange(from = 0) long offset,
+ @IntRange(from = -1) long size) throws IOException {
+ try (FileInputStream fis = new FileInputStream(fd)) {
+ final FileChannel fc = fis.getChannel();
+ size = (size == -1) ? fc.size() - offset : size;
+ mBuffer = fc.map(FileChannel.MapMode.READ_ONLY, offset, size);
+ }
+ }
+
+ /**
+ * Constructs a builder from an asset manager and a file path in an asset directory.
+ *
+ * @param am the application's asset manager
+ * @param path the file name of the font data in the asset directory
+ */
+ public Builder(@NonNull AssetManager am, @NonNull String path) throws IOException {
+ final long nativeAsset = nGetNativeAsset(am, path, true /* is asset */, 0 /* cookie */);
+ if (nativeAsset == 0) {
+ throw new FileNotFoundException("Unable to open " + path);
+ }
+ final ByteBuffer b = nGetAssetBuffer(nativeAsset);
+ sAssetByteBufferRegistroy.registerNativeAllocation(b, nativeAsset);
+ if (b == null) {
+ throw new FileNotFoundException(path + " not found");
+ }
+ mBuffer = b;
+ }
+
+ /**
+ * Constructs a builder from resources.
+ *
+ * Resource ID must points the font file. XML font can not be used here.
+ *
+ * @param res the resource of this application.
+ * @param resId the resource ID of font file.
+ */
+ public Builder(@NonNull Resources res, int resId) throws IOException {
+ final TypedValue value = new TypedValue();
+ res.getValue(resId, value, true);
+ if (value.string == null) {
+ throw new FileNotFoundException(resId + " not found");
+ }
+ final String str = value.string.toString();
+ if (str.toLowerCase().endsWith(".xml")) {
+ throw new FileNotFoundException(resId + " must be font file.");
+ }
+ final long nativeAsset = nGetNativeAsset(res.getAssets(), str, false /* is asset */,
+ value.assetCookie);
+ if (nativeAsset == 0) {
+ throw new FileNotFoundException("Unable to open " + str);
+ }
+ final ByteBuffer b = nGetAssetBuffer(nativeAsset);
+ sAssetByteBufferRegistroy.registerNativeAllocation(b, nativeAsset);
+ if (b == null) {
+ throw new FileNotFoundException(str + " not found");
+ }
+ mBuffer = b;
+ }
+
+ /**
+ * Sets weight of the font.
+ *
+ * Tells the system the weight of the given font. If this function is not called, the system
+ * will resolve the weight value by reading font tables.
+ *
+ * Here are pairs of the common names and their values.
+ * <p>
+ * <table>
+ * <thead>
+ * <tr>
+ * <th align="center">Value</th>
+ * <th align="center">Name</th>
+ * <th align="center">Android Definition</th>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr>
+ * <td align="center">100</td>
+ * <td align="center">Thin</td>
+ * <td align="center">{@link Font#FONT_WEIGHT_THIN}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">200</td>
+ * <td align="center">Extra Light (Ultra Light)</td>
+ * <td align="center">{@link Font#FONT_WEIGHT_EXTRA_LIGHT}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">300</td>
+ * <td align="center">Light</td>
+ * <td align="center">{@link Font#FONT_WEIGHT_LIGHT}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">400</td>
+ * <td align="center">Normal (Regular)</td>
+ * <td align="center">{@link Font#FONT_WEIGHT_NORMAL}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">500</td>
+ * <td align="center">Medium</td>
+ * <td align="center">{@link Font#FONT_WEIGHT_MEDIUM}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">600</td>
+ * <td align="center">Semi Bold (Demi Bold)</td>
+ * <td align="center">{@link Font#FONT_WEIGHT_SEMI_BOLD}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">700</td>
+ * <td align="center">Bold</td>
+ * <td align="center">{@link Font#FONT_WEIGHT_BOLD}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">800</td>
+ * <td align="center">Extra Bold (Ultra Bold)</td>
+ * <td align="center">{@link Font#FONT_WEIGHT_EXTRA_BOLD}</td>
+ * </tr>
+ * <tr>
+ * <td align="center">900</td>
+ * <td align="center">Black (Heavy)</td>
+ * <td align="center">{@link Font#FONT_WEIGHT_BLACK}</td>
+ * </tr>
+ * </tbody>
+ * </p>
+ *
+ * @see Font#FONT_WEIGHT_THIN
+ * @see Font#FONT_WEIGHT_EXTRA_LIGHT
+ * @see Font#FONT_WEIGHT_LIGHT
+ * @see Font#FONT_WEIGHT_NORMAL
+ * @see Font#FONT_WEIGHT_MEDIUM
+ * @see Font#FONT_WEIGHT_SEMI_BOLD
+ * @see Font#FONT_WEIGHT_BOLD
+ * @see Font#FONT_WEIGHT_EXTRA_BOLD
+ * @see Font#FONT_WEIGHT_BLACK
+ * @param weight a weight value
+ * @return this builder
+ */
+ public @NonNull Builder setWeight(@IntRange(from = 1, to = 1000) int weight) {
+ Preconditions.checkArgument(1 <= weight && weight <= 1000);
+ mWeight = weight;
+ return this;
+ }
+
+ /**
+ * Sets italic information of the font.
+ *
+ * Tells the system the style of the given font. If this function is not called, the system
+ * will resolve the style by reading font tables.
+ *
+ * For example, if you want to use italic font as upright font, call {@code
+ * setItalic(false)} explicitly.
+ *
+ * @param italic {@code true} if the font is italic. Otherwise {@code false}.
+ * @return this builder
+ */
+ public @NonNull Builder setItalic(boolean italic) {
+ mItalic = italic ? STYLE_ITALIC : STYLE_NORMAL;
+ return this;
+ }
+
+ /**
+ * Sets an index of the font collection. See {@link android.R.attr#ttcIndex}.
+ *
+ * @param ttcIndex An index of the font collection. If the font source is not font
+ * collection, do not call this method or specify 0.
+ * @return this builder
+ */
+ public @NonNull Builder setTtcIndex(@IntRange(from = 0) int ttcIndex) {
+ mTtcIndex = ttcIndex;
+ return this;
+ }
+
+ /**
+ * Sets the font variation settings.
+ *
+ * @param variationSettings see {@link FontVariationAxis#fromFontVariationSettings(String)}
+ * @return this builder
+ * @throws IllegalArgumentException If given string is not a valid font variation settings
+ * format.
+ */
+ public @NonNull Builder setFontVariationSettings(@Nullable String variationSettings) {
+ mAxes = FontVariationAxis.fromFontVariationSettings(variationSettings);
+ return this;
+ }
+
+ /**
+ * Sets the font variation settings.
+ *
+ * @param axes an array of font variation axis tag-value pairs
+ * @return this builder
+ */
+ public @NonNull Builder setFontVariationSettings(@Nullable FontVariationAxis[] axes) {
+ mAxes = axes;
+ return this;
+ }
+
+ /**
+ * Creates the font based on the configured values.
+ * @return the Font object
+ */
+ public @Nullable Font build() {
+ if (mWeight == NOT_SPECIFIED || mItalic == NOT_SPECIFIED) {
+ final int packed = FontFileUtil.analyzeStyle(mBuffer, mTtcIndex, mAxes);
+ if (FontFileUtil.isSuccess(packed)) {
+ if (mWeight == NOT_SPECIFIED) {
+ mWeight = FontFileUtil.unpackWeight(packed);
+ }
+ if (mItalic == NOT_SPECIFIED) {
+ mItalic = FontFileUtil.unpackItalic(packed) ? STYLE_ITALIC : STYLE_NORMAL;
+ }
+ } else {
+ mWeight = 400;
+ mItalic = STYLE_NORMAL;
+ }
+ }
+ final boolean italic = (mItalic == STYLE_ITALIC);
+ final long builderPtr = nInitBuilder();
+ if (mAxes != null) {
+ for (FontVariationAxis axis : mAxes) {
+ nAddAxis(builderPtr, axis.getOpenTypeTagValue(), axis.getStyleValue());
+ }
+ }
+ final long ptr = nBuild(builderPtr, mBuffer, mWeight, italic, mTtcIndex);
+ final Font font = new Font(ptr, mWeight, italic, mTtcIndex, mAxes);
+ sFontRegistory.registerNativeAllocation(font, ptr);
+ return font;
+ }
+
+ /**
+ * Native methods for accessing underlying buffer in Asset
+ */
+ private static native long nGetNativeAsset(
+ @NonNull AssetManager am, @NonNull String path, boolean isAsset, int cookie);
+ private static native ByteBuffer nGetAssetBuffer(long nativeAsset);
+ @CriticalNative
+ private static native long nGetReleaseNativeAssetFunc();
+
+ /**
+ * Native methods for creating Font
+ */
+ private static native long nInitBuilder();
+ @CriticalNative
+ private static native void nAddAxis(long builderPtr, int tag, float value);
+ private static native long nBuild(
+ long builderPtr, ByteBuffer buffer, int weight, boolean italic, int ttcIndex);
+ @CriticalNative
+ private static native long nGetReleaseNativeFont();
+ }
+
+ private final long mNativePtr; // address of the shared ptr of minikin::Font
+ private final @IntRange(from = 0, to = 1000) int mWeight;
+ private final boolean mItalic;
+ private final @IntRange(from = 0) int mTtcIndex;
+ private final @Nullable FontVariationAxis[] mAxes;
+
+ /**
+ * Use Builder instead
+ */
+ private Font(long nativePtr, @IntRange(from = 0, to = 1000) int weight, boolean italic,
+ @IntRange(from = 0) int ttcIndex, @Nullable FontVariationAxis[] axes) {
+ mWeight = weight;
+ mItalic = italic;
+ mNativePtr = nativePtr;
+ mTtcIndex = ttcIndex;
+ mAxes = axes;
+ }
+
+ /**
+ * Get a weight value associated with this font.
+ *
+ * @see Builder#setWeight(int)
+ * @return a weight value
+ */
+ public @IntRange(from = 0, to = 1000)int getWeight() {
+ return mWeight;
+ }
+
+ /**
+ * Returns true if this font is marked as italic, otherwise returns false.
+ *
+ * @see Builder#setItalic(boolean)
+ * @return true if italic, otherwise false
+ */
+ public boolean isItalic() {
+ return mItalic;
+ }
+
+ /**
+ * Get a TTC index value associated with this font.
+ *
+ * If TTF/OTF file is provided, this value is always 0.
+ *
+ * @see Builder#setTtcIndex(int)
+ * @return a TTC index value
+ */
+ public @IntRange(from = 0) int getTtcIndex() {
+ return mTtcIndex;
+ }
+
+ /**
+ * Get a font variation settings associated with this font
+ *
+ * @see Builder#setFontVariationSettings(String)
+ * @see Builder#setFontVariationSettings(FontVariationAxis[])
+ * @return font variation settings
+ */
+ public @Nullable FontVariationAxis[] getAxes() {
+ return mAxes;
+ }
+
+ /** @hide */
+ public long getNativePtr() {
+ return mNativePtr;
+ }
+}
diff --git a/graphics/java/android/graphics/fonts/FontFileUtil.java b/graphics/java/android/graphics/fonts/FontFileUtil.java
index d15f581f918c..f8b456b982c5 100644
--- a/graphics/java/android/graphics/fonts/FontFileUtil.java
+++ b/graphics/java/android/graphics/fonts/FontFileUtil.java
@@ -20,7 +20,6 @@ import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -46,6 +45,13 @@ public class FontFileUtil {
return (packed & 0x10000) != 0;
}
+ /**
+ * Returns true if the analyzeStyle succeeded
+ */
+ public static boolean isSuccess(int packed) {
+ return packed != ANALYZE_ERROR;
+ }
+
private static int pack(@IntRange(from = 0, to = 1000) int weight, boolean italic) {
return weight | (italic ? 0x10000 : 0);
}
@@ -55,12 +61,13 @@ public class FontFileUtil {
private static final int TTC_TAG = 0x74746366;
private static final int OS2_TABLE_TAG = 0x4F532F32;
+ private static final int ANALYZE_ERROR = 0xFFFFFFFF;
+
/**
* Analyze the font file returns packed style info
*/
public static final int analyzeStyle(@NonNull ByteBuffer buffer,
- @IntRange(from = 0) int ttcIndex, @Nullable FontVariationAxis[] varSettings)
- throws IOException {
+ @IntRange(from = 0) int ttcIndex, @Nullable FontVariationAxis[] varSettings) {
int weight = -1;
int italic = -1;
if (varSettings != null) {
@@ -88,7 +95,7 @@ public class FontFileUtil {
if (magicNumber == TTC_TAG) {
// TTC file.
if (ttcIndex >= buffer.getInt(8 /* offset to number of fonts in TTC */)) {
- throw new IOException("Font index out of bounds");
+ return ANALYZE_ERROR;
}
fontFileOffset = buffer.getInt(
12 /* offset to array of offsets of font files */ + 4 * ttcIndex);
@@ -96,7 +103,7 @@ public class FontFileUtil {
int sfntVersion = buffer.getInt(fontFileOffset);
if (sfntVersion != SFNT_VERSION_1 && sfntVersion != SFNT_VERSION_OTTO) {
- throw new IOException("Unknown font file format");
+ return ANALYZE_ERROR;
}
int numTables = buffer.getShort(fontFileOffset + 4 /* offset to number of tables */);
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 59760ab0a20f..8d0b615c7ba0 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -230,7 +230,6 @@ cc_defaults {
"RenderProperties.cpp",
"ResourceCache.cpp",
"SkiaCanvas.cpp",
- "SkiaCanvasProxy.cpp",
"Snapshot.cpp",
"Texture.cpp",
"VectorDrawable.cpp",
diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp
deleted file mode 100644
index fc009d871620..000000000000
--- a/libs/hwui/SkiaCanvasProxy.cpp
+++ /dev/null
@@ -1,486 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "SkiaCanvasProxy.h"
-
-#include <memory>
-
-#include <log/log.h>
-
-#include <SkLatticeIter.h>
-#include <SkPaint.h>
-#include <SkPatchUtils.h>
-#include <SkPath.h>
-#include <SkPixelRef.h>
-#include <SkRRect.h>
-#include <SkRSXform.h>
-#include <SkRect.h>
-#include <SkSurface.h>
-#include <SkTextBlobRunIterator.h>
-#include <SkVertices.h>
-#include "hwui/Bitmap.h"
-
-namespace android {
-namespace uirenderer {
-
-SkiaCanvasProxy::SkiaCanvasProxy(Canvas* canvas, bool filterHwuiCalls)
- : INHERITED(canvas->width(), canvas->height())
- , mCanvas(canvas)
- , mFilterHwuiCalls(filterHwuiCalls) {}
-
-void SkiaCanvasProxy::onDrawPaint(const SkPaint& paint) {
- mCanvas->drawPaint(paint);
-}
-
-void SkiaCanvasProxy::onDrawPoints(PointMode pointMode, size_t count, const SkPoint pts[],
- const SkPaint& paint) {
- if (!pts || count == 0) {
- return;
- }
-
- // convert the SkPoints into floats
- static_assert(sizeof(SkPoint) == sizeof(float) * 2, "SkPoint is no longer two floats");
- const size_t floatCount = count << 1;
- const float* floatArray = &pts[0].fX;
-
- switch (pointMode) {
- case kPoints_PointMode: {
- mCanvas->drawPoints(floatArray, floatCount, paint);
- break;
- }
- case kLines_PointMode: {
- mCanvas->drawLines(floatArray, floatCount, paint);
- break;
- }
- case kPolygon_PointMode: {
- SkPaint strokedPaint(paint);
- strokedPaint.setStyle(SkPaint::kStroke_Style);
-
- SkPath path;
- for (size_t i = 0; i < count - 1; i++) {
- path.moveTo(pts[i]);
- path.lineTo(pts[i + 1]);
- this->drawPath(path, strokedPaint);
- path.rewind();
- }
- break;
- }
- default:
- LOG_ALWAYS_FATAL("Unknown point type");
- }
-}
-
-void SkiaCanvasProxy::onDrawOval(const SkRect& rect, const SkPaint& paint) {
- mCanvas->drawOval(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, paint);
-}
-
-void SkiaCanvasProxy::onDrawRect(const SkRect& rect, const SkPaint& paint) {
- mCanvas->drawRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, paint);
-}
-
-void SkiaCanvasProxy::onDrawRRect(const SkRRect& roundRect, const SkPaint& paint) {
- if (!roundRect.isComplex()) {
- const SkRect& rect = roundRect.rect();
- SkVector radii = roundRect.getSimpleRadii();
- mCanvas->drawRoundRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, radii.fX, radii.fY,
- paint);
- } else {
- SkPath path;
- path.addRRect(roundRect);
- mCanvas->drawPath(path, paint);
- }
-}
-
-void SkiaCanvasProxy::onDrawArc(const SkRect& rect, SkScalar startAngle, SkScalar sweepAngle,
- bool useCenter, const SkPaint& paint) {
- mCanvas->drawArc(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, startAngle, sweepAngle,
- useCenter, paint);
-}
-
-void SkiaCanvasProxy::onDrawPath(const SkPath& path, const SkPaint& paint) {
- mCanvas->drawPath(path, paint);
-}
-
-void SkiaCanvasProxy::onDrawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
- const SkPaint* paint) {
- sk_sp<Bitmap> hwuiBitmap = Bitmap::createFrom(bitmap.info(), *bitmap.pixelRef());
- // HWUI doesn't support extractSubset(), so convert any subsetted bitmap into
- // a drawBitmapRect(); pass through an un-subsetted bitmap.
- if (hwuiBitmap && bitmap.dimensions() != hwuiBitmap->info().dimensions()) {
- SkIPoint origin = bitmap.pixelRefOrigin();
- mCanvas->drawBitmap(
- *hwuiBitmap, origin.fX, origin.fY, origin.fX + bitmap.dimensions().width(),
- origin.fY + bitmap.dimensions().height(), left, top,
- left + bitmap.dimensions().width(), top + bitmap.dimensions().height(), paint);
- } else {
- mCanvas->drawBitmap(*hwuiBitmap, left, top, paint);
- }
-}
-
-void SkiaCanvasProxy::onDrawBitmapRect(const SkBitmap& skBitmap, const SkRect* srcPtr,
- const SkRect& dst, const SkPaint* paint, SrcRectConstraint) {
- SkRect src = (srcPtr) ? *srcPtr : SkRect::MakeWH(skBitmap.width(), skBitmap.height());
- // TODO: if bitmap is a subset, do we need to add pixelRefOrigin to src?
- Bitmap* bitmap = reinterpret_cast<Bitmap*>(skBitmap.pixelRef());
- mCanvas->drawBitmap(*bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom, dst.fLeft, dst.fTop,
- dst.fRight, dst.fBottom, paint);
-}
-
-void SkiaCanvasProxy::onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
- const SkRect& dst, const SkPaint*) {
- // TODO make nine-patch drawing a method on Canvas.h
- SkDEBUGFAIL("SkiaCanvasProxy::onDrawBitmapNine is not yet supported");
-}
-
-void SkiaCanvasProxy::onDrawImage(const SkImage* image, SkScalar left, SkScalar top,
- const SkPaint* paint) {
- SkBitmap skiaBitmap;
- SkPixmap pixmap;
- if (image->peekPixels(&pixmap) && skiaBitmap.installPixels(pixmap)) {
- onDrawBitmap(skiaBitmap, left, top, paint);
- }
-}
-
-void SkiaCanvasProxy::onDrawImageRect(const SkImage* image, const SkRect* srcPtr, const SkRect& dst,
- const SkPaint* paint, SrcRectConstraint constraint) {
- SkBitmap skiaBitmap;
- SkPixmap pixmap;
- if (image->peekPixels(&pixmap) && skiaBitmap.installPixels(pixmap)) {
- sk_sp<Bitmap> bitmap = Bitmap::createFrom(skiaBitmap.info(), *skiaBitmap.pixelRef());
- SkRect src = (srcPtr) ? *srcPtr : SkRect::MakeWH(image->width(), image->height());
- mCanvas->drawBitmap(*bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom, dst.fLeft,
- dst.fTop, dst.fRight, dst.fBottom, paint);
- }
-}
-
-void SkiaCanvasProxy::onDrawImageNine(const SkImage*, const SkIRect& center, const SkRect& dst,
- const SkPaint*) {
- SkDEBUGFAIL("SkiaCanvasProxy::onDrawImageNine is not yet supported");
-}
-
-void SkiaCanvasProxy::onDrawImageLattice(const SkImage* image, const Lattice& lattice,
- const SkRect& dst, const SkPaint* paint) {
- SkLatticeIter iter(lattice, dst);
- SkRect srcR, dstR;
- while (iter.next(&srcR, &dstR)) {
- onDrawImageRect(image, &srcR, dstR, paint, SkCanvas::kFast_SrcRectConstraint);
- }
-}
-
-void SkiaCanvasProxy::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode bmode,
- const SkPaint& paint) {
- if (mFilterHwuiCalls) {
- return;
- }
- mCanvas->drawVertices(vertices, bmode, paint);
-}
-
-sk_sp<SkSurface> SkiaCanvasProxy::onNewSurface(const SkImageInfo&, const SkSurfaceProps&) {
- SkDEBUGFAIL("SkiaCanvasProxy::onNewSurface is not supported");
- return NULL;
-}
-
-void SkiaCanvasProxy::willSave() {
- mCanvas->save(android::SaveFlags::MatrixClip);
-}
-
-static inline SaveFlags::Flags saveFlags(SkCanvas::SaveLayerFlags layerFlags) {
- SaveFlags::Flags saveFlags = 0;
-
- if (!(layerFlags & SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag)) {
- saveFlags |= SaveFlags::ClipToLayer;
- }
-
- return saveFlags;
-}
-
-SkCanvas::SaveLayerStrategy SkiaCanvasProxy::getSaveLayerStrategy(
- const SaveLayerRec& saveLayerRec) {
- SkRect rect;
- if (saveLayerRec.fBounds) {
- rect = *saveLayerRec.fBounds;
- } else if (!mCanvas->getClipBounds(&rect)) {
- rect = SkRect::MakeEmpty();
- }
- mCanvas->saveLayer(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, saveLayerRec.fPaint,
- saveFlags(saveLayerRec.fSaveLayerFlags));
- return SkCanvas::kNoLayer_SaveLayerStrategy;
-}
-
-void SkiaCanvasProxy::willRestore() {
- mCanvas->restore();
-}
-
-void SkiaCanvasProxy::didConcat(const SkMatrix& matrix) {
- mCanvas->concat(matrix);
-}
-
-void SkiaCanvasProxy::didSetMatrix(const SkMatrix& matrix) {
- mCanvas->setMatrix(matrix);
-}
-
-void SkiaCanvasProxy::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
- const SkPaint& paint) {
- SkPath path;
- path.addRRect(outer);
- path.addRRect(inner);
- path.setFillType(SkPath::kEvenOdd_FillType);
- this->drawPath(path, paint);
-}
-
-/**
- * Utility class that converts the incoming text & paint from the given encoding
- * into glyphIDs.
- */
-class GlyphIDConverter {
-public:
- GlyphIDConverter(const void* text, size_t byteLength, const SkPaint& origPaint) {
- paint = origPaint;
- if (paint.getTextEncoding() == SkPaint::kGlyphID_TextEncoding) {
- glyphIDs = (uint16_t*)text;
- count = byteLength >> 1;
- } else {
- // ensure space for one glyph per ID given UTF8 encoding.
- storage.reset(new uint16_t[byteLength]);
- glyphIDs = storage.get();
- count = paint.textToGlyphs(text, byteLength, storage.get());
- paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
- }
- }
-
- SkPaint paint;
- uint16_t* glyphIDs;
- int count;
-
-private:
- std::unique_ptr<uint16_t[]> storage;
-};
-
-void SkiaCanvasProxy::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
- const SkPaint& origPaint) {
- // convert to glyphIDs if necessary
- GlyphIDConverter glyphs(text, byteLength, origPaint);
-
- // compute the glyph positions
- std::unique_ptr<SkScalar[]> glyphWidths(new SkScalar[glyphs.count]);
- glyphs.paint.getTextWidths(glyphs.glyphIDs, glyphs.count << 1, glyphWidths.get());
-
- // compute conservative bounds
- // NOTE: We could call the faster paint.getFontBounds for a less accurate,
- // but even more conservative bounds if this is too slow.
- SkRect bounds;
- glyphs.paint.measureText(glyphs.glyphIDs, glyphs.count << 1, &bounds);
-
- // adjust for non-left alignment
- if (glyphs.paint.getTextAlign() != SkPaint::kLeft_Align) {
- SkScalar stop = 0;
- for (int i = 0; i < glyphs.count; i++) {
- stop += glyphWidths[i];
- }
- if (glyphs.paint.getTextAlign() == SkPaint::kCenter_Align) {
- stop = SkScalarHalf(stop);
- }
- if (glyphs.paint.isVerticalText()) {
- y -= stop;
- } else {
- x -= stop;
- }
- }
-
- // setup the first glyph position and adjust bounds if needed
- int xBaseline = 0;
- int yBaseline = 0;
- if (mCanvas->drawTextAbsolutePos()) {
- bounds.offset(x, y);
- xBaseline = x;
- yBaseline = y;
- }
-
- static_assert(sizeof(SkPoint) == sizeof(float) * 2, "SkPoint is no longer two floats");
- auto glyphFunc = [&](uint16_t* text, float* positions) {
- memcpy(text, glyphs.glyphIDs, glyphs.count * sizeof(uint16_t));
- size_t posIndex = 0;
- // setup the first glyph position
- positions[posIndex++] = xBaseline;
- positions[posIndex++] = yBaseline;
- // setup the remaining glyph positions
- if (glyphs.paint.isVerticalText()) {
- float yPosition = yBaseline;
- for (int i = 1; i < glyphs.count; i++) {
- positions[posIndex++] = xBaseline;
- yPosition += glyphWidths[i - 1];
- positions[posIndex++] = yPosition;
- }
- } else {
- float xPosition = xBaseline;
- for (int i = 1; i < glyphs.count; i++) {
- xPosition += glyphWidths[i - 1];
- positions[posIndex++] = xPosition;
- positions[posIndex++] = yBaseline;
- }
- }
- };
- mCanvas->drawGlyphs(glyphFunc, glyphs.count, glyphs.paint, x, y, bounds.fLeft, bounds.fTop,
- bounds.fRight, bounds.fBottom, 0);
-}
-
-void SkiaCanvasProxy::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
- const SkPaint& origPaint) {
- // convert to glyphIDs if necessary
- GlyphIDConverter glyphs(text, byteLength, origPaint);
-
- // convert to relative positions if necessary
- int x, y;
- if (mCanvas->drawTextAbsolutePos()) {
- x = 0;
- y = 0;
- } else {
- x = pos[0].fX;
- y = pos[0].fY;
- }
-
- // Compute conservative bounds. If the content has already been processed
- // by Minikin then it had already computed these bounds. Unfortunately,
- // there is no way to capture those bounds as part of the Skia drawPosText
- // API so we need to do that computation again here.
- SkRect bounds = SkRect::MakeEmpty();
- for (int i = 0; i < glyphs.count; i++) {
- SkRect glyphBounds = SkRect::MakeEmpty();
- glyphs.paint.measureText(&glyphs.glyphIDs[i], sizeof(uint16_t), &glyphBounds);
- glyphBounds.offset(pos[i].fX, pos[i].fY);
- bounds.join(glyphBounds);
- }
-
- static_assert(sizeof(SkPoint) == sizeof(float) * 2, "SkPoint is no longer two floats");
- auto glyphFunc = [&](uint16_t* text, float* positions) {
- memcpy(text, glyphs.glyphIDs, glyphs.count * sizeof(uint16_t));
- if (mCanvas->drawTextAbsolutePos()) {
- memcpy(positions, pos, 2 * glyphs.count * sizeof(float));
- } else {
- for (int i = 0, posIndex = 0; i < glyphs.count; i++) {
- positions[posIndex++] = pos[i].fX - x;
- positions[posIndex++] = pos[i].fY - y;
- }
- }
- };
- mCanvas->drawGlyphs(glyphFunc, glyphs.count, glyphs.paint, x, y, bounds.fLeft, bounds.fTop,
- bounds.fRight, bounds.fBottom, 0);
-}
-
-void SkiaCanvasProxy::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
- SkScalar constY, const SkPaint& paint) {
- const size_t pointCount = byteLength >> 1;
- std::unique_ptr<SkPoint[]> pts(new SkPoint[pointCount]);
- for (size_t i = 0; i < pointCount; i++) {
- pts[i].set(xpos[i], constY);
- }
- this->onDrawPosText(text, byteLength, pts.get(), paint);
-}
-
-void SkiaCanvasProxy::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
- const SkMatrix* matrix, const SkPaint& origPaint) {
- SkDEBUGFAIL("SkiaCanvasProxy::onDrawTextOnPath is not supported");
-}
-
-void SkiaCanvasProxy::onDrawTextRSXform(const void* text, size_t byteLength,
- const SkRSXform xform[], const SkRect* cullRect,
- const SkPaint& paint) {
- GlyphIDConverter glyphs(text, byteLength, paint); // Just get count
- SkMatrix localM, currM, origM;
- mCanvas->getMatrix(&currM);
- origM = currM;
- for (int i = 0; i < glyphs.count; i++) {
- localM.setRSXform(*xform++);
- currM.setConcat(origM, localM);
- mCanvas->setMatrix(currM);
- this->onDrawText((char*)text + (byteLength / glyphs.count * i), byteLength / glyphs.count,
- 0, 0, paint);
- }
- mCanvas->setMatrix(origM);
-}
-
-void SkiaCanvasProxy::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
- const SkPaint& paint) {
- SkPaint runPaint = paint;
-
- SkTextBlobRunIterator it(blob);
- for (; !it.done(); it.next()) {
- size_t textLen = it.glyphCount() * sizeof(uint16_t);
- const SkPoint& offset = it.offset();
- // applyFontToPaint() always overwrites the exact same attributes,
- // so it is safe to not re-seed the paint for this reason.
- it.applyFontToPaint(&runPaint);
-
- switch (it.positioning()) {
- case SkTextBlob::kDefault_Positioning:
- this->drawText(it.glyphs(), textLen, x + offset.x(), y + offset.y(), runPaint);
- break;
- case SkTextBlob::kHorizontal_Positioning: {
- std::unique_ptr<SkPoint[]> pts(new SkPoint[it.glyphCount()]);
- for (size_t i = 0; i < it.glyphCount(); i++) {
- pts[i].set(x + offset.x() + it.pos()[i], y + offset.y());
- }
- this->drawPosText(it.glyphs(), textLen, pts.get(), runPaint);
- break;
- }
- case SkTextBlob::kFull_Positioning: {
- std::unique_ptr<SkPoint[]> pts(new SkPoint[it.glyphCount()]);
- for (size_t i = 0; i < it.glyphCount(); i++) {
- const size_t xIndex = i * 2;
- const size_t yIndex = xIndex + 1;
- pts[i].set(x + offset.x() + it.pos()[xIndex],
- y + offset.y() + it.pos()[yIndex]);
- }
- this->drawPosText(it.glyphs(), textLen, pts.get(), runPaint);
- break;
- }
- default:
- SK_ABORT("unhandled positioning mode");
- }
- }
-}
-
-void SkiaCanvasProxy::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
- const SkPoint texCoords[4], SkBlendMode bmode,
- const SkPaint& paint) {
- if (mFilterHwuiCalls) {
- return;
- }
- SkMatrix matrix;
- mCanvas->getMatrix(&matrix);
- SkISize lod = SkPatchUtils::GetLevelOfDetail(cubics, &matrix);
-
- mCanvas->drawVertices(
- SkPatchUtils::MakeVertices(cubics, colors, texCoords, lod.width(), lod.height()).get(),
- bmode, paint);
-}
-
-void SkiaCanvasProxy::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle) {
- mCanvas->clipRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, op);
-}
-
-void SkiaCanvasProxy::onClipRRect(const SkRRect& roundRect, SkClipOp op, ClipEdgeStyle) {
- SkPath path;
- path.addRRect(roundRect);
- mCanvas->clipPath(&path, op);
-}
-
-void SkiaCanvasProxy::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle) {
- mCanvas->clipPath(&path, op);
-}
-
-}; // namespace uirenderer
-}; // namespace android
diff --git a/libs/hwui/SkiaCanvasProxy.h b/libs/hwui/SkiaCanvasProxy.h
deleted file mode 100644
index 360d5a08b974..000000000000
--- a/libs/hwui/SkiaCanvasProxy.h
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef SkiaCanvasProxy_DEFINED
-#define SkiaCanvasProxy_DEFINED
-
-#include <SkCanvas.h>
-#include <cutils/compiler.h>
-
-#include "hwui/Canvas.h"
-
-namespace android {
-namespace uirenderer {
-
-/**
- * This class serves as a proxy between Skia's SkCanvas and Android Framework's
- * Canvas. The class does not maintain any draw-related state and will pass
- * through most requests directly to the Canvas provided in the constructor.
- *
- * Upon construction it is expected that the provided Canvas has already been
- * prepared for recording and will continue to be in the recording state while
- * this proxy class is being used.
- *
- * If filterHwuiCalls is true, the proxy silently ignores away draw calls that
- * aren't supported by HWUI.
- */
-class ANDROID_API SkiaCanvasProxy : public SkCanvas {
-public:
- explicit SkiaCanvasProxy(Canvas* canvas, bool filterHwuiCalls = false);
- virtual ~SkiaCanvasProxy() {}
-
-protected:
- virtual sk_sp<SkSurface> onNewSurface(const SkImageInfo&, const SkSurfaceProps&) override;
-
- virtual void willSave() override;
- virtual SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec&) override;
- virtual void willRestore() override;
-
- virtual void didConcat(const SkMatrix&) override;
- virtual void didSetMatrix(const SkMatrix&) override;
-
- virtual void onDrawPaint(const SkPaint& paint) override;
- virtual void onDrawPoints(PointMode, size_t count, const SkPoint pts[],
- const SkPaint&) override;
- virtual void onDrawOval(const SkRect&, const SkPaint&) override;
- virtual void onDrawRect(const SkRect&, const SkPaint&) override;
- virtual void onDrawRRect(const SkRRect&, const SkPaint&) override;
- virtual void onDrawPath(const SkPath& path, const SkPaint&) override;
- virtual void onDrawArc(const SkRect&, SkScalar startAngle, SkScalar sweepAngle, bool useCenter,
- const SkPaint&) override;
- virtual void onDrawBitmap(const SkBitmap&, SkScalar left, SkScalar top,
- const SkPaint*) override;
- virtual void onDrawBitmapRect(const SkBitmap&, const SkRect* src, const SkRect& dst,
- const SkPaint* paint, SrcRectConstraint) override;
- virtual void onDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center, const SkRect& dst,
- const SkPaint*) override;
- virtual void onDrawImage(const SkImage*, SkScalar dx, SkScalar dy, const SkPaint*);
- virtual void onDrawImageRect(const SkImage*, const SkRect*, const SkRect&, const SkPaint*,
- SrcRectConstraint);
- virtual void onDrawImageNine(const SkImage*, const SkIRect& center, const SkRect& dst,
- const SkPaint*);
- virtual void onDrawImageLattice(const SkImage*, const Lattice& lattice, const SkRect& dst,
- const SkPaint*);
- virtual void onDrawVerticesObject(const SkVertices*, SkBlendMode, const SkPaint&) override;
-
- virtual void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override;
-
- virtual void onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
- const SkPaint&) override;
- virtual void onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[],
- const SkPaint&) override;
- virtual void onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
- SkScalar constY, const SkPaint&) override;
- virtual void onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path,
- const SkMatrix* matrix, const SkPaint&) override;
- virtual void onDrawTextRSXform(const void* text, size_t byteLength, const SkRSXform[],
- const SkRect* cullRect, const SkPaint& paint);
- virtual void onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
- const SkPaint& paint) override;
-
- virtual void onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
- const SkPoint texCoords[4], SkBlendMode,
- const SkPaint& paint) override;
-
- virtual void onClipRect(const SkRect&, SkClipOp, ClipEdgeStyle) override;
- virtual void onClipRRect(const SkRRect&, SkClipOp, ClipEdgeStyle) override;
- virtual void onClipPath(const SkPath&, SkClipOp, ClipEdgeStyle) override;
-
-private:
- Canvas* mCanvas;
- bool mFilterHwuiCalls;
-
- typedef SkCanvas INHERITED;
-};
-
-}; // namespace uirenderer
-}; // namespace android
-
-#endif // SkiaCanvasProxy_DEFINED
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 5d380a68631f..b9af7de26721 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -74,7 +74,6 @@ typedef uint32_t Flags;
} // namespace SaveFlags
namespace uirenderer {
-class SkiaCanvasProxy;
namespace VectorDrawable {
class Tree;
};
@@ -305,7 +304,6 @@ protected:
friend class DrawTextFunctor;
friend class DrawTextOnPathFunctor;
- friend class uirenderer::SkiaCanvasProxy;
};
}; // namespace android
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index 9a72706e44dd..69641d57ccbb 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -17,11 +17,14 @@
#ifndef VULKANMANAGER_H
#define VULKANMANAGER_H
+#if !defined(VK_USE_PLATFORM_ANDROID_KHR)
+# define VK_USE_PLATFORM_ANDROID_KHR
+#endif
+#include <vulkan/vulkan.h>
+
#include <SkSurface.h>
#include <vk/GrVkBackendContext.h>
-#include <vulkan/vulkan.h>
-
namespace android {
namespace uirenderer {
namespace renderthread {
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index dccfd7a87ba7..d5dd46caca7b 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -370,8 +370,6 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
* after current data source is finished.
*
* @param dsd the descriptor of data source you want to play after current one
- * @throws IllegalStateException if it is called in an invalid state
- * @throws NullPointerException if dsd is null
*/
@Override
public void setNextDataSource(@NonNull DataSourceDesc dsd) {
@@ -384,14 +382,8 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
mNextDSDs.add(dsd);
mNextSrcId = mSrcIdGenerator++;
mNextSourceState = NEXT_SOURCE_STATE_INIT;
- mNextSourcePlayPending = false;
- }
- int state = getState();
- if (state != PLAYER_STATE_IDLE) {
- synchronized (mSrcLock) {
- prepareNextDataSource_l();
- }
}
+ prepareNextDataSource();
}
});
}
@@ -400,8 +392,6 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
* Sets a list of data sources to be played sequentially after current data source is done.
*
* @param dsds the list of data sources you want to play after current one
- * @throws IllegalStateException if it is called in an invalid state
- * @throws IllegalArgumentException if dsds is null or empty, or contains null DataSourceDesc
*/
@Override
public void setNextDataSources(@NonNull List<DataSourceDesc> dsds) {
@@ -422,14 +412,8 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
mNextDSDs = new ArrayList(dsds);
mNextSrcId = mSrcIdGenerator++;
mNextSourceState = NEXT_SOURCE_STATE_INIT;
- mNextSourcePlayPending = false;
- }
- int state = getState();
- if (state != PLAYER_STATE_IDLE) {
- synchronized (mSrcLock) {
- prepareNextDataSource_l();
- }
}
+ prepareNextDataSource();
}
});
}
@@ -904,67 +888,84 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
private native void nativeHandleDataSourceCallback(
boolean isCurrent, long srcId, Media2DataSource dataSource);
- // This function shall be called with |mSrcLock| acquired.
- private void prepareNextDataSource_l() {
- if (mNextDSDs == null || mNextDSDs.isEmpty()
- || mNextSourceState != NEXT_SOURCE_STATE_INIT) {
- // There is no next source or it's in preparing or prepared state.
- return;
+ // This function should be always called on |mHandlerThread|.
+ private void prepareNextDataSource() {
+ if (Looper.myLooper() != mHandlerThread.getLooper()) {
+ Log.e(TAG, "prepareNextDataSource: called on wrong looper");
}
- try {
- mNextSourceState = NEXT_SOURCE_STATE_PREPARING;
- handleDataSource(false /* isCurrent */, mNextDSDs.get(0), mNextSrcId);
- } catch (Exception e) {
- Message msg2 = mTaskHandler.obtainMessage(
- MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null);
- final long nextSrcId = mNextSrcId;
- mTaskHandler.post(new Runnable() {
- @Override
- public void run() {
- mTaskHandler.handleMessage(msg2, nextSrcId);
- }
- });
- }
- }
-
- // This function shall be called with |mSrcLock| acquired.
- private void playNextDataSource_l() {
- if (mNextDSDs == null || mNextDSDs.isEmpty()) {
+ int state = getState();
+ if (state == PLAYER_STATE_ERROR || state == PLAYER_STATE_IDLE) {
+ // Current source has not been prepared yet.
return;
}
- if (mNextSourceState == NEXT_SOURCE_STATE_PREPARED) {
- // Switch to next source only when it's in prepared state.
- mCurrentDSD = mNextDSDs.get(0);
- mCurrentSrcId = mNextSrcId;
- mBufferedPercentageCurrent.set(mBufferedPercentageNext.get());
- mNextDSDs.remove(0);
- mNextSrcId = mSrcIdGenerator++; // make it different from mCurrentSrcId
- mBufferedPercentageNext.set(0);
- mNextSourceState = NEXT_SOURCE_STATE_INIT;
- mNextSourcePlayPending = false;
+ synchronized (mSrcLock) {
+ if (mNextDSDs == null || mNextDSDs.isEmpty()
+ || mNextSourceState != NEXT_SOURCE_STATE_INIT) {
+ // There is no next source or it's in preparing or prepared state.
+ return;
+ }
- long srcId = mCurrentSrcId;
try {
- nativePlayNextDataSource(srcId);
+ mNextSourceState = NEXT_SOURCE_STATE_PREPARING;
+ handleDataSource(false /* isCurrent */, mNextDSDs.get(0), mNextSrcId);
} catch (Exception e) {
- Message msg2 = mTaskHandler.obtainMessage(
- MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null);
- mTaskHandler.post(new Runnable() {
- @Override
- public void run() {
- mTaskHandler.handleMessage(msg2, srcId);
- }
- });
+ Message msg = mTaskHandler.obtainMessage(
+ MEDIA_ERROR, MEDIA_ERROR_IO, MEDIA_ERROR_UNKNOWN, null);
+ mTaskHandler.handleMessage(msg, mNextSrcId);
+
+ mNextDSDs.remove(0);
+ // make a new SrcId to obsolete notification for previous one.
+ mNextSrcId = mSrcIdGenerator++;
+ mNextSourceState = NEXT_SOURCE_STATE_INIT;
+ prepareNextDataSource();
}
+ }
+ }
- // Wait for MEDIA2_INFO_STARTED_AS_NEXT to prepare next source.
- } else {
- if (mNextSourceState == NEXT_SOURCE_STATE_INIT) {
- prepareNextDataSource_l();
+ // This function should be always called on |mHandlerThread|.
+ private void playNextDataSource() {
+ if (Looper.myLooper() != mHandlerThread.getLooper()) {
+ Log.e(TAG, "playNextDataSource: called on wrong looper");
+ }
+
+ synchronized (mSrcLock) {
+ if (mNextDSDs == null || mNextDSDs.isEmpty()) {
+ return;
+ }
+
+ if (mNextSourceState == NEXT_SOURCE_STATE_PREPARED) {
+ // Switch to next source only when it has been prepared.
+ mCurrentDSD = mNextDSDs.get(0);
+ mCurrentSrcId = mNextSrcId;
+ mBufferedPercentageCurrent.set(mBufferedPercentageNext.get());
+ mNextDSDs.remove(0);
+ mNextSrcId = mSrcIdGenerator++; // make it different from |mCurrentSrcId|
+ mBufferedPercentageNext.set(0);
+ mNextSourceState = NEXT_SOURCE_STATE_INIT;
+
+ long srcId = mCurrentSrcId;
+ try {
+ nativePlayNextDataSource(srcId);
+ } catch (Exception e) {
+ Message msg2 = mTaskHandler.obtainMessage(
+ MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null);
+ mTaskHandler.handleMessage(msg2, srcId);
+ // Keep |mNextSourcePlayPending|
+ prepareNextDataSource();
+ return;
+ }
+ stayAwake(true);
+
+ // Now a new current src is playing.
+ // Wait for MEDIA2_INFO_STARTED_AS_NEXT to prepare next source.
+ mNextSourcePlayPending = false;
+ } else {
+ if (mNextSourceState == NEXT_SOURCE_STATE_INIT) {
+ prepareNextDataSource();
+ }
}
- mNextSourcePlayPending = true;
}
}
@@ -2664,6 +2665,21 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
final int what = msg.arg1;
final int extra = msg.arg2;
+ final DataSourceDesc dsd;
+ boolean isCurrentSrcId = false;
+ boolean isNextSrcId = false;
+ synchronized (mSrcLock) {
+ if (srcId == mCurrentSrcId) {
+ dsd = mCurrentDSD;
+ isCurrentSrcId = true;
+ } else if (mNextDSDs != null && !mNextDSDs.isEmpty() && srcId == mNextSrcId) {
+ dsd = mNextDSDs.get(0);
+ isNextSrcId = true;
+ } else {
+ return;
+ }
+ }
+
switch(msg.what) {
case MEDIA_PREPARED:
{
@@ -2678,33 +2694,29 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
sendMessage(msg2);
}
- final DataSourceDesc dsd;
+ if (dsd != null) {
+ synchronized (mEventCbLock) {
+ for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+ cb.first.execute(() -> cb.second.onInfo(
+ mMediaPlayer, dsd, MEDIA_INFO_PREPARED, 0));
+ }
+ }
+ }
+
synchronized (mSrcLock) {
Log.i(TAG, "MEDIA_PREPARED: srcId=" + srcId
+ ", currentSrcId=" + mCurrentSrcId + ", nextSrcId=" + mNextSrcId);
- if (srcId == mCurrentSrcId) {
- dsd = mCurrentDSD;
- prepareNextDataSource_l();
- } else if (mNextDSDs != null && !mNextDSDs.isEmpty()
- && srcId == mNextSrcId) {
- dsd = mNextDSDs.get(0);
+
+ if (isCurrentSrcId) {
+ prepareNextDataSource();
+ } else if (isNextSrcId) {
mNextSourceState = NEXT_SOURCE_STATE_PREPARED;
if (mNextSourcePlayPending) {
- playNextDataSource_l();
+ playNextDataSource();
}
- } else {
- dsd = null;
}
}
- if (dsd != null) {
- synchronized (mEventCbLock) {
- for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
- cb.first.execute(() -> cb.second.onInfo(
- mMediaPlayer, dsd, MEDIA_INFO_PREPARED, 0));
- }
- }
- }
synchronized (mTaskLock) {
if (mCurrentTask != null
&& mCurrentTask.mMediaCallType == CALL_COMPLETED_PREPARE
@@ -2739,7 +2751,7 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
synchronized (mEventCbLock) {
for (Pair<Executor, DrmEventCallback> cb : mDrmEventCallbackRecords) {
cb.first.execute(() -> cb.second.onDrmInfo(
- mMediaPlayer, mCurrentDSD, drmInfo));
+ mMediaPlayer, dsd, drmInfo));
}
}
}
@@ -2751,22 +2763,25 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
case MEDIA_PLAYBACK_COMPLETE:
{
- final DataSourceDesc dsd = mCurrentDSD;
- synchronized (mSrcLock) {
- if (srcId == mCurrentSrcId) {
+ if (isCurrentSrcId) {
+ synchronized (mEventCbLock) {
+ for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+ cb.first.execute(() -> cb.second.onInfo(
+ mMediaPlayer, dsd, MEDIA_INFO_PLAYBACK_COMPLETE, 0));
+ }
+ }
+ stayAwake(false);
+
+ synchronized (mSrcLock) {
+ mNextSourcePlayPending = true;
+
Log.i(TAG, "MEDIA_PLAYBACK_COMPLETE: srcId=" + srcId
+ ", currentSrcId=" + mCurrentSrcId + ", nextSrcId=" + mNextSrcId);
- playNextDataSource_l();
}
- }
- synchronized (mEventCbLock) {
- for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
- cb.first.execute(() -> cb.second.onInfo(
- mMediaPlayer, dsd, MEDIA_INFO_PLAYBACK_COMPLETE, 0));
- }
+ playNextDataSource();
}
- stayAwake(false);
+
return;
}
@@ -2793,21 +2808,18 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
{
final int percent = msg.arg1;
synchronized (mEventCbLock) {
- if (srcId == mCurrentSrcId) {
+ for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
+ cb.first.execute(() -> cb.second.onInfo(
+ mMediaPlayer, dsd, MEDIA_INFO_BUFFERING_UPDATE,
+ percent));
+ }
+ }
+
+ synchronized (mSrcLock) {
+ if (isCurrentSrcId) {
mBufferedPercentageCurrent.set(percent);
- for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
- cb.first.execute(() -> cb.second.onInfo(
- mMediaPlayer, mCurrentDSD, MEDIA_INFO_BUFFERING_UPDATE,
- percent));
- }
- } else if (srcId == mNextSrcId && !mNextDSDs.isEmpty()) {
+ } else if (isNextSrcId) {
mBufferedPercentageNext.set(percent);
- DataSourceDesc nextDSD = mNextDSDs.get(0);
- for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
- cb.first.execute(() -> cb.second.onInfo(
- mMediaPlayer, nextDSD, MEDIA_INFO_BUFFERING_UPDATE,
- percent));
- }
}
}
return;
@@ -2843,7 +2855,7 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
synchronized (mEventCbLock) {
for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onVideoSizeChanged(
- mMediaPlayer, mCurrentDSD, width, height));
+ mMediaPlayer, dsd, width, height));
}
}
return;
@@ -2855,9 +2867,9 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
synchronized (mEventCbLock) {
for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onError(
- mMediaPlayer, mCurrentDSD, what, extra));
+ mMediaPlayer, dsd, what, extra));
cb.first.execute(() -> cb.second.onInfo(
- mMediaPlayer, mCurrentDSD, MEDIA_INFO_PLAYBACK_COMPLETE, 0));
+ mMediaPlayer, dsd, MEDIA_INFO_PLAYBACK_COMPLETE, 0));
}
}
stayAwake(false);
@@ -2868,8 +2880,8 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
{
switch (msg.arg1) {
case MEDIA_INFO_STARTED_AS_NEXT:
- if (srcId == mCurrentSrcId) {
- prepareNextDataSource_l();
+ if (isCurrentSrcId) {
+ prepareNextDataSource();
}
break;
@@ -2908,7 +2920,7 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
synchronized (mEventCbLock) {
for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onInfo(
- mMediaPlayer, mCurrentDSD, what, extra));
+ mMediaPlayer, dsd, what, extra));
}
}
// No real default action so far.
@@ -2938,7 +2950,7 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
synchronized (mEventCbLock) {
for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onTimedText(
- mMediaPlayer, mCurrentDSD, text));
+ mMediaPlayer, dsd, text));
}
}
return;
@@ -2953,7 +2965,7 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
synchronized (mEventCbLock) {
for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onSubtitleData(
- mMediaPlayer, mCurrentDSD, data));
+ mMediaPlayer, dsd, data));
}
}
}
@@ -2974,7 +2986,7 @@ public final class MediaPlayer2Impl extends MediaPlayer2 {
synchronized (mEventCbLock) {
for (Pair<Executor, EventCallback> cb : mEventCallbackRecords) {
cb.first.execute(() -> cb.second.onTimedMetaDataAvailable(
- mMediaPlayer, mCurrentDSD, data));
+ mMediaPlayer, dsd, data));
}
}
return;
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 54541f02f575..7cf8828c0623 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -43,27 +43,27 @@ namespace android {
#define FIND_CLASS(var, className) \
var = env->FindClass(className); \
- LOG_FATAL_IF(! (var), "Unable to find class " className);
+ LOG_FATAL_IF(! (var), "Unable to find class %s", className);
#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
- LOG_FATAL_IF(! (var), "Unable to find field " fieldName);
+ LOG_FATAL_IF(! (var), "Unable to find field %s", fieldName);
#define GET_METHOD_ID(var, clazz, fieldName, fieldDescriptor) \
var = env->GetMethodID(clazz, fieldName, fieldDescriptor); \
- LOG_FATAL_IF(! (var), "Unable to find method " fieldName);
+ LOG_FATAL_IF(! (var), "Unable to find method %s", fieldName);
#define GET_STATIC_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
var = env->GetStaticFieldID(clazz, fieldName, fieldDescriptor); \
- LOG_FATAL_IF(! (var), "Unable to find field " fieldName);
+ LOG_FATAL_IF(! (var), "Unable to find field %s", fieldName);
#define GET_STATIC_METHOD_ID(var, clazz, fieldName, fieldDescriptor) \
var = env->GetStaticMethodID(clazz, fieldName, fieldDescriptor); \
- LOG_FATAL_IF(! (var), "Unable to find static method " fieldName);
+ LOG_FATAL_IF(! (var), "Unable to find static method %s", fieldName);
-#define GET_STATIC_OBJECT_FIELD(var, clazz, fieldName) \
- var = env->GetStaticObjectField(clazz, fieldName); \
- LOG_FATAL_IF(! (var), "Unable to find static object field " fieldName);
+#define GET_STATIC_OBJECT_FIELD(var, clazz, fieldId) \
+ var = env->GetStaticObjectField(clazz, fieldId); \
+ LOG_FATAL_IF(! (var), "Unable to find static object field %p", fieldId);
struct RequestFields {
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 7788f7e8c585..04f2411a46bd 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1120,4 +1120,5 @@
<!-- time label for event have that happened very recently [CHAR LIMIT=60] -->
<string name="time_unit_just_now">Just now</string>
-</resources>
+
+ </resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 77eb6c472e4a..10c1211b1d7f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -186,9 +186,21 @@ class SettingsProtoDumpUtil {
GlobalSettingsProto.Auto.TIME_ZONE);
p.end(autoToken);
+ final long autofillToken = p.start(GlobalSettingsProto.AUTOFILL);
dumpSetting(s, p,
Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES,
- GlobalSettingsProto.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES);
+ GlobalSettingsProto.Autofill.COMPAT_MODE_ALLOWED_PACKAGES);
+ dumpSetting(s, p,
+ Settings.Global.AUTOFILL_LOGGING_LEVEL,
+ GlobalSettingsProto.Autofill.LOGGING_LEVEL);
+ dumpSetting(s, p,
+ Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE,
+ GlobalSettingsProto.Autofill.MAX_PARTITIONS_SIZE);
+ dumpSetting(s, p,
+ Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS,
+ GlobalSettingsProto.Autofill.MAX_VISIBLE_DATASETS);
+ p.end(autofillToken);
+
dumpSetting(s, p,
Settings.Global.BACKUP_AGENT_TIMEOUT_PARAMETERS,
GlobalSettingsProto.BACKUP_AGENT_TIMEOUT_PARAMETERS);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 9592b63c6d1e..8ac9fb91d39d 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -816,7 +816,7 @@ public class SettingsProvider extends ContentProvider {
@Override
public void onPackageRemoved(String packageName, int uid) {
synchronized (mLock) {
- mSettingsRegistry.onPackageRemovedLocked(packageName,
+ mSettingsRegistry.removeSettingsForPackageLocked(packageName,
UserHandle.getUserId(uid));
}
}
@@ -827,6 +827,14 @@ public class SettingsProvider extends ContentProvider {
mSettingsRegistry.onUidRemovedLocked(uid);
}
}
+
+ @Override
+ public void onPackageDataCleared(String packageName, int uid) {
+ synchronized (mLock) {
+ mSettingsRegistry.removeSettingsForPackageLocked(packageName,
+ UserHandle.getUserId(uid));
+ }
+ }
};
// package changes
@@ -2547,7 +2555,7 @@ public class SettingsProvider extends ContentProvider {
}
}
- public void onPackageRemovedLocked(String packageName, int userId) {
+ public void removeSettingsForPackageLocked(String packageName, int userId) {
// Global and secure settings are signature protected. Apps signed
// by the platform certificate are generally not uninstalled and
// the main exception is tests. We trust components signed
@@ -2556,7 +2564,7 @@ public class SettingsProvider extends ContentProvider {
final int systemKey = makeKey(SETTINGS_TYPE_SYSTEM, userId);
SettingsState systemSettings = mSettingsStates.get(systemKey);
if (systemSettings != null) {
- systemSettings.onPackageRemovedLocked(packageName);
+ systemSettings.removeSettingsForPackageLocked(packageName);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 449946d7ab15..e57483a8c734 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -293,7 +293,7 @@ final class SettingsState {
}
// The settings provider must hold its lock when calling here.
- public void onPackageRemovedLocked(String packageName) {
+ public void removeSettingsForPackageLocked(String packageName) {
boolean removedSomething = false;
final int settingCount = mSettings.size();
diff --git a/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml b/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml
index 37e2b537f90c..c9f51486e983 100644
--- a/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml
+++ b/packages/SystemUI/res/layout/car_fullscreen_user_switcher.xml
@@ -38,8 +38,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="@dimen/car_user_switcher_margin_top"
+ android:theme="@style/Theme.Car.Light.List"
app:verticallyCenterListContent="true"
- app:dayNightStyle="always_light"
app:showPagedListViewDivider="false"
app:gutter="both"
app:itemSpacing="@dimen/car_user_switcher_vertical_spacing_between_users"/>
diff --git a/packages/SystemUI/res/layout/car_qs_panel.xml b/packages/SystemUI/res/layout/car_qs_panel.xml
index c43afc980bbf..e7413de342fa 100644
--- a/packages/SystemUI/res/layout/car_qs_panel.xml
+++ b/packages/SystemUI/res/layout/car_qs_panel.xml
@@ -39,7 +39,7 @@
android:id="@+id/user_grid"
android:layout_width="match_parent"
android:layout_height="match_parent"
- app:dayNightStyle="always_light"
+ android:theme="@style/Theme.Car.Light.List"
app:showPagedListViewDivider="false"
app:gutter="both"
app:itemSpacing="@dimen/car_user_switcher_vertical_spacing_between_users"/>
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 3ac67056ff80..d5383b975211 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -146,6 +146,7 @@ public class BatteryMeterView extends LinearLayout implements
getContext().getContentResolver().registerContentObserver(
Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mSettingObserver,
newUserId);
+ updateShowPercent();
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index 4bb4e79c91f6..36b234704592 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -23,6 +23,8 @@ import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Handler;
import android.os.Trace;
+import android.os.UserHandle;
+import android.provider.Settings;
import com.android.internal.annotations.VisibleForTesting;
@@ -111,7 +113,7 @@ public class DozeScreenBrightness implements DozeMachine.Part, SensorEventListen
int brightness = computeBrightness(mLastSensorValue);
boolean brightnessReady = brightness > 0;
if (brightnessReady) {
- mDozeService.setDozeScreenBrightness(brightness);
+ mDozeService.setDozeScreenBrightness(clampToUserSetting(brightness));
}
int scrimOpacity = -1;
@@ -150,10 +152,17 @@ public class DozeScreenBrightness implements DozeMachine.Part, SensorEventListen
}
private void resetBrightnessToDefault() {
- mDozeService.setDozeScreenBrightness(mDefaultDozeBrightness);
+ mDozeService.setDozeScreenBrightness(clampToUserSetting(mDefaultDozeBrightness));
mDozeHost.setAodDimmingScrim(0f);
}
+ private int clampToUserSetting(int brightness) {
+ int userSetting = Settings.System.getIntForUser(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS, Integer.MAX_VALUE,
+ UserHandle.USER_CURRENT);
+ return Math.min(brightness, userSetting);
+ }
+
private void setLightSensorEnabled(boolean enabled) {
if (enabled && !mRegistered && mLightSensor != null) {
// Wait until we get an event from the sensor until indicating ready.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 02937d8457bc..bb69b7a17660 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -50,6 +50,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
+import java.util.function.Predicate;
/** Platform implementation of the quick settings tile host **/
public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory> {
@@ -226,23 +227,22 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory> {
}
@Override
- public void removeTile(String tileSpec) {
- ArrayList<String> specs = new ArrayList<>(mTileSpecs);
- specs.remove(tileSpec);
- Settings.Secure.putStringForUser(mContext.getContentResolver(), TILES_SETTING,
- TextUtils.join(",", specs), ActivityManager.getCurrentUser());
+ public void removeTile(String spec) {
+ changeTileSpecs(tileSpecs-> tileSpecs.remove(spec));
}
public void addTile(String spec) {
+ changeTileSpecs(tileSpecs-> tileSpecs.add(spec));
+ }
+
+ private void changeTileSpecs(Predicate<List<String>> changeFunction) {
final String setting = Settings.Secure.getStringForUser(mContext.getContentResolver(),
- TILES_SETTING, ActivityManager.getCurrentUser());
+ TILES_SETTING, ActivityManager.getCurrentUser());
final List<String> tileSpecs = loadTileSpecs(mContext, setting);
- if (tileSpecs.contains(spec)) {
- return;
- }
- tileSpecs.add(spec);
- Settings.Secure.putStringForUser(mContext.getContentResolver(), TILES_SETTING,
+ if (changeFunction.test(tileSpecs)) {
+ Settings.Secure.putStringForUser(mContext.getContentResolver(), TILES_SETTING,
TextUtils.join(",", tileSpecs), ActivityManager.getCurrentUser());
+ }
}
public void addTile(ComponentName tile) {
@@ -300,7 +300,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory> {
throw new RuntimeException("Default factory didn't create view for " + tile.getTileSpec());
}
- protected List<String> loadTileSpecs(Context context, String tileList) {
+ protected static List<String> loadTileSpecs(Context context, String tileList) {
final Resources res = context.getResources();
final String defaultTileList = res.getString(R.string.quick_settings_tiles_default);
if (tileList == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index e8cd32b0dfde..987de0e2a646 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -3287,8 +3287,20 @@ public class NotificationStackScrollLayout extends ViewGroup
private void generateViewResizeEvent() {
if (mNeedViewResizeAnimation) {
- mAnimationEvents.add(
- new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_VIEW_RESIZE));
+ boolean hasDisappearAnimation = false;
+ for (AnimationEvent animationEvent : mAnimationEvents) {
+ final int type = animationEvent.animationType;
+ if (type == AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
+ || type == AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR) {
+ hasDisappearAnimation = true;
+ break;
+ }
+ }
+
+ if (!hasDisappearAnimation) {
+ mAnimationEvents.add(
+ new AnimationEvent(null, AnimationEvent.ANIMATION_TYPE_VIEW_RESIZE));
+ }
}
mNeedViewResizeAnimation = false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index c4008934c49f..4c91a9d5ff60 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -516,7 +516,7 @@ public class StatusBar extends SystemUI implements DemoMode,
}
WallpaperInfo info = wallpaperManager.getWallpaperInfo(UserHandle.USER_CURRENT);
final boolean supportsAmbientMode = info != null &&
- info.getSupportsAmbientMode();
+ info.supportsAmbientMode();
mStatusBarWindowManager.setWallpaperSupportsAmbientMode(supportsAmbientMode);
mScrimController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index f729120f98bc..8dbdd21c2d64 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -16,6 +16,12 @@
package com.android.systemui.statusbar.policy;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_IN;
+import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_INOUT;
+import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_NONE;
+import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_OUT;
+
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -55,7 +61,6 @@ import com.android.systemui.R;
import com.android.systemui.settings.CurrentUserTracker;
import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
-import com.android.systemui.statusbar.policy.MobileSignalController.MobileState;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -65,8 +70,6 @@ import java.util.Comparator;
import java.util.List;
import java.util.Locale;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
-
/** Platform implementation of the network controller. **/
public class NetworkControllerImpl extends BroadcastReceiver
implements NetworkController, DemoMode, DataUsageController.NetworkNameProvider,
@@ -849,20 +852,20 @@ public class NetworkControllerImpl extends BroadcastReceiver
if (activity != null) {
switch (activity) {
case "inout":
- mWifiSignalController.setActivity(WifiManager.DATA_ACTIVITY_INOUT);
+ mWifiSignalController.setActivity(DATA_ACTIVITY_INOUT);
break;
case "in":
- mWifiSignalController.setActivity(WifiManager.DATA_ACTIVITY_IN);
+ mWifiSignalController.setActivity(DATA_ACTIVITY_IN);
break;
case "out":
- mWifiSignalController.setActivity(WifiManager.DATA_ACTIVITY_OUT);
+ mWifiSignalController.setActivity(DATA_ACTIVITY_OUT);
break;
default:
- mWifiSignalController.setActivity(WifiManager.DATA_ACTIVITY_NONE);
+ mWifiSignalController.setActivity(DATA_ACTIVITY_NONE);
break;
}
} else {
- mWifiSignalController.setActivity(WifiManager.DATA_ACTIVITY_NONE);
+ mWifiSignalController.setActivity(DATA_ACTIVITY_NONE);
}
String ssid = args.getString("ssid");
if (ssid != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index cf80988ba61d..0233ad1c86ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -15,21 +15,19 @@
*/
package com.android.systemui.statusbar.policy;
+import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_IN;
+import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_INOUT;
+import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_OUT;
+
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkCapabilities;
import android.net.NetworkScoreManager;
import android.net.wifi.WifiManager;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Messenger;
import android.text.TextUtils;
-import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.AsyncChannel;
import com.android.settingslib.wifi.WifiStatusTracker;
import com.android.systemui.R;
import com.android.systemui.statusbar.policy.NetworkController.IconState;
@@ -37,10 +35,8 @@ import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
import java.util.Objects;
-
public class WifiSignalController extends
SignalController<WifiSignalController.WifiState, SignalController.IconGroup> {
- private final AsyncChannel mWifiChannel;
private final boolean mHasMobileData;
private final WifiStatusTracker mWifiTracker;
@@ -57,12 +53,7 @@ public class WifiSignalController extends
connectivityManager, this::handleStatusUpdated);
mWifiTracker.setListening(true);
mHasMobileData = hasMobileData;
- Handler handler = new WifiHandler(Looper.getMainLooper());
- mWifiChannel = new AsyncChannel();
- Messenger wifiMessenger = wifiManager.getWifiServiceMessenger();
- if (wifiMessenger != null) {
- mWifiChannel.connect(context, handler, wifiMessenger);
- }
+ wifiManager.registerTrafficStateCallback(new WifiTrafficStateCallback(), null);
// WiFi only has one state.
mCurrentState.iconGroup = mLastState.iconGroup = new IconGroup(
"Wi-Fi Icons",
@@ -124,39 +115,20 @@ public class WifiSignalController extends
@VisibleForTesting
void setActivity(int wifiActivity) {
- mCurrentState.activityIn = wifiActivity == WifiManager.DATA_ACTIVITY_INOUT
- || wifiActivity == WifiManager.DATA_ACTIVITY_IN;
- mCurrentState.activityOut = wifiActivity == WifiManager.DATA_ACTIVITY_INOUT
- || wifiActivity == WifiManager.DATA_ACTIVITY_OUT;
+ mCurrentState.activityIn = wifiActivity == DATA_ACTIVITY_INOUT
+ || wifiActivity == DATA_ACTIVITY_IN;
+ mCurrentState.activityOut = wifiActivity == DATA_ACTIVITY_INOUT
+ || wifiActivity == DATA_ACTIVITY_OUT;
notifyListenersIfNecessary();
}
/**
* Handler to receive the data activity on wifi.
*/
- private class WifiHandler extends Handler {
- WifiHandler(Looper looper) {
- super(looper);
- }
-
+ private class WifiTrafficStateCallback implements WifiManager.TrafficStateCallback {
@Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
- if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
- mWifiChannel.sendMessage(Message.obtain(this,
- AsyncChannel.CMD_CHANNEL_FULL_CONNECTION));
- } else {
- Log.e(mTag, "Failed to connect to wifi");
- }
- break;
- case WifiManager.DATA_ACTIVITY_NOTIFICATION:
- setActivity(msg.arg1);
- break;
- default:
- // Ignore
- break;
- }
+ public void onStateChanged(int state) {
+ setActivity(state);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
index 5e12781399e5..eaa0dcf88588 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -34,6 +34,8 @@ import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import android.os.PowerManager;
+import android.os.UserHandle;
+import android.provider.Settings;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -60,6 +62,9 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
@Before
public void setUp() throws Exception {
+ Settings.System.putIntForUser(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS, DEFAULT_BRIGHTNESS,
+ UserHandle.USER_CURRENT);
mServiceFake = new DozeServiceFake();
mHostFake = new DozeHostFake();
mSensorManager = new FakeSensorManager(mContext);
@@ -88,6 +93,17 @@ public class DozeScreenBrightnessTest extends SysuiTestCase {
}
@Test
+ public void testAod_usesLightSensorRespectingUserSetting() throws Exception {
+ int maxBrightness = 3;
+ Settings.System.putIntForUser(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS, maxBrightness,
+ UserHandle.USER_CURRENT);
+
+ mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+ assertEquals(maxBrightness, mServiceFake.screenBrightness);
+ }
+
+ @Test
public void testPausingAod_doesntPauseLightSensor() throws Exception {
mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
mScreen.transitionTo(INITIALIZED, DOZE_AOD);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index dff06659906e..6e3d9067f63c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -87,15 +87,15 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest {
WifiIcons.QS_WIFI_SIGNAL_STRENGTH[1][testLevel], testSsid);
// Set to different activity state first to ensure a callback happens.
- setWifiActivity(WifiManager.DATA_ACTIVITY_IN);
+ setWifiActivity(WifiManager.TrafficStateCallback.DATA_ACTIVITY_IN);
- setWifiActivity(WifiManager.DATA_ACTIVITY_NONE);
+ setWifiActivity(WifiManager.TrafficStateCallback.DATA_ACTIVITY_NONE);
verifyLastQsDataDirection(false, false);
- setWifiActivity(WifiManager.DATA_ACTIVITY_IN);
+ setWifiActivity(WifiManager.TrafficStateCallback.DATA_ACTIVITY_IN);
verifyLastQsDataDirection(true, false);
- setWifiActivity(WifiManager.DATA_ACTIVITY_OUT);
+ setWifiActivity(WifiManager.TrafficStateCallback.DATA_ACTIVITY_OUT);
verifyLastQsDataDirection(false, true);
- setWifiActivity(WifiManager.DATA_ACTIVITY_INOUT);
+ setWifiActivity(WifiManager.TrafficStateCallback.DATA_ACTIVITY_INOUT);
verifyLastQsDataDirection(true, true);
}
diff --git a/proto/Android.bp b/proto/Android.bp
index 5fd288513384..f3811bdd7d81 100644
--- a/proto/Android.bp
+++ b/proto/Android.bp
@@ -17,33 +17,3 @@ java_library_static {
},
},
}
-
-cc_library {
- name: "libmetricprotos",
- host_supported: true,
- proto: {
- export_proto_headers: true,
- include_dirs: ["external/protobuf/src"],
- },
- cflags: [
- "-Wall",
- "-Werror",
- "-Wno-unused-parameter",
- ],
- srcs: ["src/metrics_constants.proto"],
- target: {
- host: {
- proto: {
- type: "full",
- },
- },
- android: {
- proto: {
- type: "lite",
- },
- shared: {
- enabled: false,
- },
- },
- },
-}
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 496cd962c085..02f04398d6ac 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -6175,6 +6175,11 @@ message MetricsEvent {
// OS: Q
FACE = 1511;
+ // OPEN: Settings > Acessibility > HearingAid pairing instructions dialog
+ // CATEGORY: SETTINGS
+ // OS: Q
+ DIALOG_ACCESSIBILITY_HEARINGAID = 1512;
+
// ---- End Q Constants, all Q constants go above this line ----
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 2da0818ebc95..9c0e1105eb4a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -16,12 +16,18 @@
package com.android.server.accessibility;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_MASK;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ACCESSIBILITY_FOCUSED;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS;
import static com.android.internal.util.FunctionalUtils.ignoreRemoteException;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_WITH_HARD_KEYBOARD;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN;
import android.Manifest;
import android.accessibilityservice.AccessibilityService;
@@ -1807,7 +1813,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
updateDisplayDaltonizerLocked(userState);
updateDisplayInversionLocked(userState);
updateMagnificationLocked(userState);
- updateSoftKeyboardShowModeLocked(userState);
scheduleUpdateFingerprintGestureHandling(userState);
scheduleUpdateInputFilter(userState);
scheduleUpdateClientsIfNeededLocked(userState);
@@ -2001,18 +2006,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return false;
}
- private boolean readSoftKeyboardShowModeChangedLocked(UserState userState) {
- final int softKeyboardShowMode = Settings.Secure.getIntForUser(
- mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0,
- userState.mUserId);
- if (softKeyboardShowMode != userState.mSoftKeyboardShowMode) {
- userState.mSoftKeyboardShowMode = softKeyboardShowMode;
- return true;
- }
- return false;
- }
-
private void updateTouchExplorationLocked(UserState userState) {
boolean enabled = mUiAutomationManager.isTouchExplorationEnabledLocked();
final int serviceCount = userState.mBoundServices.size();
@@ -2211,34 +2204,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return false;
}
- private void updateSoftKeyboardShowModeLocked(UserState userState) {
- final int userId = userState.mUserId;
- // Only check whether we need to reset the soft keyboard mode if it is not set to the
- // default.
- if ((userId == mCurrentUserId) && (userState.mSoftKeyboardShowMode != 0)) {
- // Check whether the last AccessibilityService that changed the soft keyboard mode to
- // something other than the default is still enabled and, if not, remove flag and
- // reset to the default soft keyboard behavior.
- boolean serviceChangingSoftKeyboardModeIsEnabled =
- userState.mEnabledServices.contains(userState.mServiceChangingSoftKeyboardMode);
-
- if (!serviceChangingSoftKeyboardModeIsEnabled) {
- final long identity = Binder.clearCallingIdentity();
- try {
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
- 0,
- userState.mUserId);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- userState.mSoftKeyboardShowMode = 0;
- userState.mServiceChangingSoftKeyboardMode = null;
- notifySoftKeyboardShowModeChangedLocked(userState.mSoftKeyboardShowMode);
- }
- }
- }
-
private void updateFingerprintGestureHandling(UserState userState) {
final List<AccessibilityServiceConnection> services;
synchronized (mLock) {
@@ -2473,6 +2438,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
}
+ private void putSecureIntForUser(String key, int value, int userid) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ Settings.Secure.putIntForUser(mContext.getContentResolver(), key, value, userid);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
class RemoteAccessibilityConnection implements DeathRecipient {
private final int mUid;
private final String mPackageName;
@@ -3683,7 +3657,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
public int mLastSentClientState = -1;
- public int mSoftKeyboardShowMode = 0;
+ private int mSoftKeyboardShowMode = 0;
public boolean mIsNavBarMagnificationAssignedToAccessibilityButton;
public ComponentName mServiceAssignedToAccessibilityButton;
@@ -3744,7 +3718,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mServiceAssignedToAccessibilityButton = null;
mIsNavBarMagnificationAssignedToAccessibilityButton = false;
mIsAutoclickEnabled = false;
- mSoftKeyboardShowMode = 0;
}
public void addServiceLocked(AccessibilityServiceConnection serviceConnection) {
@@ -3766,6 +3739,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
public void removeServiceLocked(AccessibilityServiceConnection serviceConnection) {
mBoundServices.remove(serviceConnection);
serviceConnection.onRemoved();
+ if ((mServiceChangingSoftKeyboardMode != null)
+ && (mServiceChangingSoftKeyboardMode.equals(
+ serviceConnection.getServiceInfo().getComponentName()))) {
+ setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
+ }
// It may be possible to bind a service twice, which confuses the map. Rebuild the map
// to make sure we can still reach a service
mComponentNameToServiceMap.clear();
@@ -3792,6 +3770,134 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
public Set<ComponentName> getBindingServicesLocked() {
return mBindingServices;
}
+
+ public int getSoftKeyboardShowMode() {
+ return mSoftKeyboardShowMode;
+ }
+
+ /**
+ * Set the soft keyboard mode. This mode is a bit odd, as it spans multiple settings.
+ * The ACCESSIBILITY_SOFT_KEYBOARD_MODE setting can be checked by the rest of the system
+ * to see if it should suppress showing the IME. The SHOW_IME_WITH_HARD_KEYBOARD setting
+ * setting can be changed by the user, and prevents the system from suppressing the soft
+ * keyboard when the hard keyboard is connected. The hard keyboard setting needs to defer
+ * to the user's preference, if they have supplied one.
+ *
+ * @param newMode The new mode
+ * @param requester The service requesting the change, so we can undo it when the
+ * service stops. Set to null if something other than a service is forcing
+ * the change.
+ *
+ * @return Whether or not the soft keyboard mode equals the new mode after the call
+ */
+ public boolean setSoftKeyboardModeLocked(int newMode, @Nullable ComponentName requester) {
+ if ((newMode != SHOW_MODE_AUTO) && (newMode != SHOW_MODE_HIDDEN)
+ && (newMode != SHOW_MODE_WITH_HARD_KEYBOARD))
+ {
+ Slog.w(LOG_TAG, "Invalid soft keyboard mode");
+ return false;
+ }
+ if (mSoftKeyboardShowMode == newMode) return true;
+
+ if (newMode == SHOW_MODE_WITH_HARD_KEYBOARD) {
+ if (hasUserOverriddenHardKeyboardSettingLocked()) {
+ // The user has specified a default for this setting
+ return false;
+ }
+ // Save the original value. But don't do this if the value in settings is already
+ // the new mode. That happens when we start up after a reboot, and we don't want
+ // to overwrite the value we had from when we first started controlling the setting.
+ if (getSoftKeyboardValueFromSettings() != SHOW_MODE_WITH_HARD_KEYBOARD) {
+ setOriginalHardKeyboardValue(
+ Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0);
+ }
+ putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 1, mUserId);
+ } else if (mSoftKeyboardShowMode == SHOW_MODE_WITH_HARD_KEYBOARD) {
+ putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD,
+ getOriginalHardKeyboardValue() ? 1 : 0, mUserId);
+ }
+
+ saveSoftKeyboardValueToSettings(newMode);
+ mSoftKeyboardShowMode = newMode;
+ mServiceChangingSoftKeyboardMode = requester;
+ notifySoftKeyboardShowModeChangedLocked(mSoftKeyboardShowMode);
+ return true;
+ }
+
+ /**
+ * If the settings are inconsistent with the internal state, make the internal state
+ * match the settings.
+ */
+ public void reconcileSoftKeyboardModeWithSettingsLocked() {
+ final ContentResolver cr = mContext.getContentResolver();
+ final boolean showWithHardKeyboardSettings =
+ Settings.Secure.getInt(cr, Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0;
+ if (mSoftKeyboardShowMode == SHOW_MODE_WITH_HARD_KEYBOARD) {
+ if (!showWithHardKeyboardSettings) {
+ // The user has overridden the setting. Respect that and prevent further changes
+ // to this behavior.
+ setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
+ setUserOverridesHardKeyboardSettingLocked();
+ }
+ }
+
+ // If the setting and the internal state are out of sync, set both to default
+ if (getSoftKeyboardValueFromSettings() != mSoftKeyboardShowMode)
+ {
+ Slog.e(LOG_TAG,
+ "Show IME setting inconsistent with internal state. Overwriting");
+ setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
+ putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+ SHOW_MODE_AUTO, mUserId);
+ }
+ }
+
+ private void setUserOverridesHardKeyboardSettingLocked() {
+ final int softKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0);
+ putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+ softKeyboardSetting | SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN,
+ mUserId);
+ }
+
+ private boolean hasUserOverriddenHardKeyboardSettingLocked() {
+ final int softKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0);
+ return (softKeyboardSetting & SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN)
+ != 0;
+ }
+
+ private void setOriginalHardKeyboardValue(boolean originalHardKeyboardValue) {
+ final int oldSoftKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0);
+ final int newSoftKeyboardSetting = oldSoftKeyboardSetting
+ & (~SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE)
+ | ((originalHardKeyboardValue) ? SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE : 0);
+ putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+ newSoftKeyboardSetting, mUserId);
+ }
+
+ private void saveSoftKeyboardValueToSettings(int softKeyboardShowMode) {
+ final int oldSoftKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0);
+ final int newSoftKeyboardSetting = oldSoftKeyboardSetting & (~SHOW_MODE_MASK)
+ | softKeyboardShowMode;
+ putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+ newSoftKeyboardSetting, mUserId);
+ }
+
+ private int getSoftKeyboardValueFromSettings() {
+ return Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+ SHOW_MODE_AUTO) & SHOW_MODE_MASK;
+ }
+
+ private boolean getOriginalHardKeyboardValue() {
+ return (Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0)
+ & SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE) != 0;
+ }
}
private final class AccessibilityContentObserver extends ContentObserver {
@@ -3829,6 +3935,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private final Uri mAccessibilitySoftKeyboardModeUri = Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE);
+ private final Uri mShowImeWithHardKeyboardUri = Settings.Secure.getUriFor(
+ Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD);
+
private final Uri mAccessibilityShortcutServiceIdUri = Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
@@ -3864,6 +3973,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
contentResolver.registerContentObserver(
mAccessibilitySoftKeyboardModeUri, false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(
+ mShowImeWithHardKeyboardUri, false, this, UserHandle.USER_ALL);
+ contentResolver.registerContentObserver(
mAccessibilityShortcutServiceIdUri, false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(
mAccessibilityButtonComponentIdUri, false, this, UserHandle.USER_ALL);
@@ -3906,11 +4017,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
if (readHighTextContrastEnabledSettingLocked(userState)) {
onUserStateChangedLocked(userState);
}
- } else if (mAccessibilitySoftKeyboardModeUri.equals(uri)) {
- if (readSoftKeyboardShowModeChangedLocked(userState)) {
- notifySoftKeyboardShowModeChangedLocked(userState.mSoftKeyboardShowMode);
- onUserStateChangedLocked(userState);
- }
+ } else if (mAccessibilitySoftKeyboardModeUri.equals(uri)
+ || mShowImeWithHardKeyboardUri.equals(uri)) {
+ userState.reconcileSoftKeyboardModeWithSettingsLocked();
} else if (mAccessibilityShortcutServiceIdUri.equals(uri)) {
if (readAccessibilityShortcutSettingLocked(userState)) {
onUserStateChangedLocked(userState);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 8f921454196f..6237212685a4 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -218,26 +218,20 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
if (!isCalledForCurrentUserLocked()) {
return false;
}
+ final UserState userState = mUserStateWeakReference.get();
+ if (userState == null) return false;
+ return userState.setSoftKeyboardModeLocked(showMode, mComponentName);
}
- UserState userState = mUserStateWeakReference.get();
- if (userState == null) return false;
- final long identity = Binder.clearCallingIdentity();
- try {
- // Keep track of the last service to request a non-default show mode. The show mode
- // should be restored to default should this service be disabled.
- userState.mServiceChangingSoftKeyboardMode = (showMode == SHOW_MODE_AUTO)
- ? null : mComponentName;
-
- Settings.Secure.putIntForUser(mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, showMode,
- userState.mUserId);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- return true;
}
@Override
+ public int getSoftKeyboardShowMode() {
+ final UserState userState = mUserStateWeakReference.get();
+ return (userState != null) ? userState.getSoftKeyboardShowMode() : 0;
+ }
+
+
+ @Override
public boolean isAccessibilityButtonAvailable() {
synchronized (mLock) {
if (!isCalledForCurrentUserLocked()) {
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index ed3b3e770338..ff2931160f09 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -259,6 +259,11 @@ class UiAutomationManager {
}
@Override
+ public int getSoftKeyboardShowMode() {
+ return 0;
+ }
+
+ @Override
public boolean isAccessibilityButtonAvailable() {
return false;
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 021fdcd84971..0610256ae10b 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -166,9 +166,9 @@ public final class AutofillManagerService extends SystemService {
mContext = context;
mUi = new AutoFillUI(ActivityThread.currentActivityThread().getSystemUiContext());
- final boolean debug = Build.IS_DEBUGGABLE;
- Slog.i(TAG, "Setting debug to " + debug);
- setDebugLocked(debug);
+ setLogLevelFromSettings();
+ setMaxPartitionsFromSettings();
+ setMaxVisibleDatasetsFromSettings();
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
@@ -438,12 +438,28 @@ public final class AutofillManagerService extends SystemService {
Slog.i(TAG, "setLogLevel(): " + level);
mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.AUTOFILL_LOGGING_LEVEL, level);
+ }
+
+ private void setLogLevelFromSettings() {
+ final int level = Settings.Global.getInt(
+ mContext.getContentResolver(),
+ Settings.Global.AUTOFILL_LOGGING_LEVEL, AutofillManager.DEFAULT_LOGGING_LEVEL);
boolean debug = false;
boolean verbose = false;
- if (level == AutofillManager.FLAG_ADD_CLIENT_VERBOSE) {
- debug = verbose = true;
- } else if (level == AutofillManager.FLAG_ADD_CLIENT_DEBUG) {
- debug = true;
+ if (level != AutofillManager.NO_LOGGING) {
+ if (level == AutofillManager.FLAG_ADD_CLIENT_VERBOSE) {
+ debug = verbose = true;
+ } else if (level == AutofillManager.FLAG_ADD_CLIENT_DEBUG) {
+ debug = true;
+ } else {
+ Slog.w(TAG, "setLogLevelFromSettings(): invalid level: " + level);
+ }
+ }
+ if (debug || sDebug) {
+ Slog.d(TAG, "setLogLevelFromSettings(): level=" + level + ", debug=" + debug
+ + ", verbose=" + verbose);
}
synchronized (mLock) {
setDebugLocked(debug);
@@ -475,6 +491,17 @@ public final class AutofillManagerService extends SystemService {
void setMaxPartitions(int max) {
mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
Slog.i(TAG, "setMaxPartitions(): " + max);
+
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE, max);
+ }
+
+ private void setMaxPartitionsFromSettings() {
+ final int max = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE,
+ AutofillManager.DEFAULT_MAX_PARTITIONS_SIZE);
+ if (sDebug) Slog.d(TAG, "setMaxPartitionsFromSettings(): " + max);
+
synchronized (mLock) {
sPartitionMaxCount = max;
}
@@ -493,6 +520,16 @@ public final class AutofillManagerService extends SystemService {
void setMaxVisibleDatasets(int max) {
mContext.enforceCallingPermission(MANAGE_AUTO_FILL, TAG);
Slog.i(TAG, "setMaxVisibleDatasets(): " + max);
+
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS, max);
+ }
+
+ private void setMaxVisibleDatasetsFromSettings() {
+ final int max = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS, 0);
+
+ if (sDebug) Slog.d(TAG, "setMaxVisibleDatasetsFromSettings(): " + max);
synchronized (mLock) {
sVisibleDatasetsMaxCount = max;
}
@@ -1289,13 +1326,34 @@ public final class AutofillManagerService extends SystemService {
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES), false, this,
UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.AUTOFILL_LOGGING_LEVEL), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS), false, this,
+ UserHandle.USER_ALL);
}
@Override
public void onChange(boolean selfChange, Uri uri, int userId) {
if (sVerbose) Slog.v(TAG, "onChange(): uri=" + uri + ", userId=" + userId);
- synchronized (mLock) {
- updateCachedServiceLocked(userId);
+ switch (uri.getLastPathSegment()) {
+ case Settings.Global.AUTOFILL_LOGGING_LEVEL:
+ setLogLevelFromSettings();
+ break;
+ case Settings.Global.AUTOFILL_MAX_PARTITIONS_SIZE:
+ setMaxPartitionsFromSettings();
+ break;
+ case Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS:
+ setMaxVisibleDatasetsFromSettings();
+ break;
+ default:
+ synchronized (mLock) {
+ updateCachedServiceLocked(userId);
+ }
}
}
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
index f7b7ceb4a6da..522280e4690b 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceShellCommand.java
@@ -185,7 +185,7 @@ public final class AutofillManagerServiceShellCommand extends ShellCommand {
mService.setLogLevel(AutofillManager.FLAG_ADD_CLIENT_DEBUG);
return 0;
case "off":
- mService.setLogLevel(0);
+ mService.setLogLevel(AutofillManager.NO_LOGGING);
return 0;
default:
pw.println("Invalid level: " + logLevel);
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index 4f45a7770311..420c2be008cb 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -28,6 +28,7 @@ import android.util.ArraySet;
import android.util.Slog;
import android.view.WindowManager;
import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -43,28 +44,32 @@ public final class Helper {
/**
* Defines a logging flag that can be dynamically changed at runtime using
- * {@code cmd autofill set log_level debug}.
+ * {@code cmd autofill set log_level debug} or through
+ * {@link android.provider.Settings.Global#AUTOFILL_LOGGING_LEVEL}.
*/
public static boolean sDebug = false;
/**
* Defines a logging flag that can be dynamically changed at runtime using
- * {@code cmd autofill set log_level verbose}.
+ * {@code cmd autofill set log_level verbose} or through
+ * {@link android.provider.Settings.Global#AUTOFILL_LOGGING_LEVEL}.
*/
public static boolean sVerbose = false;
/**
* Maximum number of partitions that can be allowed in a session.
*
- * <p>Can be modified using {@code cmd autofill set max_partitions}.
+ * <p>Can be modified using {@code cmd autofill set max_partitions} or through
+ * {@link android.provider.Settings.Global#AUTOFILL_MAX_PARTITIONS_SIZE}.
*/
- static int sPartitionMaxCount = 10;
+ static int sPartitionMaxCount = AutofillManager.DEFAULT_MAX_PARTITIONS_SIZE;
/**
* Maximum number of visible datasets in the dataset picker UI, or {@code 0} to use default
* value from resources.
*
- * <p>Can be modified using {@code cmd autofill set max_visible_datasets}.
+ * <p>Can be modified using {@code cmd autofill set max_visible_datasets} or through
+ * {@link android.provider.Settings.Global#AUTOFILL_MAX_VISIBLE_DATASETS}.
*/
public static int sVisibleDatasetsMaxCount = 0;
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 8f59ca9c4b8d..48c2eaef02db 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -37,7 +37,6 @@ import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUC
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
-import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.IAssistDataReceiver;
import android.app.assist.AssistStructure;
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index fd746131862f..784dfb4d0cec 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -51,6 +51,7 @@ import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
import android.Manifest;
+import android.accessibilityservice.AccessibilityService;
import android.annotation.AnyThread;
import android.annotation.BinderThread;
import android.annotation.ColorInt;
@@ -888,10 +889,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
if (showImeUri.equals(uri)) {
updateKeyboardFromSettingsLocked();
} else if (accessibilityRequestingNoImeUri.equals(uri)) {
- mAccessibilityRequestingNoSoftKeyboard = Settings.Secure.getIntForUser(
+ final int accessibilitySoftKeyboardSetting = Settings.Secure.getIntForUser(
mContext.getContentResolver(),
- Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
- 0, mUserId) == 1;
+ Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0, mUserId);
+ mAccessibilityRequestingNoSoftKeyboard =
+ (accessibilitySoftKeyboardSetting & AccessibilityService.SHOW_MODE_MASK)
+ == AccessibilityService.SHOW_MODE_HIDDEN;
if (mAccessibilityRequestingNoSoftKeyboard) {
final boolean showRequested = mShowRequested;
hideCurrentInputLocked(0, null);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 29937ab42d15..5bf6892adb23 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -3658,7 +3658,7 @@ public final class ActiveServices {
}
app = r.app;
- if (app != null && app.debugging) {
+ if (app != null && app.isDebugging()) {
// The app's being debugged; let it ride
return;
}
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index f166e64fd881..73ffd5cc4ff4 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import static android.app.ActivityTaskManager.INVALID_STACK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -31,13 +32,18 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.FLAG_PRIVATE;
import static android.view.Display.REMOVE_MODE_DESTROY_CONTENT;
import static com.android.server.am.ActivityDisplayProto.CONFIGURATION_CONTAINER;
+import static com.android.server.am.ActivityDisplayProto.FOCUSED_STACK_ID;
import static com.android.server.am.ActivityDisplayProto.ID;
+import static com.android.server.am.ActivityDisplayProto.RESUMED_ACTIVITY;
import static com.android.server.am.ActivityDisplayProto.STACKS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STATES;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_STACK;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityStackSupervisor.TAG_STATES;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityOptions;
import android.app.WindowConfiguration;
@@ -319,16 +325,6 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
+ windowingMode);
}
- if (windowingMode == WINDOWING_MODE_UNDEFINED) {
- // TODO: Should be okay to have stacks with with undefined windowing mode long term, but
- // have to set them to something for now due to logic that depending on them.
- windowingMode = getWindowingMode(); // Put in current display's windowing mode
- if (windowingMode == WINDOWING_MODE_UNDEFINED) {
- // Else fullscreen for now...
- windowingMode = WINDOWING_MODE_FULLSCREEN;
- }
- }
-
final int stackId = getNextStackId();
return createStackUnchecked(windowingMode, activityType, stackId, onTop);
}
@@ -354,6 +350,45 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
return null;
}
+ ActivityStack getNextFocusableStack() {
+ return getNextFocusableStack(null /* currentFocus */, false /* ignoreCurrent */);
+ }
+
+ ActivityStack getNextFocusableStack(ActivityStack currentFocus, boolean ignoreCurrent) {
+ final int currentWindowingMode = currentFocus != null
+ ? currentFocus.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
+
+ ActivityStack candidate = null;
+ for (int i = mStacks.size() - 1; i >= 0; --i) {
+ final ActivityStack stack = mStacks.get(i);
+ if (ignoreCurrent && stack == currentFocus) {
+ continue;
+ }
+ if (!stack.isFocusable() || !stack.shouldBeVisible(null)) {
+ continue;
+ }
+
+ if (currentWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+ && candidate == null && stack.inSplitScreenPrimaryWindowingMode()) {
+ // If the currently focused stack is in split-screen secondary we save off the
+ // top primary split-screen stack as a candidate for focus because we might
+ // prefer focus to move to an other stack to avoid primary split-screen stack
+ // overlapping with a fullscreen stack when a fullscreen stack is higher in z
+ // than the next split-screen stack. Assistant stack, I am looking at you...
+ // We only move the focus to the primary-split screen stack if there isn't a
+ // better alternative.
+ candidate = stack;
+ continue;
+ }
+ if (candidate != null && stack.inSplitScreenSecondaryWindowingMode()) {
+ // Use the candidate stack since we are now at the secondary split-screen.
+ return candidate;
+ }
+ return stack;
+ }
+ return candidate;
+ }
+
ActivityRecord getResumedActivity() {
final ActivityStack focusedStack = getFocusedStack();
if (focusedStack == null) {
@@ -376,6 +411,30 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
}
/**
+ * Pause all activities in either all of the stacks or just the back stacks.
+ * @param userLeaving Passed to pauseActivity() to indicate whether to call onUserLeaving().
+ * @param resuming The resuming activity.
+ * @param dontWait The resuming activity isn't going to wait for all activities to be paused
+ * before resuming.
+ * @return true if any activity was paused as a result of this call.
+ */
+ boolean pauseBackStacks(boolean userLeaving, ActivityRecord resuming, boolean dontWait) {
+ boolean someActivityPaused = false;
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ final ActivityStack stack = mStacks.get(stackNdx);
+ // TODO(b/111541062): Check if resumed activity on this display instead
+ if (!mSupervisor.isTopDisplayFocusedStack(stack)
+ && stack.getResumedActivity() != null) {
+ if (DEBUG_STATES) Slog.d(TAG_STATES, "pauseBackStacks: stack=" + stack +
+ " mResumedActivity=" + stack.getResumedActivity());
+ someActivityPaused |= stack.startPausingLocked(userLeaving, false, resuming,
+ dontWait);
+ }
+ }
+ return someActivityPaused;
+ }
+
+ /**
* Removes stacks in the input windowing modes from the system if they are of activity type
* ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
*/
@@ -582,7 +641,21 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
windowingMode = getWindowingMode();
}
}
+ return validateWindowingMode(windowingMode, r, task, activityType);
+ }
+ /**
+ * Check that the requested windowing-mode is appropriate for the specified task and/or activity
+ * on this display.
+ *
+ * @param windowingMode The windowing-mode to validate.
+ * @param r The {@link ActivityRecord} to check against.
+ * @param task The {@link TaskRecord} to check against.
+ * @param activityType An activity type.
+ * @return The provided windowingMode or the closest valid mode which is appropriate.
+ */
+ int validateWindowingMode(int windowingMode, @Nullable ActivityRecord r,
+ @Nullable TaskRecord task, int activityType) {
// Make sure the windowing mode we are trying to use makes sense for what is supported.
final ActivityTaskManagerService service = mSupervisor.mService;
boolean supportsMultiWindow = service.mSupportsMultiWindow;
@@ -902,6 +975,16 @@ class ActivityDisplay extends ConfigurationContainer<ActivityStack>
final long token = proto.start(fieldId);
super.writeToProto(proto, CONFIGURATION_CONTAINER, false /* trim */);
proto.write(ID, mDisplayId);
+ final ActivityStack focusedStack = getFocusedStack();
+ if (focusedStack != null) {
+ proto.write(FOCUSED_STACK_ID, focusedStack.mStackId);
+ final ActivityRecord focusedActivity = focusedStack.getDisplay().getResumedActivity();
+ if (focusedActivity != null) {
+ focusedActivity.writeIdentifierToProto(proto, RESUMED_ACTIVITY);
+ }
+ } else {
+ proto.write(FOCUSED_STACK_ID, INVALID_STACK_ID);
+ }
for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = mStacks.get(stackNdx);
stack.writeToProto(proto, STACKS);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 3a7c00cbb141..c64dfe0a4052 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -463,12 +463,6 @@ public class ActivityManagerService extends IActivityManager.Stub
static final int BROADCAST_FG_TIMEOUT = 10*1000;
static final int BROADCAST_BG_TIMEOUT = 60*1000;
- // How long we wait until we timeout on key dispatching.
- static final int KEY_DISPATCHING_TIMEOUT = 5*1000;
-
- // How long we wait until we timeout on key dispatching during instrumentation.
- static final int INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT = 60*1000;
-
// Disable hidden API checks for the newly started instrumentation.
// Must be kept in sync with Am.
private static final int INSTRUMENTATION_FLAG_DISABLE_HIDDEN_API_CHECKS = 1 << 0;
@@ -574,8 +568,6 @@ public class ActivityManagerService extends IActivityManager.Stub
final AppErrors mAppErrors;
- final AppWarnings mAppWarnings;
-
/**
* Dump of the activity state at the time of the last ANR. Cleared after
* {@link WindowManagerService#LAST_ANR_LIFETIME_DURATION_MSECS}
@@ -695,10 +687,42 @@ public class ActivityManagerService extends IActivityManager.Stub
* All of the processes we currently have running organized by pid.
* The keys are the pid running the application.
*
- * <p>NOTE: This object is protected by its own lock, NOT the global
- * activity manager lock!
+ * <p>NOTE: This object is protected by its own lock, NOT the global activity manager lock!
*/
- final SparseArray<ProcessRecord> mPidsSelfLocked = new SparseArray<ProcessRecord>();
+ final PidMap mPidsSelfLocked = new PidMap();
+ final class PidMap {
+ private final SparseArray<ProcessRecord> mPidMap = new SparseArray<>();
+
+ void put(int key, ProcessRecord value) {
+ mPidMap.put(key, value);
+ mAtmInternal.onProcessMapped(key, value.getWindowProcessController());
+ }
+
+ void remove(int pid) {
+ mPidMap.remove(pid);
+ mAtmInternal.onProcessUnMapped(pid);
+ }
+
+ ProcessRecord get(int pid) {
+ return mPidMap.get(pid);
+ }
+
+ int size() {
+ return mPidMap.size();
+ }
+
+ ProcessRecord valueAt(int index) {
+ return mPidMap.valueAt(index);
+ }
+
+ int keyAt(int index) {
+ return mPidMap.keyAt(index);
+ }
+
+ int indexOfKey(int key) {
+ return mPidMap.indexOfKey(key);
+ }
+ }
/**
* All of the processes that have been forced to be important. The key
@@ -1425,7 +1449,6 @@ public class ActivityManagerService extends IActivityManager.Stub
static final int CHECK_EXCESSIVE_POWER_USE_MSG = 27;
static final int CLEAR_DNS_CACHE_MSG = 28;
static final int UPDATE_HTTP_PROXY_MSG = 29;
- static final int SHOW_COMPAT_MODE_DIALOG_UI_MSG = 30;
static final int DISPATCH_PROCESSES_CHANGED_UI_MSG = 31;
static final int DISPATCH_PROCESS_DIED_UI_MSG = 32;
static final int REPORT_MEM_USAGE_MSG = 33;
@@ -1456,7 +1479,6 @@ public class ActivityManagerService extends IActivityManager.Stub
static ServiceThread sKillThread = null;
static KillHandler sKillHandler = null;
- CompatModeDialog mCompatModeDialog;
long mLastMemUsageReportTime = 0;
/**
@@ -1587,34 +1609,6 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
} break;
- case SHOW_COMPAT_MODE_DIALOG_UI_MSG: {
- synchronized (ActivityManagerService.this) {
- ActivityRecord ar = (ActivityRecord) msg.obj;
- if (mCompatModeDialog != null) {
- if (mCompatModeDialog.mAppInfo.packageName.equals(
- ar.info.applicationInfo.packageName)) {
- return;
- }
- mCompatModeDialog.dismiss();
- mCompatModeDialog = null;
- }
- if (ar != null && false) {
- if (mCompatModePackages.getPackageAskCompatModeLocked(
- ar.packageName)) {
- int mode = mCompatModePackages.computeCompatModeLocked(
- ar.info.applicationInfo);
- if (mode == ActivityManager.COMPAT_MODE_DISABLED
- || mode == ActivityManager.COMPAT_MODE_ENABLED) {
- mCompatModeDialog = new CompatModeDialog(
- ActivityManagerService.this, mUiContext,
- ar.info.applicationInfo);
- mCompatModeDialog.show();
- }
- }
- }
- }
- break;
- }
case DISPATCH_PROCESSES_CHANGED_UI_MSG: {
dispatchProcessesChanged();
break;
@@ -2433,7 +2427,6 @@ public class ActivityManagerService extends IActivityManager.Stub
mUiContext = null;
GL_ES_VERSION = 0;
mAppErrors = null;
- mAppWarnings = null;
mAppOpsService = mInjector.getAppOpsService(null, null);
mBatteryStatsService = null;
mCompatModePackages = null;
@@ -2500,8 +2493,6 @@ public class ActivityManagerService extends IActivityManager.Stub
final File systemDir = SystemServiceManager.ensureSystemDir();
- mAppWarnings = new AppWarnings(this, mUiContext, mHandler, mUiHandler, systemDir);
-
// TODO: Move creation of battery stats service outside of activity manager service.
mBatteryStatsService = new BatteryStatsService(systemContext, systemDir, mHandler);
mBatteryStatsService.getActiveStatistics().readLocked();
@@ -2891,28 +2882,6 @@ public class ActivityManagerService extends IActivityManager.Stub
mActivityTaskManager.unregisterTaskStackListener(listener);
}
- final void showAskCompatModeDialogLocked(ActivityRecord r) {
- Message msg = Message.obtain();
- msg.what = SHOW_COMPAT_MODE_DIALOG_UI_MSG;
- msg.obj = r.getTask().askedCompatMode ? null : r;
- mUiHandler.sendMessage(msg);
- }
-
- final AppWarnings getAppWarningsLocked() {
- return mAppWarnings;
- }
-
- /**
- * Shows app warning dialogs, if necessary.
- *
- * @param r activity record for which the warnings may be displayed
- */
- final void showAppWarningsIfNeededLocked(ActivityRecord r) {
- mAppWarnings.showUnsupportedCompileSdkDialogIfNeeded(r);
- mAppWarnings.showUnsupportedDisplaySizeDialogIfNeeded(r);
- mAppWarnings.showDeprecatedTargetDialogIfNeeded(r);
- }
-
private int updateLruProcessInternalLocked(ProcessRecord app, long now, int index,
String what, Object obj, ProcessRecord srcApp) {
app.lastActivityTime = now;
@@ -3635,8 +3604,8 @@ public class ActivityManagerService extends IActivityManager.Stub
app.pendingStart = false;
return;
}
- app.usingWrapper = invokeWith != null
- || SystemProperties.get("wrap." + app.processName) != null;
+ app.setUsingWrapper(invokeWith != null
+ || SystemProperties.get("wrap." + app.processName) != null);
mPendingStarts.put(startSeq, app);
}
final ProcessStartResult startResult = startProcess(app.hostingType, entryPoint,
@@ -3688,13 +3657,13 @@ public class ActivityManagerService extends IActivityManager.Stub
startResult = startWebView(entryPoint,
app.processName, uid, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
- app.info.dataDir, null,
+ app.info.dataDir, null, app.info.packageName,
new String[] {PROC_START_SEQ_IDENT + app.startSeq});
} else {
startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
- app.info.dataDir, invokeWith,
+ app.info.dataDir, invokeWith, app.info.packageName,
new String[] {PROC_START_SEQ_IDENT + app.startSeq});
}
checkTime(startTime, "startProcess: returned from zygote!");
@@ -3732,7 +3701,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// Indicates that this process start has been taken care of.
if (mPendingStarts.get(expectedStartSeq) == null) {
if (pending.pid == startResult.pid) {
- pending.usingWrapper = startResult.usingWrapper;
+ pending.setUsingWrapper(startResult.usingWrapper);
// TODO: Update already existing clients of usingWrapper
}
return false;
@@ -3795,7 +3764,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
reportUidInfoMessageLocked(TAG, buf.toString(), app.startUid);
app.setPid(pid);
- app.usingWrapper = usingWrapper;
+ app.setUsingWrapper(usingWrapper);
app.pendingStart = false;
checkTime(app.startTime, "startProcess: starting to update pids map");
ProcessRecord oldApp;
@@ -3880,7 +3849,7 @@ public class ActivityManagerService extends IActivityManager.Stub
aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
ProcessRecord app = getProcessRecordLocked(aInfo.processName,
aInfo.applicationInfo.uid, true);
- if (app == null || app.instr == null) {
+ if (app == null || app.getActiveInstrumentation() == null) {
intent.setFlags(intent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
final int resolvedUserId = UserHandle.getUserId(aInfo.applicationInfo.uid);
// For ANR debugging to verify if the user activity is the one that actually
@@ -4400,9 +4369,9 @@ public class ActivityManagerService extends IActivityManager.Stub
app.clearRecentTasks();
app.clearActivities();
- if (app.instr != null) {
+ if (app.getActiveInstrumentation() != null) {
Slog.w(TAG, "Crash of app " + app.processName
- + " running instrumentation " + app.instr.mClass);
+ + " running instrumentation " + app.getActiveInstrumentation().mClass);
Bundle info = new Bundle();
info.putString("shortMsg", "Process crashed.");
finishInstrumentationLocked(app, Activity.RESULT_CANCELED, info);
@@ -4411,7 +4380,7 @@ public class ActivityManagerService extends IActivityManager.Stub
mWindowManager.deferSurfaceLayout();
try {
if (!restarting && hasVisibleActivities
- && !mStackSupervisor.resumeFocusedStackTopActivityLocked()) {
+ && !mStackSupervisor.resumeFocusedStacksTopActivitiesLocked()) {
// If there was nothing to resume, and we are not already restarting this process, but
// there is a visible activity that is hosted by the process... then make sure all
// visible activities are running, taking care of restarting this process.
@@ -4556,7 +4525,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// Clean up already done if the process has been re-started.
if (app.pid == pid && app.thread != null &&
app.thread.asBinder() == thread.asBinder()) {
- boolean doLowMem = app.instr == null;
+ boolean doLowMem = app.getActiveInstrumentation() == null;
boolean doOomAdj = doLowMem;
if (!app.killedByAm) {
reportUidInfoMessageLocked(TAG,
@@ -5499,7 +5468,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// Clean-up disabled activities.
if (mStackSupervisor.finishDisabledPackageActivitiesLocked(
packageName, disabledClasses, true, false, userId) && mBooted) {
- mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
mStackSupervisor.scheduleIdleLocked();
}
@@ -5673,7 +5642,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
if (mBooted) {
- mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
mStackSupervisor.scheduleIdleLocked();
}
}
@@ -5885,7 +5854,7 @@ public class ActivityManagerService extends IActivityManager.Stub
if (app == null && startSeq > 0) {
final ProcessRecord pending = mPendingStarts.get(startSeq);
if (pending != null && pending.startUid == callingUid
- && handleProcessStartedLocked(pending, pid, pending.usingWrapper,
+ && handleProcessStartedLocked(pending, pid, pending.isUsingWrapper(),
startSeq, true)) {
app = pending;
}
@@ -5939,7 +5908,7 @@ public class ActivityManagerService extends IActivityManager.Stub
app.forcingToImportant = null;
updateProcessForegroundLocked(app, false, false);
app.hasShownUi = false;
- app.debugging = false;
+ app.setDebugging(false);
app.cached = false;
app.killedByAm = false;
app.killed = false;
@@ -5976,7 +5945,7 @@ public class ActivityManagerService extends IActivityManager.Stub
testMode = mWaitForDebugger
? ApplicationThreadConstants.DEBUG_WAIT
: ApplicationThreadConstants.DEBUG_ON;
- app.debugging = true;
+ app.setDebugging(true);
if (mDebugTransient) {
mDebugApp = mOrigDebugApp;
mWaitForDebugger = mOrigWaitForDebugger;
@@ -5998,13 +5967,15 @@ public class ActivityManagerService extends IActivityManager.Stub
|| (mBackupTarget.backupMode == BackupRecord.BACKUP_FULL));
}
- if (app.instr != null) {
- notifyPackageUse(app.instr.mClass.getPackageName(),
+ final ActiveInstrumentation instr = app.getActiveInstrumentation();
+
+ if (instr != null) {
+ notifyPackageUse(instr.mClass.getPackageName(),
PackageManager.NOTIFY_PACKAGE_USE_INSTRUMENTATION);
}
if (DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION, "Binding proc "
+ processName + " with config " + getGlobalConfiguration());
- ApplicationInfo appInfo = app.instr != null ? app.instr.mTargetInfo : app.info;
+ ApplicationInfo appInfo = instr != null ? instr.mTargetInfo : app.info;
app.compat = compatibilityInfoForPackageLocked(appInfo);
ProfilerInfo profilerInfo = null;
@@ -6021,8 +5992,8 @@ public class ActivityManagerService extends IActivityManager.Stub
preBindAgent = mProfilerInfo.agent;
}
}
- } else if (app.instr != null && app.instr.mProfileFile != null) {
- profilerInfo = new ProfilerInfo(app.instr.mProfileFile, null, 0, false, false,
+ } else if (instr != null && instr.mProfileFile != null) {
+ profilerInfo = new ProfilerInfo(instr.mProfileFile, null, 0, false, false,
null, false);
}
if (mAppAgentMap != null && mAppAgentMap.containsKey(processName)) {
@@ -6059,21 +6030,22 @@ public class ActivityManagerService extends IActivityManager.Stub
// currently active instrumentation. (Note we do this AFTER all of the profiling
// stuff above because profiling can currently happen only in the primary
// instrumentation process.)
- if (mActiveInstrumentation.size() > 0 && app.instr == null) {
- for (int i = mActiveInstrumentation.size() - 1; i >= 0 && app.instr == null; i--) {
+ if (mActiveInstrumentation.size() > 0 && instr == null) {
+ for (int i = mActiveInstrumentation.size() - 1;
+ i >= 0 && app.getActiveInstrumentation() == null; i--) {
ActiveInstrumentation aInstr = mActiveInstrumentation.get(i);
if (!aInstr.mFinished && aInstr.mTargetInfo.uid == app.uid) {
if (aInstr.mTargetProcesses.length == 0) {
// This is the wildcard mode, where every process brought up for
// the target instrumentation should be included.
if (aInstr.mTargetInfo.packageName.equals(app.info.packageName)) {
- app.instr = aInstr;
+ app.setActiveInstrumentation(aInstr);
aInstr.mRunningProcesses.add(app);
}
} else {
for (String proc : aInstr.mTargetProcesses) {
if (proc.equals(app.processName)) {
- app.instr = aInstr;
+ app.setActiveInstrumentation(aInstr);
aInstr.mRunningProcesses.add(app);
break;
}
@@ -6103,16 +6075,17 @@ public class ActivityManagerService extends IActivityManager.Stub
checkTime(startTime, "attachApplicationLocked: immediately before bindApplication");
mStackSupervisor.getActivityMetricsLogger().notifyBindApplication(app);
+ final ActiveInstrumentation instr2 = app.getActiveInstrumentation();
if (app.isolatedEntryPoint != null) {
// This is an isolated process which should just call an entry point instead of
// being bound to an application.
thread.runIsolatedEntryPoint(app.isolatedEntryPoint, app.isolatedEntryPointArgs);
- } else if (app.instr != null) {
+ } else if (instr2 != null) {
thread.bindApplication(processName, appInfo, providers,
- app.instr.mClass,
- profilerInfo, app.instr.mArguments,
- app.instr.mWatcher,
- app.instr.mUiAutomationConnection, testMode,
+ instr2.mClass,
+ profilerInfo, instr2.mArguments,
+ instr2.mWatcher,
+ instr2.mUiAutomationConnection, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.isPersistent(),
new Configuration(getGlobalConfiguration()), app.compat,
@@ -9257,7 +9230,8 @@ public class ActivityManagerService extends IActivityManager.Stub
if (proc == null) {
throw new SecurityException("Unknown process: " + callingPid);
}
- if (proc.instr == null || proc.instr.mUiAutomationConnection == null) {
+ if (proc.getActiveInstrumentation() == null
+ || proc.getActiveInstrumentation().mUiAutomationConnection == null) {
throw new SecurityException("Only an instrumentation process "
+ "with a UiAutomation can call setUserIsMonkey");
}
@@ -9380,89 +9354,6 @@ public class ActivityManagerService extends IActivityManager.Stub
ActivityManager.BUGREPORT_OPTION_WIFI);
}
-
- public static long getInputDispatchingTimeoutLocked(ActivityRecord r) {
- if (r == null || !r.hasProcess()) {
- return KEY_DISPATCHING_TIMEOUT;
- }
- return getInputDispatchingTimeoutLocked((ProcessRecord) r.app.mOwner);
- }
-
- public static long getInputDispatchingTimeoutLocked(ProcessRecord r) {
- if (r != null && (r.instr != null || r.usingWrapper)) {
- return INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT;
- }
- return KEY_DISPATCHING_TIMEOUT;
- }
-
- @Override
- public long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
- if (checkCallingPermission(android.Manifest.permission.FILTER_EVENTS)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Requires permission "
- + android.Manifest.permission.FILTER_EVENTS);
- }
- ProcessRecord proc;
- long timeout;
- synchronized (this) {
- synchronized (mPidsSelfLocked) {
- proc = mPidsSelfLocked.get(pid);
- }
- timeout = getInputDispatchingTimeoutLocked(proc);
- }
-
- if (inputDispatchingTimedOut(proc, null, null, aboveSystem, reason)) {
- return -1;
- }
-
- return timeout;
- }
-
- /**
- * Handle input dispatching timeouts.
- * Returns whether input dispatching should be aborted or not.
- */
- public boolean inputDispatchingTimedOut(final ProcessRecord proc,
- final ActivityRecord activity, final ActivityRecord parent,
- final boolean aboveSystem, String reason) {
- if (checkCallingPermission(android.Manifest.permission.FILTER_EVENTS)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Requires permission "
- + android.Manifest.permission.FILTER_EVENTS);
- }
-
- final String annotation;
- if (reason == null) {
- annotation = "Input dispatching timed out";
- } else {
- annotation = "Input dispatching timed out (" + reason + ")";
- }
-
- if (proc != null) {
- synchronized (this) {
- if (proc.debugging) {
- return false;
- }
-
- if (proc.instr != null) {
- Bundle info = new Bundle();
- info.putString("shortMsg", "keyDispatchingTimedOut");
- info.putString("longMsg", annotation);
- finishInstrumentationLocked(proc, Activity.RESULT_CANCELED, info);
- return true;
- }
- }
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mAppErrors.appNotResponding(proc, activity, parent, aboveSystem, annotation);
- }
- });
- }
-
- return true;
- }
-
public void registerProcessObserver(IProcessObserver observer) {
enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
"registerProcessObserver()");
@@ -10307,7 +10198,7 @@ public class ActivityManagerService extends IActivityManager.Stub
} finally {
Binder.restoreCallingIdentity(ident);
}
- mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
mUserController.sendUserSwitchBroadcasts(-1, currentUserId);
BinderInternal.nSetBinderProxyCountWatermarks(6000,5500);
@@ -10406,6 +10297,15 @@ public class ActivityManagerService extends IActivityManager.Stub
: StatsLog.APP_CRASH_OCCURRED__FOREGROUND_STATE__UNKNOWN
);
+ final int relaunchReason = r == null ? ActivityRecord.RELAUNCH_REASON_NONE
+ : r.getWindowProcessController().computeRelaunchReason();
+ final String relaunchReasonString = ActivityRecord.relaunchReasonToString(relaunchReason);
+ if (crashInfo.crashTag == null) {
+ crashInfo.crashTag = relaunchReasonString;
+ } else {
+ crashInfo.crashTag = crashInfo.crashTag + " " + relaunchReasonString;
+ }
+
addErrorToDropBox(eventType, r, processName, null, null, null, null, null, crashInfo);
mAppErrors.crashApplication(r, crashInfo);
@@ -10732,6 +10632,9 @@ public class ActivityManagerService extends IActivityManager.Stub
if (Debug.isDebuggerConnected()) {
sb.append("Debugger: Connected\n");
}
+ if (crashInfo != null && crashInfo.crashTag != null && !crashInfo.crashTag.isEmpty()) {
+ sb.append("Crash-Tag: ").append(crashInfo.crashTag).append("\n");
+ }
sb.append("\n");
// Do the rest in a worker thread to avoid blocking the caller on I/O
@@ -16413,7 +16316,7 @@ public class ActivityManagerService extends IActivityManager.Stub
mActivityTaskManager.getRecentTasks().removeTasksByPackageName(ssp, userId);
mServices.forceStopPackageLocked(ssp, userId);
- mAppWarnings.onPackageUninstalled(ssp);
+ mAtmInternal.onPackageUninstalled(ssp);
mCompatModePackages.handlePackageUninstalledLocked(ssp);
mBatteryStatsService.notePackageUninstalled(ssp);
}
@@ -16494,7 +16397,7 @@ public class ActivityManagerService extends IActivityManager.Stub
String ssp;
if (data != null && (ssp = data.getSchemeSpecificPart()) != null) {
mCompatModePackages.handlePackageDataClearedLocked(ssp);
- mAppWarnings.onPackageDataCleared(ssp);
+ mAtmInternal.onPackageDataCleared(ssp);
}
break;
}
@@ -17125,7 +17028,7 @@ public class ActivityManagerService extends IActivityManager.Stub
ProcessRecord app = addAppLocked(ai, defProcess, false, disableHiddenApiChecks,
abiOverride);
- app.instr = activeInstr;
+ app.setActiveInstrumentation(activeInstr);
activeInstr.mFinished = false;
activeInstr.mRunningProcesses.add(app);
if (!mActiveInstrumentation.contains(activeInstr)) {
@@ -17158,16 +17061,17 @@ public class ActivityManagerService extends IActivityManager.Stub
}
void addInstrumentationResultsLocked(ProcessRecord app, Bundle results) {
- if (app.instr == null) {
+ final ActiveInstrumentation instr = app.getActiveInstrumentation();
+ if (instr == null) {
Slog.w(TAG, "finishInstrumentation called on non-instrumented: " + app);
return;
}
- if (!app.instr.mFinished && results != null) {
- if (app.instr.mCurResults == null) {
- app.instr.mCurResults = new Bundle(results);
+ if (!instr.mFinished && results != null) {
+ if (instr.mCurResults == null) {
+ instr.mCurResults = new Bundle(results);
} else {
- app.instr.mCurResults.putAll(results);
+ instr.mCurResults.putAll(results);
}
}
}
@@ -17193,37 +17097,38 @@ public class ActivityManagerService extends IActivityManager.Stub
@GuardedBy("this")
void finishInstrumentationLocked(ProcessRecord app, int resultCode, Bundle results) {
- if (app.instr == null) {
+ final ActiveInstrumentation instr = app.getActiveInstrumentation();
+ if (instr == null) {
Slog.w(TAG, "finishInstrumentation called on non-instrumented: " + app);
return;
}
- if (!app.instr.mFinished) {
- if (app.instr.mWatcher != null) {
- Bundle finalResults = app.instr.mCurResults;
+ if (!instr.mFinished) {
+ if (instr.mWatcher != null) {
+ Bundle finalResults = instr.mCurResults;
if (finalResults != null) {
- if (app.instr.mCurResults != null && results != null) {
+ if (instr.mCurResults != null && results != null) {
finalResults.putAll(results);
}
} else {
finalResults = results;
}
- mInstrumentationReporter.reportFinished(app.instr.mWatcher,
- app.instr.mClass, resultCode, finalResults);
+ mInstrumentationReporter.reportFinished(instr.mWatcher,
+ instr.mClass, resultCode, finalResults);
}
// Can't call out of the system process with a lock held, so post a message.
- if (app.instr.mUiAutomationConnection != null) {
+ if (instr.mUiAutomationConnection != null) {
mAppOpsService.setAppOpsServiceDelegate(null);
getPackageManagerInternalLocked().setCheckPermissionDelegate(null);
mHandler.obtainMessage(SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG,
- app.instr.mUiAutomationConnection).sendToTarget();
+ instr.mUiAutomationConnection).sendToTarget();
}
- app.instr.mFinished = true;
+ instr.mFinished = true;
}
- app.instr.removeProcess(app);
- app.instr = null;
+ instr.removeProcess(app);
+ app.setActiveInstrumentation(null);
forceStopPackageLocked(app.info.packageName, -1, false, false, true, true, false, app.userId,
"finished inst");
@@ -17697,7 +17602,7 @@ public class ActivityManagerService extends IActivityManager.Stub
if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making running remote anim: " + app);
}
- } else if (app.instr != null) {
+ } else if (app.getActiveInstrumentation() != null) {
// Don't want to kill running instrumentation.
adj = ProcessList.FOREGROUND_APP_ADJ;
schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
@@ -18332,8 +18237,10 @@ public class ActivityManagerService extends IActivityManager.Stub
}
if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
- reportOomAdjMessageLocked(TAG_OOM_ADJ,
- "Raise procstate to external provider: " + app);
+ if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
+ reportOomAdjMessageLocked(TAG_OOM_ADJ,
+ "Raise procstate to external provider: " + app);
+ }
}
}
}
@@ -21194,6 +21101,13 @@ public class ActivityManagerService extends IActivityManager.Stub
return ActivityManagerService.this.getHomeIntent();
}
}
+
+ @Override
+ public void scheduleAppGcs() {
+ synchronized (ActivityManagerService.this) {
+ ActivityManagerService.this.scheduleAppGcsLocked();
+ }
+ }
}
/**
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 68e70a9634d1..67abedc96eed 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -339,6 +339,17 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
int mStartingWindowState = STARTING_WINDOW_NOT_SHOWN;
boolean mTaskOverlay = false; // Task is always on-top of other activities in the task.
+ // This activity is not being relaunched, or being relaunched for a non-resize reason.
+ static final int RELAUNCH_REASON_NONE = 0;
+ // This activity is being relaunched due to windowing mode change.
+ static final int RELAUNCH_REASON_WINDOWING_MODE_RESIZE = 1;
+ // This activity is being relaunched due to a free-resize operation.
+ static final int RELAUNCH_REASON_FREE_RESIZE = 2;
+ // Marking the reason why this activity is being relaunched. Mainly used to track that this
+ // activity is being relaunched to fulfill a resize request due to compatibility issues, e.g. in
+ // pre-NYC apps that don't have a sense of being resized.
+ int mRelaunchReason = RELAUNCH_REASON_NONE;
+
TaskDescription taskDescription; // the recents information for this activity
boolean mLaunchTaskBehind; // this activity is actively being launched with
// ActivityOptions.setLaunchTaskBehind, will be cleared once launch is completed.
@@ -1025,7 +1036,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
(info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, info.configChanges,
task.voiceSession != null, mLaunchTaskBehind, isAlwaysFocusable(),
appInfo.targetSdkVersion, mRotationAnimationHint,
- ActivityManagerService.getInputDispatchingTimeoutLocked(this) * 1000000L);
+ ActivityTaskManagerService.getInputDispatchingTimeoutLocked(this) * 1000000L);
task.addActivityToTop(this);
@@ -1930,7 +1941,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
} else {
if (deferRelaunchUntilPaused) {
stack.destroyActivityLocked(this, true /* removeFromApp */, "stop-config");
- mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
} else {
mStackSupervisor.updatePreviousProcessLocked(this);
}
@@ -2123,7 +2134,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
mStackSupervisor.processStoppingActivitiesLocked(null /* idleActivity */,
false /* remove */, true /* processPausingActivities */);
}
- service.mAm.scheduleAppGcsLocked();
+ service.scheduleAppGcsLocked();
}
}
}
@@ -2148,13 +2159,11 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
!hasProcess() || app.getPid() == windowPid || windowPid == -1;
}
if (windowFromSameProcessAsActivity) {
- return service.mAm.inputDispatchingTimedOut(
- (ProcessRecord) anrApp.mOwner, anrActivity, this, false, reason);
+ return service.inputDispatchingTimedOut(anrApp, anrActivity, this, false, reason);
} else {
// In this case another process added windows using this activity token. So, we call the
// generic service input dispatch timed out method so that the right process is blamed.
- return service.mAm.inputDispatchingTimedOut(
- windowPid, false /* aboveSystem */, reason) < 0;
+ return service.inputDispatchingTimedOut(windowPid, false /* aboveSystem */, reason) < 0;
}
}
@@ -2353,7 +2362,7 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
frozenBeforeDestroy = true;
if (!service.updateDisplayOverrideConfigurationLocked(config, this,
false /* deferResume */, displayId)) {
- mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
}
}
service.getTaskChangeNotificationController().notifyActivityRequestedOrientationChanged(
@@ -2617,6 +2626,15 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
startFreezingScreenLocked(app, globalChanges);
forceNewConfig = false;
preserveWindow &= isResizeOnlyChange(changes);
+ final boolean hasResizeChange = hasResizeChange(changes & ~info.getRealConfigChanged());
+ if (hasResizeChange) {
+ final boolean isDragResizing =
+ getTask().getWindowContainerController().isDragResizing();
+ mRelaunchReason = isDragResizing ? RELAUNCH_REASON_FREE_RESIZE
+ : RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
+ } else {
+ mRelaunchReason = RELAUNCH_REASON_NONE;
+ }
if (!attachedToProcess()) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
"Config is destroying non-running " + this);
@@ -2738,6 +2756,11 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
| CONFIG_SCREEN_LAYOUT)) == 0;
}
+ private static boolean hasResizeChange(int change) {
+ return (change & (CONFIG_SCREEN_SIZE | CONFIG_SMALLEST_SCREEN_SIZE | CONFIG_ORIENTATION
+ | CONFIG_SCREEN_LAYOUT)) != 0;
+ }
+
void relaunchActivityLocked(boolean andResume, boolean preserveWindow) {
if (service.mSuppressResizeConfigChanges && preserveWindow) {
configChangeFlags = 0;
@@ -2794,10 +2817,12 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
}
results = null;
newIntents = null;
- service.mAm.getAppWarningsLocked().onResumeActivity(this);
- service.mAm.showAskCompatModeDialogLocked(this);
+ service.getAppWarningsLocked().onResumeActivity(this);
} else {
- service.mAm.mHandler.removeMessages(PAUSE_TIMEOUT_MSG, this);
+ final ActivityStack stack = getStack();
+ if (stack != null) {
+ stack.mHandler.removeMessages(PAUSE_TIMEOUT_MSG, this);
+ }
setState(PAUSED, "relaunchActivityLocked");
}
@@ -3017,6 +3042,17 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo
mWindowContainerController.registerRemoteAnimations(definition);
}
+ static String relaunchReasonToString(int relaunchReason) {
+ switch (relaunchReason) {
+ case RELAUNCH_REASON_WINDOWING_MODE_RESIZE:
+ return "window_resize";
+ case RELAUNCH_REASON_FREE_RESIZE:
+ return "free_resize";
+ default:
+ return null;
+ }
+ }
+
@Override
public String toString() {
if (stringName != null) {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 68a6b5afe71d..19b62e718164 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -72,6 +72,8 @@ import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_USER_LEAV
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_VISIBILITY;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.am.ActivityRecord.RELAUNCH_REASON_FREE_RESIZE;
+import static com.android.server.am.ActivityRecord.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
import static com.android.server.am.ActivityStack.ActivityState.DESTROYED;
import static com.android.server.am.ActivityStack.ActivityState.DESTROYING;
import static com.android.server.am.ActivityStack.ActivityState.FINISHING;
@@ -495,7 +497,10 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
if (DEBUG_STACK) Slog.v(TAG_STACK, "set resumed activity to:" + record + " reason:"
+ reason);
setResumedActivity(record, reason + " - onActivityStateChanged");
- mService.setResumedActivityUncheckLocked(record, reason);
+ if (record == mStackSupervisor.getTopResumedActivity()) {
+ // TODO(b/111541062): Support tracking multiple resumed activities
+ mService.setResumedActivityUncheckLocked(record, reason);
+ }
mStackSupervisor.mRecentTasks.add(record.getTask());
}
}
@@ -535,15 +540,17 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
final ActivityStack splitScreenStack = display.getSplitScreenPrimaryStack();
mTmpOptions.setLaunchWindowingMode(preferredWindowingMode);
+ int windowingMode = preferredWindowingMode;
// Need to make sure windowing mode is supported. If we in the process of creating the stack
// no need to resolve the windowing mode again as it is already resolved to the right mode.
- int windowingMode = creating
- ? preferredWindowingMode
- : display.resolveWindowingMode(
- null /* ActivityRecord */, mTmpOptions, topTask, getActivityType());
- if (splitScreenStack == this && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
- // Resolution to split-screen secondary for the primary split-screen stack means we want
- // to go fullscreen.
+ if (!creating) {
+ windowingMode = display.validateWindowingMode(windowingMode,
+ null /* ActivityRecord */, topTask, getActivityType());
+ }
+ if (splitScreenStack == this
+ && windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
+ // Resolution to split-screen secondary for the primary split-screen stack means
+ // we want to go fullscreen.
windowingMode = WINDOWING_MODE_FULLSCREEN;
}
@@ -593,6 +600,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
mStackSupervisor.mNoAnimActivities.add(topActivity);
}
super.setWindowingMode(windowingMode);
+ windowingMode = getWindowingMode();
if (creating) {
// Nothing else to do if we don't have a window container yet. E.g. call from ctor.
@@ -619,15 +627,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
mTmpRect2.setEmpty();
if (windowingMode != WINDOWING_MODE_FULLSCREEN) {
mWindowContainerController.getRawBounds(mTmpRect2);
- if (windowingMode == WINDOWING_MODE_FREEFORM) {
- if (topTask != null) {
- // TODO: Can we consolidate this and other sites that call this methods?
- Rect bounds = topTask().getLaunchBounds();
- if (bounds != null) {
- mTmpRect2.set(bounds);
- }
- }
- }
}
if (!Objects.equals(getOverrideBounds(), mTmpRect2)) {
@@ -661,7 +660,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
if (!deferEnsuringVisibility) {
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
- mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
}
}
@@ -694,7 +693,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
mWindowContainerController.reparent(activityDisplay.mDisplayId, mTmpRect2, onTop);
postAddToDisplay(activityDisplay, mTmpRect2.isEmpty() ? null : mTmpRect2, onTop);
adjustFocusToNextFocusableStack("reparent", true /* allowFocusSelf */);
- mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
// Update visibility of activities before notifying WM. This way it won't try to resize
// windows that are no longer visible.
mStackSupervisor.ensureActivitiesVisibleLocked(null /* starting */, 0 /* configChanges */,
@@ -1443,7 +1442,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
if (prev == null) {
if (resuming == null) {
Slog.wtf(TAG, "Trying to pause when nothing is resumed");
- mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
}
return false;
}
@@ -1523,7 +1522,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// pause, so just treat it as being paused now.
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Activity not running, resuming next.");
if (resuming == null) {
- mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
}
return false;
}
@@ -1617,7 +1616,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
if (resumeNext) {
final ActivityStack topStack = mStackSupervisor.getTopDisplayFocusedStack();
if (!topStack.shouldSleepOrShutDownActivities()) {
- mStackSupervisor.resumeFocusedStackTopActivityLocked(topStack, prev, null);
+ mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(topStack, prev, null);
} else {
checkReadyForSleep();
ActivityRecord top = topStack.topRunningActivityLocked();
@@ -1626,7 +1625,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// something. Also if the top activity on the stack is not the just paused
// activity, we need to go ahead and resume it to ensure we complete an
// in-flight app switch.
- mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
}
}
}
@@ -2305,7 +2304,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
*
* NOTE: It is not safe to call this method directly as it can cause an activity in a
* non-focused stack to be resumed.
- * Use {@link ActivityStackSupervisor#resumeFocusedStackTopActivityLocked} to resume the
+ * Use {@link ActivityStackSupervisor#resumeFocusedStacksTopActivitiesLocked} to resume the
* right activity for the current system state.
*/
@GuardedBy("mService")
@@ -2468,7 +2467,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
final boolean resumeWhilePausing = (next.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0
&& !lastResumedCanPip;
- boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, next, false);
+ boolean pausing = getDisplay().pauseBackStacks(userLeaving, next, false);
if (mResumedActivity != null) {
if (DEBUG_STATES) Slog.d(TAG_STATES,
"resumeTopActivityLocked: Pausing " + mResumedActivity);
@@ -2657,7 +2656,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// the screen based on the new activity order.
boolean notUpdated = true;
- if (mStackSupervisor.isTopDisplayFocusedStack(this)) {
+ if (isFocusedStackOnDisplay()) {
// We have special rotation behavior when here is some active activity that
// requests specific orientation or Keyguard is locked. Make sure all activity
// visibilities are set correctly as well as the transition is updated if needed
@@ -2720,8 +2719,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
next.shortComponentName);
next.sleeping = false;
- mService.mAm.getAppWarningsLocked().onResumeActivity(next);
- mService.mAm.showAskCompatModeDialogLocked(next);
+ mService.getAppWarningsLocked().onResumeActivity(next);
next.app.setPendingUiCleanAndForceProcessStateUpTo(mService.mTopProcessState);
next.clearOptionsLocked();
transaction.setLifecycleStateRequest(
@@ -2790,12 +2788,13 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
private boolean resumeTopActivityInNextFocusableStack(ActivityRecord prev,
ActivityOptions options, String reason) {
- if (adjustFocusToNextFocusableStack(reason)) {
+ final ActivityStack nextFocusedStack = adjustFocusToNextFocusableStack(reason);
+ if (nextFocusedStack != null) {
// Try to move focus to the next visible stack with a running activity if this
// stack is not covering the entire screen or is on a secondary display (with no home
// stack).
- return mStackSupervisor.resumeFocusedStackTopActivityLocked(
- mStackSupervisor.getTopDisplayFocusedStack(), prev, null);
+ return mStackSupervisor.resumeFocusedStacksTopActivitiesLocked(nextFocusedStack, prev,
+ null /* targetOptions */);
}
// Let's just start up the Launcher...
@@ -2808,20 +2807,6 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
mStackSupervisor.resumeHomeStackTask(prev, reason);
}
- private TaskRecord getNextTask(TaskRecord targetTask) {
- final int index = mTaskHistory.indexOf(targetTask);
- if (index >= 0) {
- final int numTasks = mTaskHistory.size();
- for (int i = index + 1; i < numTasks; ++i) {
- TaskRecord task = mTaskHistory.get(i);
- if (task.userId == targetTask.userId) {
- return task;
- }
- }
- }
- return null;
- }
-
/** Returns the position the input task should be placed in this stack. */
int getAdjustedPositionForTask(TaskRecord task, int suggestedPosition,
ActivityRecord starting) {
@@ -3436,7 +3421,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
// Move focus to next focusable stack if possible.
- if (adjustFocusToNextFocusableStack(myReason)) {
+ if (adjustFocusToNextFocusableStack(myReason) != null) {
return;
}
@@ -3444,21 +3429,25 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
mStackSupervisor.moveHomeStackTaskToTop(myReason);
}
- /** Find next proper focusable stack and make it focused. */
- boolean adjustFocusToNextFocusableStack(String reason) {
+ /**
+ * Find next proper focusable stack and make it focused.
+ * @return The stack that now got the focus, {@code null} if none found.
+ */
+ ActivityStack adjustFocusToNextFocusableStack(String reason) {
return adjustFocusToNextFocusableStack(reason, false /* allowFocusSelf */);
}
/**
* Find next proper focusable stack and make it focused.
* @param allowFocusSelf Is the focus allowed to remain on the same stack.
+ * @return The stack that now got the focus, {@code null} if none found.
*/
- private boolean adjustFocusToNextFocusableStack(String reason, boolean allowFocusSelf) {
+ private ActivityStack adjustFocusToNextFocusableStack(String reason, boolean allowFocusSelf) {
final ActivityStack stack =
mStackSupervisor.getNextFocusableStackLocked(this, !allowFocusSelf);
final String myReason = reason + " adjustFocusToNextFocusableStack";
if (stack == null) {
- return false;
+ return null;
}
final ActivityRecord top = stack.topRunningActivityLocked();
@@ -3466,11 +3455,12 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
if (stack.isActivityTypeHome() && (top == null || !top.visible)) {
// If we will be focusing on the home stack next and its current top activity isn't
// visible, then use the move the home stack task to top to make the activity visible.
- return mStackSupervisor.moveHomeStackTaskToTop(reason);
+ mStackSupervisor.moveHomeStackTaskToTop(reason);
+ return stack;
}
stack.moveToFront(myReason);
- return true;
+ return stack;
}
final void stopActivityLocked(ActivityRecord r) {
@@ -3876,7 +3866,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
false /* markFrozenIfConfigChanged */, true /* deferResume */);
}
if (activityRemoved) {
- mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
}
if (DEBUG_CONTAINERS) Slog.d(TAG_CONTAINERS,
"destroyActivityLocked: finishCurrentActivityLocked r=" + r +
@@ -3889,7 +3879,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
if (DEBUG_ALL) Slog.v(TAG, "Enqueueing pending finish: " + r);
mStackSupervisor.mFinishingActivities.add(r);
r.resumeKeyDispatchingLocked();
- mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
return r;
}
@@ -4235,7 +4225,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
}
if (activityRemoved) {
- mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
}
}
@@ -4430,7 +4420,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
}
}
- mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
}
private void removeHistoryRecordsForAppLocked(ArrayList<ActivityRecord> list,
@@ -4483,7 +4473,14 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
hasVisibleActivities = true;
}
final boolean remove;
- if ((!r.haveState && !r.stateNotNeeded) || r.finishing) {
+ if ((r.mRelaunchReason == RELAUNCH_REASON_WINDOWING_MODE_RESIZE
+ || r.mRelaunchReason == RELAUNCH_REASON_FREE_RESIZE)
+ && r.launchCount < 3 && !r.finishing) {
+ // If the process crashed during a resize, always try to relaunch it, unless
+ // it has failed more than twice. Skip activities that's already finishing
+ // cleanly by itself.
+ remove = false;
+ } else if ((!r.haveState && !r.stateNotNeeded) || r.finishing) {
// Don't currently have state for the activity, or
// it is finishing -- always remove it.
remove = true;
@@ -4656,7 +4653,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
topActivity.supportsEnterPipOnTaskSwitch = true;
}
- mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, tr.userId, tr.taskId);
mService.getTaskChangeNotificationController().notifyTaskMovedToFront(tr.taskId);
@@ -4727,7 +4724,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
return true;
}
- mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
return true;
}
@@ -4774,7 +4771,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
if (updatedConfig) {
// Ensure the resumed state of the focus activity if we updated the configuration of
// any activity.
- mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
}
}
@@ -4809,9 +4806,11 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// For freeform stack we don't adjust the size of the tasks to match that
// of the stack, but we do try to make sure the tasks are still contained
// with the bounds of the stack.
- mTmpRect2.set(task.getOverrideBounds());
- fitWithinBounds(mTmpRect2, bounds);
- task.updateOverrideConfiguration(mTmpRect2);
+ if (task.getOverrideBounds() != null) {
+ mTmpRect2.set(task.getOverrideBounds());
+ fitWithinBounds(mTmpRect2, bounds);
+ task.updateOverrideConfiguration(mTmpRect2);
+ }
} else {
task.updateOverrideConfiguration(taskBounds, insetBounds);
}
@@ -5174,7 +5173,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
if (isOnHomeDisplay() && mode != REMOVE_TASK_MODE_MOVING_TO_TOP
&& mStackSupervisor.isTopDisplayFocusedStack(this)) {
String myReason = reason + " leftTaskHistoryEmpty";
- if (!inMultiWindowMode() || !adjustFocusToNextFocusableStack(myReason)) {
+ if (!inMultiWindowMode() || adjustFocusToNextFocusableStack(myReason) == null) {
mStackSupervisor.moveHomeStackToFront(myReason);
}
}
@@ -5279,7 +5278,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
// The task might have already been running and its visibility needs to be synchronized with
// the visibility of the stack / windows.
ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
- mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
}
private ActivityStack preAddTask(TaskRecord task, String reason, boolean toTop) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index b08efde051b7..d1585cf8fb48 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -17,10 +17,8 @@
package com.android.server.am;
import static android.Manifest.permission.ACTIVITY_EMBEDDING;
-import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.Manifest.permission.START_ANY_ACTIVITY;
-import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED;
import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
import static android.app.ActivityManager.START_TASK_TO_FRONT;
@@ -74,8 +72,8 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.ActivityManagerService.ANIMATE;
import static com.android.server.am.ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG;
+import static com.android.server.am.ActivityRecord.RELAUNCH_REASON_NONE;
import static com.android.server.am.ActivityStack.ActivityState.DESTROYED;
-import static com.android.server.am.ActivityStack.ActivityState.DESTROYING;
import static com.android.server.am.ActivityStack.ActivityState.INITIALIZING;
import static com.android.server.am.ActivityStack.ActivityState.PAUSED;
import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
@@ -200,8 +198,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
private static final String TAG_RECENTS = TAG + POSTFIX_RECENTS;
private static final String TAG_RELEASE = TAG + POSTFIX_RELEASE;
private static final String TAG_STACK = TAG + POSTFIX_STACK;
- private static final String TAG_STATES = TAG + POSTFIX_STATES;
private static final String TAG_SWITCH = TAG + POSTFIX_SWITCH;
+ static final String TAG_STATES = TAG + POSTFIX_STATES;
static final String TAG_TASKS = TAG + POSTFIX_TASKS;
/** How long we wait until giving up on the last activity telling us it is idle. */
@@ -820,7 +818,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// Only resume home activity if isn't finishing.
if (r != null && !r.finishing) {
moveFocusableActivityStackToFrontLocked(r, myReason);
- return resumeFocusedStackTopActivityLocked(mHomeStack, prev, null);
+ return resumeFocusedStacksTopActivitiesLocked(mHomeStack, prev, null);
}
return mService.mAm.startHomeActivityLocked(mCurrentUser, myReason);
}
@@ -1118,16 +1116,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
boolean pauseBackStacks(boolean userLeaving, ActivityRecord resuming, boolean dontWait) {
boolean someActivityPaused = false;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
- for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = display.getChildAt(stackNdx);
- if (!isTopDisplayFocusedStack(stack) && stack.getResumedActivity() != null) {
- if (DEBUG_STATES) Slog.d(TAG_STATES, "pauseBackStacks: stack=" + stack +
- " mResumedActivity=" + stack.getResumedActivity());
- someActivityPaused |= stack.startPausingLocked(userLeaving, false, resuming,
- dontWait);
- }
- }
+ someActivityPaused |= mActivityDisplays.valueAt(displayNdx)
+ .pauseBackStacks(userLeaving, resuming, dontWait);
}
return someActivityPaused;
}
@@ -1399,7 +1389,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// (e.g. AMS.startActivityAsUser).
final long token = Binder.clearCallingIdentity();
try {
- return mService.mAm.getPackageManagerInternalLocked().resolveIntent(
+ return mService.getPackageManagerInternalLocked().resolveIntent(
intent, resolvedType, modifiedFlags, userId, true, filterCallingUid);
} finally {
Binder.restoreCallingIdentity(token);
@@ -1522,8 +1512,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
PackageManager.NOTIFY_PACKAGE_USE_ACTIVITY);
r.sleeping = false;
r.forceNewConfig = false;
- mService.mAm.getAppWarningsLocked().onStartActivity(r);
- mService.mAm.showAskCompatModeDialogLocked(r);
+ mService.getAppWarningsLocked().onStartActivity(r);
r.compat = mService.mAm.compatibilityInfoForPackageLocked(r.info.applicationInfo);
ProfilerInfo profilerInfo = null;
if (mService.mAm.mProfileApp != null && mService.mAm.mProfileApp.equals(app.processName)) {
@@ -2100,11 +2089,15 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
if (isTopDisplayFocusedStack(r.getStack()) || fromTimeout) {
booting = checkFinishBootingLocked();
}
+
+ // When activity is idle, we consider the relaunch must be successful, so let's clear
+ // the flag.
+ r.mRelaunchReason = RELAUNCH_REASON_NONE;
}
if (allResumedActivitiesIdle()) {
if (r != null) {
- mService.mAm.scheduleAppGcsLocked();
+ mService.scheduleAppGcsLocked();
}
if (mLaunchingActivity.isHeld()) {
@@ -2171,7 +2164,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
//mWindowManager.dump();
if (activityRemoved) {
- resumeFocusedStackTopActivityLocked();
+ resumeFocusedStacksTopActivitiesLocked();
}
return r;
@@ -2267,28 +2260,35 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
}
- boolean resumeFocusedStackTopActivityLocked() {
- return resumeFocusedStackTopActivityLocked(null, null, null);
+ boolean resumeFocusedStacksTopActivitiesLocked() {
+ return resumeFocusedStacksTopActivitiesLocked(null, null, null);
}
- boolean resumeFocusedStackTopActivityLocked(
+ boolean resumeFocusedStacksTopActivitiesLocked(
ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
if (!readyToResume()) {
return false;
}
- if (targetStack != null && isTopDisplayFocusedStack(targetStack)) {
+ if (targetStack != null && targetStack.isTopStackOnDisplay()) {
return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
}
- final ActivityStack focusedStack = getTopDisplayFocusedStack();
- final ActivityRecord r = focusedStack.topRunningActivityLocked();
- if (r == null || !r.isState(RESUMED)) {
- focusedStack.resumeTopActivityUncheckedLocked(null, null);
- } else if (r.isState(RESUMED)) {
- // Kick off any lingering app transitions form the MoveTaskToFront operation.
- focusedStack.executeAppTransition(targetOptions);
+ // Resume all top activities in focused stacks on all displays.
+ for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
+ final ActivityDisplay display = mActivityDisplays.valueAt(displayNdx);
+ final ActivityStack focusedStack = display.getFocusedStack();
+ if (focusedStack == null) {
+ continue;
+ }
+ final ActivityRecord r = focusedStack.topRunningActivityLocked();
+ if (r == null || !r.isState(RESUMED)) {
+ focusedStack.resumeTopActivityUncheckedLocked(null, null);
+ } else if (r.isState(RESUMED)) {
+ // Kick off any lingering app transitions form the MoveTaskToFront operation.
+ focusedStack.executeAppTransition(targetOptions);
+ }
}
return false;
@@ -2339,6 +2339,9 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
}
+ /**
+ * This doesn't just find a task, it also moves the task to front.
+ */
void findTaskToMoveToFront(TaskRecord task, int flags, ActivityOptions options, String reason,
boolean forceNonResizeable) {
final ActivityStack currentStack = task.getStack();
@@ -2608,74 +2611,58 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
case ACTIVITY_TYPE_RECENTS: return r.isActivityTypeRecents();
case ACTIVITY_TYPE_ASSISTANT: return r.isActivityTypeAssistant();
}
- switch (stack.getWindowingMode()) {
- case WINDOWING_MODE_FULLSCREEN: return true;
- case WINDOWING_MODE_FREEFORM: return r.supportsFreeform();
- case WINDOWING_MODE_PINNED: return r.supportsPictureInPicture();
- case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY: return r.supportsSplitScreenWindowingMode();
- case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY: return r.supportsSplitScreenWindowingMode();
- }
-
- if (!stack.isOnHomeDisplay()) {
- return r.canBeLaunchedOnDisplay(displayId);
+ // There is a 1-to-1 relationship between stack and task when not in
+ // primary split-windowing mode.
+ if (stack.getWindowingMode() != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+ return false;
+ } else {
+ return r.supportsSplitScreenWindowingMode();
}
- Slog.e(TAG, "isValidLaunchStack: Unexpected stack=" + stack);
- return false;
}
/**
- * Get next focusable stack in the system. This will search across displays and stacks
- * in last-focused order for a focusable and visible stack, different from the target stack.
+ * Get next focusable stack in the system. This will search through the stack on the same
+ * display as the current focused stack, looking for a focusable and visible stack, different
+ * from the target stack. If no valid candidates will be found, it will then go through all
+ * displays and stacks in last-focused order.
*
* @param currentFocus The stack that previously had focus.
* @param ignoreCurrent If we should ignore {@param currentFocus} when searching for next
* candidate.
- * @return Next focusable {@link ActivityStack}, null if not found.
+ * @return Next focusable {@link ActivityStack}, {@code null} if not found.
*/
- ActivityStack getNextFocusableStackLocked(ActivityStack currentFocus, boolean ignoreCurrent) {
- mWindowManager.getDisplaysInFocusOrder(mTmpOrderedDisplayIds);
+ ActivityStack getNextFocusableStackLocked(@NonNull ActivityStack currentFocus,
+ boolean ignoreCurrent) {
+ // First look for next focusable stack on the same display
+ final ActivityDisplay preferredDisplay = currentFocus.getDisplay();
+ final ActivityStack preferredFocusableStack = preferredDisplay.getNextFocusableStack(
+ currentFocus, ignoreCurrent);
+ if (preferredFocusableStack != null) {
+ return preferredFocusableStack;
+ }
- final int currentWindowingMode = currentFocus != null
- ? currentFocus.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
- ActivityStack candidate = null;
+ // Now look through all displays
+ mWindowManager.getDisplaysInFocusOrder(mTmpOrderedDisplayIds);
for (int i = mTmpOrderedDisplayIds.size() - 1; i >= 0; --i) {
final int displayId = mTmpOrderedDisplayIds.get(i);
+ if (displayId == preferredDisplay.mDisplayId) {
+ // We've already checked this one
+ continue;
+ }
// If a display is registered in WM, it must also be available in AM.
final ActivityDisplay display = getActivityDisplayOrCreateLocked(displayId);
if (display == null) {
// Looks like the display no longer exists in the system...
continue;
}
- for (int j = display.getChildCount() - 1; j >= 0; --j) {
- final ActivityStack stack = display.getChildAt(j);
- if (ignoreCurrent && stack == currentFocus) {
- continue;
- }
- if (!stack.isFocusable() || !stack.shouldBeVisible(null)) {
- continue;
- }
-
- if (currentWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
- && candidate == null && stack.inSplitScreenPrimaryWindowingMode()) {
- // If the currently focused stack is in split-screen secondary we save off the
- // top primary split-screen stack as a candidate for focus because we might
- // prefer focus to move to an other stack to avoid primary split-screen stack
- // overlapping with a fullscreen stack when a fullscreen stack is higher in z
- // than the next split-screen stack. Assistant stack, I am looking at you...
- // We only move the focus to the primary-split screen stack if there isn't a
- // better alternative.
- candidate = stack;
- continue;
- }
- if (candidate != null && stack.inSplitScreenSecondaryWindowingMode()) {
- // Use the candidate stack since we are now at the secondary split-screen.
- return candidate;
- }
- return stack;
+ final ActivityStack nextFocusableStack = display.getNextFocusableStack(currentFocus,
+ ignoreCurrent);
+ if (nextFocusableStack != null) {
+ return nextFocusableStack;
}
}
- return candidate;
+ return null;
}
/**
@@ -2874,7 +2861,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
}
ensureActivitiesVisibleLocked(null, 0, PRESERVE_WINDOWS);
- resumeFocusedStackTopActivityLocked();
+ resumeFocusedStacksTopActivitiesLocked();
} finally {
mAllowDockedStackResize = true;
mWindowManager.continueSurfaceLayout();
@@ -3431,7 +3418,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// drawn signal is scheduled after the bounds animation start call on the bounds animator
// thread.
ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
- resumeFocusedStackTopActivityLocked();
+ resumeFocusedStacksTopActivitiesLocked();
mService.getTaskChangeNotificationController().notifyActivityPinned(r);
}
@@ -3462,6 +3449,11 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
"moveActivityStackToFront: r=" + r);
stack.moveToFront(reason, task);
+ // Report top activity change to tracking services and WM
+ if (r == getTopResumedActivity()) {
+ // TODO(b/111541062): Support tracking multiple resumed activities
+ mService.setResumedActivityUncheckLocked(r, reason);
+ }
return true;
}
@@ -3618,7 +3610,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
// It is possible that the display will not be awake at the time we
// process the keyguard going away, which can happen before the sleep token
// is released. As a result, it is important we resume the activity here.
- resumeFocusedStackTopActivityLocked();
+ resumeFocusedStacksTopActivitiesLocked();
}
}
}
@@ -4724,7 +4716,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
} break;
case RESUME_TOP_ACTIVITY_MSG: {
synchronized (mService.mGlobalLock) {
- resumeFocusedStackTopActivityLocked();
+ resumeFocusedStacksTopActivitiesLocked();
}
} break;
case SLEEP_TIMEOUT_MSG: {
diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
index ca12716e7199..177e2f563a4b 100644
--- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
@@ -237,7 +237,7 @@ class ActivityStartInterceptor {
(mAInfo.applicationInfo.flags & FLAG_SUSPENDED) == 0) {
return false;
}
- final PackageManagerInternal pmi = mService.mAm.getPackageManagerInternalLocked();
+ final PackageManagerInternal pmi = mService.getPackageManagerInternalLocked();
if (pmi == null) {
return false;
}
@@ -318,7 +318,7 @@ class ActivityStartInterceptor {
private boolean interceptHarmfulAppIfNeeded() {
CharSequence harmfulAppWarning;
try {
- harmfulAppWarning = mService.mAm.getPackageManager()
+ harmfulAppWarning = mService.getPackageManager()
.getHarmfulAppWarning(mAInfo.packageName, mUserId);
} catch (RemoteException ex) {
return false;
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 8be5adab1713..05fae837364a 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -672,7 +672,7 @@ class ActivityStarter {
&& sourceRecord.info.applicationInfo.uid != aInfo.applicationInfo.uid) {
try {
intent.addCategory(Intent.CATEGORY_VOICE);
- if (!mService.mAm.getPackageManager().activitySupportsIntent(
+ if (!mService.getPackageManager().activitySupportsIntent(
intent.getComponent(), intent, resolvedType)) {
Slog.w(TAG,
"Activity being started in current voice task does not support voice: "
@@ -690,7 +690,7 @@ class ActivityStarter {
// If the caller is starting a new voice session, just make sure the target
// is actually allowing it to run this way.
try {
- if (!mService.mAm.getPackageManager().activitySupportsIntent(intent.getComponent(),
+ if (!mService.getPackageManager().activitySupportsIntent(intent.getComponent(),
intent, resolvedType)) {
Slog.w(TAG,
"Activity being started in new voice task does not support: "
@@ -772,7 +772,7 @@ class ActivityStarter {
// launch the review activity and pass a pending intent to start the activity
// we are to launching now after the review is completed.
if (aInfo != null) {
- if (mService.mAm.getPackageManagerInternalLocked().isPermissionsReviewRequired(
+ if (mService.getPackageManagerInternalLocked().isPermissionsReviewRequired(
aInfo.packageName, userId)) {
IIntentSender target = mService.mAm.getIntentSenderLocked(
ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
@@ -870,7 +870,7 @@ class ActivityStarter {
String resolvedType, int userId) {
if (auxiliaryResponse != null && auxiliaryResponse.needsPhaseTwo) {
// request phase two resolution
- mService.mAm.getPackageManagerInternalLocked().requestInstantAppResolutionPhaseTwo(
+ mService.getPackageManagerInternalLocked().requestInstantAppResolutionPhaseTwo(
auxiliaryResponse, originalIntent, resolvedType, callingPackage,
verificationBundle, userId);
}
@@ -970,7 +970,7 @@ class ActivityStarter {
&& !(Intent.ACTION_VIEW.equals(intent.getAction()) && intent.getData() == null)
&& !Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE.equals(intent.getAction())
&& !Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE.equals(intent.getAction())
- && mService.mAm.getPackageManagerInternalLocked()
+ && mService.getPackageManagerInternalLocked()
.isInstantAppInstallerComponent(intent.getComponent())) {
// intercept intents targeted directly to the ephemeral installer the
// ephemeral installer should never be started with a raw Intent; instead
@@ -1367,7 +1367,7 @@ class ActivityStarter {
// For paranoia, make sure we have correctly resumed the top activity.
topStack.mLastPausedActivity = null;
if (mDoResume) {
- mSupervisor.resumeFocusedStackTopActivityLocked();
+ mSupervisor.resumeFocusedStacksTopActivitiesLocked();
}
ActivityOptions.abort(mOptions);
if ((mStartFlags & START_FLAG_ONLY_IF_NEEDED) != 0) {
@@ -1451,7 +1451,7 @@ class ActivityStarter {
&& !mSupervisor.isTopDisplayFocusedStack(mTargetStack)) {
mTargetStack.moveToFront("startActivityUnchecked");
}
- mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,
+ mSupervisor.resumeFocusedStacksTopActivitiesLocked(mTargetStack, mStartActivity,
mOptions);
}
} else if (mStartActivity != null) {
@@ -2023,7 +2023,7 @@ class ActivityStarter {
private void resumeTargetStackIfNeeded() {
if (mDoResume) {
- mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, null, mOptions);
+ mSupervisor.resumeFocusedStacksTopActivitiesLocked(mTargetStack, null, mOptions);
} else {
ActivityOptions.abort(mOptions);
}
@@ -2139,7 +2139,7 @@ class ActivityStarter {
// For paranoia, make sure we have correctly resumed the top activity.
mTargetStack.mLastPausedActivity = null;
if (mDoResume) {
- mSupervisor.resumeFocusedStackTopActivityLocked();
+ mSupervisor.resumeFocusedStacksTopActivitiesLocked();
}
ActivityOptions.abort(mOptions);
return START_DELIVERED_TO_TOP;
@@ -2157,7 +2157,7 @@ class ActivityStarter {
deliverNewIntent(top);
mTargetStack.mLastPausedActivity = null;
if (mDoResume) {
- mSupervisor.resumeFocusedStackTopActivityLocked();
+ mSupervisor.resumeFocusedStacksTopActivitiesLocked();
}
return START_DELIVERED_TO_TOP;
}
diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
index 20f2ece9565c..748b2d2d612a 100644
--- a/services/core/java/com/android/server/am/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
@@ -19,6 +19,7 @@ package com.android.server.am;
import static android.Manifest.permission.BIND_VOICE_INTERACTION;
import static android.Manifest.permission.CHANGE_CONFIGURATION;
import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
+import static android.Manifest.permission.FILTER_EVENTS;
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
import static android.Manifest.permission.READ_FRAME_BUFFER;
@@ -36,15 +37,12 @@ import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTE
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_RECEIVER_EXTRAS;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
-import static android.app.ActivityTaskManager.INVALID_STACK_ID;
import static android.app.ActivityTaskManager.RESIZE_MODE_PRESERVE_WINDOW;
import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
import static android.app.AppOpsManager.OP_NONE;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
@@ -74,7 +72,6 @@ import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATI
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_IMMERSIVE;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_OOM_ADJ;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_STACK;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SWITCH;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
@@ -122,6 +119,8 @@ import android.app.ActivityThread;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManagerInternal;
import android.database.ContentObserver;
import android.os.IUserManager;
import android.os.PowerManager;
@@ -131,9 +130,9 @@ import android.os.UserManager;
import android.os.WorkSource;
import android.view.WindowManager;
import com.android.internal.R;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IAppOpsService;
import com.android.server.AppOpsService;
+import com.android.server.SystemServiceManager;
import com.android.server.pm.UserManagerService;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
@@ -181,7 +180,6 @@ import android.os.LocaleList;
import android.os.Looper;
import android.os.Message;
import android.os.PersistableBundle;
-import android.os.Process;
import android.os.RemoteException;
import android.os.StrictMode;
import android.os.SystemClock;
@@ -250,6 +248,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
private static final String TAG_LOCKTASK = TAG + POSTFIX_LOCKTASK;
private static final String TAG_CONFIGURATION = TAG + POSTFIX_CONFIGURATION;
+ // How long we wait until we timeout on key dispatching.
+ private static final int KEY_DISPATCHING_TIMEOUT_MS = 5 * 1000;
+ // How long we wait until we timeout on key dispatching during instrumentation.
+ private static final int INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS = 60 * 1000;
+
Context mContext;
/**
* This Context is themable and meant for UI display (AlertDialogs, etc.). The theme can
@@ -261,6 +264,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
ActivityManagerService mAm;
ActivityManagerInternal mAmInternal;
UriGrantsManagerInternal mUgmInternal;
+ private PackageManagerInternal mPmInternal;
/* Global service lock used by the package the owns this service. */
Object mGlobalLock;
ActivityStackSupervisor mStackSupervisor;
@@ -269,6 +273,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
private AppOpsService mAppOpsService;
/** All processes currently running that might have a window organized by name. */
final ProcessMap<WindowProcessController> mProcessNames = new ProcessMap<>();
+ /** All processes we currently have running mapped by pid */
+ final SparseArray<WindowProcessController> mPidMap = new SparseArray<>();
/** This is the process holding what we currently consider to be the "home" activity. */
WindowProcessController mHomeProcess;
/**
@@ -466,6 +472,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
/** If non-null, we are tracking the time the user spends in the currently focused app. */
AppTimeTracker mCurAppTimeTracker;
+ private AppWarnings mAppWarnings;
+
private FontScaleSettingObserver mFontScaleSettingObserver;
private final class FontScaleSettingObserver extends ContentObserver {
@@ -595,6 +603,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
mGlobalLock = mAm;
mH = new H(mAm.mHandlerThread.getLooper());
mUiHandler = new UiHandler();
+ mAppWarnings = new AppWarnings(
+ this, mUiContext, mH, mUiHandler, SystemServiceManager.ensureSystemDir());
mTempConfig.setToDefaults();
mTempConfig.setLocales(LocaleList.getDefault());
@@ -1195,6 +1205,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
if (!res) {
Slog.i(TAG, "Removing task failed to finish activity");
}
+ // Explicitly dismissing the activity so reset its relaunch flag.
+ r.mRelaunchReason = ActivityRecord.RELAUNCH_REASON_NONE;
} else {
res = tr.getStack().requestFinishActivityLocked(token, resultCode,
resultData, "app-request", true);
@@ -1612,7 +1624,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
final ActivityRecord r = stack.topRunningActivityLocked();
if (mStackSupervisor.moveFocusableActivityStackToFrontLocked(
r, "setFocusedStack")) {
- mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
}
}
} finally {
@@ -1633,7 +1645,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
final ActivityRecord r = task.topRunningActivityLocked();
if (mStackSupervisor.moveFocusableActivityStackToFrontLocked(r, "setFocusedTask")) {
- mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
}
}
} finally {
@@ -2398,10 +2410,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
public boolean isTopOfTask(IBinder token) {
synchronized (mGlobalLock) {
ActivityRecord r = ActivityRecord.isInStackLocked(token);
- if (r == null) {
- throw new IllegalArgumentException();
- }
- return r.getTask().getTopActivity() == r;
+ return r != null && r.getTask().getTopActivity() == r;
}
}
@@ -4006,7 +4015,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
synchronized (mGlobalLock) {
final long origId = Binder.clearCallingIdentity();
try {
- mAm.mAppWarnings.alwaysShowUnsupportedCompileSdkWarning(activity);
+ mAppWarnings.alwaysShowUnsupportedCompileSdkWarning(activity);
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -4491,7 +4500,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
final boolean isDensityChange = (changes & ActivityInfo.CONFIG_DENSITY) != 0;
if (isDensityChange && displayId == DEFAULT_DISPLAY) {
- mAm.mAppWarnings.onDensityChanged();
+ mAppWarnings.onDensityChanged();
mAm.killAllBackgroundProcessesExcept(N,
ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE);
@@ -4538,6 +4547,80 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
&& mAmInternal.getCurrentUser().isDemo());
}
+ static long getInputDispatchingTimeoutLocked(ActivityRecord r) {
+ if (r == null || !r.hasProcess()) {
+ return KEY_DISPATCHING_TIMEOUT_MS;
+ }
+ return getInputDispatchingTimeoutLocked(r.app);
+ }
+
+ private static long getInputDispatchingTimeoutLocked(WindowProcessController r) {
+ if (r != null && (r.isInstrumenting() || r.isUsingWrapper())) {
+ return INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MS;
+ }
+ return KEY_DISPATCHING_TIMEOUT_MS;
+ }
+
+ long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) {
+ if (checkCallingPermission(FILTER_EVENTS) != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires permission " + FILTER_EVENTS);
+ }
+ WindowProcessController proc;
+ long timeout;
+ synchronized (mGlobalLock) {
+ proc = mPidMap.get(pid);
+ timeout = getInputDispatchingTimeoutLocked(proc);
+ }
+
+ if (inputDispatchingTimedOut(proc, null, null, aboveSystem, reason)) {
+ return -1;
+ }
+
+ return timeout;
+ }
+
+ /**
+ * Handle input dispatching timeouts.
+ * Returns whether input dispatching should be aborted or not.
+ */
+ boolean inputDispatchingTimedOut(final WindowProcessController proc,
+ final ActivityRecord activity, final ActivityRecord parent,
+ final boolean aboveSystem, String reason) {
+ if (checkCallingPermission(FILTER_EVENTS) != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires permission " + FILTER_EVENTS);
+ }
+
+ final String annotation;
+ if (reason == null) {
+ annotation = "Input dispatching timed out";
+ } else {
+ annotation = "Input dispatching timed out (" + reason + ")";
+ }
+
+ if (proc != null) {
+ synchronized (mGlobalLock) {
+ if (proc.isDebugging()) {
+ return false;
+ }
+
+ if (proc.isInstrumenting()) {
+ Bundle info = new Bundle();
+ info.putString("shortMsg", "keyDispatchingTimedOut");
+ info.putString("longMsg", annotation);
+ mAm.finishInstrumentationLocked(
+ (ProcessRecord) proc.mOwner, Activity.RESULT_CANCELED, info);
+ return true;
+ }
+ }
+ mH.post(() -> {
+ mAm.mAppErrors.appNotResponding(
+ (ProcessRecord) proc.mOwner, activity, parent, aboveSystem, annotation);
+ });
+ }
+
+ return true;
+ }
+
/**
* Decide based on the configuration whether we should show the ANR,
* crash, etc dialogs. The idea is that if there is no affordance to
@@ -4649,6 +4732,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
updateResumedAppTrace(r);
mLastResumedActivity = r;
+ // TODO(b/111541062): Support multiple focused apps in WM
mWindowManager.setFocusedApp(r.appToken, true);
applyUpdateLockStateLocked(r);
@@ -4782,6 +4866,30 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
return kept;
}
+ void scheduleAppGcsLocked() {
+ mH.post(() -> mAmInternal.scheduleAppGcs());
+ }
+
+ /**
+ * Returns the PackageManager. Used by classes hosted by {@link ActivityTaskManagerService}. The
+ * PackageManager could be unavailable at construction time and therefore needs to be accessed
+ * on demand.
+ */
+ IPackageManager getPackageManager() {
+ return AppGlobals.getPackageManager();
+ }
+
+ PackageManagerInternal getPackageManagerInternalLocked() {
+ if (mPmInternal == null) {
+ mPmInternal = LocalServices.getService(PackageManagerInternal.class);
+ }
+ return mPmInternal;
+ }
+
+ AppWarnings getAppWarningsLocked() {
+ return mAppWarnings;
+ }
+
void logAppTooSlow(WindowProcessController app, long startTime, String msg) {
if (true || Build.IS_USER) {
return;
@@ -5039,7 +5147,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
if (mStackSupervisor.moveFocusableActivityStackToFrontLocked(
r, "setFocusedActivity")) {
- mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
}
}
}
@@ -5230,5 +5338,41 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
}
}
+
+ @Override
+ public long inputDispatchingTimedOut(int pid, boolean aboveSystem, String reason) {
+ synchronized (mGlobalLock) {
+ return ActivityTaskManagerService.this.inputDispatchingTimedOut(
+ pid, aboveSystem, reason);
+ }
+ }
+
+ @Override
+ public void onProcessMapped(int pid, WindowProcessController proc) {
+ synchronized (mGlobalLock) {
+ mPidMap.put(pid, proc);
+ }
+ }
+
+ @Override
+ public void onProcessUnMapped(int pid) {
+ synchronized (mGlobalLock) {
+ mPidMap.remove(pid);
+ }
+ }
+
+ @Override
+ public void onPackageDataCleared(String name) {
+ synchronized (mGlobalLock) {
+ mAppWarnings.onPackageDataCleared(name);
+ }
+ }
+
+ @Override
+ public void onPackageUninstalled(String name) {
+ synchronized (mGlobalLock) {
+ mAppWarnings.onPackageUninstalled(name);
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 27567a77f1f7..3b98f373d817 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -410,6 +410,10 @@ class AppErrors {
RescueParty.notePersistentAppCrash(mContext, r.uid);
}
+ final int relaunchReason = r != null
+ ? r.getWindowProcessController().computeRelaunchReason()
+ : ActivityRecord.RELAUNCH_REASON_NONE;
+
AppErrorResult result = new AppErrorResult();
TaskRecord task;
synchronized (mService) {
@@ -422,11 +426,17 @@ class AppErrors {
return;
}
+ // Suppress crash dialog if the process is being relaunched due to a crash during a free
+ // resize.
+ if (relaunchReason == ActivityRecord.RELAUNCH_REASON_FREE_RESIZE) {
+ return;
+ }
+
/**
* If this process was running instrumentation, finish now - it will be handled in
* {@link ActivityManagerService#handleAppDiedLocked}.
*/
- if (r != null && r.instr != null) {
+ if (r != null && r.getActiveInstrumentation() != null) {
return;
}
@@ -493,7 +503,7 @@ class AppErrors {
mService.mStackSupervisor.handleAppCrashLocked(r.getWindowProcessController());
if (!r.isPersistent()) {
mService.removeProcessLocked(r, false, false, "crash");
- mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ mService.mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
}
} finally {
Binder.restoreCallingIdentity(orig);
@@ -733,12 +743,12 @@ class AppErrors {
// annoy the user repeatedly. Unless it is persistent, since those
// processes run critical code.
mService.removeProcessLocked(app, false, tryAgain, "crash");
- mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ mService.mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
if (!showBackground) {
return false;
}
}
- mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ mService.mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
} else {
final TaskRecord affectedTask =
mService.mStackSupervisor.finishTopCrashedActivitiesLocked(app.getWindowProcessController(), reason);
diff --git a/services/core/java/com/android/server/am/AppWarnings.java b/services/core/java/com/android/server/am/AppWarnings.java
index 30a384434675..a705180e48ee 100644
--- a/services/core/java/com/android/server/am/AppWarnings.java
+++ b/services/core/java/com/android/server/am/AppWarnings.java
@@ -17,7 +17,6 @@
package com.android.server.am;
import android.annotation.UiThread;
-import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.res.Configuration;
@@ -57,9 +56,9 @@ class AppWarnings {
private final HashMap<String, Integer> mPackageFlags = new HashMap<>();
- private final ActivityManagerService mAms;
+ private final ActivityTaskManagerService mAtm;
private final Context mUiContext;
- private final ConfigHandler mAmsHandler;
+ private final ConfigHandler mHandler;
private final UiHandler mUiHandler;
private final AtomicFile mConfigFile;
@@ -81,17 +80,17 @@ class AppWarnings {
* <p>
* <strong>Note:</strong> Must be called from the ActivityManagerService thread.
*
- * @param ams
+ * @param atm
* @param uiContext
- * @param amsHandler
+ * @param handler
* @param uiHandler
* @param systemDir
*/
- public AppWarnings(ActivityManagerService ams, Context uiContext, Handler amsHandler,
+ public AppWarnings(ActivityTaskManagerService atm, Context uiContext, Handler handler,
Handler uiHandler, File systemDir) {
- mAms = ams;
+ mAtm = atm;
mUiContext = uiContext;
- mAmsHandler = new ConfigHandler(amsHandler.getLooper());
+ mHandler = new ConfigHandler(handler.getLooper());
mUiHandler = new UiHandler(uiHandler.getLooper());
mConfigFile = new AtomicFile(new File(systemDir, CONFIG_FILE_NAME), "warnings-config");
@@ -104,7 +103,7 @@ class AppWarnings {
* @param r activity record for which the warning may be displayed
*/
public void showUnsupportedDisplaySizeDialogIfNeeded(ActivityRecord r) {
- final Configuration globalConfig = mAms.getGlobalConfiguration();
+ final Configuration globalConfig = mAtm.getGlobalConfiguration();
if (globalConfig.densityDpi != DisplayMetrics.DENSITY_DEVICE_STABLE
&& r.appInfo.requiresSmallestWidthDp > globalConfig.smallestScreenWidthDp) {
mUiHandler.showUnsupportedDisplaySizeDialog(r);
@@ -211,7 +210,7 @@ class AppWarnings {
synchronized (mPackageFlags) {
mPackageFlags.remove(name);
- mAmsHandler.scheduleWrite();
+ mHandler.scheduleWrite();
}
}
@@ -351,7 +350,7 @@ class AppWarnings {
} else {
mPackageFlags.remove(name);
}
- mAmsHandler.scheduleWrite();
+ mHandler.scheduleWrite();
}
}
}
@@ -430,10 +429,10 @@ class AppWarnings {
}
/**
- * Handles messages on the ActivityManagerService thread.
+ * Handles messages on the ActivityTaskManagerService thread.
*/
private final class ConfigHandler extends Handler {
- private static final int MSG_WRITE = ActivityManagerService.FIRST_COMPAT_MODE_MSG;
+ private static final int MSG_WRITE = 1;
private static final int DELAY_MSG_WRITE = 10000;
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 046cfc7820b5..b36b5d35f28e 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -1462,7 +1462,7 @@ public final class BroadcastQueue {
// If the receiver app is being debugged we quietly ignore unresponsiveness, just
// tidying up and moving on to the next broadcast without crashing or ANRing this
// app just because it's stopped at a breakpoint.
- final boolean debugging = (r.curApp != null && r.curApp.debugging);
+ final boolean debugging = (r.curApp != null && r.curApp.isDebugging());
Slog.w(TAG, "Timeout of broadcast " + r + " - receiver=" + r.receiver
+ ", started " + (now - r.receiverTime) + "ms ago");
diff --git a/services/core/java/com/android/server/am/CompatModeDialog.java b/services/core/java/com/android/server/am/CompatModeDialog.java
deleted file mode 100644
index 202cc7cad79d..000000000000
--- a/services/core/java/com/android/server/am/CompatModeDialog.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.am;
-
-import android.app.ActivityManager;
-import android.app.Dialog;
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.view.Gravity;
-import android.view.View;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.CheckBox;
-import android.widget.CompoundButton;
-import android.widget.Switch;
-
-public final class CompatModeDialog extends Dialog {
- final ActivityManagerService mService;
- final ApplicationInfo mAppInfo;
-
- final Switch mCompatEnabled;
- final CheckBox mAlwaysShow;
- final View mHint;
-
- public CompatModeDialog(ActivityManagerService service, Context context,
- ApplicationInfo appInfo) {
- super(context, com.android.internal.R.style.Theme_Holo_Dialog_MinWidth);
- setCancelable(true);
- setCanceledOnTouchOutside(true);
- getWindow().requestFeature(Window.FEATURE_NO_TITLE);
- getWindow().setType(WindowManager.LayoutParams.TYPE_PHONE);
- getWindow().setGravity(Gravity.BOTTOM|Gravity.CENTER_HORIZONTAL);
- mService = service;
- mAppInfo = appInfo;
-
- setContentView(com.android.internal.R.layout.am_compat_mode_dialog);
- mCompatEnabled = (Switch)findViewById(com.android.internal.R.id.compat_checkbox);
- mCompatEnabled.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- synchronized (mService) {
- mService.mCompatModePackages.setPackageScreenCompatModeLocked(
- mAppInfo.packageName,
- mCompatEnabled.isChecked() ? ActivityManager.COMPAT_MODE_ENABLED
- : ActivityManager.COMPAT_MODE_DISABLED);
- updateControls();
- }
- }
- });
- mAlwaysShow = (CheckBox)findViewById(com.android.internal.R.id.ask_checkbox);
- mAlwaysShow.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- synchronized (mService) {
- mService.mCompatModePackages.setPackageAskCompatModeLocked(
- mAppInfo.packageName, mAlwaysShow.isChecked());
- updateControls();
- }
- }
- });
- mHint = findViewById(com.android.internal.R.id.reask_hint);
-
- updateControls();
- }
-
- void updateControls() {
- synchronized (mService) {
- int mode = mService.mCompatModePackages.computeCompatModeLocked(mAppInfo);
- mCompatEnabled.setChecked(mode == ActivityManager.COMPAT_MODE_ENABLED);
- boolean ask = mService.mCompatModePackages.getPackageAskCompatModeLocked(
- mAppInfo.packageName);
- mAlwaysShow.setChecked(ask);
- mHint.setVisibility(ask ? View.INVISIBLE : View.VISIBLE);
- }
- }
-}
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index e345b4d61cc1..ee4e36ff1fd1 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -163,7 +163,7 @@ class KeyguardController {
updateKeyguardSleepToken();
// Some stack visibility might change (e.g. docked stack)
- mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
mStackSupervisor.addStartingWindowsForVisibleActivities(true /* taskSwitch */);
mWindowManager.executeAppTransition();
diff --git a/services/core/java/com/android/server/am/LockTaskController.java b/services/core/java/com/android/server/am/LockTaskController.java
index 4fd01cdeaa3b..643c922ad2ca 100644
--- a/services/core/java/com/android/server/am/LockTaskController.java
+++ b/services/core/java/com/android/server/am/LockTaskController.java
@@ -446,7 +446,7 @@ public class LockTaskController {
return;
}
task.performClearTaskLocked();
- mSupervisor.resumeFocusedStackTopActivityLocked();
+ mSupervisor.resumeFocusedStacksTopActivitiesLocked();
}
/**
@@ -578,7 +578,7 @@ public class LockTaskController {
if (andResume) {
mSupervisor.findTaskToMoveToFront(task, 0, null, reason,
lockTaskModeState != LOCK_TASK_MODE_NONE);
- mSupervisor.resumeFocusedStackTopActivityLocked();
+ mSupervisor.resumeFocusedStacksTopActivitiesLocked();
mWindowManager.executeAppTransition();
} else if (lockTaskModeState != LOCK_TASK_MODE_NONE) {
mSupervisor.handleNonResizableTaskIfNeeded(task, WINDOWING_MODE_UNDEFINED,
@@ -653,7 +653,7 @@ public class LockTaskController {
}
if (taskChanged) {
- mSupervisor.resumeFocusedStackTopActivityLocked();
+ mSupervisor.resumeFocusedStacksTopActivitiesLocked();
}
}
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index b33ce2b71019..8c552b98698a 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -187,8 +187,9 @@ final class ProcessRecord implements WindowProcessListener {
int lruSeq; // Sequence id for identifying LRU update cycles
CompatibilityInfo compat; // last used compatibility mode
IBinder.DeathRecipient deathRecipient; // Who is watching for the death.
- ActiveInstrumentation instr;// Set to currently active instrumentation running in process
- boolean usingWrapper; // Set to true when process was launched with a wrapper attached
+ private ActiveInstrumentation mInstr; // Set to currently active instrumentation running in
+ // process.
+ private boolean mUsingWrapper; // Set to true when process was launched with a wrapper attached
final ArraySet<BroadcastRecord> curReceivers = new ArraySet<BroadcastRecord>();// receivers currently running in the app
long whenUnimportant; // When (uptime) the process last became unimportant
long lastCpuTime; // How long proc has run CPU at last check
@@ -232,7 +233,7 @@ final class ProcessRecord implements WindowProcessListener {
private boolean mNotResponding; // does the app have a not responding dialog?
Dialog anrDialog; // dialog being displayed due to app not resp.
boolean removed; // has app package been removed from device?
- boolean debugging; // was app launched for debugging?
+ private boolean mDebugging; // was app launched for debugging?
boolean waitedForDebugger; // has process show wait for debugger dialog?
Dialog waitDialog; // current wait for debugger dialog
@@ -317,8 +318,8 @@ final class ProcessRecord implements WindowProcessListener {
pw.println("}");
}
pw.print(prefix); pw.print("compat="); pw.println(compat);
- if (instr != null) {
- pw.print(prefix); pw.print("instr="); pw.println(instr);
+ if (mInstr != null) {
+ pw.print(prefix); pw.print("mInstr="); pw.println(mInstr);
}
pw.print(prefix); pw.print("thread="); pw.println(thread);
pw.print(prefix); pw.print("pid="); pw.print(pid); pw.print(" starting=");
@@ -435,9 +436,9 @@ final class ProcessRecord implements WindowProcessListener {
pw.print(" killedByAm="); pw.print(killedByAm);
pw.print(" waitingToKill="); pw.println(waitingToKill);
}
- if (debugging || mCrashing || crashDialog != null || mNotResponding
+ if (mDebugging || mCrashing || crashDialog != null || mNotResponding
|| anrDialog != null || bad) {
- pw.print(prefix); pw.print("debugging="); pw.print(debugging);
+ pw.print(prefix); pw.print("mDebugging="); pw.print(mDebugging);
pw.print(" mCrashing="); pw.print(mCrashing);
pw.print(" "); pw.print(crashDialog);
pw.print(" mNotResponding="); pw.print(mNotResponding);
@@ -975,6 +976,33 @@ final class ProcessRecord implements WindowProcessListener {
return mHasForegroundServices;
}
+ void setDebugging(boolean debugging) {
+ mDebugging = debugging;
+ mWindowProcessController.setDebugging(debugging);
+ }
+
+ boolean isDebugging() {
+ return mDebugging;
+ }
+
+ void setUsingWrapper(boolean usingWrapper) {
+ mUsingWrapper = usingWrapper;
+ mWindowProcessController.setUsingWrapper(usingWrapper);
+ }
+
+ boolean isUsingWrapper() {
+ return mUsingWrapper;
+ }
+
+ void setActiveInstrumentation(ActiveInstrumentation instr) {
+ mInstr = instr;
+ mWindowProcessController.setInstrumenting(instr != null);
+ }
+
+ ActiveInstrumentation getActiveInstrumentation() {
+ return mInstr;
+ }
+
@Override
public void clearProfilerIfNeeded() {
synchronized (mService) {
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index f0bd8fa31478..8ce650c1a514 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -612,7 +612,8 @@ public final class ProcessStatsService extends IProcessStats.Stub {
stats.dumpCheckinLocked(pw, reqPackage);
} else {
if (dumpDetails || dumpFullDetails) {
- stats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpAll, activeOnly);
+ stats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpDetails, dumpAll,
+ activeOnly);
} else {
stats.dumpSummaryLocked(pw, reqPackage, now, activeOnly);
}
@@ -974,8 +975,8 @@ public final class ProcessStatsService extends IProcessStats.Stub {
if (checkedIn) pw.print(" (checked in)");
pw.println(":");
if (dumpDetails || dumpFullDetails) {
- processStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpAll,
- activeOnly);
+ processStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpDetails,
+ dumpAll, activeOnly);
if (dumpAll) {
pw.print(" mFile="); pw.println(mFile.getBaseFile());
}
@@ -1030,7 +1031,7 @@ public final class ProcessStatsService extends IProcessStats.Stub {
// much crud.
if (dumpFullDetails) {
processStats.dumpLocked(pw, reqPackage, now, false, false,
- activeOnly);
+ false, activeOnly);
} else {
processStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly);
}
@@ -1060,8 +1061,8 @@ public final class ProcessStatsService extends IProcessStats.Stub {
}
pw.println("CURRENT STATS:");
if (dumpDetails || dumpFullDetails) {
- mProcessStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpAll,
- activeOnly);
+ mProcessStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpDetails,
+ dumpAll, activeOnly);
if (dumpAll) {
pw.print(" mFile="); pw.println(mFile.getBaseFile());
}
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index 230810b58515..fb6b5c1f05ec 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -288,7 +288,7 @@ class RecentTasks {
* @return whether the home app is also the active handler of recent tasks.
*/
boolean isRecentsComponentHomeActivity(int userId) {
- final ComponentName defaultHomeActivity = mService.mAm.getPackageManagerInternalLocked()
+ final ComponentName defaultHomeActivity = mService.getPackageManagerInternalLocked()
.getDefaultHomeActivity(userId);
return defaultHomeActivity != null && mRecentsComponent != null &&
defaultHomeActivity.getPackageName().equals(mRecentsComponent.getPackageName());
diff --git a/services/core/java/com/android/server/am/RecentsAnimation.java b/services/core/java/com/android/server/am/RecentsAnimation.java
index 1c7ad3f33310..c5586bbc3e82 100644
--- a/services/core/java/com/android/server/am/RecentsAnimation.java
+++ b/services/core/java/com/android/server/am/RecentsAnimation.java
@@ -287,7 +287,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks,
mWindowManager.prepareAppTransition(TRANSIT_NONE, false);
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, false);
- mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
// No reason to wait for the pausing activity in this case, as the hiding of
// surfaces needs to be done immediately.
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 2d0c2a86b7b7..7256e2350219 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -16,9 +16,9 @@
package com.android.server.am;
+import static android.app.ActivityTaskManager.INVALID_STACK_ID;
import static android.app.ActivityTaskManager.RESIZE_MODE_FORCED;
import static android.app.ActivityTaskManager.RESIZE_MODE_SYSTEM;
-import static android.app.ActivityTaskManager.INVALID_STACK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
@@ -63,6 +63,7 @@ import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
import static com.android.server.am.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.am.TaskRecordProto.ACTIVITIES;
+import static com.android.server.am.TaskRecordProto.ACTIVITY_TYPE;
import static com.android.server.am.TaskRecordProto.BOUNDS;
import static com.android.server.am.TaskRecordProto.CONFIGURATION_CONTAINER;
import static com.android.server.am.TaskRecordProto.FULLSCREEN;
@@ -74,7 +75,6 @@ import static com.android.server.am.TaskRecordProto.ORIG_ACTIVITY;
import static com.android.server.am.TaskRecordProto.REAL_ACTIVITY;
import static com.android.server.am.TaskRecordProto.RESIZE_MODE;
import static com.android.server.am.TaskRecordProto.STACK_ID;
-import static com.android.server.am.TaskRecordProto.ACTIVITY_TYPE;
import static java.lang.Integer.MAX_VALUE;
@@ -87,7 +87,6 @@ import android.app.ActivityManager.TaskSnapshot;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
import android.app.AppGlobals;
-import android.app.IActivityManager;
import android.app.TaskInfo;
import android.content.ComponentName;
import android.content.Intent;
@@ -478,7 +477,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
mResizeMode = resizeMode;
mWindowContainerController.setResizeable(resizeMode);
mService.mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
- mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ mService.mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
}
void setTaskDockedResizing(boolean resizing) {
@@ -552,7 +551,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
mService.mStackSupervisor.ensureActivitiesVisibleLocked(r, 0,
preserveWindow);
if (!kept) {
- mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ mService.mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
}
}
}
@@ -752,7 +751,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
// The task might have already been running and its visibility needs to be synchronized
// with the visibility of the stack / windows.
supervisor.ensureActivitiesVisibleLocked(null, 0, !mightReplaceWindow);
- supervisor.resumeFocusedStackTopActivityLocked();
+ supervisor.resumeFocusedStacksTopActivitiesLocked();
}
// TODO: Handle incorrect request to move before the actual move, not after.
@@ -1748,6 +1747,14 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
return updateOverrideConfiguration(bounds, null /* insetBounds */);
}
+ void setLastNonFullscreenBounds(Rect bounds) {
+ if (mLastNonFullscreenBounds == null) {
+ mLastNonFullscreenBounds = new Rect(bounds);
+ } else {
+ mLastNonFullscreenBounds.set(bounds);
+ }
+ }
+
/**
* Update task's override configuration based on the bounds.
* @param bounds The bounds of the task.
@@ -1769,7 +1776,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
final boolean persistBounds = getWindowConfiguration().persistTaskBounds();
if (matchParentBounds) {
if (!currentBounds.isEmpty() && persistBounds) {
- mLastNonFullscreenBounds = currentBounds;
+ setLastNonFullscreenBounds(currentBounds);
}
setBounds(null);
newConfig.unset();
@@ -1779,7 +1786,7 @@ class TaskRecord extends ConfigurationContainer implements TaskWindowContainerLi
setBounds(mTmpRect);
if (mStack == null || persistBounds) {
- mLastNonFullscreenBounds = getOverrideBounds();
+ setLastNonFullscreenBounds(getOverrideBounds());
}
computeOverrideConfiguration(newConfig, mTmpRect, insetBounds,
mTmpRect.right != bounds.right, mTmpRect.bottom != bounds.bottom);
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index ba604e08a061..f854df663e2e 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -104,11 +104,9 @@ import com.android.server.wm.WindowManagerService;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
-import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -2243,7 +2241,7 @@ class UserController implements Handler.Callback {
protected void stackSupervisorResumeFocusedStackTopActivity() {
synchronized (mService) {
- mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
+ mService.mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
}
}
diff --git a/services/core/java/com/android/server/am/WindowProcessController.java b/services/core/java/com/android/server/am/WindowProcessController.java
index 6f3fb8eb9b72..e5551b51a093 100644
--- a/services/core/java/com/android/server/am/WindowProcessController.java
+++ b/services/core/java/com/android/server/am/WindowProcessController.java
@@ -93,6 +93,12 @@ public class WindowProcessController {
private volatile String mRequiredAbi;
// Running any services that are foreground?
private volatile boolean mHasForegroundServices;
+ // was app launched for debugging?
+ private volatile boolean mDebugging;
+ // Active instrumentation running in process?
+ private volatile boolean mInstrumenting;
+ // Set to true when process was launched with a wrapper attached
+ private volatile boolean mUsingWrapper;
// Thread currently set for VR scheduling
int mVrThreadTid;
@@ -189,6 +195,30 @@ public class WindowProcessController {
return mRequiredAbi;
}
+ public void setDebugging(boolean debugging) {
+ mDebugging = debugging;
+ }
+
+ boolean isDebugging() {
+ return mDebugging;
+ }
+
+ public void setUsingWrapper(boolean usingWrapper) {
+ mUsingWrapper = usingWrapper;
+ }
+
+ boolean isUsingWrapper() {
+ return mUsingWrapper;
+ }
+
+ public void setInstrumenting(boolean instrumenting) {
+ mInstrumenting = instrumenting;
+ }
+
+ boolean isInstrumenting() {
+ return mInstrumenting;
+ }
+
public void addPackage(String packageName) {
synchronized (mAtm.mGlobalLock) {
mPkgList.add(packageName);
@@ -436,6 +466,19 @@ public class WindowProcessController {
return minTaskLayer;
}
+ int computeRelaunchReason() {
+ synchronized (mAtm.mGlobalLock) {
+ final int activitiesSize = mActivities.size();
+ for (int i = activitiesSize - 1; i >= 0; i--) {
+ final ActivityRecord r = mActivities.get(i);
+ if (r.mRelaunchReason != ActivityRecord.RELAUNCH_REASON_NONE) {
+ return r.mRelaunchReason;
+ }
+ }
+ }
+ return ActivityRecord.RELAUNCH_REASON_NONE;
+ }
+
void clearProfilerIfNeeded() {
if (mListener == null) return;
// Posting on handler so WM lock isn't held when we call into AM.
diff --git a/services/core/java/com/android/server/hdmi/ArcTerminationActionFromAvr.java b/services/core/java/com/android/server/hdmi/ArcTerminationActionFromAvr.java
new file mode 100644
index 000000000000..7e7332180f8a
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/ArcTerminationActionFromAvr.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.hdmi;
+
+import android.hardware.tv.cec.V1_0.SendMessageResult;
+
+/**
+ * Feature action that handles Audio Return Channel terminated by AVR devices.
+ */
+public class ArcTerminationActionFromAvr extends HdmiCecFeatureAction {
+
+ // State in which waits for ARC response.
+ private static final int STATE_WAITING_FOR_INITIATE_ARC_RESPONSE = 1;
+ private static final int STATE_ARC_TERMINATED = 2;
+
+ // the required maximum response time specified in CEC 9.2
+ private static final int TIMEOUT_MS = 1000;
+
+ ArcTerminationActionFromAvr(HdmiCecLocalDevice source) {
+ super(source);
+ }
+
+ @Override
+ boolean start() {
+ mState = STATE_WAITING_FOR_INITIATE_ARC_RESPONSE;
+ addTimer(mState, TIMEOUT_MS);
+ sendTerminateArc();
+ return true;
+ }
+
+ @Override
+ boolean processCommand(HdmiCecMessage cmd) {
+ if (mState != STATE_WAITING_FOR_INITIATE_ARC_RESPONSE) {
+ return false;
+ }
+ switch (cmd.getOpcode()) {
+ case Constants.MESSAGE_REPORT_ARC_TERMINATED:
+ mState = STATE_ARC_TERMINATED;
+ audioSystem().setArcStatus(false);
+ finish();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ void handleTimerEvent(int state) {
+ if (mState != state) {
+ return;
+ }
+
+ switch (mState) {
+ case STATE_WAITING_FOR_INITIATE_ARC_RESPONSE:
+ handleTerminateArcTimeout();
+ break;
+ }
+ }
+
+ protected void sendTerminateArc() {
+ sendCommand(HdmiCecMessageBuilder.buildTerminateArc(getSourceAddress(), Constants.ADDR_TV),
+ result -> {
+ if (result != SendMessageResult.SUCCESS) {
+ HdmiLogger.debug("Terminate ARC was not successfully sent.");
+ finish();
+ }
+ });
+ }
+
+ private void handleTerminateArcTimeout() {
+ HdmiLogger.debug("handleTerminateArcTimeout");
+ finish();
+ }
+}
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index ba5ee0231753..dea9309d550b 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -295,6 +295,13 @@ final class Constants {
"persist.sys.hdmi.last_system_audio_control";
/**
+ * Property to indicate if device supports ARC or not
+ * <p>Default is true.
+ */
+ static final String PROPERTY_ARC_SUPPORT =
+ "persist.sys.hdmi.property_arc_support";
+
+ /**
* Property to save the audio port to switch to when system audio control is on.
* <P>Audio system should switch to this port when cec active source is not its child in the tree
* or is not itself.
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 7aab75090b19..ca85249b1f87 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -189,15 +189,14 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDevice {
@ServiceThreadOnly
protected boolean handleRequestArcInitiate(HdmiCecMessage message) {
assertRunOnServiceThread();
- // TODO(b/80296911): Check if ARC supported.
-
- // TODO(b/80296911): Check if port is ready to accept.
-
- // TODO(b/80296911): if both true, activate ARC functinality and
- mService.sendCecCommand(
- HdmiCecMessageBuilder.buildInitiateArc(mAddress, message.getSource()));
- // TODO(b/80296911): else, send <Feature Abort>["Unrecongnized opcode"]
-
+ if (!SystemProperties.getBoolean(Constants.PROPERTY_ARC_SUPPORT, true)) {
+ mService.maySendFeatureAbortCommand(message, Constants.ABORT_UNRECOGNIZED_OPCODE);
+ } else if (!isDirectConnectToTv()) {
+ HdmiLogger.debug("AVR device is not directly connected with TV");
+ mService.maySendFeatureAbortCommand(message, Constants.ABORT_NOT_IN_CORRECT_MODE);
+ } else {
+ addAndStartAction(new ArcInitiationActionFromAvr(this));
+ }
return true;
}
@@ -205,15 +204,14 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDevice {
@ServiceThreadOnly
protected boolean handleRequestArcTermination(HdmiCecMessage message) {
assertRunOnServiceThread();
- // TODO(b/80297105): Check if ARC supported.
-
- // TODO(b/80297105): Check is currently in arc.
-
- // TODO(b/80297105): If both true, deactivate ARC functionality and
- mService.sendCecCommand(
- HdmiCecMessageBuilder.buildTerminateArc(mAddress, message.getSource()));
- // TODO(b/80297105): else, send <Feature Abort>["Unrecongnized opcode"]
-
+ if (!SystemProperties.getBoolean(Constants.PROPERTY_ARC_SUPPORT, true)) {
+ mService.maySendFeatureAbortCommand(message, Constants.ABORT_UNRECOGNIZED_OPCODE);
+ } else if (!isArcEnabled()) {
+ HdmiLogger.debug("ARC is not established between TV and AVR device");
+ mService.maySendFeatureAbortCommand(message, Constants.ABORT_NOT_IN_CORRECT_MODE);
+ } else {
+ addAndStartAction(new ArcTerminationActionFromAvr(this));
+ }
return true;
}
@@ -377,6 +375,11 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDevice {
// TODO(b/111396634): switch input according to PROPERTY_SYSTEM_AUDIO_MODE_AUDIO_PORT
}
+ protected boolean isDirectConnectToTv() {
+ int myPhysicalAddress = mService.getPhysicalAddress();
+ return (myPhysicalAddress & Constants.ROUTING_PATH_TOP_MASK) == myPhysicalAddress;
+ }
+
private void updateAudioManagerForSystemAudio(boolean on) {
int device = mService.getAudioManager().setHdmiSystemAudioSupported(on);
HdmiLogger.debug("[A]UpdateSystemAudio mode[on=%b] output=[%X]", on, device);
@@ -435,4 +438,11 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDevice {
void setTvSystemAudioModeSupport(boolean supported) {
mTvSystemAudioModeSupport = supported;
}
+
+ @VisibleForTesting
+ protected boolean isArcEnabled() {
+ synchronized (mLock) {
+ return mArcEstablished;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
index f1cb246d0c21..649a2da1c451 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
@@ -18,6 +18,7 @@ package com.android.server.hdmi;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
+import com.android.server.hdmi.Constants.AudioCodec;
/**
* A helper class to build {@link HdmiCecMessage} from various cec commands.
@@ -265,6 +266,25 @@ public class HdmiCecMessageBuilder {
return buildCommand(src, dest, Constants.MESSAGE_REPORT_ARC_TERMINATED);
}
+
+ /**
+ * Build &lt;Request Short Audio Descriptor&gt; command.
+ *
+ * @param src source address of command
+ * @param dest destination address of command
+ * @param audioFormats the {@link AudioCodec}s desired
+ * @return newly created {@link HdmiCecMessage}
+ */
+ static HdmiCecMessage buildRequestShortAudioDescriptor(int src, int dest,
+ @AudioCodec int[] audioFormats) {
+ byte[] params = new byte[Math.min(audioFormats.length,4)] ;
+ for (int i = 0; i < params.length ; i++){
+ params[i] = (byte) (audioFormats[i] & 0xff);
+ }
+ return buildCommand(src, dest, Constants.MESSAGE_REQUEST_SHORT_AUDIO_DESCRIPTOR, params);
+ }
+
+
/**
* Build &lt;Text View On&gt; command.
*
diff --git a/services/core/java/com/android/server/job/controllers/IdleController.java b/services/core/java/com/android/server/job/controllers/IdleController.java
index 644f2c4d6864..e3c311f9e327 100644
--- a/services/core/java/com/android/server/job/controllers/IdleController.java
+++ b/services/core/java/com/android/server/job/controllers/IdleController.java
@@ -16,41 +16,33 @@
package com.android.server.job.controllers;
-import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
-
-import android.app.AlarmManager;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.os.UserHandle;
import android.util.ArraySet;
-import android.util.Log;
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.util.IndentingPrintWriter;
-import com.android.server.am.ActivityManagerService;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.StateControllerProto;
+import com.android.server.job.controllers.idle.CarIdlenessTracker;
+import com.android.server.job.controllers.idle.DeviceIdlenessTracker;
+import com.android.server.job.controllers.idle.IdlenessListener;
+import com.android.server.job.controllers.idle.IdlenessTracker;
import java.util.function.Predicate;
-public final class IdleController extends StateController {
- private static final String TAG = "JobScheduler.Idle";
- private static final boolean DEBUG = JobSchedulerService.DEBUG
- || Log.isLoggable(TAG, Log.DEBUG);
-
+public final class IdleController extends StateController implements IdlenessListener {
+ private static final String TAG = "JobScheduler.IdleController";
// Policy: we decide that we're "idle" if the device has been unused /
// screen off or dreaming or wireless charging dock idle for at least this long
- private long mInactivityIdleThreshold;
- private long mIdleWindowSlop;
final ArraySet<JobStatus> mTrackedTasks = new ArraySet<>();
IdlenessTracker mIdleTracker;
public IdleController(JobSchedulerService service) {
super(service);
- initIdleStateTracking();
+ initIdleStateTracking(mContext);
}
/**
@@ -74,9 +66,10 @@ public final class IdleController extends StateController {
}
/**
- * Interaction with the task manager service
+ * State-change notifications from the idleness tracker
*/
- void reportNewIdleState(boolean isIdle) {
+ @Override
+ public void reportNewIdleState(boolean isIdle) {
synchronized (mLock) {
for (int i = mTrackedTasks.size()-1; i >= 0; i--) {
mTrackedTasks.valueAt(i).setIdleConstraintSatisfied(isIdle);
@@ -89,141 +82,22 @@ public final class IdleController extends StateController {
* Idle state tracking, and messaging with the task manager when
* significant state changes occur
*/
- private void initIdleStateTracking() {
- mInactivityIdleThreshold = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_jobSchedulerInactivityIdleThreshold);
- mIdleWindowSlop = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_jobSchedulerIdleWindowSlop);
- mIdleTracker = new IdlenessTracker();
- mIdleTracker.startTracking();
- }
-
- final class IdlenessTracker extends BroadcastReceiver {
- private AlarmManager mAlarm;
-
- // After construction, mutations of idle/screen-on state will only happen
- // on the main looper thread, either in onReceive() or in an alarm callback.
- private boolean mIdle;
- private boolean mScreenOn;
- private boolean mDockIdle;
-
- private AlarmManager.OnAlarmListener mIdleAlarmListener = () -> {
- handleIdleTrigger();
- };
-
- public IdlenessTracker() {
- mAlarm = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
-
- // At boot we presume that the user has just "interacted" with the
- // device in some meaningful way.
- mIdle = false;
- mScreenOn = true;
- mDockIdle = false;
- }
-
- public boolean isIdle() {
- return mIdle;
- }
-
- public void startTracking() {
- IntentFilter filter = new IntentFilter();
-
- // Screen state
- filter.addAction(Intent.ACTION_SCREEN_ON);
- filter.addAction(Intent.ACTION_SCREEN_OFF);
-
- // Dreaming state
- filter.addAction(Intent.ACTION_DREAMING_STARTED);
- filter.addAction(Intent.ACTION_DREAMING_STOPPED);
-
- // Debugging/instrumentation
- filter.addAction(ActivityManagerService.ACTION_TRIGGER_IDLE);
-
- // Wireless charging dock state
- filter.addAction(Intent.ACTION_DOCK_IDLE);
- filter.addAction(Intent.ACTION_DOCK_ACTIVE);
-
- mContext.registerReceiver(this, filter);
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (action.equals(Intent.ACTION_SCREEN_ON)
- || action.equals(Intent.ACTION_DREAMING_STOPPED)
- || action.equals(Intent.ACTION_DOCK_ACTIVE)) {
- if (action.equals(Intent.ACTION_DOCK_ACTIVE)) {
- if (!mScreenOn) {
- // Ignore this intent during screen off
- return;
- } else {
- mDockIdle = false;
- }
- } else {
- mScreenOn = true;
- mDockIdle = false;
- }
- if (DEBUG) {
- Slog.v(TAG,"exiting idle : " + action);
- }
- //cancel the alarm
- mAlarm.cancel(mIdleAlarmListener);
- if (mIdle) {
- // possible transition to not-idle
- mIdle = false;
- reportNewIdleState(mIdle);
- }
- } else if (action.equals(Intent.ACTION_SCREEN_OFF)
- || action.equals(Intent.ACTION_DREAMING_STARTED)
- || action.equals(Intent.ACTION_DOCK_IDLE)) {
- // when the screen goes off or dreaming starts or wireless charging dock in idle,
- // we schedule the alarm that will tell us when we have decided the device is
- // truly idle.
- if (action.equals(Intent.ACTION_DOCK_IDLE)) {
- if (!mScreenOn) {
- // Ignore this intent during screen off
- return;
- } else {
- mDockIdle = true;
- }
- } else {
- mScreenOn = false;
- mDockIdle = false;
- }
- final long nowElapsed = sElapsedRealtimeClock.millis();
- final long when = nowElapsed + mInactivityIdleThreshold;
- if (DEBUG) {
- Slog.v(TAG, "Scheduling idle : " + action + " now:" + nowElapsed + " when="
- + when);
- }
- mAlarm.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- when, mIdleWindowSlop, "JS idleness", mIdleAlarmListener, null);
- } else if (action.equals(ActivityManagerService.ACTION_TRIGGER_IDLE)) {
- handleIdleTrigger();
- }
- }
-
- private void handleIdleTrigger() {
- // idle time starts now. Do not set mIdle if screen is on.
- if (!mIdle && (!mScreenOn || mDockIdle)) {
- if (DEBUG) {
- Slog.v(TAG, "Idle trigger fired @ " + sElapsedRealtimeClock.millis());
- }
- mIdle = true;
- reportNewIdleState(mIdle);
- } else {
- if (DEBUG) {
- Slog.v(TAG, "TRIGGER_IDLE received but not changing state; idle="
- + mIdle + " screen=" + mScreenOn);
- }
- }
+ private void initIdleStateTracking(Context ctx) {
+ final boolean isCar = mContext.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_AUTOMOTIVE);
+ if (isCar) {
+ mIdleTracker = new CarIdlenessTracker();
+ } else {
+ mIdleTracker = new DeviceIdlenessTracker();
}
+ mIdleTracker.startTracking(ctx, this);
}
@Override
public void dumpControllerStateLocked(IndentingPrintWriter pw,
Predicate<JobStatus> predicate) {
pw.println("Currently idle: " + mIdleTracker.isIdle());
+ pw.println("Idleness tracker:"); mIdleTracker.dump(pw);
pw.println();
for (int i = 0; i < mTrackedTasks.size(); i++) {
diff --git a/services/core/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java b/services/core/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java
new file mode 100644
index 000000000000..a3949a4521c2
--- /dev/null
+++ b/services/core/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.job.controllers.idle;
+
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+
+import android.util.Log;
+import android.util.Slog;
+import com.android.server.am.ActivityManagerService;
+import com.android.server.job.JobSchedulerService;
+
+import java.io.PrintWriter;
+
+public final class CarIdlenessTracker extends BroadcastReceiver implements IdlenessTracker {
+ private static final String TAG = "JobScheduler.CarIdlenessTracker";
+ private static final boolean DEBUG = JobSchedulerService.DEBUG
+ || Log.isLoggable(TAG, Log.DEBUG);
+
+ public static final String ACTION_FORCE_IDLE = "com.android.server.ACTION_FORCE_IDLE";
+ public static final String ACTION_UNFORCE_IDLE = "com.android.server.ACTION_UNFORCE_IDLE";
+
+ // After construction, mutations of idle/screen-on state will only happen
+ // on the main looper thread, either in onReceive() or in an alarm callback.
+ private boolean mIdle;
+ private boolean mScreenOn;
+ private IdlenessListener mIdleListener;
+
+ public CarIdlenessTracker() {
+ // At boot we presume that the user has just "interacted" with the
+ // device in some meaningful way.
+ mIdle = false;
+ mScreenOn = true;
+ }
+
+ @Override
+ public boolean isIdle() {
+ return mIdle;
+ }
+
+ @Override
+ public void startTracking(Context context, IdlenessListener listener) {
+ mIdleListener = listener;
+
+ IntentFilter filter = new IntentFilter();
+
+ // Screen state
+ filter.addAction(Intent.ACTION_SCREEN_ON);
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+
+ // Debugging/instrumentation
+ filter.addAction(ACTION_FORCE_IDLE);
+ filter.addAction(ACTION_UNFORCE_IDLE);
+ filter.addAction(ActivityManagerService.ACTION_TRIGGER_IDLE);
+
+ context.registerReceiver(this, filter);
+ }
+
+ @Override
+ public void dump(PrintWriter pw) {
+ pw.print(" mIdle: "); pw.println(mIdle);
+ pw.print(" mScreenOn: "); pw.println(mScreenOn);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ logIfDebug("Received action: " + action);
+
+ // Check for forced actions
+ if (action.equals(ACTION_FORCE_IDLE)) {
+ logIfDebug("Forcing idle...");
+ enterIdleState(true);
+ } else if (action.equals(ACTION_UNFORCE_IDLE)) {
+ logIfDebug("Unforcing idle...");
+ exitIdleState(true);
+ } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
+ logIfDebug("Going idle...");
+ mScreenOn = false;
+ enterIdleState(false);
+ } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
+ logIfDebug("exiting idle...");
+ mScreenOn = true;
+ exitIdleState(true);
+ } else if (action.equals(ActivityManagerService.ACTION_TRIGGER_IDLE)) {
+ if (!mScreenOn) {
+ logIfDebug("Idle trigger fired...");
+ enterIdleState(false);
+ } else {
+ logIfDebug("TRIGGER_IDLE received but not changing state; idle="
+ + mIdle + " screen=" + mScreenOn);
+ }
+ }
+ }
+
+ private void enterIdleState(boolean forced) {
+ if (!forced && mIdle) {
+ // Already idle and don't need to trigger callbacks since not forced
+ logIfDebug("Device is already considered idle");
+ return;
+ }
+ mIdle = true;
+ mIdleListener.reportNewIdleState(mIdle);
+ }
+
+ private void exitIdleState(boolean forced) {
+ if (!forced && !mIdle) {
+ // Already out of idle and don't need to trigger callbacks since not forced
+ logIfDebug("Device is already considered not idle");
+ return;
+ }
+ mIdle = false;
+ mIdleListener.reportNewIdleState(mIdle);
+ }
+
+ private void logIfDebug(String msg) {
+ if (DEBUG) {
+ Slog.v(TAG, msg);
+ }
+ }
+} \ No newline at end of file
diff --git a/services/core/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java b/services/core/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java
new file mode 100644
index 000000000000..a85bd4066ad3
--- /dev/null
+++ b/services/core/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.job.controllers.idle;
+
+import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+
+import android.app.AlarmManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+
+import android.util.Log;
+import android.util.Slog;
+import com.android.server.am.ActivityManagerService;
+import com.android.server.job.JobSchedulerService;
+
+import java.io.PrintWriter;
+
+public final class DeviceIdlenessTracker extends BroadcastReceiver implements IdlenessTracker {
+ private static final String TAG = "JobScheduler.DeviceIdlenessTracker";
+ private static final boolean DEBUG = JobSchedulerService.DEBUG
+ || Log.isLoggable(TAG, Log.DEBUG);
+
+ private AlarmManager mAlarm;
+
+ // After construction, mutations of idle/screen-on state will only happen
+ // on the main looper thread, either in onReceive() or in an alarm callback.
+ private long mInactivityIdleThreshold;
+ private long mIdleWindowSlop;
+ private boolean mIdle;
+ private boolean mScreenOn;
+ private boolean mDockIdle;
+ private IdlenessListener mIdleListener;
+
+ private AlarmManager.OnAlarmListener mIdleAlarmListener = () -> {
+ handleIdleTrigger();
+ };
+
+ public DeviceIdlenessTracker() {
+ // At boot we presume that the user has just "interacted" with the
+ // device in some meaningful way.
+ mIdle = false;
+ mScreenOn = true;
+ mDockIdle = false;
+ }
+
+ @Override
+ public boolean isIdle() {
+ return mIdle;
+ }
+
+ @Override
+ public void startTracking(Context context, IdlenessListener listener) {
+ mIdleListener = listener;
+ mInactivityIdleThreshold = context.getResources().getInteger(
+ com.android.internal.R.integer.config_jobSchedulerInactivityIdleThreshold);
+ mIdleWindowSlop = context.getResources().getInteger(
+ com.android.internal.R.integer.config_jobSchedulerIdleWindowSlop);
+ mAlarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+
+ IntentFilter filter = new IntentFilter();
+
+ // Screen state
+ filter.addAction(Intent.ACTION_SCREEN_ON);
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+
+ // Dreaming state
+ filter.addAction(Intent.ACTION_DREAMING_STARTED);
+ filter.addAction(Intent.ACTION_DREAMING_STOPPED);
+
+ // Debugging/instrumentation
+ filter.addAction(ActivityManagerService.ACTION_TRIGGER_IDLE);
+
+ // Wireless charging dock state
+ filter.addAction(Intent.ACTION_DOCK_IDLE);
+ filter.addAction(Intent.ACTION_DOCK_ACTIVE);
+
+ context.registerReceiver(this, filter);
+ }
+
+ @Override
+ public void dump(PrintWriter pw) {
+ pw.print(" mIdle: "); pw.println(mIdle);
+ pw.print(" mScreenOn: "); pw.println(mScreenOn);
+ pw.print(" mDockIdle: "); pw.println(mDockIdle);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ if (action.equals(Intent.ACTION_SCREEN_ON)
+ || action.equals(Intent.ACTION_DREAMING_STOPPED)
+ || action.equals(Intent.ACTION_DOCK_ACTIVE)) {
+ if (action.equals(Intent.ACTION_DOCK_ACTIVE)) {
+ if (!mScreenOn) {
+ // Ignore this intent during screen off
+ return;
+ } else {
+ mDockIdle = false;
+ }
+ } else {
+ mScreenOn = true;
+ mDockIdle = false;
+ }
+ if (DEBUG) {
+ Slog.v(TAG,"exiting idle : " + action);
+ }
+ //cancel the alarm
+ mAlarm.cancel(mIdleAlarmListener);
+ if (mIdle) {
+ // possible transition to not-idle
+ mIdle = false;
+ mIdleListener.reportNewIdleState(mIdle);
+ }
+ } else if (action.equals(Intent.ACTION_SCREEN_OFF)
+ || action.equals(Intent.ACTION_DREAMING_STARTED)
+ || action.equals(Intent.ACTION_DOCK_IDLE)) {
+ // when the screen goes off or dreaming starts or wireless charging dock in idle,
+ // we schedule the alarm that will tell us when we have decided the device is
+ // truly idle.
+ if (action.equals(Intent.ACTION_DOCK_IDLE)) {
+ if (!mScreenOn) {
+ // Ignore this intent during screen off
+ return;
+ } else {
+ mDockIdle = true;
+ }
+ } else {
+ mScreenOn = false;
+ mDockIdle = false;
+ }
+ final long nowElapsed = sElapsedRealtimeClock.millis();
+ final long when = nowElapsed + mInactivityIdleThreshold;
+ if (DEBUG) {
+ Slog.v(TAG, "Scheduling idle : " + action + " now:" + nowElapsed + " when="
+ + when);
+ }
+ mAlarm.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ when, mIdleWindowSlop, "JS idleness", mIdleAlarmListener, null);
+ } else if (action.equals(ActivityManagerService.ACTION_TRIGGER_IDLE)) {
+ handleIdleTrigger();
+ }
+ }
+
+ private void handleIdleTrigger() {
+ // idle time starts now. Do not set mIdle if screen is on.
+ if (!mIdle && (!mScreenOn || mDockIdle)) {
+ if (DEBUG) {
+ Slog.v(TAG, "Idle trigger fired @ " + sElapsedRealtimeClock.millis());
+ }
+ mIdle = true;
+ mIdleListener.reportNewIdleState(mIdle);
+ } else {
+ if (DEBUG) {
+ Slog.v(TAG, "TRIGGER_IDLE received but not changing state; idle="
+ + mIdle + " screen=" + mScreenOn);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/services/core/java/com/android/server/job/controllers/idle/IdlenessListener.java b/services/core/java/com/android/server/job/controllers/idle/IdlenessListener.java
new file mode 100644
index 000000000000..7ffd7cd3e2e0
--- /dev/null
+++ b/services/core/java/com/android/server/job/controllers/idle/IdlenessListener.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.job.controllers.idle;
+
+/**
+ * Interface through which an IdlenessTracker informs the job scheduler of
+ * changes in the device's inactivity state.
+ */
+public interface IdlenessListener {
+ /**
+ * Tell the job scheduler that the device's idle state has changed.
+ *
+ * @param deviceIsIdle {@code true} to indicate that the device is now considered
+ * to be idle; {@code false} to indicate that the device is now being interacted with,
+ * so jobs with idle constraints should not be run.
+ */
+ void reportNewIdleState(boolean deviceIsIdle);
+}
diff --git a/services/core/java/com/android/server/job/controllers/idle/IdlenessTracker.java b/services/core/java/com/android/server/job/controllers/idle/IdlenessTracker.java
new file mode 100644
index 000000000000..09f01c2e4113
--- /dev/null
+++ b/services/core/java/com/android/server/job/controllers/idle/IdlenessTracker.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.job.controllers.idle;
+
+import android.content.Context;
+
+import java.io.PrintWriter;
+
+public interface IdlenessTracker {
+ /**
+ * One-time initialization: this method is called once, after construction of
+ * the IdlenessTracker instance. This is when the tracker should actually begin
+ * monitoring whatever signals it consumes in deciding when the device is in a
+ * non-interacting state. When the idle state changes thereafter, the given
+ * listener must be called to report the new state.
+ */
+ void startTracking(Context context, IdlenessListener listener);
+
+ /**
+ * Report whether the device is currently considered "idle" for purposes of
+ * running scheduled jobs with idleness constraints.
+ *
+ * @return {@code true} if the job scheduler should consider idleness
+ * constraints to be currently satisfied; {@code false} otherwise.
+ */
+ boolean isIdle();
+
+ /**
+ * Dump useful information about tracked idleness-related state in plaintext.
+ */
+ void dump(PrintWriter pw);
+}
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index 38b9024ac048..06f67cd9cb9a 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -1176,15 +1176,13 @@ class InstantAppRegistry {
private final class CookiePersistence extends Handler {
private static final long PERSIST_COOKIE_DELAY_MILLIS = 1000L; /* one second */
- // In case you wonder why we stash the cookies aside, we use
- // the user id for the message id and the package for the payload.
- // Handler allows removing messages by id and tag where the
- // tag is compared using ==. So to allow cancelling the
- // pending persistence for an app under a given user we use
- // the fact that package are cached by the system so the ==
- // comparison would match and we end up with a way to cancel
- // persisting the cookie for a user and package.
- private final SparseArray<ArrayMap<PackageParser.Package, SomeArgs>> mPendingPersistCookies
+ // The cookies are cached per package name per user-id in this sparse
+ // array. The caching is so that pending persistence can be canceled within
+ // a short interval. To ensure we still return pending persist cookies
+ // for a package that uninstalled and reinstalled while the persistence
+ // was still pending, we use the package name as a key for
+ // mPendingPersistCookies, since that stays stable across reinstalls.
+ private final SparseArray<ArrayMap<String, SomeArgs>> mPendingPersistCookies
= new SparseArray<>();
public CookiePersistence(Looper looper) {
@@ -1214,10 +1212,10 @@ class InstantAppRegistry {
public @Nullable byte[] getPendingPersistCookieLPr(@NonNull PackageParser.Package pkg,
@UserIdInt int userId) {
- ArrayMap<PackageParser.Package, SomeArgs> pendingWorkForUser =
+ ArrayMap<String, SomeArgs> pendingWorkForUser =
mPendingPersistCookies.get(userId);
if (pendingWorkForUser != null) {
- SomeArgs state = pendingWorkForUser.get(pkg);
+ SomeArgs state = pendingWorkForUser.get(pkg.packageName);
if (state != null) {
return (byte[]) state.arg1;
}
@@ -1237,7 +1235,7 @@ class InstantAppRegistry {
private void addPendingPersistCookieLPw(@UserIdInt int userId,
@NonNull PackageParser.Package pkg, @NonNull byte[] cookie,
@NonNull File cookieFile) {
- ArrayMap<PackageParser.Package, SomeArgs> pendingWorkForUser =
+ ArrayMap<String, SomeArgs> pendingWorkForUser =
mPendingPersistCookies.get(userId);
if (pendingWorkForUser == null) {
pendingWorkForUser = new ArrayMap<>();
@@ -1246,16 +1244,16 @@ class InstantAppRegistry {
SomeArgs args = SomeArgs.obtain();
args.arg1 = cookie;
args.arg2 = cookieFile;
- pendingWorkForUser.put(pkg, args);
+ pendingWorkForUser.put(pkg.packageName, args);
}
private SomeArgs removePendingPersistCookieLPr(@NonNull PackageParser.Package pkg,
@UserIdInt int userId) {
- ArrayMap<PackageParser.Package, SomeArgs> pendingWorkForUser =
+ ArrayMap<String, SomeArgs> pendingWorkForUser =
mPendingPersistCookies.get(userId);
SomeArgs state = null;
if (pendingWorkForUser != null) {
- state = pendingWorkForUser.remove(pkg);
+ state = pendingWorkForUser.remove(pkg.packageName);
if (pendingWorkForUser.isEmpty()) {
mPendingPersistCookies.remove(userId);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 5878762e2272..3834a88d1c0c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -547,6 +547,10 @@ class PackageManagerShellCommand extends ShellCommand {
case "-e":
listEnabled = true;
break;
+ case "-a":
+ getFlags |= PackageManager.MATCH_KNOWN_PACKAGES;
+ getFlags |= PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS;
+ break;
case "-f":
showSourceDir = true;
break;
@@ -2688,6 +2692,7 @@ class PackageManagerShellCommand extends ShellCommand {
pw.println(" Prints all packages; optionally only those whose name contains");
pw.println(" the text in FILTER. Options are:");
pw.println(" -f: see their associated file");
+ pw.println(" -a: all known packages");
pw.println(" -d: filter to only show disabled packages");
pw.println(" -e: filter to only show enabled packages");
pw.println(" -s: filter to only show system packages");
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 8ef1196bf625..e46c03edc77d 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -4391,17 +4391,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (isKeyguardShowingAndNotOccluded()) {
// don't launch home if keyguard showing
return;
- } else if (mKeyguardOccluded && mKeyguardDelegate.isShowing()) {
- mKeyguardDelegate.dismiss(new KeyguardDismissCallback() {
- @Override
- public void onDismissSucceeded() throws RemoteException {
- mHandler.post(() -> {
- startDockOrHome(true /*fromHomeKey*/, awakenFromDreams);
- });
- }
- }, null /* message */);
- return;
- } else if (!mKeyguardOccluded && mKeyguardDelegate.isInputRestricted()) {
+ }
+
+ if (!mKeyguardOccluded && mKeyguardDelegate.isInputRestricted()) {
// when in keyguard restricted mode, must first verify unlock
// before launching home
mKeyguardDelegate.verifyUnlock(new OnKeyguardExitResult() {
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 8a135b81e562..3291a45c94c4 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -47,7 +47,6 @@ import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
-import android.os.storage.StorageManager;
import android.provider.Settings;
import android.service.trust.TrustAgentService;
import android.text.TextUtils;
@@ -61,7 +60,6 @@ import android.view.IWindowManager;
import android.view.WindowManagerGlobal;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
-import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.util.DumpUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.SystemService;
@@ -432,13 +430,20 @@ public class TrustManagerService extends SystemService {
for (int i = 0; i < userInfos.size(); i++) {
UserInfo info = userInfos.get(i);
- if (info == null || info.partial || !info.isEnabled() || info.guestToRemove
- || !info.supportsSwitchToByUser()) {
+ if (info == null || info.partial || !info.isEnabled() || info.guestToRemove) {
continue;
}
int id = info.id;
boolean secure = mLockPatternUtils.isSecure(id);
+
+ if (!info.supportsSwitchToByUser()) {
+ if (info.isManagedProfile() && !secure) {
+ setDeviceLockedForUser(id, false);
+ }
+ continue;
+ }
+
boolean trusted = aggregateIsTrusted(id);
boolean showingKeyguard = true;
boolean biometricAuthenticated = false;
@@ -993,7 +998,8 @@ public class TrustManagerService extends SystemService {
enforceReportPermission();
final long identity = Binder.clearCallingIdentity();
try {
- if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
+ if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
+ && mLockPatternUtils.isSecure(userId)) {
synchronized (mDeviceLockedForUser) {
mDeviceLockedForUser.put(userId, locked);
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 9d68c63cc38c..c8bd2113ac6c 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -965,7 +965,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
}
mPaddingChanged = false;
}
- if (mInfo != null && mInfo.getSupportsAmbientMode()) {
+ if (mInfo != null && mInfo.supportsAmbientMode()) {
try {
mEngine.setInAmbientMode(mInAmbientMode, false /* animated */);
} catch (RemoteException e) {
@@ -1777,7 +1777,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
mInAmbientMode = inAmbienMode;
final WallpaperData data = mWallpaperMap.get(mCurrentUserId);
if (data != null && data.connection != null && data.connection.mInfo != null
- && data.connection.mInfo.getSupportsAmbientMode()) {
+ && data.connection.mInfo.supportsAmbientMode()) {
engine = data.connection.mEngine;
} else {
engine = null;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index bfecd9d99cd9..83075ed24e33 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -274,4 +274,11 @@ public abstract class ActivityTaskManagerInternal {
public abstract void enableScreenAfterBoot(boolean booted);
public abstract boolean showStrictModeViolationDialog();
public abstract void showSystemReadyErrorDialogsIfNeeded();
+
+ public abstract long inputDispatchingTimedOut(int pid, boolean aboveSystem, String reason);
+ public abstract void onProcessMapped(int pid, WindowProcessController proc);
+ public abstract void onProcessUnMapped(int pid);
+
+ public abstract void onPackageDataCleared(String name);
+ public abstract void onPackageUninstalled(String name);
}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 4b8b6074a480..b7759182fb01 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -78,6 +78,7 @@ import static com.android.server.wm.AppWindowTokenProto.STARTING_MOVED;
import static com.android.server.wm.AppWindowTokenProto.STARTING_WINDOW;
import static com.android.server.wm.AppWindowTokenProto.THUMBNAIL;
import static com.android.server.wm.AppWindowTokenProto.WINDOW_TOKEN;
+import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
import android.annotation.CallSuper;
import android.app.Activity;
@@ -264,6 +265,12 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
*/
private boolean mWillCloseOrEnterPip;
+ /** Layer used to constrain the animation to a token's stack bounds. */
+ SurfaceControl mAnimationBoundsLayer;
+
+ /** Whether this token needs to create mAnimationBoundsLayer for cropping animations. */
+ boolean mNeedsAnimationBoundsLayer;
+
AppWindowToken(WindowManagerService service, IApplicationToken token, boolean voiceInteraction,
DisplayContent dc, long inputDispatchingTimeoutNanos, boolean fullscreen,
boolean showForAllUsers, int targetSdk, int orientation, int rotationAnimationHint,
@@ -1720,6 +1727,20 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
return !isSplitScreenPrimary || allowSplitScreenPrimaryAnimation;
}
+ /**
+ * Creates a layer to apply crop to an animation.
+ */
+ private SurfaceControl createAnimationBoundsLayer(Transaction t) {
+ if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.i(TAG, "Creating animation bounds layer");
+ final SurfaceControl.Builder builder = makeAnimationLeash()
+ .setParent(getAnimationLeashParent())
+ .setName(getSurfaceControl() + " - animation-bounds")
+ .setSize(getSurfaceWidth(), getSurfaceHeight());
+ final SurfaceControl boundsLayer = builder.build();
+ t.show(boundsLayer);
+ return boundsLayer;
+ }
+
boolean applyAnimationLocked(WindowManager.LayoutParams lp, int transit, boolean enter,
boolean isVoiceInteraction) {
@@ -1753,12 +1774,15 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
adapter = mService.mAppTransition.getRemoteAnimationController()
.createAnimationAdapter(this, mTmpPoint, mTmpRect);
} else {
+ final int appStackClipMode = mService.mAppTransition.getAppStackClipMode();
+ mNeedsAnimationBoundsLayer = (appStackClipMode == STACK_CLIP_AFTER_ANIM);
+
final Animation a = loadAnimation(lp, transit, enter, isVoiceInteraction);
if (a != null) {
adapter = new LocalAnimationAdapter(
new WindowAnimationSpec(a, mTmpPoint, mTmpRect,
mService.mAppTransition.canSkipFirstFrame(),
- mService.mAppTransition.getAppStackClipMode(),
+ appStackClipMode,
true /* isAppAnimation */),
mService.mSurfaceAnimationRunner);
if (a.getZAdjustment() == Animation.ZORDER_TOP) {
@@ -1858,6 +1882,11 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
@Override
public void onAnimationLeashDestroyed(Transaction t) {
super.onAnimationLeashDestroyed(t);
+ if (mAnimationBoundsLayer != null) {
+ t.destroy(mAnimationBoundsLayer);
+ mAnimationBoundsLayer = null;
+ }
+
if (mAnimatingAppWindowTokenRegistry != null) {
mAnimatingAppWindowTokenRegistry.notifyFinished(this);
}
@@ -1908,6 +1937,26 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
if (mAnimatingAppWindowTokenRegistry != null) {
mAnimatingAppWindowTokenRegistry.notifyStarting(this);
}
+
+ // If the animation needs to be cropped then an animation bounds layer is created as a child
+ // of the pinned stack or animation layer. The leash is then reparented to this new layer.
+ if (mNeedsAnimationBoundsLayer) {
+ final TaskStack stack = getStack();
+ if (stack == null) {
+ return;
+ }
+ mAnimationBoundsLayer = createAnimationBoundsLayer(t);
+
+ // Set clip rect to stack bounds.
+ mTmpRect.setEmpty();
+ stack.getBounds(mTmpRect);
+
+ // Crop to stack bounds.
+ t.setWindowCrop(mAnimationBoundsLayer, mTmpRect);
+
+ // Reparent leash to animation bounds layer.
+ t.reparent(leash, mAnimationBoundsLayer.getHandle());
+ }
}
/**
@@ -1927,6 +1976,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree
mTransit = TRANSIT_UNSET;
mTransitFlags = 0;
mNeedsZBoost = false;
+ mNeedsAnimationBoundsLayer = false;
setAppLayoutChanges(FINISH_LAYOUT_REDO_ANIM | FINISH_LAYOUT_REDO_WALLPAPER,
"AppWindowToken");
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 016921df6047..b5e2f01d8fbb 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -123,17 +123,14 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal
return appWindowToken.mInputDispatchingTimeoutNanos;
}
} else if (windowState != null) {
- try {
- // Notify the activity manager about the timeout and let it decide whether
- // to abort dispatching or keep waiting.
- long timeout = ActivityManager.getService().inputDispatchingTimedOut(
- windowState.mSession.mPid, aboveSystem, reason);
- if (timeout >= 0) {
- // The activity manager declined to abort dispatching.
- // Wait a bit longer and timeout again later.
- return timeout * 1000000L; // nanoseconds
- }
- } catch (RemoteException ex) {
+ // Notify the activity manager about the timeout and let it decide whether
+ // to abort dispatching or keep waiting.
+ long timeout = mService.mAtmInternal.inputDispatchingTimedOut(
+ windowState.mSession.mPid, aboveSystem, reason);
+ if (timeout >= 0) {
+ // The activity manager declined to abort dispatching.
+ // Wait a bit longer and timeout again later.
+ return timeout * 1000000L; // nanoseconds
}
}
return 0; // abort dispatching
diff --git a/services/core/java/com/android/server/wm/TaskWindowContainerController.java b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
index d83f28ccb31c..8b634b1bb938 100644
--- a/services/core/java/com/android/server/wm/TaskWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
@@ -209,6 +209,12 @@ public class TaskWindowContainerController
}
}
+ public boolean isDragResizing() {
+ synchronized (mWindowMap) {
+ return mContainer.isDragResizing();
+ }
+ }
+
void reportSnapshotChanged(TaskSnapshot snapshot) {
mHandler.obtainMessage(H.REPORT_SNAPSHOT_CHANGED, snapshot).sendToTarget();
}
diff --git a/services/core/java/com/android/server/wm/WindowAnimationSpec.java b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
index 548e23a4d984..825255e556ff 100644
--- a/services/core/java/com/android/server/wm/WindowAnimationSpec.java
+++ b/services/core/java/com/android/server/wm/WindowAnimationSpec.java
@@ -99,13 +99,7 @@ public class WindowAnimationSpec implements AnimationSpec {
tmp.transformation.getMatrix().postTranslate(mPosition.x, mPosition.y);
t.setMatrix(leash, tmp.transformation.getMatrix(), tmp.floats);
t.setAlpha(leash, tmp.transformation.getAlpha());
- if (mStackClipMode == STACK_CLIP_NONE) {
- t.setWindowCrop(leash, tmp.transformation.getClipRect());
- } else if (mStackClipMode == STACK_CLIP_AFTER_ANIM) {
- mTmpRect.set(mStackBounds);
- // Offset stack bounds to stack position so the final crop is in screen space.
- mTmpRect.offsetTo(mPosition.x, mPosition.y);
- t.setFinalCrop(leash, mTmpRect);
+ if (mStackClipMode == STACK_CLIP_NONE || mStackClipMode == STACK_CLIP_AFTER_ANIM) {
t.setWindowCrop(leash, tmp.transformation.getClipRect());
} else {
mTmpRect.set(mStackBounds);
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java
index 420987d03509..41c7f1d787a4 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java
@@ -123,7 +123,7 @@ public class ActivityStartInterceptorTest {
mDevicePolicyManager);
when(mDevicePolicyManager.createShowAdminSupportIntent(TEST_USER_ID, true))
.thenReturn(ADMIN_SUPPORT_INTENT);
- when(mAm.getPackageManagerInternalLocked()).thenReturn(mPackageManagerInternal);
+ when(mService.getPackageManagerInternalLocked()).thenReturn(mPackageManagerInternal);
// Mock UserManager
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
@@ -136,7 +136,7 @@ public class ActivityStartInterceptorTest {
thenReturn(CONFIRM_CREDENTIALS_INTENT);
// Mock PackageManager
- when(mAm.getPackageManager()).thenReturn(mPackageManager);
+ when(mService.getPackageManager()).thenReturn(mPackageManager);
when(mPackageManager.getHarmfulAppWarning(TEST_PACKAGE_NAME, TEST_USER_ID))
.thenReturn(null);
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index 7e8697d95f51..a9e0aa805c37 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -32,12 +32,16 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
+import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
+import android.content.pm.PackageManagerInternal;
import com.android.server.uri.UriGrantsManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.DisplayWindowController;
import org.junit.Rule;
@@ -128,17 +132,16 @@ public class ActivityTestsBase {
}
ActivityManagerService setupActivityManagerService(TestActivityTaskManagerService atm) {
- final ActivityManagerService am = spy(new TestActivityManagerService(mContext, atm));
+ final TestActivityManagerService am = spy(new TestActivityManagerService(mContext, atm));
setupActivityManagerService(am, atm);
return am;
}
- void setupActivityManagerService(ActivityManagerService am, ActivityTaskManagerService atm) {
+ void setupActivityManagerService(
+ TestActivityManagerService am, TestActivityTaskManagerService atm) {
atm.setActivityManagerService(am);
- atm.mAmInternal = am.new LocalService();
- am.mAtmInternal = atm.new LocalService();
- am.mUgmInternal = mock(UriGrantsManagerInternal.class);
- atm.mUgmInternal = mock(UriGrantsManagerInternal.class);
+ atm.mAmInternal = am.getLocalService();
+ am.mAtmInternal = atm.getLocalService();
// Makes sure the supervisor is using with the spy object.
atm.mStackSupervisor.setService(atm);
doReturn(mock(IPackageManager.class)).when(am).getPackageManager();
@@ -378,6 +381,8 @@ public class ActivityTestsBase {
protected static class TestActivityTaskManagerService extends ActivityTaskManagerService {
private LockTaskController mLockTaskController;
+ private ActivityTaskManagerInternal mInternal;
+ private PackageManagerInternal mPmInternal;
TestActivityTaskManagerService(Context context) {
super(context);
@@ -386,6 +391,7 @@ public class ActivityTestsBase {
mSupportsSplitScreenMultiWindow = true;
mSupportsFreeformWindowManagement = true;
mSupportsPictureInPicture = true;
+ mUgmInternal = mock(UriGrantsManagerInternal.class);
}
@Override
@@ -430,16 +436,34 @@ public class ActivityTestsBase {
protected ActivityStackSupervisor createTestSupervisor() {
return new TestActivityStackSupervisor(this, mH.getLooper());
}
+
+ ActivityTaskManagerInternal getLocalService() {
+ if (mInternal == null) {
+ mInternal = new ActivityTaskManagerService.LocalService();
+ }
+ return mInternal;
+ }
+
+ PackageManagerInternal getPackageManagerInternalLocked() {
+ if (mPmInternal == null) {
+ mPmInternal = mock(PackageManagerInternal.class);
+ doReturn(false).when(mPmInternal).isPermissionsReviewRequired(anyString(), anyInt());
+ }
+ return mPmInternal;
+ }
}
/**
* An {@link ActivityManagerService} subclass which provides a test
* {@link ActivityStackSupervisor}.
*/
- protected static class TestActivityManagerService extends ActivityManagerService {
+ static class TestActivityManagerService extends ActivityManagerService {
+
+ private ActivityManagerInternal mInternal;
TestActivityManagerService(Context context, TestActivityTaskManagerService atm) {
super(context, atm);
+ mUgmInternal = mock(UriGrantsManagerInternal.class);
}
@Override
@@ -450,6 +474,13 @@ public class ActivityTestsBase {
Configuration getGlobalConfiguration() {
return mContext.getResources().getConfiguration();
}
+
+ ActivityManagerInternal getLocalService() {
+ if (mInternal == null) {
+ mInternal = new LocalService();
+ }
+ return mInternal;
+ }
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
index 37de79524ee3..227a70f7a5a3 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -115,7 +115,8 @@ public class RecentTasksTest extends ActivityTestsBase {
mTaskPersister = new TestTaskPersister(mContext.getFilesDir());
mService = spy(new MyTestActivityTaskManagerService(mContext));
- final ActivityManagerService am = spy(new MyTestActivityManagerService(mContext, mService));
+ final TestActivityManagerService am =
+ spy(new MyTestActivityManagerService(mContext, mService));
setupActivityManagerService(am, mService);
mRecentTasks = (TestRecentTasks) mService.getRecentTasks();
mRecentTasks.loadParametersFromResources(mContext.getResources());
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
new file mode 100644
index 000000000000..14695c526bff
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/hdmi/ArcTerminationActionFromAvrTest.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.hdmi;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.annotation.Nullable;
+import android.hardware.hdmi.HdmiDeviceInfo;
+import android.hardware.tv.cec.V1_0.SendMessageResult;
+import android.os.Looper;
+import android.os.test.TestLooper;
+import android.support.test.filters.SmallTest;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for {@link ArcTerminationActionFromAvr} */
+@SmallTest
+@RunWith(JUnit4.class)
+public class ArcTerminationActionFromAvrTest {
+
+ private HdmiDeviceInfo mDeviceInfoForTests;
+ private HdmiCecLocalDeviceAudioSystem mHdmiCecLocalDeviceAudioSystem;
+ private ArcTerminationActionFromAvr mAction;
+
+ private TestLooper mTestLooper = new TestLooper();
+ private boolean mSendCecCommandSuccess;
+ private boolean mShouldDispatchReportArcTerminated;
+ private boolean mArcEnabled;
+ private boolean mSetArcStatusCalled;
+
+ @Before
+ public void setUp() {
+ mDeviceInfoForTests = new HdmiDeviceInfo(1000, 1);
+
+ HdmiControlService hdmiControlService =
+ new HdmiControlService(null) {
+ @Override
+ void sendCecCommand(
+ HdmiCecMessage command, @Nullable SendMessageCallback callback) {
+ switch (command.getOpcode()) {
+ case Constants.MESSAGE_TERMINATE_ARC:
+ if (callback != null) {
+ callback.onSendCompleted(
+ mSendCecCommandSuccess
+ ? SendMessageResult.SUCCESS
+ : SendMessageResult.NACK);
+ }
+ if (mShouldDispatchReportArcTerminated) {
+ mHdmiCecLocalDeviceAudioSystem.dispatchMessage(
+ HdmiCecMessageBuilder.buildReportArcTerminated(
+ Constants.ADDR_TV,
+ mHdmiCecLocalDeviceAudioSystem.mAddress));
+ }
+ break;
+ default:
+ throw new IllegalArgumentException("Unexpected message");
+ }
+ }
+
+ @Override
+ boolean isPowerStandby() {
+ return false;
+ }
+
+ @Override
+ boolean isAddressAllocated() {
+ return true;
+ }
+
+ @Override
+ Looper getServiceLooper() {
+ return mTestLooper.getLooper();
+ }
+ };
+
+ mHdmiCecLocalDeviceAudioSystem =
+ new HdmiCecLocalDeviceAudioSystem(hdmiControlService) {
+ @Override
+ HdmiDeviceInfo getDeviceInfo() {
+ return mDeviceInfoForTests;
+ }
+
+ @Override
+ void setArcStatus(boolean enabled) {
+ mSetArcStatusCalled = true;
+ mArcEnabled = enabled;
+ }
+ };
+ mHdmiCecLocalDeviceAudioSystem.init();
+ Looper looper = mTestLooper.getLooper();
+ hdmiControlService.setIoLooper(looper);
+
+ mArcEnabled = true;
+ mAction = new ArcTerminationActionFromAvr(mHdmiCecLocalDeviceAudioSystem);
+ }
+
+ @Test
+ public void testSendMessage_NotSuccess() {
+ mSendCecCommandSuccess = false;
+ mShouldDispatchReportArcTerminated = false;
+ mSetArcStatusCalled = false;
+ mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction);
+
+ mTestLooper.dispatchAll();
+ assertThat(mSetArcStatusCalled).isFalse();
+ assertThat(mArcEnabled).isTrue();
+ }
+
+ @Test
+ public void testReportArcTerminated_NotReceived() {
+ mSendCecCommandSuccess = true;
+ mShouldDispatchReportArcTerminated = false;
+ mSetArcStatusCalled = false;
+ mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction);
+
+ mTestLooper.moveTimeForward(1000);
+ mTestLooper.dispatchAll();
+ assertThat(mSetArcStatusCalled).isFalse();
+ assertThat(mArcEnabled).isTrue();
+ }
+
+ @Test
+ public void testReportArcTerminated_Received() {
+ mSendCecCommandSuccess = true;
+ mShouldDispatchReportArcTerminated = true;
+ mSetArcStatusCalled = false;
+ mHdmiCecLocalDeviceAudioSystem.addAndStartAction(mAction);
+
+ mTestLooper.moveTimeForward(1000);
+ mTestLooper.dispatchAll();
+ assertThat(mSetArcStatusCalled).isTrue();
+ assertThat(mArcEnabled).isFalse();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
index d437ca10bb3a..e114e032753e 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DetectTvSystemAudioModeSupportActionTest.java
@@ -23,16 +23,13 @@ import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.os.Looper;
import android.os.test.TestLooper;
import android.support.test.filters.SmallTest;
-
import com.android.server.hdmi.HdmiCecLocalDeviceAudioSystem.TvSystemAudioModeSupportedCallback;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-/**
- * Tests for {@link DetectTvSystemAudioModeSupportAction} class.
- */
+/** Tests for {@link DetectTvSystemAudioModeSupportAction} class. */
@SmallTest
@RunWith(JUnit4.class)
public class DetectTvSystemAudioModeSupportActionTest {
@@ -49,46 +46,49 @@ public class DetectTvSystemAudioModeSupportActionTest {
@Before
public void SetUp() {
mDeviceInfoForTests = new HdmiDeviceInfo(1001, 1234);
- HdmiControlService hdmiControlService = new HdmiControlService(null) {
+ HdmiControlService hdmiControlService =
+ new HdmiControlService(null) {
- @Override
- void sendCecCommand(HdmiCecMessage command,
- @Nullable SendMessageCallback callback) {
- switch (command.getOpcode()) {
- case Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE:
- if (callback != null) {
- callback.onSendCompleted(mSendCecCommandSuccess
- ? SendMessageResult.SUCCESS : SendMessageResult.NACK);
- }
- if (mShouldDispatchFeatureAbort) {
- mHdmiCecLocalDeviceAudioSystem.dispatchMessage(
- HdmiCecMessageBuilder.buildFeatureAbortCommand(
- Constants.ADDR_TV,
- mHdmiCecLocalDeviceAudioSystem.mAddress,
- Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE,
- Constants.ABORT_UNRECOGNIZED_OPCODE));
+ @Override
+ void sendCecCommand(
+ HdmiCecMessage command, @Nullable SendMessageCallback callback) {
+ switch (command.getOpcode()) {
+ case Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE:
+ if (callback != null) {
+ callback.onSendCompleted(
+ mSendCecCommandSuccess
+ ? SendMessageResult.SUCCESS
+ : SendMessageResult.NACK);
+ }
+ if (mShouldDispatchFeatureAbort) {
+ mHdmiCecLocalDeviceAudioSystem.dispatchMessage(
+ HdmiCecMessageBuilder.buildFeatureAbortCommand(
+ Constants.ADDR_TV,
+ mHdmiCecLocalDeviceAudioSystem.mAddress,
+ Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE,
+ Constants.ABORT_UNRECOGNIZED_OPCODE));
+ }
+ break;
+ default:
+ throw new IllegalArgumentException("Unexpected message");
}
- break;
- default:
- throw new IllegalArgumentException("Unexpected message");
- }
- }
+ }
- @Override
- boolean isPowerStandby() {
- return false;
- }
+ @Override
+ boolean isPowerStandby() {
+ return false;
+ }
- @Override
- boolean isAddressAllocated() {
- return true;
- }
+ @Override
+ boolean isAddressAllocated() {
+ return true;
+ }
- @Override
- Looper getServiceLooper() {
- return mTestLooper.getLooper();
- }
- };
+ @Override
+ Looper getServiceLooper() {
+ return mTestLooper.getLooper();
+ }
+ };
mHdmiCecLocalDeviceAudioSystem =
new HdmiCecLocalDeviceAudioSystem(hdmiControlService) {
@Override
@@ -100,13 +100,14 @@ public class DetectTvSystemAudioModeSupportActionTest {
Looper looper = mTestLooper.getLooper();
hdmiControlService.setIoLooper(looper);
- mAction = new DetectTvSystemAudioModeSupportAction(
- mHdmiCecLocalDeviceAudioSystem,
- new TvSystemAudioModeSupportedCallback() {
- public void onResult(boolean supported) {
- mSupported = Boolean.valueOf(supported);
- }
- });
+ mAction =
+ new DetectTvSystemAudioModeSupportAction(
+ mHdmiCecLocalDeviceAudioSystem,
+ new TvSystemAudioModeSupportedCallback() {
+ public void onResult(boolean supported) {
+ mSupported = Boolean.valueOf(supported);
+ }
+ });
mSupported = null;
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
index 55099358f7fa..7484edd286f0 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeNativeWrapper.java
@@ -18,10 +18,10 @@ package com.android.server.hdmi;
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.tv.cec.V1_0.SendMessageResult;
import android.os.MessageQueue;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.server.hdmi.HdmiCecController.NativeWrapper;
import java.util.ArrayList;
import java.util.List;
-import com.android.internal.annotations.VisibleForTesting;
/** Fake {@link NativeWrapper} useful for testing. */
final class FakeNativeWrapper implements NativeWrapper {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
index 84e1b7e18909..6cf5f0045c2d 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecControllerTest.java
@@ -15,17 +15,6 @@
*/
package com.android.server.hdmi;
-import android.content.Context;
-import android.hardware.hdmi.HdmiPortInfo;
-import android.hardware.tv.cec.V1_0.SendMessageResult;
-import android.os.Looper;
-import android.os.MessageQueue;
-import android.os.test.TestLooper;
-import android.support.test.filters.SmallTest;
-import android.util.Log;
-import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
-import com.android.server.hdmi.HdmiCecController.NativeWrapper;
-
import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM;
import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_PLAYBACK;
import static android.hardware.hdmi.HdmiDeviceInfo.DEVICE_TV;
@@ -37,14 +26,19 @@ import static com.android.server.hdmi.Constants.ADDR_SPECIFIC_USE;
import static com.android.server.hdmi.Constants.ADDR_TV;
import static com.android.server.hdmi.Constants.ADDR_UNREGISTERED;
import static junit.framework.Assert.assertEquals;
+
+import android.content.Context;
+import android.hardware.tv.cec.V1_0.SendMessageResult;
+import android.os.Looper;
+import android.os.test.TestLooper;
+import android.support.test.filters.SmallTest;
+import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-/**
- * Tests for {@link com.android.server.hdmi.HdmiCecController} class.
- */
+/** Tests for {@link com.android.server.hdmi.HdmiCecController} class. */
@SmallTest
@RunWith(JUnit4.class)
public class HdmiCecControllerTest {
@@ -71,12 +65,13 @@ public class HdmiCecControllerTest {
private HdmiControlService mHdmiControlService;
private HdmiCecController mHdmiCecController;
private int mLogicalAddress = 16;
- private AllocateAddressCallback mCallback = new AllocateAddressCallback() {
- @Override
- public void onAllocated(int deviceType, int logicalAddress) {
- mLogicalAddress = logicalAddress;
- }
- };
+ private AllocateAddressCallback mCallback =
+ new AllocateAddressCallback() {
+ @Override
+ public void onAllocated(int deviceType, int logicalAddress) {
+ mLogicalAddress = logicalAddress;
+ }
+ };
private Looper mMyLooper;
private TestLooper mTestLooper = new TestLooper();
@@ -86,13 +81,11 @@ public class HdmiCecControllerTest {
mMyLooper = mTestLooper.getLooper();
mHdmiControlService = new MyHdmiControlService(null);
mNativeWrapper = new FakeNativeWrapper();
- mHdmiCecController = HdmiCecController.createWithNativeWrapper(
- mHdmiControlService, mNativeWrapper);
+ mHdmiCecController =
+ HdmiCecController.createWithNativeWrapper(mHdmiControlService, mNativeWrapper);
}
- /**
- * Tests for {@link HdmiCecController#allocateLogicalAddress}
- */
+ /** Tests for {@link HdmiCecController#allocateLogicalAddress} */
@Test
public void testAllocatLogicalAddress_TvDevicePreferredNotOcupied() {
mHdmiCecController.allocateLogicalAddress(DEVICE_TV, ADDR_TV, mCallback);
@@ -128,7 +121,7 @@ public class HdmiCecControllerTest {
@Test
public void testAllocatLogicalAddress_AudioSystemNonPreferredNotOcupied() {
mHdmiCecController.allocateLogicalAddress(
- DEVICE_AUDIO_SYSTEM, ADDR_UNREGISTERED, mCallback);
+ DEVICE_AUDIO_SYSTEM, ADDR_UNREGISTERED, mCallback);
mTestLooper.dispatchAll();
assertEquals(ADDR_AUDIO_SYSTEM, mLogicalAddress);
}
@@ -137,7 +130,7 @@ public class HdmiCecControllerTest {
public void testAllocatLogicalAddress_AudioSystemNonPreferredAllOcupied() {
mNativeWrapper.setPollAddressResponse(ADDR_AUDIO_SYSTEM, SendMessageResult.SUCCESS);
mHdmiCecController.allocateLogicalAddress(
- DEVICE_AUDIO_SYSTEM, ADDR_UNREGISTERED, mCallback);
+ DEVICE_AUDIO_SYSTEM, ADDR_UNREGISTERED, mCallback);
mTestLooper.dispatchAll();
assertEquals(ADDR_UNREGISTERED, mLogicalAddress);
}
@@ -152,8 +145,7 @@ public class HdmiCecControllerTest {
@Test
public void testAllocatLogicalAddress_PlaybackPreferredOcuppied() {
mNativeWrapper.setPollAddressResponse(ADDR_PLAYBACK_1, SendMessageResult.SUCCESS);
- mHdmiCecController.allocateLogicalAddress(
- DEVICE_PLAYBACK, ADDR_PLAYBACK_1, mCallback);
+ mHdmiCecController.allocateLogicalAddress(DEVICE_PLAYBACK, ADDR_PLAYBACK_1, mCallback);
mTestLooper.dispatchAll();
assertEquals(ADDR_PLAYBACK_2, mLogicalAddress);
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index 474568305520..5e7a2522f025 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -24,11 +24,13 @@ import static com.google.common.truth.Truth.assertThat;
import android.media.AudioManager;
import android.os.Looper;
+import android.os.SystemProperties;
import android.os.test.TestLooper;
import android.support.test.filters.SmallTest;
-
import com.android.server.hdmi.HdmiCecLocalDevice.ActiveSource;
+import java.util.List;
import java.util.ArrayList;
+import java.util.Collections;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -36,9 +38,7 @@ import org.junit.runners.JUnit4;
@SmallTest
@RunWith(JUnit4.class)
-/**
- * Tests for {@link HdmiCecLocalDeviceAudioSystem} class.
- */
+/** Tests for {@link HdmiCecLocalDeviceAudioSystem} class. */
public class HdmiCecLocalDeviceAudioSystemTest {
private static final String TAG = "HdmiCecLocalDeviceAudioSystemTest";
@@ -55,66 +55,74 @@ public class HdmiCecLocalDeviceAudioSystemTest {
@Before
public void SetUp() {
- mHdmiControlService = new HdmiControlService(null) {
- @Override
- AudioManager getAudioManager() {
- return new AudioManager() {
+ mHdmiControlService =
+ new HdmiControlService(null) {
@Override
- public int getStreamVolume(int streamType) {
- switch (streamType) {
- case STREAM_MUSIC:
- return mMusicVolume;
- default:
- return 0;
- }
- }
+ AudioManager getAudioManager() {
+ return new AudioManager() {
+ @Override
+ public int getStreamVolume(int streamType) {
+ switch (streamType) {
+ case STREAM_MUSIC:
+ return mMusicVolume;
+ default:
+ return 0;
+ }
+ }
- @Override
- public boolean isStreamMute(int streamType) {
- switch (streamType) {
- case STREAM_MUSIC:
- return mMusicMute;
- default:
- return false;
- }
- }
+ @Override
+ public boolean isStreamMute(int streamType) {
+ switch (streamType) {
+ case STREAM_MUSIC:
+ return mMusicMute;
+ default:
+ return false;
+ }
+ }
- @Override
- public int getStreamMaxVolume(int streamType) {
- switch (streamType) {
- case STREAM_MUSIC:
- return mMusicMaxVolume;
- default:
- return 100;
- }
- }
+ @Override
+ public int getStreamMaxVolume(int streamType) {
+ switch (streamType) {
+ case STREAM_MUSIC:
+ return mMusicMaxVolume;
+ default:
+ return 100;
+ }
+ }
- @Override
- public void adjustStreamVolume(int streamType, int direction, int flags) {
- switch (streamType) {
- case STREAM_MUSIC:
- if (direction == AudioManager.ADJUST_UNMUTE) {
- mMusicMute = false;
- } else if (direction == AudioManager.ADJUST_MUTE) {
- mMusicMute = true;
+ @Override
+ public void adjustStreamVolume(
+ int streamType, int direction, int flags) {
+ switch (streamType) {
+ case STREAM_MUSIC:
+ if (direction == AudioManager.ADJUST_UNMUTE) {
+ mMusicMute = false;
+ } else if (direction == AudioManager.ADJUST_MUTE) {
+ mMusicMute = true;
+ }
+ default:
}
- default:
- }
+ }
+
+ @Override
+ public void setWiredDeviceConnectionState(
+ int type, int state, String address, String name) {
+ // Do nothing.
+ }
+ };
}
+
+ @Override
+ void wakeUp() {}
};
- }
- @Override
- void wakeUp() {
- }
- };
mMyLooper = mTestLooper.getLooper();
mHdmiCecLocalDeviceAudioSystem = new HdmiCecLocalDeviceAudioSystem(mHdmiControlService);
mHdmiCecLocalDeviceAudioSystem.init();
mHdmiControlService.setIoLooper(mMyLooper);
mNativeWrapper = new FakeNativeWrapper();
- mHdmiCecController = HdmiCecController.createWithNativeWrapper(
- mHdmiControlService, mNativeWrapper);
+ mHdmiCecController =
+ HdmiCecController.createWithNativeWrapper(mHdmiControlService, mNativeWrapper);
mHdmiControlService.setCecController(mHdmiCecController);
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
@@ -123,105 +131,90 @@ public class HdmiCecLocalDeviceAudioSystemTest {
// No TV device interacts with AVR so system audio control won't be turned on here
mHdmiControlService.allocateLogicalAddress(mLocalDevices, INITIATED_BY_ENABLE_CEC);
mTestLooper.dispatchAll();
+ SystemProperties.set(Constants.PROPERTY_ARC_SUPPORT, "true");
}
+
@Test
public void handleGiveAudioStatus_volume_10_mute_true() {
mMusicVolume = 10;
mMusicMute = true;
mMusicMaxVolume = 20;
int scaledVolume = VolumeControlAction.scaleToCecVolume(10, mMusicMaxVolume);
- HdmiCecMessage expectedMessage = HdmiCecMessageBuilder.buildReportAudioStatus(
- ADDR_AUDIO_SYSTEM, ADDR_TV, scaledVolume, true);
- HdmiCecMessage messageGive = HdmiCecMessageBuilder.buildGiveAudioStatus(
- ADDR_TV, ADDR_AUDIO_SYSTEM);
+ HdmiCecMessage expectedMessage =
+ HdmiCecMessageBuilder.buildReportAudioStatus(
+ ADDR_AUDIO_SYSTEM, ADDR_TV, scaledVolume, true);
+ HdmiCecMessage messageGive =
+ HdmiCecMessageBuilder.buildGiveAudioStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveAudioStatus(messageGive))
- .isEqualTo(true);
+ .isTrue();
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
}
+
@Test
public void handleGiveSystemAudioModeStatus_originalOff() {
- HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
- .buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
- HdmiCecMessage messageGive = HdmiCecMessageBuilder
- .buildGiveSystemAudioModeStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
+ HdmiCecMessage expectedMessage =
+ HdmiCecMessageBuilder.buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
+ HdmiCecMessage messageGive =
+ HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive))
- .isEqualTo(true);
- mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
- }
- @Test
- public void handleRequestArcInitiate() {
- // TODO(b/80296911): Add tests when finishing handler impl.
- HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
- .buildInitiateArc(ADDR_AUDIO_SYSTEM, ADDR_TV);
- HdmiCecMessage message = HdmiCecMessageBuilder
- .buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
- assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message))
- .isEqualTo(true);
- mTestLooper.dispatchAll();
- assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
- }
- @Test
- public void handleRequestArcTermination() {
- // TODO(b/80297105): Add tests when finishing handler impl.
- HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
- .buildTerminateArc(ADDR_AUDIO_SYSTEM, ADDR_TV);
- HdmiCecMessage messageRequestOff = HdmiCecMessageBuilder
- .buildRequestArcTermination(ADDR_TV, ADDR_AUDIO_SYSTEM);
- assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcTermination(messageRequestOff))
- .isEqualTo(true);
+ .isTrue();
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
}
+
@Test
public void handleSetSystemAudioMode_setOn_orignalOff() {
mMusicMute = true;
- HdmiCecMessage messageSet = HdmiCecMessageBuilder
- .buildSetSystemAudioMode(ADDR_TV, ADDR_AUDIO_SYSTEM, true);
- HdmiCecMessage messageGive = HdmiCecMessageBuilder
- .buildGiveSystemAudioModeStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
+ HdmiCecMessage messageSet =
+ HdmiCecMessageBuilder.buildSetSystemAudioMode(ADDR_TV, ADDR_AUDIO_SYSTEM, true);
+ HdmiCecMessage messageGive =
+ HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
// Check if originally off
- HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
- .buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
+ HdmiCecMessage expectedMessage =
+ HdmiCecMessageBuilder.buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive))
- .isEqualTo(true);
+ .isTrue();
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
// Check if correctly turned on
- expectedMessage = HdmiCecMessageBuilder
- .buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, true);
+ expectedMessage =
+ HdmiCecMessageBuilder.buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, true);
assertThat(mHdmiCecLocalDeviceAudioSystem.handleSetSystemAudioMode(messageSet))
- .isEqualTo(true);
+ .isTrue();
mTestLooper.dispatchAll();
assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive))
- .isEqualTo(true);
+ .isTrue();
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
- assertThat(mMusicMute).isEqualTo(false);
+ assertThat(mMusicMute).isFalse();
}
+
@Test
public void handleSystemAudioModeRequest_turnOffByTv() {
- assertThat(mMusicMute).isEqualTo(false);
+ assertThat(mMusicMute).isFalse();
// Check if feature correctly turned off
- HdmiCecMessage messageGive = HdmiCecMessageBuilder
- .buildGiveSystemAudioModeStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
- HdmiCecMessage messageRequestOff = HdmiCecMessageBuilder
- .buildSystemAudioModeRequest(ADDR_TV, ADDR_AUDIO_SYSTEM, 2, false);
- HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
- .buildSetSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
+ HdmiCecMessage messageGive =
+ HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(ADDR_TV, ADDR_AUDIO_SYSTEM);
+ HdmiCecMessage messageRequestOff =
+ HdmiCecMessageBuilder.buildSystemAudioModeRequest(
+ ADDR_TV, ADDR_AUDIO_SYSTEM, 2, false);
+ HdmiCecMessage expectedMessage =
+ HdmiCecMessageBuilder.buildSetSystemAudioMode(
+ ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
assertThat(mHdmiCecLocalDeviceAudioSystem.handleSystemAudioModeRequest(messageRequestOff))
- .isEqualTo(true);
+ .isTrue();
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
- expectedMessage = HdmiCecMessageBuilder
- .buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
+ expectedMessage =
+ HdmiCecMessageBuilder.buildReportSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_TV, false);
assertThat(mHdmiCecLocalDeviceAudioSystem.handleGiveSystemAudioModeStatus(messageGive))
- .isEqualTo(true);
+ .isTrue();
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
- assertThat(mMusicMute).isEqualTo(true);
+ assertThat(mMusicMute).isTrue();
}
+
@Test
public void onStandbyAudioSystem_currentSystemAudioControlOn() {
// Set system audio control on first
@@ -229,80 +222,93 @@ public class HdmiCecLocalDeviceAudioSystemTest {
// Check if standby correctly turns off the feature
mHdmiCecLocalDeviceAudioSystem.onStandby(false, STANDBY_SCREEN_OFF);
mTestLooper.dispatchAll();
- HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
- .buildSetSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
+ HdmiCecMessage expectedMessage =
+ HdmiCecMessageBuilder.buildSetSystemAudioMode(
+ ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
- assertThat(mMusicMute).isEqualTo(true);
+ assertThat(mMusicMute).isTrue();
}
+
@Test
public void systemAudioControlOnPowerOn_alwaysOn() {
- mHdmiCecLocalDeviceAudioSystem.removeAction(
- SystemAudioInitiationActionFromAvr.class);
+ mHdmiCecLocalDeviceAudioSystem.removeAction(SystemAudioInitiationActionFromAvr.class);
mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
Constants.ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, true);
- assertThat(mHdmiCecLocalDeviceAudioSystem
- .getActions(SystemAudioInitiationActionFromAvr.class)).isNotEmpty();
+ assertThat(
+ mHdmiCecLocalDeviceAudioSystem.getActions(
+ SystemAudioInitiationActionFromAvr.class))
+ .isNotEmpty();
}
+
@Test
public void systemAudioControlOnPowerOn_neverOn() {
- mHdmiCecLocalDeviceAudioSystem.removeAction(
- SystemAudioInitiationActionFromAvr.class);
+ mHdmiCecLocalDeviceAudioSystem.removeAction(SystemAudioInitiationActionFromAvr.class);
mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
Constants.NEVER_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, false);
- assertThat(mHdmiCecLocalDeviceAudioSystem
- .getActions(SystemAudioInitiationActionFromAvr.class)).isEmpty();
+ assertThat(
+ mHdmiCecLocalDeviceAudioSystem.getActions(
+ SystemAudioInitiationActionFromAvr.class))
+ .isEmpty();
}
+
@Test
public void systemAudioControlOnPowerOn_useLastState_off() {
- mHdmiCecLocalDeviceAudioSystem.removeAction(
- SystemAudioInitiationActionFromAvr.class);
+ mHdmiCecLocalDeviceAudioSystem.removeAction(SystemAudioInitiationActionFromAvr.class);
mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, false);
- assertThat(mHdmiCecLocalDeviceAudioSystem
- .getActions(SystemAudioInitiationActionFromAvr.class)).isEmpty();
+ assertThat(
+ mHdmiCecLocalDeviceAudioSystem.getActions(
+ SystemAudioInitiationActionFromAvr.class))
+ .isEmpty();
}
+
@Test
public void systemAudioControlOnPowerOn_useLastState_on() {
- mHdmiCecLocalDeviceAudioSystem.removeAction(
- SystemAudioInitiationActionFromAvr.class);
+ mHdmiCecLocalDeviceAudioSystem.removeAction(SystemAudioInitiationActionFromAvr.class);
mHdmiCecLocalDeviceAudioSystem.systemAudioControlOnPowerOn(
Constants.USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, true);
- assertThat(mHdmiCecLocalDeviceAudioSystem
- .getActions(SystemAudioInitiationActionFromAvr.class)).isNotEmpty();
+ assertThat(
+ mHdmiCecLocalDeviceAudioSystem.getActions(
+ SystemAudioInitiationActionFromAvr.class))
+ .isNotEmpty();
}
+
@Test
public void handleActiveSource_updateActiveSource() {
- HdmiCecMessage message = HdmiCecMessageBuilder
- .buildActiveSource(ADDR_TV, 0x0000);
+ HdmiCecMessage message = HdmiCecMessageBuilder.buildActiveSource(ADDR_TV, 0x0000);
ActiveSource expectedActiveSource = new ActiveSource(ADDR_TV, 0x0000);
assertThat(mHdmiCecLocalDeviceAudioSystem.handleActiveSource(message))
- .isEqualTo(true);
+ .isTrue();
mTestLooper.dispatchAll();
assertThat(mHdmiCecLocalDeviceAudioSystem.getActiveSource().equals(expectedActiveSource))
- .isEqualTo(true);
+ .isTrue();
}
+
@Test
public void terminateSystemAudioMode_systemAudioModeOff() {
mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(false);
- assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isEqualTo(false);
+ assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isFalse();
mMusicMute = false;
- HdmiCecMessage message = HdmiCecMessageBuilder
- .buildSetSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
+ HdmiCecMessage message =
+ HdmiCecMessageBuilder.buildSetSystemAudioMode(
+ ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
mHdmiCecLocalDeviceAudioSystem.terminateSystemAudioMode();
- assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isEqualTo(false);
- assertThat(mMusicMute).isEqualTo(false);
+ assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isFalse();
+ assertThat(mMusicMute).isFalse();
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(message);
}
+
@Test
public void terminateSystemAudioMode_systemAudioModeOn() {
mHdmiCecLocalDeviceAudioSystem.setSystemAudioMode(true);
- assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isEqualTo(true);
+ assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isTrue();
mMusicMute = false;
- HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
- .buildSetSystemAudioMode(ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
+ HdmiCecMessage expectedMessage =
+ HdmiCecMessageBuilder.buildSetSystemAudioMode(
+ ADDR_AUDIO_SYSTEM, ADDR_BROADCAST, false);
mHdmiCecLocalDeviceAudioSystem.terminateSystemAudioMode();
- assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isEqualTo(false);
- assertThat(mMusicMute).isEqualTo(true);
+ assertThat(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated()).isFalse();
+ assertThat(mMusicMute).isTrue();
mTestLooper.dispatchAll();
assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
}
@@ -312,7 +318,7 @@ public class HdmiCecLocalDeviceAudioSystemTest {
int targetPhysicalAddress = 0x1000;
mNativeWrapper.setPhysicalAddress(0x1000);
assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
- .isEqualTo(true);
+ .isTrue();
}
@Test
@@ -320,7 +326,7 @@ public class HdmiCecLocalDeviceAudioSystemTest {
int targetPhysicalAddress = 0x1100;
mNativeWrapper.setPhysicalAddress(0x1000);
assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
- .isEqualTo(true);
+ .isTrue();
}
@Test
@@ -328,21 +334,104 @@ public class HdmiCecLocalDeviceAudioSystemTest {
int targetPhysicalAddress = 0x3000;
mNativeWrapper.setPhysicalAddress(0x2000);
assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
- .isEqualTo(false);
+ .isFalse();
targetPhysicalAddress = 0x2200;
mNativeWrapper.setPhysicalAddress(0x3300);
assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
- .isEqualTo(false);
+ .isFalse();
targetPhysicalAddress = 0x2213;
mNativeWrapper.setPhysicalAddress(0x2212);
assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
- .isEqualTo(false);
+ .isFalse();
targetPhysicalAddress = 0x2340;
mNativeWrapper.setPhysicalAddress(0x2310);
assertThat(mHdmiCecLocalDeviceAudioSystem.isPhysicalAddressMeOrBelow(targetPhysicalAddress))
- .isEqualTo(false);
+ .isFalse();
+ }
+
+ @Test
+ public void handleRequestArcInitiate_isNotDirectConnectedToTv() {
+ HdmiCecMessage message = HdmiCecMessageBuilder
+ .buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
+ HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
+ .buildFeatureAbortCommand(
+ ADDR_AUDIO_SYSTEM, ADDR_TV,
+ Constants.MESSAGE_REQUEST_ARC_INITIATION,
+ Constants.ABORT_NOT_IN_CORRECT_MODE);
+ mNativeWrapper.setPhysicalAddress(0x1100);
+
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message))
+ .isTrue();
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
+ }
+
+ @Test
+ public void handleRequestArcInitiate_startArcInitiationActionFromAvr() {
+ HdmiCecMessage message = HdmiCecMessageBuilder
+ .buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
+ mNativeWrapper.setPhysicalAddress(0x1000);
+ mHdmiCecLocalDeviceAudioSystem.removeAction(
+ ArcInitiationActionFromAvr.class);
+
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message))
+ .isTrue();
+ mTestLooper.dispatchAll();
+ assertThat(mHdmiCecLocalDeviceAudioSystem
+ .getActions(ArcInitiationActionFromAvr.class)).isNotEmpty();
+ }
+
+ @Test
+ public void handleRequestArcTerminate_arcIsOn_startTerminationActionFromAvr() {
+ mHdmiCecLocalDeviceAudioSystem.setArcStatus(true);
+ assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isTrue();
+
+ HdmiCecMessage message = HdmiCecMessageBuilder
+ .buildRequestArcTermination(ADDR_TV, ADDR_AUDIO_SYSTEM);
+ mHdmiCecLocalDeviceAudioSystem.removeAction(
+ ArcTerminationActionFromAvr.class);
+
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcTermination(message))
+ .isTrue();
+ mTestLooper.dispatchAll();
+ assertThat(mHdmiCecLocalDeviceAudioSystem
+ .getActions(ArcTerminationActionFromAvr.class)).isNotEmpty();
+ }
+
+ @Test
+ public void handleRequestArcTerminate_arcIsNotOn() {
+ assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isFalse();
+ HdmiCecMessage message = HdmiCecMessageBuilder
+ .buildRequestArcTermination(ADDR_TV, ADDR_AUDIO_SYSTEM);
+ HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
+ .buildFeatureAbortCommand(
+ ADDR_AUDIO_SYSTEM, ADDR_TV,
+ Constants.MESSAGE_REQUEST_ARC_TERMINATION,
+ Constants.ABORT_NOT_IN_CORRECT_MODE);
+
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcTermination(message))
+ .isTrue();
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
+ }
+
+ @Test
+ public void handleRequestArcInit_arcIsNotSupported() {
+ HdmiCecMessage message = HdmiCecMessageBuilder
+ .buildRequestArcInitiation(ADDR_TV, ADDR_AUDIO_SYSTEM);
+ HdmiCecMessage expectedMessage = HdmiCecMessageBuilder
+ .buildFeatureAbortCommand(
+ ADDR_AUDIO_SYSTEM, ADDR_TV,
+ Constants.MESSAGE_REQUEST_ARC_INITIATION,
+ Constants.ABORT_UNRECOGNIZED_OPCODE);
+ SystemProperties.set(Constants.PROPERTY_ARC_SUPPORT, "false");
+
+ assertThat(mHdmiCecLocalDeviceAudioSystem.handleRequestArcInitiate(message))
+ .isTrue();
+ mTestLooper.dispatchAll();
+ assertThat(mNativeWrapper.getResultMessages()).contains(expectedMessage);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
index 833ffa6b0c70..3cd84817bcac 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTest.java
@@ -27,13 +27,8 @@ import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import android.hardware.hdmi.HdmiControlManager;
-import android.hardware.hdmi.HdmiPortInfo;
-import android.os.PowerManager;
import android.os.test.TestLooper;
import android.support.test.filters.SmallTest;
-import android.os.MessageQueue;
-import com.android.server.hdmi.HdmiCecController.NativeWrapper;
-import junit.framework.Assert;
import java.util.Arrays;
import org.junit.Before;
import org.junit.Test;
@@ -42,20 +37,19 @@ import org.junit.runners.JUnit4;
@SmallTest
@RunWith(JUnit4.class)
-/**
- * Tests for {@link HdmiCecLocalDevice} class.
- */
+/** Tests for {@link HdmiCecLocalDevice} class. */
public class HdmiCecLocalDeviceTest {
-
private static int SendCecCommandFactory(int srcAddress, int dstAddress, byte[] body) {
- switch(body[0] & 0xFF) {
- /** {@link Constants#MESSAGE_GIVE_PHYSICAL_ADDRESS} */
+ switch (body[0] & 0xFF) {
+ /** {@link Constants#MESSAGE_GIVE_PHYSICAL_ADDRESS} */
case MESSAGE_REPORT_PHYSICAL_ADDRESS:
case MESSAGE_DEVICE_VENDOR_ID:
- return srcAddress == mSrcAddr &&
- dstAddress == mDesAddr &&
- Arrays.equals(Arrays.copyOfRange(body, 1, body.length), param)? 0 : 1;
+ return srcAddress == mSrcAddr
+ && dstAddress == mDesAddr
+ && Arrays.equals(Arrays.copyOfRange(body, 1, body.length), param)
+ ? 0
+ : 1;
default:
return 1;
}
@@ -63,15 +57,12 @@ public class HdmiCecLocalDeviceTest {
private class MyHdmiCecLocalDevice extends HdmiCecLocalDevice {
-
protected MyHdmiCecLocalDevice(HdmiControlService service, int deviceType) {
super(service, deviceType);
}
@Override
- protected void onAddressAllocated(int logicalAddress, int reason) {
-
- }
+ protected void onAddressAllocated(int logicalAddress, int reason) {}
@Override
protected int getPreferredAddress() {
@@ -79,9 +70,7 @@ public class HdmiCecLocalDeviceTest {
}
@Override
- protected void setPreferredAddress(int addr) {
-
- }
+ protected void setPreferredAddress(int addr) {}
}
private MyHdmiCecLocalDevice mHdmiLocalDevice;
@@ -100,42 +89,48 @@ public class HdmiCecLocalDeviceTest {
@Before
public void SetUp() {
- mHdmiControlService = new HdmiControlService(null) {
- @Override
- boolean isControlEnabled() {
- return isControlEnabled;
- }
-
- @Override
- boolean isPowerOnOrTransient() {
- return mPowerStatus == HdmiControlManager.POWER_STATUS_ON
- || mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
- }
-
- @Override
- void standby() {
- mStandbyMessageReceived = true;
- }
- };
+ mHdmiControlService =
+ new HdmiControlService(null) {
+ @Override
+ boolean isControlEnabled() {
+ return isControlEnabled;
+ }
+
+ @Override
+ boolean isPowerOnOrTransient() {
+ return mPowerStatus == HdmiControlManager.POWER_STATUS_ON
+ || mPowerStatus == HdmiControlManager.POWER_STATUS_TRANSIENT_TO_ON;
+ }
+
+ @Override
+ void standby() {
+ mStandbyMessageReceived = true;
+ }
+ };
mHdmiControlService.setIoLooper(mTestLooper.getLooper());
- mHdmiCecController = HdmiCecController.createWithNativeWrapper(
- mHdmiControlService, new FakeNativeWrapper());
+ mHdmiCecController =
+ HdmiCecController.createWithNativeWrapper(
+ mHdmiControlService, new FakeNativeWrapper());
mHdmiControlService.setCecController(mHdmiCecController);
- mHdmiLocalDevice = new MyHdmiCecLocalDevice(
- mHdmiControlService, DEVICE_TV);
- mMessageValidator = new HdmiCecMessageValidator(mHdmiControlService){
- @Override
- int isValid(HdmiCecMessage message) {
- return HdmiCecMessageValidator.OK;
- }
- };
+ mHdmiLocalDevice = new MyHdmiCecLocalDevice(mHdmiControlService, DEVICE_TV);
+ mMessageValidator =
+ new HdmiCecMessageValidator(mHdmiControlService) {
+ @Override
+ int isValid(HdmiCecMessage message) {
+ return HdmiCecMessageValidator.OK;
+ }
+ };
mHdmiControlService.setMessageValidator(mMessageValidator);
}
@Test
public void dispatchMessage_desNotValid() {
- HdmiCecMessage msg = new HdmiCecMessage(
- ADDR_TV, ADDR_TV, Constants.MESSAGE_CEC_VERSION, HdmiCecMessage.EMPTY_PARAM);
+ HdmiCecMessage msg =
+ new HdmiCecMessage(
+ ADDR_TV,
+ ADDR_TV,
+ Constants.MESSAGE_CEC_VERSION,
+ HdmiCecMessage.EMPTY_PARAM);
boolean handleResult = mHdmiLocalDevice.dispatchMessage(msg);
assertFalse(handleResult);
}
@@ -144,18 +139,19 @@ public class HdmiCecLocalDeviceTest {
public void handleGivePhysicalAddress_success() {
mSrcAddr = ADDR_UNREGISTERED;
mDesAddr = ADDR_BROADCAST;
- param = new byte[] {
- (byte) ((mPhysicalAddr >> 8) & 0xFF),
- (byte) (mPhysicalAddr & 0xFF),
- (byte) (DEVICE_TV & 0xFF)
- };
+ param =
+ new byte[] {
+ (byte) ((mPhysicalAddr >> 8) & 0xFF),
+ (byte) (mPhysicalAddr & 0xFF),
+ (byte) (DEVICE_TV & 0xFF)
+ };
callbackResult = -1;
- boolean handleResult = mHdmiLocalDevice.handleGivePhysicalAddress(
- (int finalResult) -> callbackResult = finalResult);
+ boolean handleResult =
+ mHdmiLocalDevice.handleGivePhysicalAddress(
+ (int finalResult) -> callbackResult = finalResult);
mTestLooper.dispatchAll();
/**
- * Test if CecMessage is sent successfully
- * SendMessageResult#SUCCESS is defined in HAL as 0
+ * Test if CecMessage is sent successfully SendMessageResult#SUCCESS is defined in HAL as 0
*/
assertEquals(0, callbackResult);
assertTrue(handleResult);
@@ -166,14 +162,10 @@ public class HdmiCecLocalDeviceTest {
mSrcAddr = ADDR_UNREGISTERED;
mDesAddr = ADDR_BROADCAST;
/** nativeGetVendorId returns 0 */
- param = new byte[] {
- (byte) ((0 >> 8) & 0xFF),
- (byte) (0 & 0xFF),
- (byte) (0 & 0xFF)
- };
+ param = new byte[] {(byte) ((0 >> 8) & 0xFF), (byte) (0 & 0xFF), (byte) (0 & 0xFF)};
callbackResult = -1;
mHdmiLocalDevice.handleGiveDeviceVendorId(
- (int finalResult) -> callbackResult = finalResult);
+ (int finalResult) -> callbackResult = finalResult);
mTestLooper.dispatchAll();
assertEquals(0, callbackResult);
}
@@ -184,7 +176,7 @@ public class HdmiCecLocalDeviceTest {
isControlEnabled = true;
assertFalse(mStandbyMessageReceived);
mHdmiLocalDevice.handleStandby(
- HdmiCecMessageBuilder.buildStandby(ADDR_TV, ADDR_AUDIO_SYSTEM));
+ HdmiCecMessageBuilder.buildStandby(ADDR_TV, ADDR_AUDIO_SYSTEM));
assertTrue(mStandbyMessageReceived);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java
index a7618bf59b70..1ca48d433624 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageBuilderTest.java
@@ -15,8 +15,9 @@
*/
package com.android.server.hdmi;
-import static com.android.server.hdmi.Constants.ADDR_BROADCAST;
+import static com.android.server.hdmi.Constants.ADDR_AUDIO_SYSTEM;
import static com.android.server.hdmi.Constants.ADDR_PLAYBACK_1;
+import static com.android.server.hdmi.Constants.ADDR_TV;
import static com.google.common.truth.Truth.assertThat;
import android.hardware.hdmi.HdmiDeviceInfo;
@@ -35,12 +36,33 @@ public class HdmiCecMessageBuilderTest {
HdmiCecMessage message =
HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
ADDR_PLAYBACK_1, 0x1234, HdmiDeviceInfo.DEVICE_PLAYBACK);
- assertThat(message)
- .isEqualTo(
- new HdmiCecMessage(
- ADDR_PLAYBACK_1,
- ADDR_BROADCAST,
- Constants.MESSAGE_REPORT_PHYSICAL_ADDRESS,
- new byte[] {0x12, 0x34, 0x04}));
+ assertThat(message).isEqualTo(buildMessage("4f:84:12:34:04"));
+ }
+
+ @Test
+ public void buildRequestShortAudioDescriptor() {
+ HdmiCecMessage message =
+ HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(
+ ADDR_TV,
+ ADDR_AUDIO_SYSTEM,
+ new int[] {Constants.AUDIO_CODEC_AAC, Constants.AUDIO_CODEC_LPCM});
+ assertThat(message).isEqualTo(buildMessage("05:A4:06:01"));
+ }
+
+ /**
+ * Build a CEC message from a hex byte string with bytes separated by {@code :}.
+ *
+ * <p>This format is used by both cec-client and www.cec-o-matic.com
+ */
+ private static HdmiCecMessage buildMessage(String message) {
+ String[] parts = message.split(":");
+ int src = Integer.parseInt(parts[0].substring(0, 1), 16);
+ int dest = Integer.parseInt(parts[0].substring(1, 2), 16);
+ int opcode = Integer.parseInt(parts[1], 16);
+ byte[] params = new byte[parts.length - 2];
+ for (int i = 0; i < params.length; i++) {
+ params[i] = Byte.parseByte(parts[i + 2], 16);
+ }
+ return new HdmiCecMessage(src, dest, opcode, params);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 90ad34982421..7de637b2df3c 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -30,9 +30,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-/**
- * Tests for {@link HdmiControlService} class.
- */
+/** Tests for {@link HdmiControlService} class. */
@SmallTest
@RunWith(JUnit4.class)
public class HdmiControlServiceTest {
@@ -48,9 +46,7 @@ public class HdmiControlServiceTest {
}
@Override
- protected void onAddressAllocated(int logicalAddress, int reason) {
-
- }
+ protected void onAddressAllocated(int logicalAddress, int reason) {}
@Override
protected int getPreferredAddress() {
@@ -58,9 +54,7 @@ public class HdmiControlServiceTest {
}
@Override
- protected void setPreferredAddress(int addr) {
-
- }
+ protected void setPreferredAddress(int addr) {}
@Override
protected boolean canGoToStandby() {
@@ -68,8 +62,8 @@ public class HdmiControlServiceTest {
}
@Override
- protected void disableDevice(boolean initiatedByCec,
- final PendingActionClearedCallback originalCallback) {
+ protected void disableDevice(
+ boolean initiatedByCec, final PendingActionClearedCallback originalCallback) {
mIsDisabled = true;
originalCallback.onCleared(this);
}
@@ -105,26 +99,26 @@ public class HdmiControlServiceTest {
@Before
public void SetUp() {
- mHdmiControlService = new HdmiControlService(null) {
- @Override
- boolean isStandbyMessageReceived() {
- return mStandbyMessageReceived;
- }
- };
+ mHdmiControlService =
+ new HdmiControlService(null) {
+ @Override
+ boolean isStandbyMessageReceived() {
+ return mStandbyMessageReceived;
+ }
+ };
mMyLooper = mTestLooper.getLooper();
- mMyAudioSystemDevice = new HdmiCecLocalDeviceMyDevice(
- mHdmiControlService, DEVICE_AUDIO_SYSTEM);
- mMyPlaybackDevice = new HdmiCecLocalDeviceMyDevice(
- mHdmiControlService, DEVICE_PLAYBACK);
+ mMyAudioSystemDevice =
+ new HdmiCecLocalDeviceMyDevice(mHdmiControlService, DEVICE_AUDIO_SYSTEM);
+ mMyPlaybackDevice = new HdmiCecLocalDeviceMyDevice(mHdmiControlService, DEVICE_PLAYBACK);
mMyAudioSystemDevice.init();
mMyPlaybackDevice.init();
mHdmiControlService.setIoLooper(mMyLooper);
mNativeWrapper = new FakeNativeWrapper();
- mHdmiCecController = HdmiCecController.createWithNativeWrapper(
- mHdmiControlService, mNativeWrapper);
+ mHdmiCecController =
+ HdmiCecController.createWithNativeWrapper(mHdmiControlService, mNativeWrapper);
mHdmiControlService.setCecController(mHdmiCecController);
mHdmiControlService.setHdmiMhlController(HdmiMhlControllerStub.create(mHdmiControlService));
mHdmiControlService.setMessageValidator(new HdmiCecMessageValidator(mHdmiControlService));
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
index a1614f214850..e6ff1436468e 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/SystemAudioInitiationActionFromAvrTest.java
@@ -26,15 +26,12 @@ import android.media.AudioManager;
import android.os.Looper;
import android.os.test.TestLooper;
import android.support.test.filters.SmallTest;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-/**
- * Tests for {@link SystemAudioInitiationActionFromAvr}
- */
+/** Tests for {@link SystemAudioInitiationActionFromAvr} */
@SmallTest
@RunWith(JUnit4.class)
public class SystemAudioInitiationActionFromAvrTest {
@@ -54,88 +51,86 @@ public class SystemAudioInitiationActionFromAvrTest {
@Before
public void SetUp() {
mDeviceInfoForTests = new HdmiDeviceInfo(1001, 1234);
- HdmiControlService hdmiControlService = new HdmiControlService(null) {
-
- @Override
- void sendCecCommand(HdmiCecMessage command,
- @Nullable SendMessageCallback callback) {
- switch (command.getOpcode()) {
- case Constants.MESSAGE_REQUEST_ACTIVE_SOURCE:
- mMsgRequestActiveSourceCount++;
- if (mTryCountBeforeSucceed >= mMsgRequestActiveSourceCount
- && callback != null) {
- callback.onSendCompleted(SendMessageResult.NACK);
- break;
- }
- if (mShouldDispatchActiveSource) {
- mHdmiCecLocalDeviceAudioSystem.dispatchMessage(
- HdmiCecMessageBuilder.buildActiveSource(
- Constants.ADDR_TV, 1002));
- }
- break;
- case Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE:
- mMsgSetSystemAudioModeCount++;
- if (mTryCountBeforeSucceed >= mMsgSetSystemAudioModeCount
- && callback != null) {
- callback.onSendCompleted(SendMessageResult.NACK);
- }
- break;
- default:
- throw new IllegalArgumentException("Unexpected message");
- }
- }
-
- @Override
- AudioManager getAudioManager() {
- return new AudioManager() {
+ HdmiControlService hdmiControlService =
+ new HdmiControlService(null) {
@Override
- public int setHdmiSystemAudioSupported(boolean on) {
- return 0;
+ void sendCecCommand(
+ HdmiCecMessage command, @Nullable SendMessageCallback callback) {
+ switch (command.getOpcode()) {
+ case Constants.MESSAGE_REQUEST_ACTIVE_SOURCE:
+ mMsgRequestActiveSourceCount++;
+ if (mTryCountBeforeSucceed >= mMsgRequestActiveSourceCount
+ && callback != null) {
+ callback.onSendCompleted(SendMessageResult.NACK);
+ break;
+ }
+ if (mShouldDispatchActiveSource) {
+ mHdmiCecLocalDeviceAudioSystem.dispatchMessage(
+ HdmiCecMessageBuilder.buildActiveSource(
+ Constants.ADDR_TV, 1002));
+ }
+ break;
+ case Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE:
+ mMsgSetSystemAudioModeCount++;
+ if (mTryCountBeforeSucceed >= mMsgSetSystemAudioModeCount
+ && callback != null) {
+ callback.onSendCompleted(SendMessageResult.NACK);
+ }
+ break;
+ default:
+ throw new IllegalArgumentException("Unexpected message");
+ }
}
@Override
- public int getStreamVolume(int streamType) {
- return 0;
+ AudioManager getAudioManager() {
+ return new AudioManager() {
+
+ @Override
+ public int setHdmiSystemAudioSupported(boolean on) {
+ return 0;
+ }
+
+ @Override
+ public int getStreamVolume(int streamType) {
+ return 0;
+ }
+
+ @Override
+ public boolean isStreamMute(int streamType) {
+ return false;
+ }
+
+ @Override
+ public int getStreamMaxVolume(int streamType) {
+ return 100;
+ }
+
+ @Override
+ public void adjustStreamVolume(
+ int streamType, int direction, int flags) {}
+ };
}
@Override
- public boolean isStreamMute(int streamType) {
+ boolean isPowerStandby() {
return false;
}
@Override
- public int getStreamMaxVolume(int streamType) {
- return 100;
+ boolean isAddressAllocated() {
+ return true;
}
@Override
- public void adjustStreamVolume(int streamType, int direction, int flags) {
+ void wakeUp() {}
+ @Override
+ int getPhysicalAddress() {
+ return 0;
}
};
- }
-
- @Override
- boolean isPowerStandby() {
- return false;
- }
-
- @Override
- boolean isAddressAllocated() {
- return true;
- }
-
- @Override
- void wakeUp() {
-
- }
-
- @Override
- int getPhysicalAddress() {
- return 0;
- }
- };
mHdmiCecLocalDeviceAudioSystem =
new HdmiCecLocalDeviceAudioSystem(hdmiControlService) {
@Override
@@ -210,7 +205,6 @@ public class SystemAudioInitiationActionFromAvrTest {
assertTrue(mHdmiCecLocalDeviceAudioSystem.isSystemAudioActivated());
assertThat(mHdmiCecLocalDeviceAudioSystem.mActiveSource.physicalAddress).isEqualTo(1002);
-
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenAnimationTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
new file mode 100644
index 000000000000..c3907ff8ddb4
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.SurfaceControl.Transaction;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.SurfaceControl;
+
+import com.android.server.wm.WindowTestUtils.TestAppWindowToken;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Animation related tests for the {@link AppWindowToken} class.
+ *
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:com.android.server.wm.AppWindowTokenAnimationTests
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AppWindowTokenAnimationTests extends WindowTestsBase {
+
+ private TestAppWindowToken mToken;
+
+ @Mock
+ private Transaction mTransaction;
+ @Mock
+ private AnimationAdapter mSpec;
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ MockitoAnnotations.initMocks(this);
+
+ mToken = createTestAppWindowToken(mDisplayContent, WINDOWING_MODE_FULLSCREEN,
+ ACTIVITY_TYPE_STANDARD);
+ mToken.setPendingTransaction(mTransaction);
+ }
+
+ @Test
+ public void clipAfterAnim_boundsLayerIsCreated() throws Exception {
+ mToken.mNeedsAnimationBoundsLayer = true;
+
+ mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+ verify(mTransaction).reparent(eq(mToken.getSurfaceControl()),
+ eq(mToken.mSurfaceAnimator.mLeash.getHandle()));
+ verify(mTransaction).reparent(eq(mToken.mSurfaceAnimator.mLeash),
+ eq(mToken.mAnimationBoundsLayer.getHandle()));
+ }
+
+ @Test
+ public void clipAfterAnim_boundsLayerIsDestroyed() throws Exception {
+ mToken.mNeedsAnimationBoundsLayer = true;
+ mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+ final SurfaceControl leash = mToken.mSurfaceAnimator.mLeash;
+ final SurfaceControl animationBoundsLayer = mToken.mAnimationBoundsLayer;
+ final ArgumentCaptor<SurfaceAnimator.OnAnimationFinishedCallback> callbackCaptor =
+ ArgumentCaptor.forClass(
+ SurfaceAnimator.OnAnimationFinishedCallback.class);
+ verify(mSpec).startAnimation(any(), any(), callbackCaptor.capture());
+
+ callbackCaptor.getValue().onAnimationFinished(mSpec);
+ verify(mTransaction).destroy(eq(leash));
+ verify(mTransaction).destroy(eq(animationBoundsLayer));
+ assertThat(mToken.mNeedsAnimationBoundsLayer).isFalse();
+ }
+
+ @Test
+ public void clipAfterAnimCancelled_boundsLayerIsDestroyed() throws Exception {
+ mToken.mNeedsAnimationBoundsLayer = true;
+ mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+ final SurfaceControl leash = mToken.mSurfaceAnimator.mLeash;
+ final SurfaceControl animationBoundsLayer = mToken.mAnimationBoundsLayer;
+
+ mToken.mSurfaceAnimator.cancelAnimation();
+ verify(mTransaction).destroy(eq(leash));
+ verify(mTransaction).destroy(eq(animationBoundsLayer));
+ assertThat(mToken.mNeedsAnimationBoundsLayer).isFalse();
+ }
+
+ @Test
+ public void clipNoneAnim_boundsLayerIsNotCreated() throws Exception {
+ mToken.mNeedsAnimationBoundsLayer = false;
+
+ mToken.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */);
+ verify(mTransaction).reparent(eq(mToken.getSurfaceControl()),
+ eq(mToken.mSurfaceAnimator.mLeash.getHandle()));
+ assertThat(mToken.mAnimationBoundsLayer).isNull();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index ee028ba4262b..e4f181be857c 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -19,6 +19,7 @@ package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import android.annotation.Nullable;
@@ -159,9 +160,11 @@ class TestWindowManagerPolicy implements WindowManagerPolicy {
final WindowManagerService wm = mWmSupplier.get();
synchronized (wm.mWindowMap) {
atoken = wm.mRoot.getAppWindowToken(appToken);
+ IWindow iWindow = mock(IWindow.class);
+ doReturn(mock(IBinder.class)).when(iWindow).asBinder();
window = WindowTestsBase.createWindow(null, TYPE_APPLICATION_STARTING, atoken,
"Starting window", 0 /* ownerId */, false /* internalWindows */, wm,
- mock(Session.class), mock(IWindow.class));
+ mock(Session.class), iWindow);
atoken.startingWindow = window;
}
if (mRunnableWhenAddingSplashScreen != null) {
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java
index ca520ed76be6..9dc00251896a 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowAnimationSpecTest.java
@@ -70,8 +70,6 @@ public class WindowAnimationSpecTest {
true /* isAppAnimation */);
windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
verify(mTransaction).setWindowCrop(eq(mSurfaceControl), argThat(Rect::isEmpty));
- verify(mTransaction).setFinalCrop(eq(mSurfaceControl),
- argThat(rect -> rect.equals(mStackBounds)));
}
@Test
@@ -83,9 +81,6 @@ public class WindowAnimationSpecTest {
true /* isAppAnimation */);
windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0);
verify(mTransaction).setWindowCrop(eq(mSurfaceControl), argThat(Rect::isEmpty));
- verify(mTransaction).setFinalCrop(eq(mSurfaceControl),
- argThat(rect -> rect.left == 20 && rect.top == 40 && rect.right == 30
- && rect.bottom == 50));
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
index 2e4740b57255..a48a3b9f8022 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
@@ -24,6 +24,8 @@ import android.os.Binder;
import android.os.IBinder;
import android.view.IApplicationToken;
import android.view.IWindow;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
import android.view.WindowManager;
import static android.app.AppOpsManager.OP_NONE;
@@ -113,6 +115,7 @@ public class WindowTestUtils {
/** Used so we can gain access to some protected members of the {@link AppWindowToken} class. */
public static class TestAppWindowToken extends AppWindowToken {
boolean mOnTop = false;
+ private Transaction mPendingTransactionOverride;
private TestAppWindowToken(DisplayContent dc) {
super(dc.mService, new IApplicationToken.Stub() {
@@ -158,6 +161,17 @@ public class WindowTestUtils {
boolean isOnTop() {
return mOnTop;
}
+
+ void setPendingTransaction(Transaction transaction) {
+ mPendingTransactionOverride = transaction;
+ }
+
+ @Override
+ public Transaction getPendingTransaction() {
+ return mPendingTransactionOverride == null
+ ? super.getPendingTransaction()
+ : mPendingTransactionOverride;
+ }
}
static TestWindowToken createTestWindowToken(int type, DisplayContent dc) {
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index 473a287e3d9c..ef019fe5ee27 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -231,6 +231,11 @@ class WindowTestsBase {
}
AppWindowToken createAppWindowToken(DisplayContent dc, int windowingMode, int activityType) {
+ return createTestAppWindowToken(dc, windowingMode, activityType);
+ }
+
+ WindowTestUtils.TestAppWindowToken createTestAppWindowToken(DisplayContent dc, int
+ windowingMode, int activityType) {
final TaskStack stack = createStackControllerOnStackOnDisplay(windowingMode, activityType,
dc).mContainer;
final Task task = createTaskInStack(stack, 0 /* userId */);
diff --git a/tests/Internal/res/xml/livewallpaper.xml b/tests/Internal/res/xml/livewallpaper.xml
index dbb0e4761cba..6b5e84e8f9ad 100644
--- a/tests/Internal/res/xml/livewallpaper.xml
+++ b/tests/Internal/res/xml/livewallpaper.xml
@@ -16,5 +16,4 @@
-->
<wallpaper
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- androidprv:supportsAmbientMode="true"/> \ No newline at end of file
+ android:supportsAmbientMode="true"/> \ No newline at end of file
diff --git a/tests/Internal/src/android/app/WallpaperInfoTest.java b/tests/Internal/src/android/app/WallpaperInfoTest.java
index 9d26270fb96a..98045ae98541 100644
--- a/tests/Internal/src/android/app/WallpaperInfoTest.java
+++ b/tests/Internal/src/android/app/WallpaperInfoTest.java
@@ -55,13 +55,13 @@ public class WallpaperInfoTest {
// Defined as true in the XML
assertTrue("supportsAmbientMode should be true, as defined in the XML.",
- wallpaperInfo.getSupportsAmbientMode());
+ wallpaperInfo.supportsAmbientMode());
Parcel parcel = Parcel.obtain();
wallpaperInfo.writeToParcel(parcel, 0 /* flags */);
parcel.setDataPosition(0);
WallpaperInfo fromParcel = WallpaperInfo.CREATOR.createFromParcel(parcel);
assertTrue("supportsAmbientMode should have been restored from parcelable",
- fromParcel.getSupportsAmbientMode());
+ fromParcel.supportsAmbientMode());
parcel.recycle();
}
}
diff --git a/tests/SystemMemoryTest/Android.mk b/tests/SystemMemoryTest/Android.mk
new file mode 100644
index 000000000000..09a1618c6ad1
--- /dev/null
+++ b/tests/SystemMemoryTest/Android.mk
@@ -0,0 +1,17 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(call all-subdir-makefiles)
diff --git a/tests/SystemMemoryTest/README.txt b/tests/SystemMemoryTest/README.txt
new file mode 100644
index 000000000000..de5042cf3732
--- /dev/null
+++ b/tests/SystemMemoryTest/README.txt
@@ -0,0 +1,21 @@
+This directory contains a test for system server memory use.
+
+Directory structure
+===================
+device
+ - those parts of the test that run on device.
+
+host
+ - those parts of the test that run on host.
+
+Running the test
+================
+
+You can manually run the test as follows:
+
+ make tradefed-all system-memory-test SystemMemoryTestDevice
+ tradefed.sh run commandAndExit template/local_min --template:map test=system-memory-test
+
+This installs and runs the test on device. You can see the metrics in the
+tradefed output.
+
diff --git a/tests/SystemMemoryTest/device/Android.mk b/tests/SystemMemoryTest/device/Android.mk
new file mode 100644
index 000000000000..75408df4b295
--- /dev/null
+++ b/tests/SystemMemoryTest/device/Android.mk
@@ -0,0 +1,24 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_PACKAGE_NAME := SystemMemoryTestDevice
+LOCAL_SDK_VERSION := current
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_COMPATIBILITY_SUITE := general-tests
+include $(BUILD_PACKAGE)
diff --git a/tests/SystemMemoryTest/device/AndroidManifest.xml b/tests/SystemMemoryTest/device/AndroidManifest.xml
new file mode 100644
index 000000000000..e5e7844f9660
--- /dev/null
+++ b/tests/SystemMemoryTest/device/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.tests.sysmem.device">
+
+ <uses-sdk android:minSdkVersion="19" />
+
+ <instrumentation
+ android:name="com.android.tests.sysmem.device.Cujs"
+ android:targetPackage="com.android.tests.sysmem.device" />
+
+ <application
+ android:allowBackup="false"
+ android:label="System Memory Test">
+ </application>
+</manifest>
diff --git a/tests/SystemMemoryTest/device/src/com/android/tests/sysmem/device/Cujs.java b/tests/SystemMemoryTest/device/src/com/android/tests/sysmem/device/Cujs.java
new file mode 100644
index 000000000000..6c0c5931ed8c
--- /dev/null
+++ b/tests/SystemMemoryTest/device/src/com/android/tests/sysmem/device/Cujs.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.tests.sysmem.device;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.os.Bundle;
+import android.util.Log;
+
+/**
+ * Critical user journeys used to exercise the system for test, driven from
+ * the device.
+ */
+public class Cujs extends Instrumentation {
+
+ private static final String TAG = "SystemMemoryTest";
+
+ @Override
+ public void onCreate(Bundle arguments) {
+ start();
+ }
+
+ @Override
+ public void onStart() {
+ // TODO: Exercise the system in more interesting ways.
+ // Mostly what matters is that whatever we do here is sustainable: it
+ // can be repeated indefinitely on the system without causing
+ // problems. For example, launching activities is fine. Installing
+ // applications is only fine if we also uninstall them as part of the
+ // test.
+ try {
+ Log.i(TAG, "Sleeping for 10 seconds...");
+ Thread.sleep(10 * 1000);
+ } catch (InterruptedException ignored) {
+ }
+
+ finish(Activity.RESULT_OK, null);
+ }
+}
diff --git a/tests/SystemMemoryTest/host/Android.mk b/tests/SystemMemoryTest/host/Android.mk
new file mode 100644
index 000000000000..a516e38adfec
--- /dev/null
+++ b/tests/SystemMemoryTest/host/Android.mk
@@ -0,0 +1,23 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_MODULE := system-memory-test
+LOCAL_MODULE_TAGS := optional
+LOCAL_JAVA_LIBRARIES := tradefed
+LOCAL_COMPATIBILITY_SUITE := general-tests
+include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/tests/SystemMemoryTest/host/AndroidTest.xml b/tests/SystemMemoryTest/host/AndroidTest.xml
new file mode 100644
index 000000000000..6d2c95f3094a
--- /dev/null
+++ b/tests/SystemMemoryTest/host/AndroidTest.xml
@@ -0,0 +1,25 @@
+<?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 the system memory tests">
+ <option name="test-suite-tag" value="system-memory-test" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="SystemMemoryTestDevice.apk" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.HostTest" >
+ <option name="class" value="com.android.tests.sysmem.host.MemoryTest" />
+ </test>
+</configuration>
diff --git a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Cujs.java b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Cujs.java
new file mode 100644
index 000000000000..579d9723977f
--- /dev/null
+++ b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Cujs.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.tests.sysmem.host;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+
+/**
+ * Critical user journeys with which to exercise the system, driven from the
+ * host.
+ */
+public class Cujs {
+ private ITestDevice device;
+
+ public Cujs(ITestDevice device) {
+ this.device = device;
+ }
+
+ /**
+ * Runs the critical user journeys.
+ */
+ public void run() throws DeviceNotAvailableException {
+ // Invoke the Device Cujs instrumentation to run the cujs.
+ // TODO: Consider exercising the system in other interesting ways as
+ // well.
+ String command = "am instrument -w com.android.tests.sysmem.device/.Cujs";
+ device.executeShellCommand(command);
+ }
+}
diff --git a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/MemoryTest.java b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/MemoryTest.java
new file mode 100644
index 000000000000..bbec0654b7ea
--- /dev/null
+++ b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/MemoryTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.tests.sysmem.host;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestLogData;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestMetrics;
+import com.android.tradefed.testtype.IDeviceTest;
+import java.io.IOException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class MemoryTest implements IDeviceTest {
+
+ @Rule public TestMetrics testMetrics = new TestMetrics();
+ @Rule public TestLogData testLogs = new TestLogData();
+
+ private ITestDevice testDevice;
+ private int iterations = 0; // Number of times cujs have been run.
+ private Metrics metrics;
+ private Cujs cujs;
+
+ @Override
+ public void setDevice(ITestDevice device) {
+ testDevice = device;
+ metrics = new Metrics(device, testMetrics, testLogs);
+ cujs = new Cujs(device);
+ }
+
+ @Override
+ public ITestDevice getDevice() {
+ return testDevice;
+ }
+
+ // Invoke a single iteration of running the cujs.
+ private void runCujs() throws DeviceNotAvailableException {
+ cujs.run();
+ iterations++;
+ }
+
+ // Sample desired memory.
+ private void sample()
+ throws DeviceNotAvailableException, IOException, Metrics.MetricsException {
+ metrics.sample(String.format("%03d", iterations));
+ }
+
+ @Test
+ public void run() throws Exception {
+ sample(); // Sample before running cujs
+ runCujs();
+ sample(); // Sample after first iteration of cujs
+
+ // Run cujs in a loop to highlight memory leaks.
+ for (int i = 0; i < 5; ++i) {
+ for (int j = 0; j < 5; j++) {
+ runCujs();
+ }
+ sample();
+ }
+ }
+}
diff --git a/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Metrics.java b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Metrics.java
new file mode 100644
index 000000000000..7de092a973ad
--- /dev/null
+++ b/tests/SystemMemoryTest/host/src/com/android/tests/sysmem/host/Metrics.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.tests.sysmem.host;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.result.FileInputStreamSource;
+import com.android.tradefed.result.LogDataType;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestLogData;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestMetrics;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.InputMismatchException;
+import java.util.Scanner;
+
+/**
+ * Utilities for sampling and reporting memory metrics.
+ */
+class Metrics {
+
+ private ITestDevice device;
+ private TestMetrics metrics;
+ private TestLogData logs;
+
+ /**
+ * Exception thrown in case of error sampling metrics.
+ */
+ public static class MetricsException extends Exception {
+ public MetricsException(String msg) {
+ super(msg);
+ }
+
+ MetricsException(String msg, Exception cause) {
+ super(msg, cause);
+ }
+ }
+
+ /**
+ * Constructs a metrics instance that will output high level metrics and
+ * more detailed breakdowns using the given <code>metrics</code> and
+ * <code>logs</code> objects.
+ *
+ * @param device the device to sample metrics from
+ * @param metrics where to log the high level metrics when taking a sample
+ * @param logs where to log detailed breakdowns when taking a sample
+ */
+ public Metrics(ITestDevice device, TestMetrics metrics, TestLogData logs) {
+ this.device = device;
+ this.metrics = metrics;
+ this.logs = logs;
+ }
+
+ /**
+ * Returns the pid for the process with the given name.
+ */
+ private int getPidForProcess(String name)
+ throws DeviceNotAvailableException, IOException, MetricsException {
+ String psout = device.executeShellCommand("ps -A -o PID,CMD");
+ Scanner sc = new Scanner(psout);
+ try {
+ // ps output is of the form:
+ // PID CMD
+ // 1 init
+ // 2 kthreadd
+ // ...
+ // 9693 ps
+ sc.nextLine();
+ while (sc.hasNextLine()) {
+ int pid = sc.nextInt();
+ String cmd = sc.next();
+
+ if (name.equals(cmd)) {
+ return pid;
+ }
+ }
+ } catch (InputMismatchException e) {
+ throw new MetricsException("unexpected ps output format: " + psout, e);
+ }
+
+ throw new MetricsException("failed to get pid for process " + name);
+ }
+
+ /**
+ * Samples the current memory use on the system. Outputs high level test
+ * metrics and detailed breakdowns to the TestMetrics and TestLogData
+ * objects provided when constructing this Metrics instance. The metrics
+ * and log names are prefixed with the given label.
+ *
+ * @param label prefix to use for metrics and logs output for this sample.
+ */
+ void sample(String label) throws DeviceNotAvailableException, IOException, MetricsException {
+ // adb root access is required to get showmap
+ device.enableAdbRoot();
+
+ int pid = getPidForProcess("system_server");
+
+ // Read showmap for system server and add it as a test log
+ String showmap = device.executeShellCommand("showmap " + pid);
+ String showmapLabel = label + ".system_server.showmap";
+ File file = File.createTempFile(showmapLabel, "txt");
+ PrintStream ps = new PrintStream(file);
+ ps.print(showmap);
+ try (FileInputStreamSource dataStream = new FileInputStreamSource(file)) {
+ logs.addTestLog(showmapLabel, LogDataType.TEXT, dataStream);
+ }
+
+ // Extract VSS, PSS and RSS from the showmap and output them as metrics.
+ // The last lines of the showmap output looks something like:
+ // virtual shared shared private private
+ // size RSS PSS clean dirty clean dirty swap swapPSS # object
+ //-------- -------- -------- -------- -------- -------- -------- -------- -------- ---- ------------------------------
+ // 928480 113016 24860 87348 7916 3632 14120 1968 1968 1900 TOTAL
+ try {
+ int pos = showmap.lastIndexOf("----");
+ Scanner sc = new Scanner(showmap.substring(pos));
+ sc.next();
+ long vss = sc.nextLong();
+ long rss = sc.nextLong();
+ long pss = sc.nextLong();
+
+ metrics.addTestMetric(String.format("%s.system_server.vss", label), Long.toString(vss));
+ metrics.addTestMetric(String.format("%s.system_server.rss", label), Long.toString(rss));
+ metrics.addTestMetric(String.format("%s.system_server.pss", label), Long.toString(pss));
+ } catch (InputMismatchException e) {
+ throw new MetricsException("unexpected showmap format", e);
+ }
+
+ // TODO: Experiment with other additional metrics.
+
+ // TODO: Consider launching an instrumentation to collect metrics from
+ // within the device itself.
+ }
+}
diff --git a/tests/net/java/android/net/ip/IpClientTest.java b/tests/net/java/android/net/ip/IpClientTest.java
index 89453e0b13b7..5a8d2cd0c5a2 100644
--- a/tests/net/java/android/net/ip/IpClientTest.java
+++ b/tests/net/java/android/net/ip/IpClientTest.java
@@ -32,7 +32,6 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.AlarmManager;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.net.INetd;
@@ -62,8 +61,6 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import java.net.Inet4Address;
-import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.Arrays;
import java.util.List;
@@ -84,6 +81,7 @@ public class IpClientTest {
private static final int TEST_IFINDEX = 1001;
// See RFC 7042#section-2.1.2 for EUI-48 documentation values.
private static final MacAddress TEST_MAC = MacAddress.fromString("00:00:5E:00:53:01");
+ private static final int TEST_TIMEOUT_MS = 200;
@Mock private Context mContext;
@Mock private INetworkManagementService mNMService;
@@ -126,8 +124,8 @@ public class IpClientTest {
private IpClient makeIpClient(String ifname) throws Exception {
setTestInterfaceParams(ifname);
final IpClient ipc = new IpClient(mContext, ifname, mCb, mDependecies);
- verify(mNMService, timeout(100).times(1)).disableIpv6(ifname);
- verify(mNMService, timeout(100).times(1)).clearInterfaceAddresses(ifname);
+ verify(mNMService, timeout(TEST_TIMEOUT_MS).times(1)).disableIpv6(ifname);
+ verify(mNMService, timeout(TEST_TIMEOUT_MS).times(1)).clearInterfaceAddresses(ifname);
ArgumentCaptor<BaseNetworkObserver> arg =
ArgumentCaptor.forClass(BaseNetworkObserver.class);
verify(mNMService, times(1)).registerObserver(arg.capture());
@@ -200,13 +198,13 @@ public class IpClientTest {
ipc.startProvisioning(config);
verify(mCb, times(1)).setNeighborDiscoveryOffload(true);
- verify(mCb, timeout(100).times(1)).setFallbackMulticastFilter(false);
+ verify(mCb, timeout(TEST_TIMEOUT_MS).times(1)).setFallbackMulticastFilter(false);
verify(mCb, never()).onProvisioningFailure(any());
ipc.shutdown();
- verify(mNMService, timeout(100).times(1)).disableIpv6(iface);
- verify(mNMService, timeout(100).times(1)).clearInterfaceAddresses(iface);
- verify(mCb, timeout(100).times(1))
+ verify(mNMService, timeout(TEST_TIMEOUT_MS).times(1)).disableIpv6(iface);
+ verify(mNMService, timeout(TEST_TIMEOUT_MS).times(1)).clearInterfaceAddresses(iface);
+ verify(mCb, timeout(TEST_TIMEOUT_MS).times(1))
.onLinkPropertiesChange(eq(makeEmptyLinkProperties(iface)));
}
@@ -230,12 +228,12 @@ public class IpClientTest {
ipc.startProvisioning(config);
verify(mCb, times(1)).setNeighborDiscoveryOffload(true);
- verify(mCb, timeout(100).times(1)).setFallbackMulticastFilter(false);
+ verify(mCb, timeout(TEST_TIMEOUT_MS).times(1)).setFallbackMulticastFilter(false);
verify(mCb, never()).onProvisioningFailure(any());
for (String addr : addresses) {
String[] parts = addr.split("/");
- verify(mNetd, timeout(100).times(1))
+ verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1))
.interfaceAddAddress(iface, parts[0], Integer.parseInt(parts[1]));
}
@@ -244,7 +242,7 @@ public class IpClientTest {
// Add N - 1 addresses
for (int i = 0; i < lastAddr; i++) {
mObserver.addressUpdated(iface, new LinkAddress(addresses[i]));
- verify(mCb, timeout(100)).onLinkPropertiesChange(any());
+ verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(any());
reset(mCb);
}
@@ -252,12 +250,12 @@ public class IpClientTest {
mObserver.addressUpdated(iface, new LinkAddress(addresses[lastAddr]));
LinkProperties want = linkproperties(links(addresses), routes(prefixes));
want.setInterfaceName(iface);
- verify(mCb, timeout(100).times(1)).onProvisioningSuccess(eq(want));
+ verify(mCb, timeout(TEST_TIMEOUT_MS).times(1)).onProvisioningSuccess(eq(want));
ipc.shutdown();
- verify(mNMService, timeout(100).times(1)).disableIpv6(iface);
- verify(mNMService, timeout(100).times(1)).clearInterfaceAddresses(iface);
- verify(mCb, timeout(100).times(1))
+ verify(mNMService, timeout(TEST_TIMEOUT_MS).times(1)).disableIpv6(iface);
+ verify(mNMService, timeout(TEST_TIMEOUT_MS).times(1)).clearInterfaceAddresses(iface);
+ verify(mCb, timeout(TEST_TIMEOUT_MS).times(1))
.onLinkPropertiesChange(eq(makeEmptyLinkProperties(iface)));
}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 002953b714a2..142c88b2f67d 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -198,6 +198,9 @@ public class ConnectivityServiceTest {
// (mService.mLingerDelayMs - TEST_CALLBACK_TIMEOUT_MS) between the time callbacks are
// supposedly fired, and the time we call expectCallback.
private final static int TEST_CALLBACK_TIMEOUT_MS = 200;
+ // Chosen to be less than TEST_CALLBACK_TIMEOUT_MS. This ensures that requests have time to
+ // complete before callbacks are verified.
+ private final static int TEST_REQUEST_TIMEOUT_MS = 150;
private static final String CLAT_PREFIX = "v4-";
private static final String MOBILE_IFNAME = "test_rmnet_data0";
@@ -3212,12 +3215,12 @@ public class ConnectivityServiceTest {
NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
NetworkCapabilities.TRANSPORT_WIFI).build();
final TestNetworkCallback networkCallback = new TestNetworkCallback();
- final int timeoutMs = 150;
- mCm.requestNetwork(nr, networkCallback, timeoutMs);
+ mCm.requestNetwork(nr, networkCallback, TEST_REQUEST_TIMEOUT_MS);
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
- networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, timeoutMs);
+ networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false,
+ TEST_CALLBACK_TIMEOUT_MS);
// pass timeout and validate that UNAVAILABLE is not called
networkCallback.assertNoCallback();
@@ -3232,13 +3235,12 @@ public class ConnectivityServiceTest {
NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
NetworkCapabilities.TRANSPORT_WIFI).build();
final TestNetworkCallback networkCallback = new TestNetworkCallback();
- final int requestTimeoutMs = 50;
- mCm.requestNetwork(nr, networkCallback, requestTimeoutMs);
+ mCm.requestNetwork(nr, networkCallback, TEST_REQUEST_TIMEOUT_MS);
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
- final int assertTimeoutMs = 100;
- networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false, assertTimeoutMs);
+ networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, false,
+ TEST_CALLBACK_TIMEOUT_MS);
mWiFiNetworkAgent.disconnect();
networkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
diff --git a/wifi/java/android/net/wifi/ITrafficStateCallback.aidl b/wifi/java/android/net/wifi/ITrafficStateCallback.aidl
new file mode 100644
index 000000000000..0c8e777f67ba
--- /dev/null
+++ b/wifi/java/android/net/wifi/ITrafficStateCallback.aidl
@@ -0,0 +1,34 @@
+/*
+ * 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.net.wifi;
+
+/**
+ * Interface for Traffic state callback.
+ *
+ * @hide
+ */
+oneway interface ITrafficStateCallback
+{
+ /**
+ * Callback invoked to inform clients about the current traffic state.
+ *
+ * @param state One of the values: {@link #DATA_ACTIVITY_NONE}, {@link #DATA_ACTIVITY_IN},
+ * {@link #DATA_ACTIVITY_OUT} & {@link #DATA_ACTIVITY_INOUT}.
+ * @hide
+ */
+ void onStateChanged(int state);
+}
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index af44b7e6cd16..5631919038a2 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -26,6 +26,7 @@ import android.net.wifi.hotspot2.IProvisioningCallback;
import android.net.DhcpInfo;
import android.net.Network;
import android.net.wifi.ISoftApCallback;
+import android.net.wifi.ITrafficStateCallback;
import android.net.wifi.PasspointManagementObjectDefinition;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiActivityEnergyInfo;
@@ -180,5 +181,9 @@ interface IWifiManager
void registerSoftApCallback(in IBinder binder, in ISoftApCallback callback, int callbackIdentifier);
void unregisterSoftApCallback(int callbackIdentifier);
+
+ void registerTrafficStateCallback(in IBinder binder, in ITrafficStateCallback callback, int callbackIdentifier);
+
+ void unregisterTrafficStateCallback(int callbackIdentifier);
}
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 922e02529962..43f0e50f5ae1 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -21,7 +21,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
-import android.annotation.SuppressLint;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
import android.annotation.SystemService;
@@ -32,9 +31,9 @@ import android.net.DhcpInfo;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
+import android.net.wifi.hotspot2.IProvisioningCallback;
import android.net.wifi.hotspot2.OsuProvider;
import android.net.wifi.hotspot2.PasspointConfiguration;
-import android.net.wifi.hotspot2.IProvisioningCallback;
import android.net.wifi.hotspot2.ProvisioningCallback;
import android.os.Binder;
import android.os.Build;
@@ -920,22 +919,6 @@ public class WifiManager {
*/
public static final int WIFI_FREQUENCY_BAND_2GHZ = 2;
- /** List of asyncronous notifications
- * @hide
- */
- public static final int DATA_ACTIVITY_NOTIFICATION = 1;
-
- //Lowest bit indicates data reception and the second lowest
- //bit indicates data transmitted
- /** @hide */
- public static final int DATA_ACTIVITY_NONE = 0x00;
- /** @hide */
- public static final int DATA_ACTIVITY_IN = 0x01;
- /** @hide */
- public static final int DATA_ACTIVITY_OUT = 0x02;
- /** @hide */
- public static final int DATA_ACTIVITY_INOUT = 0x03;
-
/** @hide */
public static final boolean DEFAULT_POOR_NETWORK_AVOIDANCE_ENABLED = false;
@@ -2460,7 +2443,7 @@ public class WifiManager {
}
@Override
- public void onStateChanged(int state, int failureReason) throws RemoteException {
+ public void onStateChanged(int state, int failureReason) {
Log.v(TAG, "SoftApCallbackProxy: onStateChanged: state=" + state + ", failureReason=" +
failureReason);
mHandler.post(() -> {
@@ -2469,7 +2452,7 @@ public class WifiManager {
}
@Override
- public void onNumClientsChanged(int numClients) throws RemoteException {
+ public void onNumClientsChanged(int numClients) {
Log.v(TAG, "SoftApCallbackProxy: onNumClientsChanged: numClients=" + numClients);
mHandler.post(() -> {
mCallback.onNumClientsChanged(numClients);
@@ -3102,9 +3085,8 @@ public class WifiManager {
* an AsyncChannel communication with WifiService
*
* @return Messenger pointing to the WifiService handler
- * @hide
*/
- public Messenger getWifiServiceMessenger() {
+ private Messenger getWifiServiceMessenger() {
try {
return mService.getWifiServiceMessenger(mContext.getOpPackageName());
} catch (RemoteException e) {
@@ -3718,4 +3700,107 @@ public class WifiManager {
});
}
}
+
+ /**
+ * Base class for Traffic state callback. Should be extended by applications and set when
+ * calling {@link WifiManager#registerTrafficStateCallback(TrafficStateCallback, Handler)}.
+ * @hide
+ */
+ public interface TrafficStateCallback {
+ /**
+ * Lowest bit indicates data reception and the second lowest
+ * bit indicates data transmitted
+ */
+ /** @hide */
+ int DATA_ACTIVITY_NONE = 0x00;
+ /** @hide */
+ int DATA_ACTIVITY_IN = 0x01;
+ /** @hide */
+ int DATA_ACTIVITY_OUT = 0x02;
+ /** @hide */
+ int DATA_ACTIVITY_INOUT = 0x03;
+
+ /**
+ * Callback invoked to inform clients about the current traffic state.
+ *
+ * @param state One of the values: {@link #DATA_ACTIVITY_NONE}, {@link #DATA_ACTIVITY_IN},
+ * {@link #DATA_ACTIVITY_OUT} & {@link #DATA_ACTIVITY_INOUT}.
+ * @hide
+ */
+ void onStateChanged(int state);
+ }
+
+ /**
+ * Callback proxy for TrafficStateCallback objects.
+ *
+ * @hide
+ */
+ private static class TrafficStateCallbackProxy extends ITrafficStateCallback.Stub {
+ private final Handler mHandler;
+ private final TrafficStateCallback mCallback;
+
+ TrafficStateCallbackProxy(Looper looper, TrafficStateCallback callback) {
+ mHandler = new Handler(looper);
+ mCallback = callback;
+ }
+
+ @Override
+ public void onStateChanged(int state) {
+ Log.v(TAG, "TrafficStateCallbackProxy: onStateChanged state=" + state);
+ mHandler.post(() -> {
+ mCallback.onStateChanged(state);
+ });
+ }
+ }
+
+ /**
+ * Registers a callback for monitoring traffic state. See {@link TrafficStateCallback}. These
+ * callbacks will be invoked periodically by platform to inform clients about the current
+ * traffic state. Caller can unregister a previously registered callback using
+ * {@link #unregisterTrafficStateCallback(TrafficStateCallback)}
+ * <p>
+ * Applications should have the
+ * {@link android.Manifest.permission#NETWORK_SETTINGS NETWORK_SETTINGS} permission. Callers
+ * without the permission will trigger a {@link java.lang.SecurityException}.
+ * <p>
+ *
+ * @param callback Callback for traffic state events
+ * @param handler The Handler on whose thread to execute the callbacks of the {@code callback}
+ * object. If null, then the application's main thread will be used.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+ public void registerTrafficStateCallback(@NonNull TrafficStateCallback callback,
+ @Nullable Handler handler) {
+ if (callback == null) throw new IllegalArgumentException("callback cannot be null");
+ Log.v(TAG, "registerTrafficStateCallback: callback=" + callback + ", handler=" + handler);
+
+ Looper looper = (handler == null) ? mContext.getMainLooper() : handler.getLooper();
+ Binder binder = new Binder();
+ try {
+ mService.registerTrafficStateCallback(
+ binder, new TrafficStateCallbackProxy(looper, callback), callback.hashCode());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Allow callers to unregister a previously registered callback. After calling this method,
+ * applications will no longer receive traffic state notifications.
+ *
+ * @param callback Callback to unregister for traffic state events
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
+ public void unregisterTrafficStateCallback(@NonNull TrafficStateCallback callback) {
+ if (callback == null) throw new IllegalArgumentException("callback cannot be null");
+ Log.v(TAG, "unregisterTrafficStateCallback: callback=" + callback);
+
+ try {
+ mService.unregisterTrafficStateCallback(callback.hashCode());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index 20e49cd9e6a2..50580800c271 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -25,9 +25,6 @@ import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_NO_CHA
import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_TETHERING_DISALLOWED;
import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.REQUEST_REGISTERED;
import static android.net.wifi.WifiManager.SAP_START_FAILURE_GENERAL;
-import static android.net.wifi.WifiManager.SAP_START_FAILURE_NO_CHANNEL;
-import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
-import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLING;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;
@@ -47,6 +44,7 @@ import android.net.wifi.WifiManager.LocalOnlyHotspotObserver;
import android.net.wifi.WifiManager.LocalOnlyHotspotReservation;
import android.net.wifi.WifiManager.LocalOnlyHotspotSubscription;
import android.net.wifi.WifiManager.SoftApCallback;
+import android.net.wifi.WifiManager.TrafficStateCallback;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
@@ -57,6 +55,7 @@ import android.support.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -77,6 +76,7 @@ public class WifiManagerTest {
@Mock WifiConfiguration mApConfig;
@Mock IBinder mAppBinder;
@Mock SoftApCallback mSoftApCallback;
+ @Mock TrafficStateCallback mTrafficStateCallback;
private Handler mHandler;
private TestLooper mLooper;
@@ -702,7 +702,7 @@ public class WifiManagerTest {
}
/*
- * Verify client provided callback is being called through callback proxy
+ * Verify client-provided callback is being called through callback proxy
*/
@Test
public void softApCallbackProxyCallsOnStateChanged() throws Exception {
@@ -718,7 +718,7 @@ public class WifiManagerTest {
}
/*
- * Verify client provided callback is being called through callback proxy
+ * Verify client-provided callback is being called through callback proxy
*/
@Test
public void softApCallbackProxyCallsOnNumClientsChanged() throws Exception {
@@ -735,7 +735,7 @@ public class WifiManagerTest {
}
/*
- * Verify client provided callback is being called through callback proxy on multiple events
+ * Verify client-provided callback is being called through callback proxy on multiple events
*/
@Test
public void softApCallbackProxyCallsOnMultipleUpdates() throws Exception {
@@ -757,7 +757,7 @@ public class WifiManagerTest {
}
/*
- * Verify client provided callback is being called on the correct thread
+ * Verify client-provided callback is being called on the correct thread
*/
@Test
public void softApCallbackIsCalledOnCorrectThread() throws Exception {
@@ -1082,4 +1082,84 @@ i * Verify that a call to cancel WPS immediately returns a failure.
when(mWifiService.startScan(TEST_PACKAGE_NAME)).thenReturn(false);
assertFalse(mWifiManager.startScan());
}
+
+ /**
+ * Verify main looper is used when handler is not provided.
+ */
+ @Test
+ public void registerTrafficStateCallbackUsesMainLooperOnNullArgumentForHandler()
+ throws Exception {
+ when(mContext.getMainLooper()).thenReturn(mLooper.getLooper());
+ ArgumentCaptor<ITrafficStateCallback.Stub> callbackCaptor =
+ ArgumentCaptor.forClass(ITrafficStateCallback.Stub.class);
+ mWifiManager.registerTrafficStateCallback(mTrafficStateCallback, null);
+ verify(mWifiService).registerTrafficStateCallback(
+ any(IBinder.class), callbackCaptor.capture(), anyInt());
+
+ assertEquals(0, mLooper.dispatchAll());
+ callbackCaptor.getValue().onStateChanged(TrafficStateCallback.DATA_ACTIVITY_INOUT);
+ assertEquals(1, mLooper.dispatchAll());
+ verify(mTrafficStateCallback).onStateChanged(TrafficStateCallback.DATA_ACTIVITY_INOUT);
+ }
+
+ /**
+ * Verify the call to unregisterTrafficStateCallback goes to WifiServiceImpl.
+ */
+ @Test
+ public void unregisterTrafficStateCallbackCallGoesToWifiServiceImpl() throws Exception {
+ ArgumentCaptor<Integer> callbackIdentifier = ArgumentCaptor.forClass(Integer.class);
+ mWifiManager.registerTrafficStateCallback(mTrafficStateCallback, mHandler);
+ verify(mWifiService).registerTrafficStateCallback(any(IBinder.class),
+ any(ITrafficStateCallback.Stub.class), callbackIdentifier.capture());
+
+ mWifiManager.unregisterTrafficStateCallback(mTrafficStateCallback);
+ verify(mWifiService).unregisterTrafficStateCallback(
+ eq((int) callbackIdentifier.getValue()));
+ }
+
+ /*
+ * Verify client-provided callback is being called through callback proxy on multiple events
+ */
+ @Test
+ public void trafficStateCallbackProxyCallsOnMultipleUpdates() throws Exception {
+ ArgumentCaptor<ITrafficStateCallback.Stub> callbackCaptor =
+ ArgumentCaptor.forClass(ITrafficStateCallback.Stub.class);
+ mWifiManager.registerTrafficStateCallback(mTrafficStateCallback, mHandler);
+ verify(mWifiService).registerTrafficStateCallback(
+ any(IBinder.class), callbackCaptor.capture(), anyInt());
+
+ InOrder inOrder = inOrder(mTrafficStateCallback);
+
+ callbackCaptor.getValue().onStateChanged(TrafficStateCallback.DATA_ACTIVITY_IN);
+ callbackCaptor.getValue().onStateChanged(TrafficStateCallback.DATA_ACTIVITY_INOUT);
+ callbackCaptor.getValue().onStateChanged(TrafficStateCallback.DATA_ACTIVITY_OUT);
+
+ mLooper.dispatchAll();
+ inOrder.verify(mTrafficStateCallback).onStateChanged(
+ TrafficStateCallback.DATA_ACTIVITY_IN);
+ inOrder.verify(mTrafficStateCallback).onStateChanged(
+ TrafficStateCallback.DATA_ACTIVITY_INOUT);
+ inOrder.verify(mTrafficStateCallback).onStateChanged(
+ TrafficStateCallback.DATA_ACTIVITY_OUT);
+ }
+
+ /*
+ * Verify client-provided callback is being called on the correct thread
+ */
+ @Test
+ public void trafficStateCallbackIsCalledOnCorrectThread() throws Exception {
+ ArgumentCaptor<ITrafficStateCallback.Stub> callbackCaptor =
+ ArgumentCaptor.forClass(ITrafficStateCallback.Stub.class);
+ TestLooper altLooper = new TestLooper();
+ Handler altHandler = new Handler(altLooper.getLooper());
+ mWifiManager.registerTrafficStateCallback(mTrafficStateCallback, altHandler);
+ verify(mContext, never()).getMainLooper();
+ verify(mWifiService).registerTrafficStateCallback(
+ any(IBinder.class), callbackCaptor.capture(), anyInt());
+
+ assertEquals(0, altLooper.dispatchAll());
+ callbackCaptor.getValue().onStateChanged(TrafficStateCallback.DATA_ACTIVITY_INOUT);
+ assertEquals(1, altLooper.dispatchAll());
+ verify(mTrafficStateCallback).onStateChanged(TrafficStateCallback.DATA_ACTIVITY_INOUT);
+ }
}