diff options
982 files changed, 23947 insertions, 11912 deletions
diff --git a/Android.bp b/Android.bp index 628894016f39..4b22f9e6670e 100644 --- a/Android.bp +++ b/Android.bp @@ -845,6 +845,7 @@ java_library_host { // specified on the build command line. java_library { name: "framework-atb-backward-compatibility", + installable: true, srcs: [ "core/java/android/content/pm/AndroidTestBaseUpdater.java", ], @@ -1265,10 +1266,7 @@ stubs_defaults { "test-base/src/**/*.java", ":opt-telephony-srcs", ":opt-net-voip-srcs", - ":openjdk_javadoc_files", - ":non_openjdk_javadoc_files", - ":android_icu4j_src_files_for_docs", - ":conscrypt_public_api_files", + ":core_public_api_files", ":updatable-media-srcs-without-aidls", "test-mock/src/**/*.java", "test-runner/src/**/*.java", @@ -1327,10 +1325,7 @@ stubs_defaults { srcs: [ ":opt-telephony-srcs", ":opt-net-voip-srcs", - ":openjdk_javadoc_files", - ":non_openjdk_javadoc_files", - ":android_icu4j_src_files_for_docs", - ":conscrypt_public_api_files", + ":core_public_api_files", ":updatable-media-srcs-without-aidls", ], srcs_lib: "framework", @@ -1514,7 +1509,7 @@ droiddoc { ], proofread_file: "ds-docs-proofrerad.txt", args: framework_docs_only_args + - " -toroot / -samplegroup Admin " + + " -toroot / -yamlV2 -metalavaApiSince -samplegroup Admin " + " -samplegroup Background " + " -samplegroup Connectivity " + " -samplegroup Content " + diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java index e7fe235e0ea8..e805ab912fc1 100644 --- a/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java +++ b/apct-tests/perftests/core/src/android/graphics/perftests/RenderNodePerfTest.java @@ -71,7 +71,7 @@ public class RenderNodePerfTest { final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); RenderNode node = RenderNode.create("LinearLayout", null); while (state.keepRunning()) { - node.startRecording(100, 100); + node.beginRecording(100, 100); node.endRecording(); } } @@ -86,7 +86,7 @@ public class RenderNodePerfTest { while (state.keepRunning()) { for (int i = 0; i < nodes.length; i++) { - nodes[i].startRecording(100, 100); + nodes[i].beginRecording(100, 100); } for (int i = nodes.length - 1; i >= 0; i--) { nodes[i].endRecording(); diff --git a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java index bd7522d0359e..8a6c60f44702 100644 --- a/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java +++ b/apct-tests/perftests/core/src/android/text/StaticLayoutPerfTest.java @@ -265,7 +265,7 @@ public class StaticLayoutPerfTest { state.pauseTiming(); final StaticLayout layout = StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); - final RecordingCanvas c = node.startRecording(1200, 200); + final RecordingCanvas c = node.beginRecording(1200, 200); state.resumeTiming(); layout.draw(c); @@ -282,7 +282,7 @@ public class StaticLayoutPerfTest { final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT); final StaticLayout layout = StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); - final RecordingCanvas c = node.startRecording(1200, 200); + final RecordingCanvas c = node.beginRecording(1200, 200); state.resumeTiming(); layout.draw(c); @@ -299,7 +299,7 @@ public class StaticLayoutPerfTest { final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); final StaticLayout layout = StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); - final RecordingCanvas c = node.startRecording(1200, 200); + final RecordingCanvas c = node.beginRecording(1200, 200); state.resumeTiming(); layout.draw(c); @@ -316,7 +316,7 @@ public class StaticLayoutPerfTest { final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT); final StaticLayout layout = StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); - final RecordingCanvas c = node.startRecording(1200, 200); + final RecordingCanvas c = node.beginRecording(1200, 200); Canvas.freeTextLayoutCaches(); state.resumeTiming(); @@ -334,7 +334,7 @@ public class StaticLayoutPerfTest { final CharSequence text = mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT); final StaticLayout layout = StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); - final RecordingCanvas c = node.startRecording(1200, 200); + final RecordingCanvas c = node.beginRecording(1200, 200); Canvas.freeTextLayoutCaches(); state.resumeTiming(); @@ -353,7 +353,7 @@ public class StaticLayoutPerfTest { mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT); final StaticLayout layout = StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); - final RecordingCanvas c = node.startRecording(1200, 200); + final RecordingCanvas c = node.beginRecording(1200, 200); state.resumeTiming(); layout.draw(c); @@ -371,7 +371,7 @@ public class StaticLayoutPerfTest { mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT); final StaticLayout layout = StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); - final RecordingCanvas c = node.startRecording(1200, 200); + final RecordingCanvas c = node.beginRecording(1200, 200); state.resumeTiming(); layout.draw(c); @@ -389,7 +389,7 @@ public class StaticLayoutPerfTest { mTextUtil.nextRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT); final StaticLayout layout = StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); - final RecordingCanvas c = node.startRecording(1200, 200); + final RecordingCanvas c = node.beginRecording(1200, 200); Canvas.freeTextLayoutCaches(); state.resumeTiming(); @@ -408,7 +408,7 @@ public class StaticLayoutPerfTest { mTextUtil.nextRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT); final StaticLayout layout = StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH).build(); - final RecordingCanvas c = node.startRecording(1200, 200); + final RecordingCanvas c = node.beginRecording(1200, 200); Canvas.freeTextLayoutCaches(); state.resumeTiming(); diff --git a/apct-tests/perftests/core/src/android/widget/TextViewPrecomputedTextPerfTest.java b/apct-tests/perftests/core/src/android/widget/TextViewPrecomputedTextPerfTest.java index 0bc9ee4e53ab..55d54e452040 100644 --- a/apct-tests/perftests/core/src/android/widget/TextViewPrecomputedTextPerfTest.java +++ b/apct-tests/perftests/core/src/android/widget/TextViewPrecomputedTextPerfTest.java @@ -344,7 +344,7 @@ public class TextViewPrecomputedTextPerfTest { textView.setText(text); textView.measure(width, height); textView.layout(0, 0, textView.getMeasuredWidth(), textView.getMeasuredHeight()); - final RecordingCanvas c = node.startRecording( + final RecordingCanvas c = node.beginRecording( textView.getMeasuredWidth(), textView.getMeasuredHeight()); textView.nullLayouts(); Canvas.freeTextLayoutCaches(); @@ -371,7 +371,7 @@ public class TextViewPrecomputedTextPerfTest { textView.setText(text); textView.measure(width, height); textView.layout(0, 0, textView.getMeasuredWidth(), textView.getMeasuredHeight()); - final RecordingCanvas c = node.startRecording( + final RecordingCanvas c = node.beginRecording( textView.getMeasuredWidth(), textView.getMeasuredHeight()); textView.nullLayouts(); Canvas.freeTextLayoutCaches(); @@ -400,7 +400,7 @@ public class TextViewPrecomputedTextPerfTest { textView.setText(text); textView.measure(width, height); textView.layout(0, 0, textView.getMeasuredWidth(), textView.getMeasuredHeight()); - final RecordingCanvas c = node.startRecording( + final RecordingCanvas c = node.beginRecording( textView.getMeasuredWidth(), textView.getMeasuredHeight()); textView.nullLayouts(); Canvas.freeTextLayoutCaches(); @@ -430,7 +430,7 @@ public class TextViewPrecomputedTextPerfTest { textView.setText(text); textView.measure(width, height); textView.layout(0, 0, textView.getMeasuredWidth(), textView.getMeasuredHeight()); - final RecordingCanvas c = node.startRecording( + final RecordingCanvas c = node.beginRecording( textView.getMeasuredWidth(), textView.getMeasuredHeight()); textView.nullLayouts(); Canvas.freeTextLayoutCaches(); diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResults.java b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResults.java index e417ca791c45..c1362dc3a6a7 100644 --- a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResults.java +++ b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkResults.java @@ -60,11 +60,13 @@ public class BenchmarkResults { if (size == 0) { return 0f; } - Collections.sort(mResults); + + final ArrayList<Long> resultsCopy = new ArrayList<>(mResults); + Collections.sort(resultsCopy); final int idx = size / 2; return size % 2 == 0 - ? (double) (mResults.get(idx) + mResults.get(idx - 1)) / 2 - : mResults.get(idx); + ? (double) (resultsCopy.get(idx) + resultsCopy.get(idx - 1)) / 2 + : resultsCopy.get(idx); } private double standardDeviation() { diff --git a/api/TEST_MAPPING b/api/TEST_MAPPING index 2cdf54bbad6f..4d22d0b1f4a3 100644 --- a/api/TEST_MAPPING +++ b/api/TEST_MAPPING @@ -4,6 +4,9 @@ "name": "CtsCurrentApiSignatureTestCases" }, { + "name": "CtsSystemApiSignatureTestCases" + }, + { "name": "GtsUnofficialApisUsageTestCases" } ] diff --git a/api/current.txt b/api/current.txt index f2fa672bfd15..81a6950bbd46 100644 --- a/api/current.txt +++ b/api/current.txt @@ -287,6 +287,7 @@ package android { field public static final int alertDialogTheme = 16843529; // 0x1010309 field public static final int alignmentMode = 16843642; // 0x101037a field public static final int allContactsName = 16843468; // 0x10102cc + field public static final int allowAudioPlaybackCapture = 16844199; // 0x10105a7 field public static final int allowBackup = 16843392; // 0x1010280 field public static final int allowClearUserData = 16842757; // 0x1010005 field public static final int allowEmbedded = 16843765; // 0x10103f5 @@ -4250,10 +4251,9 @@ package android.app { public class AppComponentFactory { ctor public AppComponentFactory(); - method public android.content.pm.ApplicationInfo getApplicationInfo(); method @NonNull public android.app.Activity instantiateActivity(@NonNull ClassLoader, @NonNull String, @Nullable android.content.Intent) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException; method @NonNull public android.app.Application instantiateApplication(@NonNull ClassLoader, @NonNull String) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException; - method @NonNull public ClassLoader instantiateClassLoader(@NonNull ClassLoader); + method @NonNull public ClassLoader instantiateClassLoader(@NonNull ClassLoader, @NonNull android.content.pm.ApplicationInfo); method @NonNull public android.content.ContentProvider instantiateProvider(@NonNull ClassLoader, @NonNull String) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException; method @NonNull public android.content.BroadcastReceiver instantiateReceiver(@NonNull ClassLoader, @NonNull String, @Nullable android.content.Intent) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException; method @NonNull public android.app.Service instantiateService(@NonNull ClassLoader, @NonNull String, @Nullable android.content.Intent) throws java.lang.ClassNotFoundException, java.lang.IllegalAccessException, java.lang.InstantiationException; @@ -5473,11 +5473,12 @@ package android.app { public static final class Notification.BubbleMetadata implements android.os.Parcelable { method public int describeContents(); method public boolean getAutoExpandBubble(); + method public android.app.PendingIntent getDeleteIntent(); method public int getDesiredHeight(); method public android.graphics.drawable.Icon getIcon(); method public android.app.PendingIntent getIntent(); method public boolean getSuppressInitialNotification(); - method public CharSequence getTitle(); + method @Deprecated public CharSequence getTitle(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.app.Notification.BubbleMetadata> CREATOR; } @@ -5486,11 +5487,12 @@ package android.app { ctor public Notification.BubbleMetadata.Builder(); method public android.app.Notification.BubbleMetadata build(); method public android.app.Notification.BubbleMetadata.Builder setAutoExpandBubble(boolean); + method public android.app.Notification.BubbleMetadata.Builder setDeleteIntent(android.app.PendingIntent); method public android.app.Notification.BubbleMetadata.Builder setDesiredHeight(int); method public android.app.Notification.BubbleMetadata.Builder setIcon(android.graphics.drawable.Icon); method public android.app.Notification.BubbleMetadata.Builder setIntent(android.app.PendingIntent); method public android.app.Notification.BubbleMetadata.Builder setSuppressInitialNotification(boolean); - method public android.app.Notification.BubbleMetadata.Builder setTitle(CharSequence); + method @Deprecated public android.app.Notification.BubbleMetadata.Builder setTitle(CharSequence); } public static class Notification.Builder { @@ -6169,6 +6171,7 @@ package android.app { ctor public Service(); method protected void dump(java.io.FileDescriptor, java.io.PrintWriter, String[]); method public final android.app.Application getApplication(); + method public final int getForegroundServiceType(); method @Nullable public abstract android.os.IBinder onBind(android.content.Intent); method public void onConfigurationChanged(android.content.res.Configuration); method public void onCreate(); @@ -6662,7 +6665,7 @@ package android.app.admin { method public int getPasswordMinimumUpperCase(@Nullable android.content.ComponentName); method public int getPasswordQuality(@Nullable android.content.ComponentName); method @Nullable public android.app.admin.SystemUpdateInfo getPendingSystemUpdate(@NonNull android.content.ComponentName); - method public int getPermissionGrantState(@Nullable android.content.ComponentName, String, String); + method public int getPermissionGrantState(@Nullable android.content.ComponentName, @NonNull String, @NonNull String); method public int getPermissionPolicy(android.content.ComponentName); method @Nullable public java.util.List<java.lang.String> getPermittedAccessibilityServices(@NonNull android.content.ComponentName); method @Nullable public java.util.List<java.lang.String> getPermittedCrossProfileNotificationListeners(@NonNull android.content.ComponentName); @@ -6773,7 +6776,7 @@ package android.app.admin { method public void setPasswordMinimumSymbols(@NonNull android.content.ComponentName, int); method public void setPasswordMinimumUpperCase(@NonNull android.content.ComponentName, int); method public void setPasswordQuality(@NonNull android.content.ComponentName, int); - method public boolean setPermissionGrantState(@NonNull android.content.ComponentName, String, String, int); + method public boolean setPermissionGrantState(@NonNull android.content.ComponentName, @NonNull String, @NonNull String, int); method public void setPermissionPolicy(@NonNull android.content.ComponentName, int); method public boolean setPermittedAccessibilityServices(@NonNull android.content.ComponentName, java.util.List<java.lang.String>); method public boolean setPermittedCrossProfileNotificationListeners(@NonNull android.content.ComponentName, @Nullable java.util.List<java.lang.String>); @@ -7433,7 +7436,10 @@ package android.app.role { method @NonNull public android.content.Intent createRequestRoleIntent(@NonNull String); method public boolean isRoleAvailable(@NonNull String); method public boolean isRoleHeld(@NonNull String); + field public static final String ROLE_ASSISTANT = "android.app.role.ASSISTANT"; field public static final String ROLE_BROWSER = "android.app.role.BROWSER"; + field public static final String ROLE_CALL_REDIRECTION = "android.app.role.CALL_REDIRECTION"; + field public static final String ROLE_CALL_SCREENING = "android.app.role.CALL_SCREENING"; field public static final String ROLE_DIALER = "android.app.role.DIALER"; field public static final String ROLE_EMERGENCY = "android.app.role.EMERGENCY"; field public static final String ROLE_GALLERY = "android.app.role.GALLERY"; @@ -9511,6 +9517,7 @@ package android.content { method public static android.content.SyncAdapterType[] getSyncAdapterTypes(); method public static boolean getSyncAutomatically(android.accounts.Account, String); method @Nullable public final String getType(@NonNull android.net.Uri); + method @NonNull public final android.content.ContentResolver.TypeInfo getTypeInfo(@NonNull String); method @Nullable public final android.net.Uri insert(@RequiresPermission.Write @NonNull android.net.Uri, @Nullable android.content.ContentValues); method public static boolean isSyncActive(android.accounts.Account, String); method public static boolean isSyncPending(android.accounts.Account, String); @@ -9590,6 +9597,12 @@ package android.content { field public static final int SYNC_OBSERVER_TYPE_SETTINGS = 1; // 0x1 } + public static final class ContentResolver.TypeInfo { + method @NonNull public CharSequence getContentDescription(); + method @NonNull public android.graphics.drawable.Icon getIcon(); + method @NonNull public CharSequence getLabel(); + } + public class ContentUris { ctor public ContentUris(); method @NonNull public static android.net.Uri.Builder appendId(@NonNull android.net.Uri.Builder, long); @@ -11229,7 +11242,7 @@ package android.content.pm { public class LauncherApps { method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(String, android.os.UserHandle); method @NonNull public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getAllPackageInstallerSessions(); - method @Nullable public android.content.pm.LauncherApps.AppUsageLimit getAppUsageLimit(String, android.os.UserHandle); + method @Nullable public android.content.pm.LauncherApps.AppUsageLimit getAppUsageLimit(@NonNull String, @NonNull android.os.UserHandle); method public android.content.pm.ApplicationInfo getApplicationInfo(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException; method public android.content.pm.LauncherApps.PinItemRequest getPinItemRequest(android.content.Intent); method public java.util.List<android.os.UserHandle> getProfiles(); @@ -11366,6 +11379,7 @@ package android.content.pm { method @NonNull public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getMySessions(); method @Nullable public android.content.pm.PackageInstaller.SessionInfo getSessionInfo(int); method @NonNull public java.util.List<android.content.pm.PackageInstaller.SessionInfo> getStagedSessions(); + method @RequiresPermission(allOf={android.Manifest.permission.INSTALL_PACKAGES, "com.android.permission.INSTALL_EXISTING_PACKAGES"}) public void installExistingPackage(@NonNull String, int, @Nullable android.content.IntentSender); method @NonNull public android.content.pm.PackageInstaller.Session openSession(int) throws java.io.IOException; method public void registerSessionCallback(@NonNull android.content.pm.PackageInstaller.SessionCallback); method public void registerSessionCallback(@NonNull android.content.pm.PackageInstaller.SessionCallback, @NonNull android.os.Handler); @@ -11447,10 +11461,10 @@ package android.content.pm { method public boolean isActive(); method public boolean isMultiPackage(); method public boolean isSealed(); - method public boolean isSessionApplied(); - method public boolean isSessionFailed(); - method public boolean isSessionReady(); method public boolean isStaged(); + method public boolean isStagedSessionApplied(); + method public boolean isStagedSessionFailed(); + method public boolean isStagedSessionReady(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.content.pm.PackageInstaller.SessionInfo> CREATOR; field public static final int INVALID_ID = -1; // 0xffffffff @@ -11466,7 +11480,6 @@ package android.content.pm { method public void setAppIcon(@Nullable android.graphics.Bitmap); method public void setAppLabel(@Nullable CharSequence); method public void setAppPackageName(@Nullable String); - method public void setInstallAsApex(); method public void setInstallLocation(int); method public void setInstallReason(int); method public void setMultiPackage(); @@ -11655,6 +11668,7 @@ package android.content.pm { field public static final String FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT = "android.hardware.faketouch.multitouch.distinct"; field public static final String FEATURE_FAKETOUCH_MULTITOUCH_JAZZHAND = "android.hardware.faketouch.multitouch.jazzhand"; field public static final String FEATURE_FINGERPRINT = "android.hardware.fingerprint"; + field public static final String FEATURE_FOLDABLE = "android.hardware.type.foldable"; field public static final String FEATURE_FREEFORM_WINDOW_MANAGEMENT = "android.software.freeform_window_management"; field public static final String FEATURE_GAMEPAD = "android.hardware.gamepad"; field public static final String FEATURE_HIFI_SENSORS = "android.hardware.sensor.hifi_sensors"; @@ -13540,7 +13554,7 @@ package android.graphics { method @CheckResult public android.graphics.Bitmap extractAlpha(android.graphics.Paint, int[]); method public int getAllocationByteCount(); method public int getByteCount(); - method public android.graphics.Color getColor(int, int); + method @NonNull public android.graphics.Color getColor(int, int); method @Nullable public android.graphics.ColorSpace getColorSpace(); method public android.graphics.Bitmap.Config getConfig(); method public int getDensity(); @@ -14936,13 +14950,15 @@ package android.graphics { public final class RenderNode { ctor public RenderNode(@Nullable String); - method public int computeApproximateMemoryUsage(); + method public android.graphics.RecordingCanvas beginRecording(int, int); + method public android.graphics.RecordingCanvas beginRecording(); + method public long computeApproximateMemoryUsage(); method public void discardDisplayList(); method public void endRecording(); method public float getAlpha(); - method public int getAmbientShadowColor(); + method @ColorInt public int getAmbientShadowColor(); method public int getBottom(); - method public float getCameraDistance(); + method @FloatRange(from=0.0f, to=java.lang.Float.MAX_VALUE) public float getCameraDistance(); method public boolean getClipToBounds(); method public boolean getClipToOutline(); method public float getElevation(); @@ -14953,12 +14969,12 @@ package android.graphics { method public float getPivotX(); method public float getPivotY(); method public int getRight(); - method public float getRotation(); method public float getRotationX(); method public float getRotationY(); + method public float getRotationZ(); method public float getScaleX(); method public float getScaleY(); - method public int getSpotShadowColor(); + method @ColorInt public int getSpotShadowColor(); method public int getTop(); method public float getTranslationX(); method public float getTranslationY(); @@ -14976,36 +14992,31 @@ package android.graphics { method public boolean offsetTopAndBottom(int); method public boolean resetPivot(); method public boolean setAlpha(float); - method public boolean setAmbientShadowColor(int); - method public boolean setBottom(int); - method public boolean setCameraDistance(float); - method public boolean setClipBounds(@Nullable android.graphics.Rect); + method public boolean setAmbientShadowColor(@ColorInt int); + method public boolean setCameraDistance(@FloatRange(from=0.0f, to=java.lang.Float.MAX_VALUE) float); + method public boolean setClipRect(@Nullable android.graphics.Rect); method public boolean setClipToBounds(boolean); method public boolean setClipToOutline(boolean); method public boolean setElevation(float); method public boolean setForceDarkAllowed(boolean); method public boolean setHasOverlappingRendering(boolean); - method public boolean setLeft(int); - method public boolean setLeftTopRightBottom(int, int, int, int); method public boolean setOutline(@Nullable android.graphics.Outline); method public boolean setPivotX(float); method public boolean setPivotY(float); + method public boolean setPosition(int, int, int, int); + method public boolean setPosition(android.graphics.Rect); method public boolean setProjectBackwards(boolean); method public boolean setProjectionReceiver(boolean); - method public boolean setRight(int); - method public boolean setRotation(float); method public boolean setRotationX(float); method public boolean setRotationY(float); + method public boolean setRotationZ(float); method public boolean setScaleX(float); method public boolean setScaleY(float); - method public boolean setSpotShadowColor(int); - method public boolean setTop(int); + method public boolean setSpotShadowColor(@ColorInt int); method public boolean setTranslationX(float); method public boolean setTranslationY(float); method public boolean setTranslationZ(float); method public boolean setUseCompositingLayer(boolean, @Nullable android.graphics.Paint); - method public android.graphics.RecordingCanvas startRecording(int, int); - method public android.graphics.RecordingCanvas startRecording(); } public class Shader { @@ -23038,6 +23049,7 @@ package android.media { ctor public AudioAttributes.Builder(); ctor public AudioAttributes.Builder(android.media.AudioAttributes); method public android.media.AudioAttributes build(); + method public android.media.AudioAttributes.Builder setAllowCapture(boolean); method public android.media.AudioAttributes.Builder setContentType(int); method public android.media.AudioAttributes.Builder setFlags(int); method public android.media.AudioAttributes.Builder setLegacyStreamType(int); @@ -23371,6 +23383,18 @@ package android.media { method public void onAudioFocusChange(int); } + public final class AudioPlaybackCaptureConfiguration { + } + + public static final class AudioPlaybackCaptureConfiguration.Builder { + ctor public AudioPlaybackCaptureConfiguration.Builder(@NonNull android.media.projection.MediaProjection); + method public android.media.AudioPlaybackCaptureConfiguration.Builder addMatchingUid(int); + method public android.media.AudioPlaybackCaptureConfiguration.Builder addMatchingUsage(@NonNull android.media.AudioAttributes); + method public android.media.AudioPlaybackCaptureConfiguration build(); + method public android.media.AudioPlaybackCaptureConfiguration.Builder excludeUid(int); + method public android.media.AudioPlaybackCaptureConfiguration.Builder excludeUsage(@NonNull android.media.AudioAttributes); + } + public final class AudioPlaybackConfiguration implements android.os.Parcelable { method public int describeContents(); method public android.media.AudioAttributes getAudioAttributes(); @@ -23469,6 +23493,7 @@ package android.media { ctor public AudioRecord.Builder(); method public android.media.AudioRecord build() throws java.lang.UnsupportedOperationException; method public android.media.AudioRecord.Builder setAudioFormat(@NonNull android.media.AudioFormat) throws java.lang.IllegalArgumentException; + method public android.media.AudioRecord.Builder setAudioPlaybackCaptureConfig(@NonNull android.media.AudioPlaybackCaptureConfiguration); method public android.media.AudioRecord.Builder setAudioSource(int) throws java.lang.IllegalArgumentException; method public android.media.AudioRecord.Builder setBufferSizeInBytes(int) throws java.lang.IllegalArgumentException; } @@ -23991,8 +24016,8 @@ package android.media { method public int getMaxImages(); method public android.view.Surface getSurface(); method public int getWidth(); - method public static android.media.ImageReader newInstance(int, int, int, int); - method public static android.media.ImageReader newInstance(int, int, int, int, long); + method @NonNull public static android.media.ImageReader newInstance(@IntRange(from=1) int, @IntRange(from=1) int, int, @IntRange(from=1) int); + method @NonNull public static android.media.ImageReader newInstance(@IntRange(from=1) int, @IntRange(from=1) int, int, @IntRange(from=1) int, long); method public void setOnImageAvailableListener(android.media.ImageReader.OnImageAvailableListener, android.os.Handler); } @@ -24005,8 +24030,8 @@ package android.media { method public android.media.Image dequeueInputImage(); method public int getFormat(); method public int getMaxImages(); - method public static android.media.ImageWriter newInstance(android.view.Surface, int); - method public static android.media.ImageWriter newInstance(android.view.Surface, int, int); + method @NonNull public static android.media.ImageWriter newInstance(@NonNull android.view.Surface, @IntRange(from=1) int); + method @NonNull public static android.media.ImageWriter newInstance(@NonNull android.view.Surface, @IntRange(from=1) int, int); method public void queueInputImage(android.media.Image); method public void setOnImageReleasedListener(android.media.ImageWriter.OnImageReleasedListener, android.os.Handler); } @@ -26011,6 +26036,7 @@ package android.media { public static final class MediaSession2.Builder { ctor public MediaSession2.Builder(@NonNull android.content.Context); method @NonNull public android.media.MediaSession2 build(); + method @NonNull public android.media.MediaSession2.Builder setExtras(@Nullable android.os.Bundle); method @NonNull public android.media.MediaSession2.Builder setId(@NonNull String); method @NonNull public android.media.MediaSession2.Builder setSessionActivity(@Nullable android.app.PendingIntent); method @NonNull public android.media.MediaSession2.Builder setSessionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.MediaSession2.SessionCallback); @@ -26373,6 +26399,7 @@ package android.media { public final class Session2Token implements android.os.Parcelable { ctor public Session2Token(@NonNull android.content.Context, @NonNull android.content.ComponentName); method public int describeContents(); + method @Nullable public android.os.Bundle getExtras(); method @NonNull public String getPackageName(); method @Nullable public String getServiceName(); method public int getType(); @@ -27435,6 +27462,7 @@ package android.media.session { method @Nullable public CharSequence getQueueTitle(); method public int getRatingType(); method @Nullable public android.app.PendingIntent getSessionActivity(); + method @Nullable public android.os.Bundle getSessionInfo(); method @NonNull public android.media.session.MediaSession.Token getSessionToken(); method @NonNull public android.media.session.MediaController.TransportControls getTransportControls(); method public void registerCallback(@NonNull android.media.session.MediaController.Callback); @@ -27494,6 +27522,7 @@ package android.media.session { public final class MediaSession { ctor public MediaSession(@NonNull android.content.Context, @NonNull String); + ctor public MediaSession(@NonNull android.content.Context, @NonNull String, @Nullable android.os.Bundle); method @NonNull public android.media.session.MediaController getController(); method @NonNull public android.media.session.MediaSessionManager.RemoteUserInfo getCurrentControllerInfo(); method @NonNull public android.media.session.MediaSession.Token getSessionToken(); @@ -29330,7 +29359,7 @@ package android.net { method public android.os.ParcelFileDescriptor establish(); method public android.net.VpnService.Builder setBlocking(boolean); method public android.net.VpnService.Builder setConfigureIntent(android.app.PendingIntent); - method public android.net.VpnService.Builder setHttpProxy(android.net.ProxyInfo); + method public android.net.VpnService.Builder setHttpProxy(@NonNull android.net.ProxyInfo); method public android.net.VpnService.Builder setMetered(boolean); method public android.net.VpnService.Builder setMtu(int); method public android.net.VpnService.Builder setSession(String); @@ -29918,13 +29947,14 @@ package android.net.wifi { method public String getMacAddress(); method public int getNetworkId(); method public int getRssi(); - method public int getRxLinkSpeedMbps(); + method @IntRange(from=0xffffffff) public int getRxLinkSpeedMbps(); method public String getSSID(); method public android.net.wifi.SupplicantState getSupplicantState(); - method public int getTxLinkSpeedMbps(); + method @IntRange(from=0xffffffff) public int getTxLinkSpeedMbps(); method public void writeToParcel(android.os.Parcel, int); field public static final String FREQUENCY_UNITS = "MHz"; field public static final String LINK_SPEED_UNITS = "Mbps"; + field public static final int LINK_SPEED_UNKNOWN = -1; // 0xffffffff } public class WifiManager { @@ -29964,7 +29994,7 @@ package android.net.wifi { method @Deprecated public boolean reconnect(); method @Deprecated public boolean removeNetwork(int); method @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public int removeNetworkSuggestions(@NonNull java.util.List<android.net.wifi.WifiNetworkSuggestion>); - method @Deprecated @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", "android.permission.NETWORK_SETUP_WIZARD"}) public void removePasspointConfiguration(String); + method @Deprecated @RequiresPermission("android.permission.NETWORK_SETTINGS") public void removePasspointConfiguration(String); method @Deprecated public boolean saveConfiguration(); method public void setTdlsEnabled(java.net.InetAddress, boolean); method public void setTdlsEnabledWithMacAddress(String, boolean); @@ -35487,7 +35517,7 @@ package android.os { public abstract class VibrationEffect implements android.os.Parcelable { method public static android.os.VibrationEffect createOneShot(long, int); - method public static android.os.VibrationEffect createPrebaked(int); + method public static android.os.VibrationEffect createPredefined(int); method public static android.os.VibrationEffect createWaveform(long[], int); method public static android.os.VibrationEffect createWaveform(long[], int[], int); method public int describeContents(); @@ -38377,14 +38407,12 @@ package android.provider { public final class MediaStore { ctor public MediaStore(); - method @NonNull public static android.net.Uri createPending(@NonNull android.content.Context, @NonNull android.provider.MediaStore.PendingParams); method @NonNull public static java.util.Set<java.lang.String> getAllVolumeNames(android.content.Context); method public static android.net.Uri getDocumentUri(android.content.Context, android.net.Uri); method public static android.net.Uri getMediaScannerUri(); method public static android.net.Uri getMediaUri(android.content.Context, android.net.Uri); method public static String getVersion(android.content.Context); method @NonNull public static String getVolumeName(@NonNull android.net.Uri); - method @NonNull public static android.provider.MediaStore.PendingSession openPending(@NonNull android.content.Context, @NonNull android.net.Uri); method @NonNull public static android.net.Uri setIncludePending(@NonNull android.net.Uri); method @NonNull public static android.net.Uri setRequireOriginal(@NonNull android.net.Uri); field public static final String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE"; @@ -38657,6 +38685,7 @@ package android.provider { public static interface MediaStore.MediaColumns extends android.provider.BaseColumns { field @Deprecated public static final String DATA = "_data"; field public static final String DATE_ADDED = "date_added"; + field public static final String DATE_EXPIRES = "date_expires"; field public static final String DATE_MODIFIED = "date_modified"; field public static final String DISPLAY_NAME = "_display_name"; field public static final String DOCUMENT_ID = "document_id"; @@ -38673,23 +38702,6 @@ package android.provider { field public static final String WIDTH = "width"; } - public static class MediaStore.PendingParams { - ctor public MediaStore.PendingParams(@NonNull android.net.Uri, @NonNull String, @NonNull String); - method public void setDownloadUri(@Nullable android.net.Uri); - method public void setPrimaryDirectory(@Nullable String); - method public void setRefererUri(@Nullable android.net.Uri); - method public void setSecondaryDirectory(@Nullable String); - } - - public static class MediaStore.PendingSession implements java.lang.AutoCloseable { - method public void abandon(); - method public void close(); - method public void notifyProgress(@IntRange(from=0, to=100) int); - method @NonNull public android.os.ParcelFileDescriptor open() throws java.io.FileNotFoundException; - method @NonNull public java.io.OutputStream openOutputStream() throws java.io.FileNotFoundException; - method @NonNull public android.net.Uri publish(); - } - public static final class MediaStore.Video { ctor public MediaStore.Video(); method @Deprecated public static android.database.Cursor query(android.content.ContentResolver, android.net.Uri, String[]); @@ -38809,6 +38821,7 @@ package android.provider { field public static final String ACTION_NFC_PAYMENT_SETTINGS = "android.settings.NFC_PAYMENT_SETTINGS"; field public static final String ACTION_NFC_SETTINGS = "android.settings.NFC_SETTINGS"; field public static final String ACTION_NIGHT_DISPLAY_SETTINGS = "android.settings.NIGHT_DISPLAY_SETTINGS"; + field public static final String ACTION_NOTIFICATION_ASSISTANT_SETTINGS = "android.settings.NOTIFICATION_ASSISTANT_SETTINGS"; field public static final String ACTION_NOTIFICATION_LISTENER_SETTINGS = "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"; field public static final String ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS = "android.settings.NOTIFICATION_POLICY_ACCESS_SETTINGS"; field public static final String ACTION_PRINT_SETTINGS = "android.settings.ACTION_PRINT_SETTINGS"; @@ -38872,6 +38885,7 @@ package android.provider { field public static final String AIRPLANE_MODE_RADIOS = "airplane_mode_radios"; field public static final String ALWAYS_FINISH_ACTIVITIES = "always_finish_activities"; field public static final String ANIMATOR_DURATION_SCALE = "animator_duration_scale"; + field public static final String APPLY_RAMPING_RINGER = "apply_ramping_ringer"; field public static final String AUTO_TIME = "auto_time"; field public static final String AUTO_TIME_ZONE = "auto_time_zone"; field public static final String BLUETOOTH_ON = "bluetooth_on"; @@ -41383,7 +41397,7 @@ package android.service.carrier { public class CarrierMessagingClientService extends android.app.Service { ctor public CarrierMessagingClientService(); - method public final android.os.IBinder onBind(android.content.Intent); + method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent); } public abstract class CarrierMessagingService extends android.app.Service { @@ -42487,6 +42501,7 @@ package android.system { method public static void chown(String, int, int) throws android.system.ErrnoException; method public static void close(java.io.FileDescriptor) throws android.system.ErrnoException; method public static void connect(java.io.FileDescriptor, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException; + method public static void connect(java.io.FileDescriptor, java.net.SocketAddress) throws android.system.ErrnoException, java.net.SocketException; method public static java.io.FileDescriptor dup(java.io.FileDescriptor) throws android.system.ErrnoException; method public static java.io.FileDescriptor dup2(java.io.FileDescriptor, int) throws android.system.ErrnoException; method public static String[] environ(); @@ -43316,11 +43331,11 @@ package android.telecom { public final class CallIdentification implements android.os.Parcelable { method public int describeContents(); - method @NonNull public String getCallScreeningAppName(); + method @NonNull public CharSequence getCallScreeningAppName(); method @NonNull public String getCallScreeningPackageName(); - method @Nullable public String getDescription(); - method @Nullable public String getDetails(); - method @Nullable public String getName(); + method @Nullable public CharSequence getDescription(); + method @Nullable public CharSequence getDetails(); + method @Nullable public CharSequence getName(); method public int getNuisanceConfidence(); method @Nullable public android.graphics.drawable.Icon getPhoto(); method public void writeToParcel(android.os.Parcel, int); @@ -43335,9 +43350,9 @@ package android.telecom { public static class CallIdentification.Builder { ctor public CallIdentification.Builder(); method public android.telecom.CallIdentification build(); - method public android.telecom.CallIdentification.Builder setDescription(@Nullable String); - method public android.telecom.CallIdentification.Builder setDetails(@Nullable String); - method public android.telecom.CallIdentification.Builder setName(@Nullable String); + method public android.telecom.CallIdentification.Builder setDescription(@Nullable CharSequence); + method public android.telecom.CallIdentification.Builder setDetails(@Nullable CharSequence); + method public android.telecom.CallIdentification.Builder setName(@Nullable CharSequence); method public android.telecom.CallIdentification.Builder setNuisanceConfidence(int); method public android.telecom.CallIdentification.Builder setPhoto(@Nullable android.graphics.drawable.Icon); } @@ -44835,6 +44850,7 @@ package android.telephony { method @Deprecated public int getCdmaDbm(); method @Deprecated public int getCdmaEcio(); method @NonNull public java.util.List<android.telephony.CellSignalStrength> getCellSignalStrengths(); + method public <T extends android.telephony.CellSignalStrength> java.util.List<T> getCellSignalStrengths(@NonNull Class<T>); method @Deprecated public int getEvdoDbm(); method @Deprecated public int getEvdoEcio(); method @Deprecated public int getEvdoSnr(); @@ -44991,7 +45007,7 @@ package android.telephony { method public String getCountryIso(); method public int getDataRoaming(); method public CharSequence getDisplayName(); - method public String getGroupUuid(); + method @Nullable public String getGroupUuid(); method public String getIccId(); method public int getIconTint(); method @Deprecated public int getMcc(); @@ -45032,7 +45048,7 @@ package android.telephony { method public boolean isNetworkRoaming(int); method public static boolean isUsableSubscriptionId(int); method public static boolean isValidSubscriptionId(int); - method public void removeOnOpportunisticSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener); + method public void removeOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener); method public void removeOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean removeSubscriptionsFromGroup(@NonNull int[]); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setMetered(boolean, int); @@ -45041,7 +45057,7 @@ package android.telephony { method public void setSubscriptionOverrideCongested(int, boolean, long); method public void setSubscriptionOverrideUnmetered(int, boolean, long); method public void setSubscriptionPlans(int, @NonNull java.util.List<android.telephony.SubscriptionPlan>); - method @RequiresPermission("android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS") public void switchToSubscription(int, android.app.PendingIntent); + method @RequiresPermission("android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS") public void switchToSubscription(int, @NonNull android.app.PendingIntent); field public static final String ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED = "android.telephony.action.DEFAULT_SMS_SUBSCRIPTION_CHANGED"; field public static final String ACTION_DEFAULT_SUBSCRIPTION_CHANGED = "android.telephony.action.DEFAULT_SUBSCRIPTION_CHANGED"; field public static final String ACTION_MANAGE_SUBSCRIPTION_PLANS = "android.telephony.action.MANAGE_SUBSCRIPTION_PLANS"; @@ -45150,7 +45166,7 @@ package android.telephony { method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getSubscriberId(); method public String getTypeAllocationCode(); method public String getTypeAllocationCode(int); - method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public java.util.List<android.telephony.UiccCardInfo> getUiccCardsInfo(); + method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") @NonNull public java.util.List<android.telephony.UiccCardInfo> getUiccCardsInfo(); method @Nullable @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getVisualVoicemailPackageName(); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getVoiceMailAlphaTag(); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getVoiceMailNumber(); @@ -45169,6 +45185,7 @@ package android.telephony { method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean isDataEnabled(); method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataRoamingEnabled(); method public boolean isHearingAidCompatibilitySupported(); + method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isMultisimSupported(); method public boolean isNetworkRoaming(); method public boolean isRttSupported(); method public boolean isSmsCapable(); @@ -45320,13 +45337,13 @@ package android.telephony { } public final class UiccCardInfo implements android.os.Parcelable { - ctor public UiccCardInfo(boolean, int, String, String, int); method public int describeContents(); method public int getCardId(); - method public String getEid(); - method public String getIccId(); + method @Nullable public String getEid(); + method @Nullable public String getIccId(); method public int getSlotIndex(); method public boolean isEuicc(); + method public boolean isRemovable(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.telephony.UiccCardInfo> CREATOR; } @@ -45641,465 +45658,6 @@ package android.telephony.gsm { } -package android.telephony.ims { - - public class Rcs1To1Thread extends android.telephony.ims.RcsThread { - method @WorkerThread public long getFallbackThreadId() throws android.telephony.ims.RcsMessageStoreException; - method @NonNull @WorkerThread public android.telephony.ims.RcsParticipant getRecipient() throws android.telephony.ims.RcsMessageStoreException; - method public boolean isGroup(); - method @WorkerThread public void setFallbackThreadId(long) throws android.telephony.ims.RcsMessageStoreException; - } - - public abstract class RcsEvent { - ctor protected RcsEvent(long); - method public long getTimestamp(); - } - - public final class RcsEventQueryParams implements android.os.Parcelable { - method public int describeContents(); - method @android.telephony.ims.RcsEventQueryParams.EventType public int getEventType(); - method public int getLimit(); - method public boolean getSortDirection(); - method public int getSortingProperty(); - method public void writeToParcel(android.os.Parcel, int); - field public static final int ALL_EVENTS = -1; // 0xffffffff - field public static final int ALL_GROUP_THREAD_EVENTS = 0; // 0x0 - field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsEventQueryParams> CREATOR; - field public static final int GROUP_THREAD_ICON_CHANGED_EVENT = 8; // 0x8 - field public static final int GROUP_THREAD_NAME_CHANGED_EVENT = 16; // 0x10 - field public static final int GROUP_THREAD_PARTICIPANT_JOINED_EVENT = 2; // 0x2 - field public static final int GROUP_THREAD_PARTICIPANT_LEFT_EVENT = 4; // 0x4 - field public static final int PARTICIPANT_ALIAS_CHANGED_EVENT = 1; // 0x1 - field public static final int SORT_BY_CREATION_ORDER = 0; // 0x0 - field public static final int SORT_BY_TIMESTAMP = 1; // 0x1 - } - - public static class RcsEventQueryParams.Builder { - ctor public RcsEventQueryParams.Builder(); - method public android.telephony.ims.RcsEventQueryParams build(); - method @CheckResult public android.telephony.ims.RcsEventQueryParams.Builder setEventType(@android.telephony.ims.RcsEventQueryParams.EventType int); - method @CheckResult public android.telephony.ims.RcsEventQueryParams.Builder setGroupThread(@NonNull android.telephony.ims.RcsGroupThread); - method @CheckResult public android.telephony.ims.RcsEventQueryParams.Builder setResultLimit(@IntRange(from=0) int) throws java.security.InvalidParameterException; - method @CheckResult public android.telephony.ims.RcsEventQueryParams.Builder setSortDirection(boolean); - method @CheckResult public android.telephony.ims.RcsEventQueryParams.Builder setSortProperty(@android.telephony.ims.RcsEventQueryParams.SortingProperty int); - } - - @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsEventQueryParams.ALL_EVENTS, android.telephony.ims.RcsEventQueryParams.ALL_GROUP_THREAD_EVENTS, android.telephony.ims.RcsEventQueryParams.PARTICIPANT_ALIAS_CHANGED_EVENT, android.telephony.ims.RcsEventQueryParams.GROUP_THREAD_PARTICIPANT_JOINED_EVENT, android.telephony.ims.RcsEventQueryParams.GROUP_THREAD_PARTICIPANT_LEFT_EVENT, android.telephony.ims.RcsEventQueryParams.GROUP_THREAD_NAME_CHANGED_EVENT, android.telephony.ims.RcsEventQueryParams.GROUP_THREAD_ICON_CHANGED_EVENT}) public static @interface RcsEventQueryParams.EventType { - } - - @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsEventQueryParams.SORT_BY_CREATION_ORDER, android.telephony.ims.RcsEventQueryParams.SORT_BY_TIMESTAMP}) public static @interface RcsEventQueryParams.SortingProperty { - } - - public class RcsEventQueryResult { - method public android.telephony.ims.RcsQueryContinuationToken getContinuationToken(); - method public java.util.List<android.telephony.ims.RcsEvent> getEvents(); - } - - public final class RcsFileTransferCreationParams implements android.os.Parcelable { - method public int describeContents(); - method public String getContentMimeType(); - method public android.net.Uri getContentUri(); - method public long getFileSize(); - method @android.telephony.ims.RcsFileTransferPart.RcsFileTransferStatus public int getFileTransferStatus(); - method public int getHeight(); - method public long getMediaDuration(); - method public String getPreviewMimeType(); - method public android.net.Uri getPreviewUri(); - method public String getRcsFileTransferSessionId(); - method public long getTransferOffset(); - method public int getWidth(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsFileTransferCreationParams> CREATOR; - } - - public class RcsFileTransferCreationParams.Builder { - ctor public RcsFileTransferCreationParams.Builder(); - method public android.telephony.ims.RcsFileTransferCreationParams build(); - method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setContentMimeType(String); - method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setContentUri(android.net.Uri); - method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setFileSize(long); - method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setFileTransferSessionId(String); - method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setFileTransferStatus(@android.telephony.ims.RcsFileTransferPart.RcsFileTransferStatus int); - method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setHeight(int); - method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setMediaDuration(long); - method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setPreviewMimeType(String); - method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setPreviewUri(android.net.Uri); - method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setTransferOffset(long); - method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setWidth(int); - } - - public class RcsFileTransferPart { - method @WorkerThread @Nullable public String getContentMimeType() throws android.telephony.ims.RcsMessageStoreException; - method @Nullable @WorkerThread public android.net.Uri getContentUri() throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public long getFileSize() throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public String getFileTransferSessionId() throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread @android.telephony.ims.RcsFileTransferPart.RcsFileTransferStatus public int getFileTransferStatus() throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public int getHeight() throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public long getLength() throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public String getPreviewMimeType() throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public android.net.Uri getPreviewUri() throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public long getTransferOffset() throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public int getWidth() throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public void setContentMimeType(String) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public void setContentUri(android.net.Uri) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public void setFileSize(long) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public void setFileTransferSessionId(String) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public void setFileTransferStatus(@android.telephony.ims.RcsFileTransferPart.RcsFileTransferStatus int) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public void setHeight(int) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public void setLength(long) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public void setPreviewMimeType(String) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public void setPreviewUri(android.net.Uri) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public void setTransferOffset(long) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public void setWidth(int) throws android.telephony.ims.RcsMessageStoreException; - field public static final int DOWNLOADING = 6; // 0x6 - field public static final int DOWNLOADING_CANCELLED = 9; // 0x9 - field public static final int DOWNLOADING_FAILED = 8; // 0x8 - field public static final int DOWNLOADING_PAUSED = 7; // 0x7 - field public static final int DRAFT = 1; // 0x1 - field public static final int NOT_SET = 0; // 0x0 - field public static final int SENDING = 2; // 0x2 - field public static final int SENDING_CANCELLED = 5; // 0x5 - field public static final int SENDING_FAILED = 4; // 0x4 - field public static final int SENDING_PAUSED = 3; // 0x3 - field public static final int SUCCEEDED = 10; // 0xa - } - - @IntDef({android.telephony.ims.RcsFileTransferPart.DRAFT, android.telephony.ims.RcsFileTransferPart.SENDING, android.telephony.ims.RcsFileTransferPart.SENDING_PAUSED, android.telephony.ims.RcsFileTransferPart.SENDING_FAILED, android.telephony.ims.RcsFileTransferPart.SENDING_CANCELLED, android.telephony.ims.RcsFileTransferPart.DOWNLOADING, android.telephony.ims.RcsFileTransferPart.DOWNLOADING_PAUSED, android.telephony.ims.RcsFileTransferPart.DOWNLOADING_FAILED, android.telephony.ims.RcsFileTransferPart.DOWNLOADING_CANCELLED, android.telephony.ims.RcsFileTransferPart.SUCCEEDED}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface RcsFileTransferPart.RcsFileTransferStatus { - } - - public class RcsGroupThread extends android.telephony.ims.RcsThread { - method @WorkerThread public void addParticipant(@NonNull android.telephony.ims.RcsParticipant) throws android.telephony.ims.RcsMessageStoreException; - method @Nullable @WorkerThread public android.net.Uri getConferenceUri() throws android.telephony.ims.RcsMessageStoreException; - method @Nullable public android.net.Uri getGroupIcon() throws android.telephony.ims.RcsMessageStoreException; - method @Nullable @WorkerThread public String getGroupName() throws android.telephony.ims.RcsMessageStoreException; - method @Nullable @WorkerThread public android.telephony.ims.RcsParticipant getOwner() throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread @NonNull public java.util.Set<android.telephony.ims.RcsParticipant> getParticipants() throws android.telephony.ims.RcsMessageStoreException; - method public boolean isGroup(); - method @WorkerThread public void removeParticipant(@NonNull android.telephony.ims.RcsParticipant) throws android.telephony.ims.RcsMessageStoreException; - method @Nullable @WorkerThread public void setConferenceUri(android.net.Uri) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public void setGroupIcon(@Nullable android.net.Uri) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public void setGroupName(String) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public void setOwner(@Nullable android.telephony.ims.RcsParticipant) throws android.telephony.ims.RcsMessageStoreException; - } - - public abstract class RcsGroupThreadEvent extends android.telephony.ims.RcsEvent { - method @NonNull public android.telephony.ims.RcsParticipant getOriginatingParticipant(); - method @NonNull public android.telephony.ims.RcsGroupThread getRcsGroupThread(); - } - - public final class RcsGroupThreadIconChangedEvent extends android.telephony.ims.RcsGroupThreadEvent { - ctor public RcsGroupThreadIconChangedEvent(long, @NonNull android.telephony.ims.RcsGroupThread, @NonNull android.telephony.ims.RcsParticipant, @Nullable android.net.Uri); - method @Nullable public android.net.Uri getNewIcon(); - } - - public final class RcsGroupThreadNameChangedEvent extends android.telephony.ims.RcsGroupThreadEvent { - ctor public RcsGroupThreadNameChangedEvent(long, @NonNull android.telephony.ims.RcsGroupThread, @NonNull android.telephony.ims.RcsParticipant, @Nullable String); - method @Nullable public String getNewName(); - } - - public final class RcsGroupThreadParticipantJoinedEvent extends android.telephony.ims.RcsGroupThreadEvent { - ctor public RcsGroupThreadParticipantJoinedEvent(long, @NonNull android.telephony.ims.RcsGroupThread, @NonNull android.telephony.ims.RcsParticipant, @NonNull android.telephony.ims.RcsParticipant); - method public android.telephony.ims.RcsParticipant getJoinedParticipant(); - } - - public final class RcsGroupThreadParticipantLeftEvent extends android.telephony.ims.RcsGroupThreadEvent { - ctor public RcsGroupThreadParticipantLeftEvent(long, @NonNull android.telephony.ims.RcsGroupThread, @NonNull android.telephony.ims.RcsParticipant, @NonNull android.telephony.ims.RcsParticipant); - method @NonNull public android.telephony.ims.RcsParticipant getLeavingParticipant(); - method public void persist() throws android.telephony.ims.RcsMessageStoreException; - } - - public class RcsIncomingMessage extends android.telephony.ims.RcsMessage { - method @WorkerThread public long getArrivalTimestamp() throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public long getSeenTimestamp() throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public android.telephony.ims.RcsParticipant getSenderParticipant() throws android.telephony.ims.RcsMessageStoreException; - method public boolean isIncoming(); - method @WorkerThread public void setArrivalTimestamp(long) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public void setSeenTimestamp(long) throws android.telephony.ims.RcsMessageStoreException; - } - - public final class RcsIncomingMessageCreationParams extends android.telephony.ims.RcsMessageCreationParams implements android.os.Parcelable { - method public int describeContents(); - method public long getArrivalTimestamp(); - method public long getSeenTimestamp(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsIncomingMessageCreationParams> CREATOR; - } - - public static class RcsIncomingMessageCreationParams.Builder extends android.telephony.ims.RcsMessageCreationParams.Builder { - ctor public RcsIncomingMessageCreationParams.Builder(long, long, int); - method public android.telephony.ims.RcsIncomingMessageCreationParams build(); - method @CheckResult public android.telephony.ims.RcsIncomingMessageCreationParams.Builder setArrivalTimestamp(long); - method @CheckResult public android.telephony.ims.RcsIncomingMessageCreationParams.Builder setSeenTimestamp(long); - method @CheckResult public android.telephony.ims.RcsIncomingMessageCreationParams.Builder setSenderParticipant(android.telephony.ims.RcsParticipant); - } - - public class RcsManager { - method public android.telephony.ims.RcsMessageStore getRcsMessageStore(); - } - - public abstract class RcsMessage { - method @NonNull @WorkerThread public java.util.Set<android.telephony.ims.RcsFileTransferPart> getFileTransferParts() throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public double getLatitude() throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public double getLongitude() throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public long getOriginationTimestamp() throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public String getRcsMessageId() throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread @android.telephony.ims.RcsMessage.RcsMessageStatus public int getStatus() throws android.telephony.ims.RcsMessageStoreException; - method public int getSubscriptionId() throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public String getText() throws android.telephony.ims.RcsMessageStoreException; - method @NonNull @WorkerThread public android.telephony.ims.RcsFileTransferPart insertFileTransfer(android.telephony.ims.RcsFileTransferCreationParams) throws android.telephony.ims.RcsMessageStoreException; - method public abstract boolean isIncoming(); - method @WorkerThread public void removeFileTransferPart(@NonNull android.telephony.ims.RcsFileTransferPart) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public void setLatitude(double) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public void setLongitude(double) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public void setOriginationTimestamp(long) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public void setRcsMessageId(String) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public void setStatus(@android.telephony.ims.RcsMessage.RcsMessageStatus int) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public void setSubscriptionId(int) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public void setText(String) throws android.telephony.ims.RcsMessageStoreException; - field public static final int DRAFT = 1; // 0x1 - field public static final int FAILED = 6; // 0x6 - field public static final double LOCATION_NOT_SET = 4.9E-324; - field public static final int NOT_SET = 0; // 0x0 - field public static final int QUEUED = 2; // 0x2 - field public static final int RECEIVED = 7; // 0x7 - field public static final int RETRYING = 5; // 0x5 - field public static final int SEEN = 9; // 0x9 - field public static final int SENDING = 3; // 0x3 - field public static final int SENT = 4; // 0x4 - } - - @IntDef({android.telephony.ims.RcsMessage.DRAFT, android.telephony.ims.RcsMessage.QUEUED, android.telephony.ims.RcsMessage.SENDING, android.telephony.ims.RcsMessage.SENT, android.telephony.ims.RcsMessage.RETRYING, android.telephony.ims.RcsMessage.FAILED, android.telephony.ims.RcsMessage.RECEIVED, android.telephony.ims.RcsMessage.SEEN}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface RcsMessage.RcsMessageStatus { - } - - public class RcsMessageCreationParams { - ctor protected RcsMessageCreationParams(android.telephony.ims.RcsMessageCreationParams.Builder); - method public double getLatitude(); - method public double getLongitude(); - method public int getMessageStatus(); - method public long getOriginationTimestamp(); - method @Nullable public String getRcsMessageGlobalId(); - method public int getSubId(); - method @Nullable public String getText(); - } - - public static class RcsMessageCreationParams.Builder { - method public android.telephony.ims.RcsMessageCreationParams build(); - method @CheckResult public android.telephony.ims.RcsMessageCreationParams.Builder setLatitude(double); - method @CheckResult public android.telephony.ims.RcsMessageCreationParams.Builder setLongitude(double); - method @CheckResult public android.telephony.ims.RcsMessageCreationParams.Builder setRcsMessageId(String); - method @CheckResult public android.telephony.ims.RcsMessageCreationParams.Builder setStatus(@android.telephony.ims.RcsMessage.RcsMessageStatus int); - method @CheckResult public android.telephony.ims.RcsMessageCreationParams.Builder setText(String); - } - - public final class RcsMessageQueryParams implements android.os.Parcelable { - method public int describeContents(); - method public int getFileTransferPresence(); - method public int getLimit(); - method public String getMessageLike(); - method public int getMessageType(); - method public boolean getSortDirection(); - method @android.telephony.ims.RcsMessageQueryParams.SortingProperty public int getSortingProperty(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsMessageQueryParams> CREATOR; - field public static final int MESSAGES_WITHOUT_FILE_TRANSFERS = 8; // 0x8 - field public static final int MESSAGES_WITH_FILE_TRANSFERS = 4; // 0x4 - field public static final int MESSAGE_TYPE_INCOMING = 1; // 0x1 - field public static final int MESSAGE_TYPE_OUTGOING = 2; // 0x2 - field public static final int SORT_BY_CREATION_ORDER = 0; // 0x0 - field public static final int SORT_BY_TIMESTAMP = 1; // 0x1 - } - - public static class RcsMessageQueryParams.Builder { - ctor public RcsMessageQueryParams.Builder(); - method public android.telephony.ims.RcsMessageQueryParams build(); - method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setFileTransferPresence(int); - method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setMessageLike(String); - method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setMessageType(int); - method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setResultLimit(@IntRange(from=0) int) throws java.security.InvalidParameterException; - method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setSortDirection(boolean); - method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setSortProperty(@android.telephony.ims.RcsMessageQueryParams.SortingProperty int); - method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setThread(@Nullable android.telephony.ims.RcsThread); - } - - @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsMessageQueryParams.SORT_BY_CREATION_ORDER, android.telephony.ims.RcsMessageQueryParams.SORT_BY_TIMESTAMP}) public static @interface RcsMessageQueryParams.SortingProperty { - } - - public final class RcsMessageQueryResult implements android.os.Parcelable { - method public int describeContents(); - method @Nullable public android.telephony.ims.RcsQueryContinuationToken getContinuationToken(); - method @NonNull public java.util.List<android.telephony.ims.RcsMessage> getMessages(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsMessageQueryResult> CREATOR; - } - - public final class RcsMessageSnippet implements android.os.Parcelable { - method public int describeContents(); - method @android.telephony.ims.RcsMessage.RcsMessageStatus public int getSnippetStatus(); - method @Nullable public String getSnippetText(); - method public long getSnippetTimestamp(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsMessageSnippet> CREATOR; - } - - public class RcsMessageStore { - ctor public RcsMessageStore(); - method @WorkerThread @NonNull public android.telephony.ims.RcsGroupThread createGroupThread(@Nullable java.util.List<android.telephony.ims.RcsParticipant>, @Nullable String, @Nullable android.net.Uri) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread @NonNull public android.telephony.ims.Rcs1To1Thread createRcs1To1Thread(@NonNull android.telephony.ims.RcsParticipant) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread @NonNull public android.telephony.ims.RcsParticipant createRcsParticipant(String, @Nullable String) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public void deleteThread(@NonNull android.telephony.ims.RcsThread) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread @NonNull public android.telephony.ims.RcsEventQueryResult getRcsEvents(@Nullable android.telephony.ims.RcsEventQueryParams) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread @NonNull public android.telephony.ims.RcsEventQueryResult getRcsEvents(@NonNull android.telephony.ims.RcsQueryContinuationToken) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread @NonNull public android.telephony.ims.RcsMessageQueryResult getRcsMessages(@Nullable android.telephony.ims.RcsMessageQueryParams) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread @NonNull public android.telephony.ims.RcsMessageQueryResult getRcsMessages(@NonNull android.telephony.ims.RcsQueryContinuationToken) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread @NonNull public android.telephony.ims.RcsParticipantQueryResult getRcsParticipants(@Nullable android.telephony.ims.RcsParticipantQueryParams) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread @NonNull public android.telephony.ims.RcsParticipantQueryResult getRcsParticipants(@NonNull android.telephony.ims.RcsQueryContinuationToken) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread @NonNull public android.telephony.ims.RcsThreadQueryResult getRcsThreads(@Nullable android.telephony.ims.RcsThreadQueryParams) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread @NonNull public android.telephony.ims.RcsThreadQueryResult getRcsThreads(@NonNull android.telephony.ims.RcsQueryContinuationToken) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread @NonNull public void persistRcsEvent(android.telephony.ims.RcsEvent) throws android.telephony.ims.RcsMessageStoreException; - } - - public class RcsMessageStoreException extends java.lang.Exception { - ctor public RcsMessageStoreException(String); - } - - public class RcsOutgoingMessage extends android.telephony.ims.RcsMessage { - method @NonNull @WorkerThread public java.util.List<android.telephony.ims.RcsOutgoingMessageDelivery> getOutgoingDeliveries() throws android.telephony.ims.RcsMessageStoreException; - method public boolean isIncoming(); - } - - public final class RcsOutgoingMessageCreationParams extends android.telephony.ims.RcsMessageCreationParams implements android.os.Parcelable { - method public int describeContents(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsOutgoingMessageCreationParams> CREATOR; - } - - public static class RcsOutgoingMessageCreationParams.Builder extends android.telephony.ims.RcsMessageCreationParams.Builder { - ctor public RcsOutgoingMessageCreationParams.Builder(long, int); - method public android.telephony.ims.RcsOutgoingMessageCreationParams build(); - } - - public class RcsOutgoingMessageDelivery { - method @WorkerThread public long getDeliveredTimestamp() throws android.telephony.ims.RcsMessageStoreException; - method @NonNull public android.telephony.ims.RcsOutgoingMessage getMessage(); - method @NonNull public android.telephony.ims.RcsParticipant getRecipient(); - method @WorkerThread public long getSeenTimestamp() throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread @android.telephony.ims.RcsMessage.RcsMessageStatus public int getStatus() throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public void setDeliveredTimestamp(long) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public void setSeenTimestamp(long) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public void setStatus(@android.telephony.ims.RcsMessage.RcsMessageStatus int) throws android.telephony.ims.RcsMessageStoreException; - } - - public class RcsParticipant { - method @Nullable @WorkerThread public String getAlias() throws android.telephony.ims.RcsMessageStoreException; - method @Nullable @WorkerThread public String getCanonicalAddress() throws android.telephony.ims.RcsMessageStoreException; - method @Nullable @WorkerThread public String getContactId() throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public void setAlias(String) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public void setContactId(String) throws android.telephony.ims.RcsMessageStoreException; - } - - public final class RcsParticipantAliasChangedEvent extends android.telephony.ims.RcsEvent { - ctor public RcsParticipantAliasChangedEvent(long, @NonNull android.telephony.ims.RcsParticipant, @Nullable String); - method @Nullable public String getNewAlias(); - method @NonNull public android.telephony.ims.RcsParticipant getParticipant(); - } - - public final class RcsParticipantQueryParams implements android.os.Parcelable { - method public int describeContents(); - method public String getAliasLike(); - method public String getCanonicalAddressLike(); - method public int getLimit(); - method public boolean getSortDirection(); - method public int getSortingProperty(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsParticipantQueryParams> CREATOR; - field public static final int SORT_BY_ALIAS = 1; // 0x1 - field public static final int SORT_BY_CANONICAL_ADDRESS = 2; // 0x2 - field public static final int SORT_BY_CREATION_ORDER = 0; // 0x0 - } - - public static class RcsParticipantQueryParams.Builder { - ctor public RcsParticipantQueryParams.Builder(); - method public android.telephony.ims.RcsParticipantQueryParams build(); - method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setAliasLike(String); - method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setCanonicalAddressLike(String); - method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setResultLimit(@IntRange(from=0) int) throws java.security.InvalidParameterException; - method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setSortDirection(boolean); - method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setSortProperty(@android.telephony.ims.RcsParticipantQueryParams.SortingProperty int); - method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setThread(android.telephony.ims.RcsThread); - } - - @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsParticipantQueryParams.SORT_BY_CREATION_ORDER, android.telephony.ims.RcsParticipantQueryParams.SORT_BY_ALIAS, android.telephony.ims.RcsParticipantQueryParams.SORT_BY_CANONICAL_ADDRESS}) public static @interface RcsParticipantQueryParams.SortingProperty { - } - - public final class RcsParticipantQueryResult implements android.os.Parcelable { - method public int describeContents(); - method @Nullable public android.telephony.ims.RcsQueryContinuationToken getContinuationToken(); - method @NonNull public java.util.List<android.telephony.ims.RcsParticipant> getParticipants(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsParticipantQueryResult> CREATOR; - } - - public final class RcsQueryContinuationToken implements android.os.Parcelable { - method public int describeContents(); - method @android.telephony.ims.RcsQueryContinuationToken.ContinuationTokenType public int getQueryType(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsQueryContinuationToken> CREATOR; - field public static final int EVENT_QUERY_CONTINUATION_TOKEN_TYPE = 0; // 0x0 - field public static final int MESSAGE_QUERY_CONTINUATION_TOKEN_TYPE = 1; // 0x1 - field public static final int PARTICIPANT_QUERY_CONTINUATION_TOKEN_TYPE = 2; // 0x2 - field public static final int THREAD_QUERY_CONTINUATION_TOKEN_TYPE = 3; // 0x3 - } - - @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsQueryContinuationToken.EVENT_QUERY_CONTINUATION_TOKEN_TYPE, android.telephony.ims.RcsQueryContinuationToken.MESSAGE_QUERY_CONTINUATION_TOKEN_TYPE, android.telephony.ims.RcsQueryContinuationToken.PARTICIPANT_QUERY_CONTINUATION_TOKEN_TYPE, android.telephony.ims.RcsQueryContinuationToken.THREAD_QUERY_CONTINUATION_TOKEN_TYPE}) public static @interface RcsQueryContinuationToken.ContinuationTokenType { - } - - public abstract class RcsThread { - method @WorkerThread @NonNull public android.telephony.ims.RcsIncomingMessage addIncomingMessage(@NonNull android.telephony.ims.RcsIncomingMessageCreationParams) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread @NonNull public android.telephony.ims.RcsOutgoingMessage addOutgoingMessage(@NonNull android.telephony.ims.RcsOutgoingMessageCreationParams) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread public void deleteMessage(@NonNull android.telephony.ims.RcsMessage) throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread @NonNull public android.telephony.ims.RcsMessageQueryResult getMessages() throws android.telephony.ims.RcsMessageStoreException; - method @WorkerThread @NonNull public android.telephony.ims.RcsMessageSnippet getSnippet() throws android.telephony.ims.RcsMessageStoreException; - method public abstract boolean isGroup(); - } - - public final class RcsThreadQueryParams implements android.os.Parcelable { - method public int describeContents(); - method public int getLimit(); - method public boolean getSortDirection(); - method @android.telephony.ims.RcsThreadQueryParams.SortingProperty public int getSortingProperty(); - method public int getThreadType(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsThreadQueryParams> CREATOR; - field public static final int SORT_BY_CREATION_ORDER = 0; // 0x0 - field public static final int SORT_BY_TIMESTAMP = 1; // 0x1 - field public static final int THREAD_TYPE_1_TO_1 = 2; // 0x2 - field public static final int THREAD_TYPE_GROUP = 1; // 0x1 - } - - public static class RcsThreadQueryParams.Builder { - ctor public RcsThreadQueryParams.Builder(); - method public android.telephony.ims.RcsThreadQueryParams build(); - method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setParticipant(@NonNull android.telephony.ims.RcsParticipant); - method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setParticipants(@NonNull java.util.List<android.telephony.ims.RcsParticipant>); - method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setResultLimit(@IntRange(from=0) int) throws java.security.InvalidParameterException; - method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setSortDirection(boolean); - method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setSortProperty(@android.telephony.ims.RcsThreadQueryParams.SortingProperty int); - method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setThreadType(int); - } - - @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsThreadQueryParams.SORT_BY_CREATION_ORDER, android.telephony.ims.RcsThreadQueryParams.SORT_BY_TIMESTAMP}) public static @interface RcsThreadQueryParams.SortingProperty { - } - - public final class RcsThreadQueryResult implements android.os.Parcelable { - method public int describeContents(); - method @Nullable public android.telephony.ims.RcsQueryContinuationToken getContinuationToken(); - method @NonNull public java.util.List<android.telephony.ims.RcsThread> getThreads(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsThreadQueryResult> CREATOR; - } - -} - package android.telephony.mbms { public class DownloadProgressListener { @@ -47454,6 +47012,7 @@ package android.text.style { method public int getVerticalAlignment(); field public static final int ALIGN_BASELINE = 1; // 0x1 field public static final int ALIGN_BOTTOM = 0; // 0x0 + field public static final int ALIGN_CENTER = 2; // 0x2 field protected final int mVerticalAlignment; } @@ -50943,6 +50502,7 @@ package android.view { method public android.animation.StateListAnimator getStateListAnimator(); method protected int getSuggestedMinimumHeight(); method protected int getSuggestedMinimumWidth(); + method @NonNull public java.util.List<android.graphics.Rect> getSystemGestureExclusionRects(); method public int getSystemUiVisibility(); method @android.view.ViewDebug.ExportedProperty public Object getTag(); method public Object getTag(int); @@ -51281,6 +50841,7 @@ package android.view { method public void setSelected(boolean); method public void setSoundEffectsEnabled(boolean); method public void setStateListAnimator(android.animation.StateListAnimator); + method public void setSystemGestureExclusionRects(@NonNull java.util.List<android.graphics.Rect>); method public void setSystemUiVisibility(int); method public void setTag(Object); method public void setTag(int, Object); @@ -51633,8 +51194,8 @@ package android.view { method public int getScaledHoverSlop(); method public int getScaledMaximumDrawingCacheSize(); method public int getScaledMaximumFlingVelocity(); - method public int getScaledMinScalingSpan(); method public int getScaledMinimumFlingVelocity(); + method public int getScaledMinimumScalingSpan(); method public int getScaledOverflingDistance(); method public int getScaledOverscrollDistance(); method public int getScaledPagingTouchSlop(); @@ -52109,6 +51670,7 @@ package android.view { method public void addOnGlobalLayoutListener(android.view.ViewTreeObserver.OnGlobalLayoutListener); method public void addOnPreDrawListener(android.view.ViewTreeObserver.OnPreDrawListener); method public void addOnScrollChangedListener(android.view.ViewTreeObserver.OnScrollChangedListener); + method public void addOnSystemGestureExclusionRectsChangedListener(java.util.function.Consumer<java.util.List<android.graphics.Rect>>); method public void addOnTouchModeChangeListener(android.view.ViewTreeObserver.OnTouchModeChangeListener); method public void addOnWindowAttachListener(android.view.ViewTreeObserver.OnWindowAttachListener); method public void addOnWindowFocusChangeListener(android.view.ViewTreeObserver.OnWindowFocusChangeListener); @@ -52123,6 +51685,7 @@ package android.view { method public void removeOnGlobalLayoutListener(android.view.ViewTreeObserver.OnGlobalLayoutListener); method public void removeOnPreDrawListener(android.view.ViewTreeObserver.OnPreDrawListener); method public void removeOnScrollChangedListener(android.view.ViewTreeObserver.OnScrollChangedListener); + method public void removeOnSystemGestureExclusionRectsChangedListener(java.util.function.Consumer<java.util.List<android.graphics.Rect>>); method public void removeOnTouchModeChangeListener(android.view.ViewTreeObserver.OnTouchModeChangeListener); method public void removeOnWindowAttachListener(android.view.ViewTreeObserver.OnWindowAttachListener); method public void removeOnWindowFocusChangeListener(android.view.ViewTreeObserver.OnWindowFocusChangeListener); @@ -52408,16 +51971,16 @@ package android.view { method public boolean hasInsets(); method public boolean hasStableInsets(); method public boolean hasSystemWindowInsets(); - method @NonNull public android.view.WindowInsets inset(int, int, int, int); + method @NonNull public android.view.WindowInsets inset(@IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int); method public boolean isConsumed(); method public boolean isRound(); method @Deprecated @NonNull public android.view.WindowInsets replaceSystemWindowInsets(int, int, int, int); method @Deprecated @NonNull public android.view.WindowInsets replaceSystemWindowInsets(android.graphics.Rect); } - public static class WindowInsets.Builder { + public static final class WindowInsets.Builder { ctor public WindowInsets.Builder(); - ctor public WindowInsets.Builder(android.view.WindowInsets); + ctor public WindowInsets.Builder(@NonNull android.view.WindowInsets); method @NonNull public android.view.WindowInsets build(); method @NonNull public android.view.WindowInsets.Builder setDisplayCutout(@Nullable android.view.DisplayCutout); method @NonNull public android.view.WindowInsets.Builder setStableInsets(@NonNull android.graphics.Insets); @@ -58201,16 +57764,16 @@ package android.widget { method @Deprecated public void onZoom(boolean); } - public class ZoomControls extends android.widget.LinearLayout { - ctor public ZoomControls(android.content.Context); - ctor public ZoomControls(android.content.Context, android.util.AttributeSet); - method public void hide(); - method public void setIsZoomInEnabled(boolean); - method public void setIsZoomOutEnabled(boolean); - method public void setOnZoomInClickListener(android.view.View.OnClickListener); - method public void setOnZoomOutClickListener(android.view.View.OnClickListener); - method public void setZoomSpeed(long); - method public void show(); + @Deprecated public class ZoomControls extends android.widget.LinearLayout { + ctor @Deprecated public ZoomControls(android.content.Context); + ctor @Deprecated public ZoomControls(android.content.Context, android.util.AttributeSet); + method @Deprecated public void hide(); + method @Deprecated public void setIsZoomInEnabled(boolean); + method @Deprecated public void setIsZoomOutEnabled(boolean); + method @Deprecated public void setOnZoomInClickListener(android.view.View.OnClickListener); + method @Deprecated public void setOnZoomOutClickListener(android.view.View.OnClickListener); + method @Deprecated public void setZoomSpeed(long); + method @Deprecated public void show(); } } diff --git a/api/removed.txt b/api/removed.txt index c4ed871d0661..40b1316a2a5d 100644 --- a/api/removed.txt +++ b/api/removed.txt @@ -508,6 +508,8 @@ package android.provider { } public final class MediaStore { + method @Deprecated @NonNull public static android.net.Uri createPending(@NonNull android.content.Context, @NonNull android.provider.MediaStore.PendingParams); + method @Deprecated @NonNull public static android.provider.MediaStore.PendingSession openPending(@NonNull android.content.Context, @NonNull android.net.Uri); method @Deprecated @NonNull public static android.net.Uri setIncludeTrashed(@NonNull android.net.Uri); method @Deprecated public static void trash(@NonNull android.content.Context, @NonNull android.net.Uri); method @Deprecated public static void trash(@NonNull android.content.Context, @NonNull android.net.Uri, long); @@ -515,11 +517,27 @@ package android.provider { } public static interface MediaStore.MediaColumns extends android.provider.BaseColumns { - field @Deprecated public static final String DATE_EXPIRES = "date_expires"; field @Deprecated public static final String HASH = "_hash"; field @Deprecated public static final String IS_TRASHED = "is_trashed"; } + @Deprecated public static class MediaStore.PendingParams { + ctor public MediaStore.PendingParams(@NonNull android.net.Uri, @NonNull String, @NonNull String); + method public void setDownloadUri(@Nullable android.net.Uri); + method public void setPrimaryDirectory(@Nullable String); + method public void setRefererUri(@Nullable android.net.Uri); + method public void setSecondaryDirectory(@Nullable String); + } + + @Deprecated public static class MediaStore.PendingSession implements java.lang.AutoCloseable { + method public void abandon(); + method public void close(); + method public void notifyProgress(@IntRange(from=0, to=100) int); + method @NonNull public android.os.ParcelFileDescriptor open() throws java.io.FileNotFoundException; + method @NonNull public java.io.OutputStream openOutputStream() throws java.io.FileNotFoundException; + method @NonNull public android.net.Uri publish(); + } + public static final class Settings.Global extends android.provider.Settings.NameValueTable { field @Deprecated public static final String CONTACT_METADATA_SYNC = "contact_metadata_sync"; } diff --git a/api/system-current.txt b/api/system-current.txt index e7879277234b..a3bc88cbda85 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -15,6 +15,7 @@ package android { field public static final String ACCESS_SHORTCUTS = "android.permission.ACCESS_SHORTCUTS"; field public static final String ACCESS_SURFACE_FLINGER = "android.permission.ACCESS_SURFACE_FLINGER"; field public static final String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING"; + field public static final String ADJUST_RUNTIME_PERMISSIONS_POLICY = "android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY"; field public static final String ALLOCATE_AGGRESSIVE = "android.permission.ALLOCATE_AGGRESSIVE"; field public static final String ALLOW_ANY_CODEC_FOR_PLAYBACK = "android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK"; field public static final String AMBIENT_WALLPAPER = "android.permission.AMBIENT_WALLPAPER"; @@ -564,7 +565,7 @@ package android.app { } public class StatusBarManager { - method public android.app.StatusBarManager.DisableInfo getDisableInfo(); + method @NonNull public android.app.StatusBarManager.DisableInfo getDisableInfo(); method public void setDisabledForSetup(boolean); } @@ -1110,7 +1111,6 @@ package android.app.role { method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean removeRoleHolderFromController(@NonNull String, @NonNull String); method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public void setRoleNamesFromController(@NonNull java.util.List<java.lang.String>); field public static final int MANAGE_HOLDERS_FLAG_DONT_KILL_APP = 1; // 0x1 - field public static final String ROLE_ASSISTANT = "android.app.role.ASSISTANT"; } public interface RoleManagerCallback { @@ -1172,7 +1172,7 @@ package android.app.usage { method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getAppStandbyBucket(String); method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public java.util.Map<java.lang.String,java.lang.Integer> getAppStandbyBuckets(); method public int getUsageSource(); - method @RequiresPermission(allOf={android.Manifest.permission.SUSPEND_APPS, android.Manifest.permission.OBSERVE_APP_USAGE}) public void registerAppUsageLimitObserver(int, @NonNull String[], long, @NonNull java.util.concurrent.TimeUnit, android.app.PendingIntent); + method @RequiresPermission(allOf={android.Manifest.permission.SUSPEND_APPS, android.Manifest.permission.OBSERVE_APP_USAGE}) public void registerAppUsageLimitObserver(int, @NonNull String[], long, @NonNull java.util.concurrent.TimeUnit, @Nullable android.app.PendingIntent); method @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE) public void registerAppUsageObserver(int, @NonNull String[], long, @NonNull java.util.concurrent.TimeUnit, @NonNull android.app.PendingIntent); method @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE) public void registerUsageSessionObserver(int, @NonNull String[], long, @NonNull java.util.concurrent.TimeUnit, long, @NonNull java.util.concurrent.TimeUnit, @NonNull android.app.PendingIntent, @Nullable android.app.PendingIntent); method public void reportUsageStart(@NonNull android.app.Activity, @NonNull String); @@ -1304,9 +1304,8 @@ package android.content { } public abstract class ContentResolver { - method @Nullable public android.os.Bundle getCache(@NonNull android.net.Uri); - method public android.graphics.drawable.Drawable getTypeDrawable(String); - method public void putCache(@NonNull android.net.Uri, @Nullable android.os.Bundle); + method @Nullable @RequiresPermission("android.permission.CACHE_CONTENT") public android.os.Bundle getCache(@NonNull android.net.Uri); + method @RequiresPermission("android.permission.CACHE_CONTENT") public void putCache(@NonNull android.net.Uri, @Nullable android.os.Bundle); } public abstract class Context { @@ -1558,6 +1557,7 @@ package android.content.pm { method public void setDontKillApp(boolean); method public void setEnableRollback(); method @RequiresPermission(android.Manifest.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS) public void setGrantedRuntimePermissions(String[]); + method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setInstallAsApex(); method public void setInstallAsInstantApp(boolean); method public void setInstallAsVirtualPreload(); method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setStaged(); @@ -1576,6 +1576,7 @@ package android.content.pm { method @RequiresPermission("android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS") public abstract void addOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener); method public abstract boolean arePermissionsIndividuallyControlled(); method public abstract java.util.List<android.content.IntentFilter> getAllIntentFilters(String); + method public boolean getAppDetailsActivityEnabled(@NonNull String); method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.pm.ApplicationInfo getApplicationInfoAsUser(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException; method @NonNull public android.content.pm.dex.ArtManager getArtManager(); method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract String getDefaultBrowserPackageNameAsUser(int); @@ -1591,8 +1592,8 @@ package android.content.pm { method @android.content.pm.PackageManager.PermissionFlags @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS}) public abstract int getPermissionFlags(String, String, @NonNull android.os.UserHandle); method @NonNull @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] getUnsuspendablePackages(@NonNull String[]); method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle); - method public abstract int installExistingPackage(String) throws android.content.pm.PackageManager.NameNotFoundException; - method public abstract int installExistingPackage(String, int) throws android.content.pm.PackageManager.NameNotFoundException; + method @Deprecated public abstract int installExistingPackage(String) throws android.content.pm.PackageManager.NameNotFoundException; + method @Deprecated public abstract int installExistingPackage(String, int) throws android.content.pm.PackageManager.NameNotFoundException; method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceiversAsUser(android.content.Intent, int, android.os.UserHandle); method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentActivitiesAsUser(@NonNull android.content.Intent, int, @NonNull android.os.UserHandle); method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public java.util.List<android.content.pm.ResolveInfo> queryIntentContentProvidersAsUser(@NonNull android.content.Intent, int, @NonNull android.os.UserHandle); @@ -1602,6 +1603,7 @@ package android.content.pm { method @Deprecated public void replacePreferredActivity(@NonNull android.content.IntentFilter, int, @NonNull java.util.List<android.content.ComponentName>, @NonNull android.content.ComponentName); method @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public abstract void revokeRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle); method public void sendDeviceCustomizationReadyBroadcast(); + method @RequiresPermission(value=android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE, conditional=true) public void setAppDetailsActivityEnabled(@NonNull String, boolean); method @RequiresPermission(allOf={android.Manifest.permission.SET_PREFERRED_APPLICATIONS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public abstract boolean setDefaultBrowserPackageNameAsUser(String, int); method @NonNull @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setDistractingPackageRestrictions(@NonNull String[], int); method @RequiresPermission(android.Manifest.permission.SET_HARMFUL_APP_WARNINGS) public void setHarmfulAppWarning(@NonNull String, @Nullable CharSequence); @@ -1712,8 +1714,13 @@ package android.content.pm { field public boolean handleAllWebDataURI; } + public final class ShortcutInfo implements android.os.Parcelable { + method @Nullable public android.app.Person[] getPersons(); + } + public class ShortcutManager { method @NonNull public java.util.List<android.content.pm.ShortcutManager.ShareShortcutInfo> getShareTargets(@NonNull android.content.IntentFilter); + method public boolean hasShareTargets(@NonNull String); } public static final class ShortcutManager.ShareShortcutInfo implements android.os.Parcelable { @@ -1891,7 +1898,7 @@ package android.hardware.display { public final class BrightnessConfiguration implements android.os.Parcelable { method public int describeContents(); method @Nullable public android.hardware.display.BrightnessCorrection getCorrectionByCategory(int); - method @Nullable public android.hardware.display.BrightnessCorrection getCorrectionByPackageName(String); + method @Nullable public android.hardware.display.BrightnessCorrection getCorrectionByPackageName(@NonNull String); method public android.util.Pair<float[],float[]> getCurve(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessConfiguration> CREATOR; @@ -1899,8 +1906,8 @@ package android.hardware.display { public static class BrightnessConfiguration.Builder { ctor public BrightnessConfiguration.Builder(float[], float[]); - method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByCategory(int, android.hardware.display.BrightnessCorrection); - method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByPackageName(String, android.hardware.display.BrightnessCorrection); + method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByCategory(int, @NonNull android.hardware.display.BrightnessCorrection); + method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByPackageName(@NonNull String, @NonNull android.hardware.display.BrightnessCorrection); method public android.hardware.display.BrightnessConfiguration build(); method public int getMaxCorrectionsByCategory(); method public int getMaxCorrectionsByPackageName(); @@ -1908,7 +1915,7 @@ package android.hardware.display { } public final class BrightnessCorrection implements android.os.Parcelable { - method public float apply(float); + method @FloatRange(from=0.0) public float apply(@FloatRange(from=0.0) float); method @NonNull public static android.hardware.display.BrightnessCorrection createScaleAndTranslateLog(float, float); method public int describeContents(); method public void writeToParcel(android.os.Parcel, int); @@ -3037,21 +3044,21 @@ package android.location { method public double getHorizontalPositionUncertaintyMeters(); method public double getLatitudeDegrees(); method public double getLongitudeDegrees(); - method @Nullable public java.util.List<android.location.GnssSingleSatCorrection> getSingleSatCorrectionList(); + method @Nullable public java.util.List<android.location.GnssSingleSatCorrection> getSingleSatelliteCorrectionList(); method public long getToaGpsNanosecondsOfWeek(); method public double getVerticalPositionUncertaintyMeters(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.location.GnssMeasurementCorrections> CREATOR; } - public static class GnssMeasurementCorrections.Builder { + public static final class GnssMeasurementCorrections.Builder { ctor public GnssMeasurementCorrections.Builder(); method public android.location.GnssMeasurementCorrections build(); method public android.location.GnssMeasurementCorrections.Builder setAltitudeMeters(double); method public android.location.GnssMeasurementCorrections.Builder setHorizontalPositionUncertaintyMeters(double); method public android.location.GnssMeasurementCorrections.Builder setLatitudeDegrees(double); method public android.location.GnssMeasurementCorrections.Builder setLongitudeDegrees(double); - method public android.location.GnssMeasurementCorrections.Builder setSingleSatCorrectionList(@Nullable java.util.List<android.location.GnssSingleSatCorrection>); + method public android.location.GnssMeasurementCorrections.Builder setSingleSatelliteCorrectionList(@Nullable java.util.List<android.location.GnssSingleSatCorrection>); method public android.location.GnssMeasurementCorrections.Builder setToaGpsNanosecondsOfWeek(long); method public android.location.GnssMeasurementCorrections.Builder setVerticalPositionUncertaintyMeters(double); } @@ -3066,7 +3073,7 @@ package android.location { field public static final android.os.Parcelable.Creator<android.location.GnssReflectingPlane> CREATOR; } - public static class GnssReflectingPlane.Builder { + public static final class GnssReflectingPlane.Builder { ctor public GnssReflectingPlane.Builder(); method public android.location.GnssReflectingPlane build(); method public android.location.GnssReflectingPlane.Builder setAltitudeMeters(double); @@ -3081,14 +3088,14 @@ package android.location { method public int getConstellationType(); method public float getExcessPathLengthMeters(); method public float getExcessPathLengthUncertaintyMeters(); - method @FloatRange(from=0.0f, to=1.0f) public float getProbSatIsLos(); + method @FloatRange(from=0.0f, to=1.0f) public float getProbabilityLineOfSight(); method @Nullable public android.location.GnssReflectingPlane getReflectingPlane(); - method public int getSatId(); - method public int getSingleSatCorrectionFlags(); + method public int getSatelliteId(); + method public int getSingleSatelliteCorrectionFlags(); method public boolean hasExcessPathLength(); method public boolean hasExcessPathLengthUncertainty(); method public boolean hasReflectingPlane(); - method public boolean hasSatelliteLineOfSight(); + method public boolean hasValidSatelliteLineOfSight(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.location.GnssSingleSatCorrection> CREATOR; field public static final int HAS_EXCESS_PATH_LENGTH_MASK = 2; // 0x2 @@ -3097,17 +3104,17 @@ package android.location { field public static final int HAS_REFLECTING_PLANE_MASK = 8; // 0x8 } - public static class GnssSingleSatCorrection.Builder { + public static final class GnssSingleSatCorrection.Builder { ctor public GnssSingleSatCorrection.Builder(); method public android.location.GnssSingleSatCorrection build(); method public android.location.GnssSingleSatCorrection.Builder setCarrierFrequencyHz(float); method public android.location.GnssSingleSatCorrection.Builder setConstellationType(int); method public android.location.GnssSingleSatCorrection.Builder setExcessPathLengthMeters(float); method public android.location.GnssSingleSatCorrection.Builder setExcessPathLengthUncertaintyMeters(float); - method public android.location.GnssSingleSatCorrection.Builder setProbSatIsLos(@FloatRange(from=0.0f, to=1.0f) float); + method public android.location.GnssSingleSatCorrection.Builder setProbabilityLineOfSight(@FloatRange(from=0.0f, to=1.0f) float); method public android.location.GnssSingleSatCorrection.Builder setReflectingPlane(android.location.GnssReflectingPlane); - method public android.location.GnssSingleSatCorrection.Builder setSatId(int); - method public android.location.GnssSingleSatCorrection.Builder setSingleSatCorrectionFlags(int); + method public android.location.GnssSingleSatCorrection.Builder setSatelliteId(int); + method public android.location.GnssSingleSatCorrection.Builder setSingleSatelliteCorrectionFlags(int); } public class GpsClock implements android.os.Parcelable { @@ -3448,15 +3455,23 @@ package android.media { method @Deprecated public int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes); method public void clearAudioServerStateCallback(); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int dispatchAudioFocusChange(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy); + method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.audiopolicy.AudioProductStrategies getAudioProductStrategies(); + method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.audiopolicy.AudioVolumeGroups getAudioVolumeGroups(); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMaxVolumeIndexForAttributes(@NonNull android.media.AudioAttributes); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMinVolumeIndexForAttributes(@NonNull android.media.AudioAttributes); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getVolumeIndexForAttributes(@NonNull android.media.AudioAttributes); method public boolean isAudioServerRunning(); method public boolean isHdmiSystemAudioSupported(); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int registerAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy); + method public void registerVolumeGroupCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.VolumeGroupCallback); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int) throws java.lang.IllegalArgumentException; method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.MODIFY_AUDIO_ROUTING}) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int, android.media.audiopolicy.AudioPolicy) throws java.lang.IllegalArgumentException; method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int requestAudioFocus(@NonNull android.media.AudioFocusRequest, @Nullable android.media.audiopolicy.AudioPolicy); method public void setAudioServerStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.AudioServerStateCallback); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy); + method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setVolumeIndexForAttributes(@NonNull android.media.AudioAttributes, int, int); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicyAsync(@NonNull android.media.audiopolicy.AudioPolicy); + method public void unregisterVolumeGroupCallback(@NonNull android.media.AudioManager.VolumeGroupCallback); field public static final int AUDIOFOCUS_FLAG_DELAY_OK = 1; // 0x1 field public static final int AUDIOFOCUS_FLAG_LOCK = 4; // 0x4 field public static final int AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS = 2; // 0x2 @@ -3470,6 +3485,11 @@ package android.media { method public void onAudioServerUp(); } + public abstract static class AudioManager.VolumeGroupCallback { + ctor public AudioManager.VolumeGroupCallback(); + method public void onAudioVolumeGroupChanged(int, int); + } + public final class AudioPlaybackConfiguration implements android.os.Parcelable { method public int getClientPid(); method public int getClientUid(); @@ -3591,10 +3611,10 @@ package android.media.audiopolicy { method public int detachMixes(@NonNull java.util.List<android.media.audiopolicy.AudioMix>); method public int getFocusDuckingBehavior(); method public int getStatus(); - method public int removeUidDeviceAffinity(int); + method public boolean removeUidDeviceAffinity(int); method public int setFocusDuckingBehavior(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException; method public void setRegistration(String); - method public int setUidDeviceAffinity(int, @NonNull java.util.List<android.media.AudioDeviceInfo>); + method public boolean setUidDeviceAffinity(int, @NonNull java.util.List<android.media.AudioDeviceInfo>); method public String toLogFriendlyString(); field public static final int FOCUS_POLICY_DUCKING_DEFAULT = 0; // 0x0 field public static final int FOCUS_POLICY_DUCKING_IN_APP = 0; // 0x0 @@ -3618,6 +3638,7 @@ package android.media.audiopolicy { } public abstract static class AudioPolicy.AudioPolicyVolumeCallback { + ctor public AudioPolicy.AudioPolicyVolumeCallback(); method public void onVolumeAdjustment(int); } @@ -3639,6 +3660,9 @@ package android.media.audiopolicy { method @NonNull public android.media.AudioAttributes getAudioAttributesForProductStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy); method @Nullable public android.media.audiopolicy.AudioProductStrategy getById(int); method public int getLegacyStreamTypeForAudioAttributes(@NonNull android.media.AudioAttributes); + method @Nullable public android.media.audiopolicy.AudioProductStrategy getProductStrategyForAudioAttributes(@NonNull android.media.AudioAttributes); + method public int getVolumeGroupIdForAttributes(@NonNull android.media.AudioAttributes); + method public int getVolumeGroupIdForLegacyStreamType(int); method public java.util.Iterator<android.media.audiopolicy.AudioProductStrategy> iterator(); method public int size(); method public void writeToParcel(@NonNull android.os.Parcel, int); @@ -3654,6 +3678,27 @@ package android.media.audiopolicy { field public static final android.os.Parcelable.Creator<android.media.audiopolicy.AudioProductStrategy> CREATOR; } + public final class AudioVolumeGroup implements android.os.Parcelable { + method public int describeContents(); + method public java.util.List<android.media.AudioAttributes> getAudioAttributes(); + method public int getId(); + method @NonNull public int[] getLegacyStreamTypes(); + method @NonNull public String name(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.media.audiopolicy.AudioVolumeGroup> CREATOR; + } + + public final class AudioVolumeGroups implements java.lang.Iterable<android.media.audiopolicy.AudioVolumeGroup> android.os.Parcelable { + ctor public AudioVolumeGroups(); + method public int describeContents(); + method @Nullable public android.media.audiopolicy.AudioVolumeGroup getById(int); + method public java.util.Iterator<android.media.audiopolicy.AudioVolumeGroup> iterator(); + method public int size(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.media.audiopolicy.AudioVolumeGroups> CREATOR; + field public static final int DEFAULT_VOLUME_GROUP = -1; // 0xffffffff + } + } package android.media.session { @@ -4665,7 +4710,10 @@ package android.net.wifi { } public class WifiInfo implements android.os.Parcelable { + method @Nullable public String getFqdn(); + method @Nullable public String getProviderFriendlyName(); method public boolean isOsuAp(); + method public boolean isPasspointAp(); } public class WifiManager { @@ -4838,7 +4886,8 @@ package android.net.wifi { ctor public WifiScanner.ScanSettings(); field public int band; field public android.net.wifi.WifiScanner.ChannelSpec[] channels; - field @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean ignoreLocationSettings; + field public boolean hideFromAppOps; + field public boolean ignoreLocationSettings; field public int maxPeriodInMs; field public int maxScansToCache; field public int numBssidsPerScan; @@ -5122,6 +5171,7 @@ package android.os { method public void onError(int); method public void onFinished(); method public void onProgress(float); + field public static final int BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS = 5; // 0x5 field public static final int BUGREPORT_ERROR_INVALID_INPUT = 1; // 0x1 field public static final int BUGREPORT_ERROR_RUNTIME = 2; // 0x2 field public static final int BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT = 4; // 0x4 @@ -5538,6 +5588,7 @@ package android.os { method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.PersistableBundle getSeedAccountOptions(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getSeedAccountType(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public long[] getSerialNumbersOfUsers(boolean); + method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.graphics.Bitmap getUserIcon(); method @Deprecated @android.os.UserManager.UserRestrictionSource @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public int getUserRestrictionSource(String, android.os.UserHandle); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public java.util.List<android.os.UserManager.EnforcingUser> getUserRestrictionSources(String, android.os.UserHandle); method public boolean hasRestrictedProfiles(); @@ -5548,6 +5599,8 @@ package android.os { method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isPrimaryUser(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isRestrictedProfile(); method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean removeUser(android.os.UserHandle); + method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setUserIcon(android.graphics.Bitmap); + method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setUserName(String); field public static final String ACTION_USER_RESTRICTIONS_CHANGED = "android.os.action.USER_RESTRICTIONS_CHANGED"; field @Deprecated public static final String DISALLOW_OEM_UNLOCK = "no_oem_unlock"; field public static final String DISALLOW_RUN_IN_BACKGROUND = "no_run_in_background"; @@ -5624,6 +5677,7 @@ package android.permission { method @BinderThread public abstract void onRestoreRuntimePermissionsBackup(@NonNull android.os.UserHandle, @NonNull java.io.InputStream); method public abstract void onRevokeRuntimePermission(@NonNull String, @NonNull String); method @NonNull public abstract java.util.Map<java.lang.String,java.util.List<java.lang.String>> onRevokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull String); + method public abstract boolean onSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull String, @NonNull String, @NonNull String, int); field public static final String SERVICE_INTERFACE = "android.permission.PermissionControllerService"; } @@ -5772,28 +5826,22 @@ package android.provider { public final class DeviceConfig { method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static void addOnPropertyChangedListener(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.provider.DeviceConfig.OnPropertyChangedListener); + method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static boolean getBoolean(String, String, boolean); + method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static float getFloat(String, String, float); + method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static int getInt(String, String, int); + method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static long getLong(String, String, long); method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static String getProperty(String, String); + method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static String getString(String, String, String); method public static void removeOnPropertyChangedListener(android.provider.DeviceConfig.OnPropertyChangedListener); method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static void resetToDefaults(int, @Nullable String); method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean setProperty(String, String, String, boolean); + field public static final String NAMESPACE_ACTIVITY_MANAGER = "activity_manager"; field public static final String NAMESPACE_AUTOFILL = "autofill"; field public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture"; field public static final String NAMESPACE_GAME_DRIVER = "game_driver"; field public static final String NAMESPACE_INPUT_NATIVE_BOOT = "input_native_boot"; field public static final String NAMESPACE_NETD_NATIVE = "netd_native"; - } - - public static interface DeviceConfig.ActivityManager { - field public static final String KEY_COMPACT_ACTION_1 = "compact_action_1"; - field public static final String KEY_COMPACT_ACTION_2 = "compact_action_2"; - field public static final String KEY_COMPACT_STATSD_SAMPLE_RATE = "compact_statsd_sample_rate"; - field public static final String KEY_COMPACT_THROTTLE_1 = "compact_throttle_1"; - field public static final String KEY_COMPACT_THROTTLE_2 = "compact_throttle_2"; - field public static final String KEY_COMPACT_THROTTLE_3 = "compact_throttle_3"; - field public static final String KEY_COMPACT_THROTTLE_4 = "compact_throttle_4"; - field public static final String KEY_MAX_CACHED_PROCESSES = "max_cached_processes"; - field public static final String KEY_USE_COMPACTION = "use_compaction"; - field public static final String NAMESPACE = "activity_manager"; + field public static final String NAMESPACE_SYSTEMUI = "systemui"; } public static interface DeviceConfig.ActivityManagerNativeBoot { @@ -5823,14 +5871,6 @@ package android.provider { field public static final String NAMESPACE = "media_native"; } - public static interface DeviceConfig.NotificationAssistant { - field public static final String GENERATE_ACTIONS = "generate_actions"; - field public static final String GENERATE_REPLIES = "generate_replies"; - field public static final String MAX_MESSAGES_TO_EXTRACT = "max_messages_to_extract"; - field public static final String MAX_SUGGESTIONS = "max_suggestions"; - field public static final String NAMESPACE = "notification_assistant"; - } - public static interface DeviceConfig.OnPropertyChangedListener { method public void onPropertyChanged(String, String, String); } @@ -6386,20 +6426,12 @@ package android.service.carrier { package android.service.contentcapture { - @Deprecated public final class ContentCaptureEventsRequest implements android.os.Parcelable { - method @Deprecated public int describeContents(); - method @Deprecated @NonNull public java.util.List<android.view.contentcapture.ContentCaptureEvent> getEvents(); - method @Deprecated public void writeToParcel(android.os.Parcel, int); - field @Deprecated public static final android.os.Parcelable.Creator<android.service.contentcapture.ContentCaptureEventsRequest> CREATOR; - } - public abstract class ContentCaptureService extends android.app.Service { ctor public ContentCaptureService(); method public final void disableContentCaptureServices(); method public void onActivitySnapshot(@NonNull android.view.contentcapture.ContentCaptureSessionId, @NonNull android.service.contentcapture.SnapshotData); method public void onConnected(); method public void onContentCaptureEvent(@NonNull android.view.contentcapture.ContentCaptureSessionId, @NonNull android.view.contentcapture.ContentCaptureEvent); - method @Deprecated public void onContentCaptureEventsRequest(@NonNull android.view.contentcapture.ContentCaptureSessionId, @NonNull android.service.contentcapture.ContentCaptureEventsRequest); method public void onCreateContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureContext, @NonNull android.view.contentcapture.ContentCaptureSessionId); method public void onDestroyContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureSessionId); method public void onDisconnected(); @@ -7468,7 +7500,14 @@ package android.telephony { field public static final int VSNCP_TIMEOUT = 2236; // 0x8bc } - public class DisconnectCause { + public final class DataSpecificRegistrationStates implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public android.telephony.LteVopsSupportInfo getLteVopsSupportInfo(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.telephony.DataSpecificRegistrationStates> CREATOR; + } + + public final class DisconnectCause { field public static final int ALREADY_DIALING = 72; // 0x48 field public static final int ANSWERED_ELSEWHERE = 52; // 0x34 field public static final int BUSY = 4; // 0x4 @@ -7576,6 +7615,7 @@ package android.telephony { method public int getAccessNetworkTechnology(); method public int[] getAvailableServices(); method public android.telephony.CellIdentity getCellIdentity(); + method @Nullable public android.telephony.DataSpecificRegistrationStates getDataSpecificStates(); method public int getDomain(); method public int getRegState(); method public int getRejectCause(); @@ -7646,18 +7686,18 @@ package android.telephony { public class PhoneStateListener { method public void onCallAttributesChanged(@NonNull android.telephony.CallAttributes); - method public void onCallDisconnectCauseChanged(int, int); - method public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo); - method public void onPreciseCallStateChanged(android.telephony.PreciseCallState); - method public void onPreciseDataConnectionStateChanged(android.telephony.PreciseDataConnectionState); + method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onCallDisconnectCauseChanged(int, int); + method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onImsCallDisconnectCauseChanged(@NonNull android.telephony.ims.ImsReasonInfo); + method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onPreciseCallStateChanged(@NonNull android.telephony.PreciseCallState); + method @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public void onPreciseDataConnectionStateChanged(android.telephony.PreciseDataConnectionState); method public void onRadioPowerStateChanged(int); method public void onSrvccStateChanged(int); method public void onVoiceActivationStateChanged(int); field public static final int LISTEN_CALL_ATTRIBUTES_CHANGED = 67108864; // 0x4000000 - field public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000 - field public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 134217728; // 0x8000000 - field public static final int LISTEN_PRECISE_CALL_STATE = 2048; // 0x800 - field public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000 + field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_CALL_DISCONNECT_CAUSES = 33554432; // 0x2000000 + field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 134217728; // 0x8000000 + field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_PRECISE_CALL_STATE = 2048; // 0x800 + field @RequiresPermission("android.permission.READ_PRECISE_PHONE_STATE") public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 4096; // 0x1000 field public static final int LISTEN_RADIO_POWER_STATE_CHANGED = 8388608; // 0x800000 field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final int LISTEN_SRVCC_STATE_CHANGED = 16384; // 0x4000 field public static final int LISTEN_VOICE_ACTIVATION_STATE = 131072; // 0x20000 @@ -7692,7 +7732,7 @@ package android.telephony { field public static final android.os.Parcelable.Creator<android.telephony.PreciseDataConnectionState> CREATOR; } - public class PreciseDisconnectCause { + public final class PreciseDisconnectCause { field public static final int ACCESS_CLASS_BLOCKED = 260; // 0x104 field public static final int ACCESS_INFORMATION_DISCARDED = 43; // 0x2b field public static final int ACM_LIMIT_EXCEEDED = 68; // 0x44 @@ -7910,7 +7950,7 @@ package android.telephony { method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public int getRadioPowerState(); method public int getSimApplicationState(); method public int getSimCardState(); - method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getSimLocale(); + method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Locale getSimLocale(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getSupportedRadioAccessFamily(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public java.util.List<android.telephony.TelephonyHistogram> getTelephonyHistograms(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.UiccSlotInfo[] getUiccSlotsInfo(); @@ -7921,7 +7961,6 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isCurrentPotentialEmergencyNumber(@NonNull String); method public boolean isDataConnectivityPossible(); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle(); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isMultisimCarrierRestricted(); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook(); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRadioOn(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isRebootRequiredForModemConfigChange(); @@ -8020,7 +8059,7 @@ package android.telephony { } public class UiccSlotInfo implements android.os.Parcelable { - ctor public UiccSlotInfo(boolean, boolean, String, int, int, boolean); + ctor @Deprecated public UiccSlotInfo(boolean, boolean, String, int, int, boolean); method public int describeContents(); method public String getCardId(); method public int getCardStateInfo(); @@ -8028,6 +8067,7 @@ package android.telephony { method public boolean getIsEuicc(); method public boolean getIsExtendedApduSupported(); method public int getLogicalSlotIdx(); + method public boolean isRemovable(); method public void writeToParcel(android.os.Parcel, int); field public static final int CARD_STATE_INFO_ABSENT = 1; // 0x1 field public static final int CARD_STATE_INFO_ERROR = 3; // 0x3 @@ -8481,14 +8521,14 @@ package android.telephony.ims { method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isVtSettingEnabled(); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback) throws android.telephony.ims.ImsException; method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerMmTelCapabilityCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.CapabilityCallback) throws android.telephony.ims.ImsException; - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setAdvancedCallingSetting(boolean); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setAdvancedCallingSettingEnabled(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setRttCapabilitySetting(boolean); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiModeSetting(int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiNonPersistent(boolean, int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiRoamingModeSetting(int); - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiRoamingSetting(boolean); - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiSetting(boolean); - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVtSetting(boolean); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiRoamingSettingEnabled(boolean); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiSettingEnabled(boolean); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVtSettingEnabled(boolean); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterMmTelCapabilityCallback(@NonNull android.telephony.ims.ImsMmTelManager.CapabilityCallback); field public static final int WIFI_MODE_CELLULAR_PREFERRED = 1; // 0x1 @@ -8503,10 +8543,10 @@ package android.telephony.ims { public static class ImsMmTelManager.RegistrationCallback { ctor public ImsMmTelManager.RegistrationCallback(); - method public void onDeregistered(android.telephony.ims.ImsReasonInfo); method public void onRegistered(int); method public void onRegistering(int); method public void onTechnologyChangeFailed(int, android.telephony.ims.ImsReasonInfo); + method public void onUnregistered(android.telephony.ims.ImsReasonInfo); } public final class ImsReasonInfo implements android.os.Parcelable { @@ -8823,12 +8863,12 @@ package android.telephony.ims { method public int describeContents(); method public int getAudioDirection(); method public int getAudioQuality(); - method public boolean getRttAudioSpeech(); method public int getRttMode(); method public int getVideoDirection(); method public int getVideoQuality(); + method public boolean isReceivingRttAudio(); method public boolean isRttCall(); - method public void setRttAudioSpeech(boolean); + method public void setReceivingRttAudio(boolean); method public void setRttMode(int); method public void writeToParcel(android.os.Parcel, int); field public static final int AUDIO_QUALITY_AMR = 1; // 0x1 @@ -9404,11 +9444,13 @@ package android.view.contentcapture { method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.view.contentcapture.ContentCaptureEvent> CREATOR; field public static final int TYPE_CONTEXT_UPDATED = 6; // 0x6 - field public static final int TYPE_INITIAL_VIEW_TREE_APPEARED = 5; // 0x5 - field public static final int TYPE_INITIAL_VIEW_TREE_APPEARING = 4; // 0x4 + field public static final int TYPE_SESSION_PAUSED = 8; // 0x8 + field public static final int TYPE_SESSION_RESUMED = 7; // 0x7 field public static final int TYPE_VIEW_APPEARED = 1; // 0x1 field public static final int TYPE_VIEW_DISAPPEARED = 2; // 0x2 field public static final int TYPE_VIEW_TEXT_CHANGED = 3; // 0x3 + field public static final int TYPE_VIEW_TREE_APPEARED = 5; // 0x5 + field public static final int TYPE_VIEW_TREE_APPEARING = 4; // 0x4 } public final class ContentCaptureManager { diff --git a/api/test-current.txt b/api/test-current.txt index ab03aa490bbc..a1d691eba04b 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -17,6 +17,10 @@ package android { field public static final String WRITE_OBB = "android.permission.WRITE_OBB"; } + public static final class R.bool { + field public static final int config_perDisplayFocusEnabled = 17891332; // 0x1110004 + } + public static final class R.string { field public static final int config_defaultAssistant = 17039393; // 0x1040021 field public static final int config_defaultDialer = 17039395; // 0x1040023 @@ -48,6 +52,7 @@ package android.app { method public long getTotalRam(); method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getUidImportance(int); method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener); + method public static void resumeAppSwitches() throws android.os.RemoteException; method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public void scheduleApplicationInfoChanged(java.util.List<java.lang.String>, int); } @@ -94,6 +99,31 @@ package android.app { field public static final int SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT = 0; // 0x0 } + public class ActivityView extends android.view.ViewGroup { + ctor public ActivityView(android.content.Context); + ctor public ActivityView(android.content.Context, android.util.AttributeSet); + ctor public ActivityView(android.content.Context, android.util.AttributeSet, int); + ctor public ActivityView(android.content.Context, android.util.AttributeSet, int, boolean); + method public void onLayout(boolean, int, int, int, int); + method public void onLocationChanged(); + method public void performBackPress(); + method public void release(); + method public void setCallback(android.app.ActivityView.StateCallback); + method public void setForwardedInsets(android.graphics.Insets); + method public void startActivity(@NonNull android.content.Intent); + method public void startActivity(@NonNull android.content.Intent, android.os.UserHandle); + method public void startActivity(@NonNull android.app.PendingIntent); + } + + public abstract static class ActivityView.StateCallback { + ctor public ActivityView.StateCallback(); + method public abstract void onActivityViewDestroyed(android.app.ActivityView); + method public abstract void onActivityViewReady(android.app.ActivityView); + method public void onTaskCreated(int, android.content.ComponentName); + method public void onTaskMovedToFront(int); + method public void onTaskRemovalStarted(int); + } + public class AppDetailsActivity extends android.app.Activity { ctor public AppDetailsActivity(); } @@ -446,7 +476,6 @@ package android.app.role { method @NonNull @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public java.util.List<java.lang.String> getRoleHolders(@NonNull String); method @NonNull @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public java.util.List<java.lang.String> getRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle); method @RequiresPermission("android.permission.MANAGE_ROLE_HOLDERS") public void removeRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.role.RoleManagerCallback); - field public static final String ROLE_ASSISTANT = "android.app.role.ASSISTANT"; } public interface RoleManagerCallback { @@ -483,6 +512,31 @@ package android.bluetooth { package android.content { + public final class AutofillOptions implements android.os.Parcelable { + ctor public AutofillOptions(int, boolean); + method public int describeContents(); + method public static android.content.AutofillOptions forWhitelistingItself(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.content.AutofillOptions> CREATOR; + field public boolean augmentedEnabled; + field public final boolean compatModeEnabled; + field public final int loggingLevel; + } + + public final class ContentCaptureOptions implements android.os.Parcelable { + ctor public ContentCaptureOptions(int, int, int, int, int, @Nullable android.util.ArraySet<android.content.ComponentName>); + method public int describeContents(); + method public static android.content.ContentCaptureOptions forWhitelistingItself(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.content.ContentCaptureOptions> CREATOR; + field public final int idleFlushingFrequencyMs; + field public final int logHistorySize; + field public final int loggingLevel; + field public final int maxBufferSize; + field public final int textChangeFlushingFrequencyMs; + field @Nullable public final android.util.ArraySet<android.content.ComponentName> whitelistedComponents; + } + public class ContentProviderClient implements java.lang.AutoCloseable { method @RequiresPermission(android.Manifest.permission.REMOVE_TASKS) public void setDetectNotResponding(long); } @@ -493,9 +547,15 @@ package android.content { public abstract class Context { method public android.content.Context createPackageContextAsUser(String, int, android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException; + method public abstract android.view.Display getDisplay(); method public android.os.UserHandle getUser(); method public int getUserId(); - method public void setAutofillCompatibilityEnabled(boolean); + method public void setAutofillOptions(@Nullable android.content.AutofillOptions); + method public void setContentCaptureOptions(@Nullable android.content.ContentCaptureOptions); + } + + public class ContextWrapper extends android.content.Context { + method public android.view.Display getDisplay(); } } @@ -624,7 +684,6 @@ package android.database.sqlite { method public static int getWALAutoCheckpoint(); method public static int getWALConnectionPoolSize(); method public static String getWALSyncMode(); - method public static boolean isCompatibilityWalSupported(); method public static int releaseMemory(); } @@ -681,6 +740,13 @@ package android.hardware.display { field public static final android.os.Parcelable.Creator<android.hardware.display.AmbientBrightnessDayStats> CREATOR; } + public class AmbientDisplayConfiguration { + ctor public AmbientDisplayConfiguration(android.content.Context); + method public boolean alwaysOnAvailable(); + method public boolean alwaysOnAvailableForUser(int); + method public boolean alwaysOnEnabled(int); + } + public final class BrightnessChangeEvent implements android.os.Parcelable { method public int describeContents(); method public void writeToParcel(android.os.Parcel, int); @@ -704,7 +770,7 @@ package android.hardware.display { public final class BrightnessConfiguration implements android.os.Parcelable { method public int describeContents(); method @Nullable public android.hardware.display.BrightnessCorrection getCorrectionByCategory(int); - method @Nullable public android.hardware.display.BrightnessCorrection getCorrectionByPackageName(String); + method @Nullable public android.hardware.display.BrightnessCorrection getCorrectionByPackageName(@NonNull String); method public android.util.Pair<float[],float[]> getCurve(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessConfiguration> CREATOR; @@ -712,8 +778,8 @@ package android.hardware.display { public static class BrightnessConfiguration.Builder { ctor public BrightnessConfiguration.Builder(float[], float[]); - method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByCategory(int, android.hardware.display.BrightnessCorrection); - method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByPackageName(String, android.hardware.display.BrightnessCorrection); + method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByCategory(int, @NonNull android.hardware.display.BrightnessCorrection); + method public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByPackageName(@NonNull String, @NonNull android.hardware.display.BrightnessCorrection); method public android.hardware.display.BrightnessConfiguration build(); method public int getMaxCorrectionsByCategory(); method public int getMaxCorrectionsByPackageName(); @@ -721,7 +787,7 @@ package android.hardware.display { } public final class BrightnessCorrection implements android.os.Parcelable { - method public float apply(float); + method @FloatRange(from=0.0) public float apply(@FloatRange(from=0.0) float); method @NonNull public static android.hardware.display.BrightnessCorrection createScaleAndTranslateLog(float, float); method public int describeContents(); method public void writeToParcel(android.os.Parcel, int); @@ -943,6 +1009,50 @@ package android.media.audiofx { } +package android.metrics { + + public class LogMaker { + ctor public LogMaker(int); + ctor public LogMaker(Object[]); + method public android.metrics.LogMaker addTaggedData(int, Object); + method public android.metrics.LogMaker clearCategory(); + method public android.metrics.LogMaker clearPackageName(); + method public android.metrics.LogMaker clearSubtype(); + method public android.metrics.LogMaker clearTaggedData(int); + method public android.metrics.LogMaker clearType(); + method public void deserialize(Object[]); + method public int getCategory(); + method public long getCounterBucket(); + method public String getCounterName(); + method public int getCounterValue(); + method public String getPackageName(); + method public int getProcessId(); + method public int getSubtype(); + method public Object getTaggedData(int); + method public long getTimestamp(); + method public int getType(); + method public int getUid(); + method public boolean isLongCounterBucket(); + method public boolean isSubsetOf(android.metrics.LogMaker); + method public boolean isValidValue(Object); + method public Object[] serialize(); + method public android.metrics.LogMaker setCategory(int); + method public android.metrics.LogMaker setPackageName(String); + method public android.metrics.LogMaker setSubtype(int); + method public android.metrics.LogMaker setType(int); + } + + public class MetricsReader { + ctor public MetricsReader(); + method public void checkpoint(); + method public boolean hasNext(); + method public android.metrics.LogMaker next(); + method public void read(long); + method public void reset(); + } + +} + package android.net { public class CaptivePortal implements android.os.Parcelable { @@ -1287,6 +1397,7 @@ package android.os { public class Build { method public static boolean is64BitAbi(String); + field public static final boolean IS_EMULATOR; } public static class Build.VERSION { @@ -1810,7 +1921,12 @@ package android.provider { public final class DeviceConfig { method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static void addOnPropertyChangedListener(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.provider.DeviceConfig.OnPropertyChangedListener); + method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static boolean getBoolean(String, String, boolean); + method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static float getFloat(String, String, float); + method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static int getInt(String, String, int); + method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static long getLong(String, String, long); method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static String getProperty(String, String); + method @RequiresPermission("android.permission.READ_DEVICE_CONFIG") public static String getString(String, String, String); method public static void removeOnPropertyChangedListener(android.provider.DeviceConfig.OnPropertyChangedListener); method @RequiresPermission("android.permission.WRITE_DEVICE_CONFIG") public static void resetToDefaults(int, @Nullable String); method @RequiresPermission("android.permission.WRITE_DEVICE_CONFIG") public static boolean setProperty(String, String, String, boolean); @@ -1846,6 +1962,7 @@ package android.provider { public static final class Settings.Global extends android.provider.Settings.NameValueTable { field public static final String AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES = "autofill_compat_mode_allowed_packages"; field public static final String AUTOMATIC_POWER_SAVER_MODE = "automatic_power_saver_mode"; + field public static final String BATTERY_SAVER_CONSTANTS = "battery_saver_constants"; field public static final String CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS = "captive_portal_fallback_probe_specs"; field public static final String CAPTIVE_PORTAL_FALLBACK_URL = "captive_portal_fallback_url"; field public static final String CAPTIVE_PORTAL_HTTPS_URL = "captive_portal_https_url"; @@ -1867,6 +1984,7 @@ package android.provider { field public static final String LOCATION_GLOBAL_KILL_SWITCH = "location_global_kill_switch"; field public static final String LOW_POWER_MODE = "low_power"; field public static final String LOW_POWER_MODE_STICKY = "low_power_sticky"; + field public static final String OVERLAY_DISPLAY_DEVICES = "overlay_display_devices"; field public static final String SMS_ACCESS_RESTRICTION_ENABLED = "sms_access_restriction_enabled"; field public static final String USE_OPEN_WIFI_PACKAGE = "use_open_wifi_package"; } @@ -1884,7 +2002,9 @@ package android.provider { field public static final String AUTOFILL_USER_DATA_MIN_VALUE_LENGTH = "autofill_user_data_min_value_length"; field public static final String CONTENT_CAPTURE_ENABLED = "content_capture_enabled"; field public static final String DISABLED_PRINT_SERVICES = "disabled_print_services"; + field public static final String DOZE_ALWAYS_ON = "doze_always_on"; field @Deprecated public static final String ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES = "enabled_notification_policy_access_packages"; + field public static final String ENABLED_VR_LISTENERS = "enabled_vr_listeners"; field public static final String LOCATION_ACCESS_CHECK_DELAY_MILLIS = "location_access_check_delay_millis"; field public static final String LOCATION_ACCESS_CHECK_INTERVAL_MILLIS = "location_access_check_interval_millis"; field public static final String NOTIFICATION_BADGING = "notification_badging"; @@ -2111,20 +2231,12 @@ package android.service.autofill.augmented { package android.service.contentcapture { - @Deprecated public final class ContentCaptureEventsRequest implements android.os.Parcelable { - method @Deprecated public int describeContents(); - method @Deprecated @NonNull public java.util.List<android.view.contentcapture.ContentCaptureEvent> getEvents(); - method @Deprecated public void writeToParcel(android.os.Parcel, int); - field @Deprecated public static final android.os.Parcelable.Creator<android.service.contentcapture.ContentCaptureEventsRequest> CREATOR; - } - public abstract class ContentCaptureService extends android.app.Service { ctor public ContentCaptureService(); method public final void disableContentCaptureServices(); method public void onActivitySnapshot(@NonNull android.view.contentcapture.ContentCaptureSessionId, @NonNull android.service.contentcapture.SnapshotData); method public void onConnected(); method public void onContentCaptureEvent(@NonNull android.view.contentcapture.ContentCaptureSessionId, @NonNull android.view.contentcapture.ContentCaptureEvent); - method @Deprecated public void onContentCaptureEventsRequest(@NonNull android.view.contentcapture.ContentCaptureSessionId, @NonNull android.service.contentcapture.ContentCaptureEventsRequest); method public void onCreateContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureContext, @NonNull android.view.contentcapture.ContentCaptureSessionId); method public void onDestroyContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureSessionId); method public void onDisconnected(); @@ -2373,6 +2485,10 @@ package android.util { method public E valueAtUnchecked(int); } + public class TimeUtils { + method public static String formatDuration(long); + } + } package android.util.proto { @@ -2594,6 +2710,10 @@ package android.view { field public static final int CALLBACK_ANIMATION = 1; // 0x1 } + public final class Display { + method public boolean supportsSystemDecorations(); + } + public class FocusFinder { method public static void sort(android.view.View[], int, int, android.view.ViewGroup, boolean); } @@ -2755,11 +2875,13 @@ package android.view.contentcapture { method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.view.contentcapture.ContentCaptureEvent> CREATOR; field public static final int TYPE_CONTEXT_UPDATED = 6; // 0x6 - field public static final int TYPE_INITIAL_VIEW_TREE_APPEARED = 5; // 0x5 - field public static final int TYPE_INITIAL_VIEW_TREE_APPEARING = 4; // 0x4 + field public static final int TYPE_SESSION_PAUSED = 8; // 0x8 + field public static final int TYPE_SESSION_RESUMED = 7; // 0x7 field public static final int TYPE_VIEW_APPEARED = 1; // 0x1 field public static final int TYPE_VIEW_DISAPPEARED = 2; // 0x2 field public static final int TYPE_VIEW_TEXT_CHANGED = 3; // 0x3 + field public static final int TYPE_VIEW_TREE_APPEARED = 5; // 0x5 + field public static final int TYPE_VIEW_TREE_APPEARING = 4; // 0x4 } public final class ContentCaptureManager { diff --git a/cmds/bootanimation/bootanim.rc b/cmds/bootanimation/bootanim.rc index 469c9646a4aa..3666d6af29f5 100644 --- a/cmds/bootanimation/bootanim.rc +++ b/cmds/bootanimation/bootanim.rc @@ -2,6 +2,9 @@ service bootanim /system/bin/bootanimation class core animation user graphics group graphics audio + # bootanimation depends on libandroidicu in the Runtime APEX. + # TODO(b/124939955): Remove this dependency on libandroidicu + updatable disabled oneshot writepid /dev/stune/top-app/tasks diff --git a/cmds/idmap2/idmap2/Create.cpp b/cmds/idmap2/idmap2/Create.cpp index 6703909d887e..c416fa123b73 100644 --- a/cmds/idmap2/idmap2/Create.cpp +++ b/cmds/idmap2/idmap2/Create.cpp @@ -28,7 +28,6 @@ #include "idmap2/FileUtils.h" #include "idmap2/Idmap.h" #include "idmap2/Policies.h" -#include "idmap2/Result.h" #include "idmap2/SysTrace.h" using android::ApkAssets; @@ -38,7 +37,6 @@ using android::idmap2::Idmap; using android::idmap2::PoliciesToBitmask; using android::idmap2::PolicyBitmask; using android::idmap2::PolicyFlags; -using android::idmap2::Result; using android::idmap2::utils::kIdmapFilePermissionMask; using android::idmap2::utils::UidHasWriteAccessToPath; @@ -77,9 +75,11 @@ bool Create(const std::vector<std::string>& args, std::ostream& out_error) { } PolicyBitmask fulfilled_policies = 0; - if (auto result = PoliciesToBitmask(policies, out_error)) { - fulfilled_policies |= *result; + auto conv_result = PoliciesToBitmask(policies); + if (conv_result) { + fulfilled_policies |= *conv_result; } else { + out_error << "error: " << conv_result.GetErrorMessage() << std::endl; return false; } diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp index 553d8cac99e4..83a40efee3f3 100644 --- a/cmds/idmap2/idmap2/Lookup.cpp +++ b/cmds/idmap2/idmap2/Lookup.cpp @@ -53,6 +53,7 @@ using android::ResTable_config; using android::StringPiece16; using android::base::StringPrintf; using android::idmap2::CommandLineOptions; +using android::idmap2::Error; using android::idmap2::IdmapHeader; using android::idmap2::ResourceId; using android::idmap2::Result; @@ -71,17 +72,17 @@ Result<ResourceId> WARN_UNUSED ParseResReference(const AssetManager2& am, const ResourceId resid; resid = strtol(res.c_str(), &endptr, kBaseHex); if (*endptr == '\0') { - return {resid}; + return resid; } // next, try to parse as a package:type/name string resid = am.GetResourceId(res, "", fallback_package); if (is_valid_resid(resid)) { - return {resid}; + return resid; } // end of the road: res could not be parsed - return {}; + return Error("failed to obtain resource id for %s", res.c_str()); } Result<std::string> WARN_UNUSED GetValue(const AssetManager2& am, ResourceId resid) { @@ -90,7 +91,7 @@ Result<std::string> WARN_UNUSED GetValue(const AssetManager2& am, ResourceId res uint32_t flags; ApkAssetsCookie cookie = am.GetResource(resid, false, 0, &value, &config, &flags); if (cookie == kInvalidCookie) { - return {}; + return Error("no resource 0x%08x in asset manager", resid); } std::string out; @@ -128,31 +129,31 @@ Result<std::string> WARN_UNUSED GetValue(const AssetManager2& am, ResourceId res out.append(StringPrintf("dataType=0x%02x data=0x%08x", value.dataType, value.data)); break; } - return {out}; + return out; } Result<std::string> GetTargetPackageNameFromManifest(const std::string& apk_path) { const auto zip = ZipFile::Open(apk_path); if (!zip) { - return {}; + return Error("failed to open %s as zip", apk_path.c_str()); } const auto entry = zip->Uncompress("AndroidManifest.xml"); if (!entry) { - return {}; + return Error("failed to uncompress AndroidManifest.xml in %s", apk_path.c_str()); } const auto xml = Xml::Create(entry->buf, entry->size); if (!xml) { - return {}; + return Error("failed to create XML buffer"); } const auto tag = xml->FindTag("overlay"); if (!tag) { - return {}; + return Error("failed to find <overlay> tag"); } const auto iter = tag->find("targetPackage"); if (iter == tag->end()) { - return {}; + return Error("failed to find targetPackage attribute"); } - return {iter->second}; + return iter->second; } } // namespace diff --git a/cmds/idmap2/idmap2/Scan.cpp b/cmds/idmap2/idmap2/Scan.cpp index 873779f386f5..e5f6223ef2b4 100644 --- a/cmds/idmap2/idmap2/Scan.cpp +++ b/cmds/idmap2/idmap2/Scan.cpp @@ -142,9 +142,9 @@ bool Scan(const std::vector<std::string>& args, std::ostream& out_error) { std::vector<InputOverlay> interesting_apks; for (const std::string& path : *apk_paths) { Result<OverlayManifestInfo> overlay_info = - ExtractOverlayManifestInfo(path, out_error, - /* assert_overlay */ false); + ExtractOverlayManifestInfo(path, /* assert_overlay */ false); if (!overlay_info) { + out_error << "error: " << overlay_info.GetErrorMessage() << std::endl; return false; } @@ -163,9 +163,11 @@ bool Scan(const std::vector<std::string>& args, std::ostream& out_error) { PolicyBitmask fulfilled_policies; if (!override_policies.empty()) { - if (Result<PolicyBitmask> result = PoliciesToBitmask(override_policies, out_error)) { - fulfilled_policies = *result; + auto conv_result = PoliciesToBitmask(override_policies); + if (conv_result) { + fulfilled_policies = *conv_result; } else { + out_error << "error: " << conv_result.GetErrorMessage() << std::endl; return false; } } else { diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp index 0e4bd89e355c..fa944143e408 100644 --- a/cmds/idmap2/idmap2d/Idmap2Service.cpp +++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp @@ -34,7 +34,6 @@ #include "idmap2/FileUtils.h" #include "idmap2/Idmap.h" #include "idmap2/Policies.h" -#include "idmap2/Result.h" #include "idmap2/SysTrace.h" #include "idmap2d/Idmap2Service.h" @@ -45,7 +44,6 @@ using android::idmap2::BinaryStreamVisitor; using android::idmap2::Idmap; using android::idmap2::IdmapHeader; using android::idmap2::PolicyBitmask; -using android::idmap2::Result; using android::idmap2::utils::kIdmapCacheDir; using android::idmap2::utils::kIdmapFilePermissionMask; using android::idmap2::utils::UidHasWriteAccessToPath; diff --git a/cmds/idmap2/include/idmap2/Policies.h b/cmds/idmap2/include/idmap2/Policies.h index eecee25445e2..911d3f2cd109 100644 --- a/cmds/idmap2/include/idmap2/Policies.h +++ b/cmds/idmap2/include/idmap2/Policies.h @@ -14,7 +14,6 @@ * limitations under the License. */ -#include <ostream> #include <string> #include <vector> @@ -33,8 +32,7 @@ using PolicyBitmask = uint32_t; // Parses a the string representation of a set of policies into a bitmask. The format of the string // is the same as for the <policy> element. -Result<PolicyBitmask> PoliciesToBitmask(const std::vector<std::string>& policies, - std::ostream& err); +Result<PolicyBitmask> PoliciesToBitmask(const std::vector<std::string>& policies); } // namespace android::idmap2 diff --git a/cmds/idmap2/include/idmap2/ResourceUtils.h b/cmds/idmap2/include/idmap2/ResourceUtils.h index 22827ac45f9b..1d81c486d504 100644 --- a/cmds/idmap2/include/idmap2/ResourceUtils.h +++ b/cmds/idmap2/include/idmap2/ResourceUtils.h @@ -18,10 +18,8 @@ #define IDMAP2_INCLUDE_IDMAP2_RESOURCEUTILS_H_ #include <optional> -#include <ostream> #include <string> -#include "android-base/macros.h" #include "androidfw/AssetManager2.h" #include "idmap2/Idmap.h" @@ -38,10 +36,9 @@ struct OverlayManifestInfo { }; Result<OverlayManifestInfo> ExtractOverlayManifestInfo(const std::string& path, - std::ostream& out_error, bool assert_overlay = true); -Result<std::string> WARN_UNUSED ResToTypeEntryName(const AssetManager2& am, ResourceId resid); +Result<std::string> ResToTypeEntryName(const AssetManager2& am, ResourceId resid); } // namespace android::idmap2::utils diff --git a/cmds/idmap2/include/idmap2/Result.h b/cmds/idmap2/include/idmap2/Result.h index d88dd5179610..41b15ebf4afc 100644 --- a/cmds/idmap2/include/idmap2/Result.h +++ b/cmds/idmap2/include/idmap2/Result.h @@ -17,7 +17,6 @@ #ifndef IDMAP2_INCLUDE_IDMAP2_RESULT_H_ #define IDMAP2_INCLUDE_IDMAP2_RESULT_H_ -#include <optional> #include <string> #include <utility> #include <variant> @@ -26,13 +25,6 @@ namespace android::idmap2 { -template <typename T> -using Result = std::optional<T>; - -static constexpr std::nullopt_t kResultError = std::nullopt; - -namespace v2 { - using Unit = std::monostate; class Error { @@ -148,8 +140,6 @@ inline bool Result<T>::is_ok() const { return std::holds_alternative<T>(data_); } -} // namespace v2 - } // namespace android::idmap2 #endif // IDMAP2_INCLUDE_IDMAP2_RESULT_H_ diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp index ec498ffb393c..a1341fb001f6 100644 --- a/cmds/idmap2/libidmap2/Idmap.cpp +++ b/cmds/idmap2/libidmap2/Idmap.cpp @@ -121,7 +121,9 @@ const LoadedPackage* GetPackageAtIndex0(const LoadedArsc& loaded_arsc) { Result<uint32_t> GetCrc(const ZipFile& zip) { const Result<uint32_t> a = zip.Crc("resources.arsc"); const Result<uint32_t> b = zip.Crc("AndroidManifest.xml"); - return a && b ? Result<uint32_t>(*a ^ *b) : kResultError; + return a && b + ? Result<uint32_t>(*a ^ *b) + : Error("Couldn't get CRC for \"%s\"", a ? "AndroidManifest.xml" : "resources.arsc"); } } // namespace @@ -355,9 +357,9 @@ std::unique_ptr<const Idmap> Idmap::FromApkAssets( return nullptr; } - Result<utils::OverlayManifestInfo> overlay_info = - utils::ExtractOverlayManifestInfo(overlay_apk_path, out_error); + auto overlay_info = utils::ExtractOverlayManifestInfo(overlay_apk_path); if (!overlay_info) { + out_error << "error: " << overlay_info.GetErrorMessage() << std::endl; return nullptr; } diff --git a/cmds/idmap2/libidmap2/Policies.cpp b/cmds/idmap2/libidmap2/Policies.cpp index 6649288dfa41..c6ba87dfc2fb 100644 --- a/cmds/idmap2/libidmap2/Policies.cpp +++ b/cmds/idmap2/libidmap2/Policies.cpp @@ -16,7 +16,6 @@ #include <iterator> #include <map> -#include <sstream> #include <string> #include <vector> @@ -39,16 +38,14 @@ const std::map<android::StringPiece, PolicyFlags> kStringToFlag = { }; } // namespace -Result<PolicyBitmask> PoliciesToBitmask(const std::vector<std::string>& policies, - std::ostream& err) { +Result<PolicyBitmask> PoliciesToBitmask(const std::vector<std::string>& policies) { PolicyBitmask bitmask = 0; for (const std::string& policy : policies) { const auto iter = kStringToFlag.find(policy); if (iter != kStringToFlag.end()) { bitmask |= iter->second; } else { - err << "error: unknown policy \"" << policy << "\""; - return kResultError; + return Error("unknown policy \"%s\"", policy.c_str()); } } diff --git a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp index b78e942d4049..1149c905a178 100644 --- a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp +++ b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp @@ -75,7 +75,7 @@ void RawPrintVisitor::visit(const IdmapData::TypeEntry& type_entry) { type_entry.GetEntryOffset() + i); const ResourceId overlay_resid = RESID(last_seen_package_id_, type_entry.GetOverlayTypeId(), entry); - Result<std::string> name; + Result<std::string> name(Error("")); if (target_package_loaded) { name = utils::ResToTypeEntryName(target_am_, target_resid); } diff --git a/cmds/idmap2/libidmap2/ResourceUtils.cpp b/cmds/idmap2/libidmap2/ResourceUtils.cpp index 7a984f3f98ad..a24836da7f3a 100644 --- a/cmds/idmap2/libidmap2/ResourceUtils.cpp +++ b/cmds/idmap2/libidmap2/ResourceUtils.cpp @@ -33,10 +33,10 @@ using android::util::Utf16ToUtf8; namespace android::idmap2::utils { -Result<std::string> WARN_UNUSED ResToTypeEntryName(const AssetManager2& am, ResourceId resid) { +Result<std::string> ResToTypeEntryName(const AssetManager2& am, ResourceId resid) { AssetManager2::ResourceName name; if (!am.GetResourceName(resid, &name)) { - return {}; + return Error("no resource 0x%08x in asset manager", resid); } std::string out; if (name.type != nullptr) { @@ -50,36 +50,31 @@ Result<std::string> WARN_UNUSED ResToTypeEntryName(const AssetManager2& am, Reso } else { out += Utf16ToUtf8(StringPiece16(name.entry16, name.entry_len)); } - return {out}; + return out; } Result<OverlayManifestInfo> ExtractOverlayManifestInfo(const std::string& path, - std::ostream& out_error, bool assert_overlay) { std::unique_ptr<const ZipFile> zip = ZipFile::Open(path); if (!zip) { - out_error << "error: failed to open " << path << " as a zip file" << std::endl; - return kResultError; + return Error("failed to open %s as a zip file", path.c_str()); } std::unique_ptr<const MemoryChunk> entry = zip->Uncompress("AndroidManifest.xml"); if (!entry) { - out_error << "error: failed to uncompress AndroidManifest.xml from " << path << std::endl; - return kResultError; + return Error("failed to uncompress AndroidManifest.xml from %s", path.c_str()); } std::unique_ptr<const Xml> xml = Xml::Create(entry->buf, entry->size); if (!xml) { - out_error << "error: failed to parse AndroidManifest.xml from " << path << std::endl; - return kResultError; + return Error("failed to parse AndroidManifest.xml from %s", path.c_str()); } OverlayManifestInfo info{}; const auto tag = xml->FindTag("overlay"); if (!tag) { if (assert_overlay) { - out_error << "error: <overlay> missing from AndroidManifest.xml of " << path << std::endl; - return kResultError; + return Error("<overlay> missing from AndroidManifest.xml of %s", path.c_str()); } return info; } @@ -87,8 +82,7 @@ Result<OverlayManifestInfo> ExtractOverlayManifestInfo(const std::string& path, auto iter = tag->find("targetPackage"); if (iter == tag->end()) { if (assert_overlay) { - out_error << "error: android:targetPackage missing from <overlay> of " << path << std::endl; - return kResultError; + return Error("android:targetPackage missing from <overlay> of %s", path.c_str()); } } else { info.target_package = iter->second; diff --git a/cmds/idmap2/libidmap2/Result.cpp b/cmds/idmap2/libidmap2/Result.cpp index bd4fabd3bdac..471dab2e0411 100644 --- a/cmds/idmap2/libidmap2/Result.cpp +++ b/cmds/idmap2/libidmap2/Result.cpp @@ -23,7 +23,7 @@ namespace android::idmap2 { // NOLINTNEXTLINE(cert-dcl50-cpp) -v2::Error::Error(const char* fmt, ...) { +Error::Error(const char* fmt, ...) { va_list ap; va_start(ap, fmt); base::StringAppendV(&msg_, fmt, ap); @@ -31,7 +31,7 @@ v2::Error::Error(const char* fmt, ...) { } // NOLINTNEXTLINE(cert-dcl50-cpp) -v2::Error::Error(const Error& parent, const char* fmt, ...) : msg_(parent.msg_) { +Error::Error(const Error& parent, const char* fmt, ...) : msg_(parent.msg_) { msg_.append(" -> "); va_list ap; diff --git a/cmds/idmap2/libidmap2/ZipFile.cpp b/cmds/idmap2/libidmap2/ZipFile.cpp index 15ec3f9d3afe..0f0732466256 100644 --- a/cmds/idmap2/libidmap2/ZipFile.cpp +++ b/cmds/idmap2/libidmap2/ZipFile.cpp @@ -59,7 +59,10 @@ std::unique_ptr<const MemoryChunk> ZipFile::Uncompress(const std::string& entryP Result<uint32_t> ZipFile::Crc(const std::string& entryPath) const { ::ZipEntry entry; int32_t status = ::FindEntry(handle_, ::ZipString(entryPath.c_str()), &entry); - return status == 0 ? Result<uint32_t>(entry.crc32) : kResultError; + if (status != 0) { + return Error("failed to find zip entry %s", entryPath.c_str()); + } + return entry.crc32; } } // namespace android::idmap2 diff --git a/cmds/idmap2/tests/FileUtilsTests.cpp b/cmds/idmap2/tests/FileUtilsTests.cpp index 2e85eb6215d1..34a0097b0316 100644 --- a/cmds/idmap2/tests/FileUtilsTests.cpp +++ b/cmds/idmap2/tests/FileUtilsTests.cpp @@ -58,21 +58,15 @@ TEST(FileUtilsTests, FindFilesFindApkFilesRecursive) { }); ASSERT_THAT(v, NotNull()); ASSERT_EQ(v->size(), 10U); - ASSERT_EQ( - std::set<std::string>(v->begin(), v->end()), - std::set<std::string>( - { - root + "/target/target.apk", - root + "/target/target-no-overlayable.apk", - root + "/overlay/overlay.apk", - root + "/overlay/overlay-no-name.apk", - root + "/overlay/overlay-no-name-static.apk", - root + "/overlay/overlay-static-1.apk", - root + "/overlay/overlay-static-2.apk", - root + "/signature-overlay/signature-overlay.apk", - root + "/system-overlay/system-overlay.apk", - root + "/system-overlay-invalid/system-overlay-invalid.apk" - })); + ASSERT_EQ(std::set<std::string>(v->begin(), v->end()), + std::set<std::string>( + {root + "/target/target.apk", root + "/target/target-no-overlayable.apk", + root + "/overlay/overlay.apk", root + "/overlay/overlay-no-name.apk", + root + "/overlay/overlay-no-name-static.apk", + root + "/overlay/overlay-static-1.apk", root + "/overlay/overlay-static-2.apk", + root + "/signature-overlay/signature-overlay.apk", + root + "/system-overlay/system-overlay.apk", + root + "/system-overlay-invalid/system-overlay-invalid.apk"})); } TEST(FileUtilsTests, ReadFile) { diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp index 53ec03ba3d5e..bbfbad9aa8ca 100644 --- a/cmds/idmap2/tests/IdmapTests.cpp +++ b/cmds/idmap2/tests/IdmapTests.cpp @@ -256,10 +256,10 @@ TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySystemPublic) { ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U); ASSERT_EQ(types[0]->GetEntryCount(), 4U); ASSERT_EQ(types[0]->GetEntryOffset(), 6U); - ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/policy_public - ASSERT_EQ(types[0]->GetEntry(1), kNoEntry); // string/policy_signature - ASSERT_EQ(types[0]->GetEntry(2), 0x0001U); // string/policy_system - ASSERT_EQ(types[0]->GetEntry(3), 0x0002U); // string/policy_system_vendor + ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/policy_public + ASSERT_EQ(types[0]->GetEntry(1), kNoEntry); // string/policy_signature + ASSERT_EQ(types[0]->GetEntry(2), 0x0001U); // string/policy_system + ASSERT_EQ(types[0]->GetEntry(3), 0x0002U); // string/policy_system_vendor } TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySignature) { @@ -267,7 +267,8 @@ TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySignature) { std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path); ASSERT_THAT(target_apk, NotNull()); - const std::string overlay_apk_path(GetTestDataPath() + "/signature-overlay/signature-overlay.apk"); + const std::string overlay_apk_path(GetTestDataPath() + + "/signature-overlay/signature-overlay.apk"); std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path); ASSERT_THAT(overlay_apk, NotNull()); @@ -294,7 +295,7 @@ TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySignature) { ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U); ASSERT_EQ(types[0]->GetEntryCount(), 1U); ASSERT_EQ(types[0]->GetEntryOffset(), 7U); - ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/policy_signature + ASSERT_EQ(types[0]->GetEntry(0), 0x0000U); // string/policy_signature } TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySignatureNotFulfilled) { @@ -302,7 +303,8 @@ TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySignatureNotFulfilled) std::unique_ptr<const ApkAssets> target_apk = ApkAssets::Load(target_apk_path); ASSERT_THAT(target_apk, NotNull()); - const std::string overlay_apk_path(GetTestDataPath() + "/signature-overlay/signature-overlay.apk"); + const std::string overlay_apk_path(GetTestDataPath() + + "/signature-overlay/signature-overlay.apk"); std::unique_ptr<const ApkAssets> overlay_apk = ApkAssets::Load(overlay_apk_path); ASSERT_THAT(overlay_apk, NotNull()); @@ -323,7 +325,7 @@ TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySignatureNotFulfilled) ASSERT_EQ(data->GetHeader()->GetTypeCount(), 0U); const std::vector<std::unique_ptr<const IdmapData::TypeEntry>>& types = data->GetTypeEntries(); - ASSERT_EQ(types.size(), 0U); // can't overlay, so contains nothing + ASSERT_EQ(types.size(), 0U); // can't overlay, so contains nothing } // Overlays should abide by all overlayable restrictions if enforcement of overlayable is enabled. @@ -359,10 +361,10 @@ TEST(IdmapOverlayableTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalid) { ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U); ASSERT_EQ(types[0]->GetEntryCount(), 4U); ASSERT_EQ(types[0]->GetEntryOffset(), 6U); - ASSERT_EQ(types[0]->GetEntry(0), 0x0003U); // string/policy_public - ASSERT_EQ(types[0]->GetEntry(1), kNoEntry); // string/policy_signature - ASSERT_EQ(types[0]->GetEntry(2), 0x0005U); // string/policy_system - ASSERT_EQ(types[0]->GetEntry(3), 0x0006U); // string/policy_system_vendor + ASSERT_EQ(types[0]->GetEntry(0), 0x0003U); // string/policy_public + ASSERT_EQ(types[0]->GetEntry(1), kNoEntry); // string/policy_signature + ASSERT_EQ(types[0]->GetEntry(2), 0x0005U); // string/policy_system + ASSERT_EQ(types[0]->GetEntry(3), 0x0006U); // string/policy_system_vendor } // Overlays should ignore all overlayable restrictions if enforcement of overlayable is disabled. diff --git a/cmds/idmap2/tests/PoliciesTests.cpp b/cmds/idmap2/tests/PoliciesTests.cpp index ab567adc6f19..a76da533cdcb 100644 --- a/cmds/idmap2/tests/PoliciesTests.cpp +++ b/cmds/idmap2/tests/PoliciesTests.cpp @@ -27,44 +27,42 @@ using android::idmap2::PolicyFlags; namespace android::idmap2 { TEST(PoliciesTests, PoliciesToBitmasks) { - const Result<PolicyBitmask> bitmask1 = PoliciesToBitmask({"system"}, std::cerr); - ASSERT_NE(bitmask1, kResultError); - ASSERT_EQ(bitmask1, PolicyFlags::POLICY_SYSTEM_PARTITION); + const auto bitmask1 = PoliciesToBitmask({"system"}); + ASSERT_TRUE(bitmask1); + ASSERT_EQ(*bitmask1, PolicyFlags::POLICY_SYSTEM_PARTITION); - const Result<PolicyBitmask> bitmask2 = PoliciesToBitmask({"system", "vendor"}, std::cerr); - ASSERT_NE(bitmask2, kResultError); - ASSERT_EQ(bitmask2, PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_VENDOR_PARTITION); + const auto bitmask2 = PoliciesToBitmask({"system", "vendor"}); + ASSERT_TRUE(bitmask2); + ASSERT_EQ(*bitmask2, PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_VENDOR_PARTITION); - const Result<PolicyBitmask> bitmask3 = PoliciesToBitmask({"vendor", "system"}, std::cerr); - ASSERT_NE(bitmask3, kResultError); - ASSERT_EQ(bitmask3, PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_VENDOR_PARTITION); + const auto bitmask3 = PoliciesToBitmask({"vendor", "system"}); + ASSERT_TRUE(bitmask3); + ASSERT_EQ(*bitmask3, PolicyFlags::POLICY_SYSTEM_PARTITION | PolicyFlags::POLICY_VENDOR_PARTITION); - const Result<PolicyBitmask> bitmask4 = - PoliciesToBitmask({"public", "product", "system", "vendor"}, std::cerr); - ASSERT_NE(bitmask4, kResultError); - ASSERT_EQ(bitmask4, PolicyFlags::POLICY_PUBLIC | PolicyFlags::POLICY_PRODUCT_PARTITION | - PolicyFlags::POLICY_SYSTEM_PARTITION | - PolicyFlags::POLICY_VENDOR_PARTITION); + const auto bitmask4 = PoliciesToBitmask({"public", "product", "system", "vendor"}); + ASSERT_TRUE(bitmask4); + ASSERT_EQ(*bitmask4, PolicyFlags::POLICY_PUBLIC | PolicyFlags::POLICY_PRODUCT_PARTITION | + PolicyFlags::POLICY_SYSTEM_PARTITION | + PolicyFlags::POLICY_VENDOR_PARTITION); - const Result<PolicyBitmask> bitmask5 = - PoliciesToBitmask({"system", "system", "system"}, std::cerr); - ASSERT_NE(bitmask5, kResultError); - ASSERT_EQ(bitmask5, PolicyFlags::POLICY_SYSTEM_PARTITION); + const auto bitmask5 = PoliciesToBitmask({"system", "system", "system"}); + ASSERT_TRUE(bitmask5); + ASSERT_EQ(*bitmask5, PolicyFlags::POLICY_SYSTEM_PARTITION); - const Result<PolicyBitmask> bitmask6 = PoliciesToBitmask({""}, std::cerr); - ASSERT_EQ(bitmask6, kResultError); + const auto bitmask6 = PoliciesToBitmask({""}); + ASSERT_FALSE(bitmask6); - const Result<PolicyBitmask> bitmask7 = PoliciesToBitmask({"foo"}, std::cerr); - ASSERT_EQ(bitmask7, kResultError); + const auto bitmask7 = PoliciesToBitmask({"foo"}); + ASSERT_FALSE(bitmask7); - const Result<PolicyBitmask> bitmask8 = PoliciesToBitmask({"system", "foo"}, std::cerr); - ASSERT_EQ(bitmask8, kResultError); + const auto bitmask8 = PoliciesToBitmask({"system", "foo"}); + ASSERT_FALSE(bitmask8); - const Result<PolicyBitmask> bitmask9 = PoliciesToBitmask({"system", ""}, std::cerr); - ASSERT_EQ(bitmask9, kResultError); + const auto bitmask9 = PoliciesToBitmask({"system", ""}); + ASSERT_FALSE(bitmask9); - const Result<PolicyBitmask> bitmask10 = PoliciesToBitmask({"system "}, std::cerr); - ASSERT_EQ(bitmask10, kResultError); + const auto bitmask10 = PoliciesToBitmask({"system "}); + ASSERT_FALSE(bitmask10); } } // namespace android::idmap2 diff --git a/cmds/idmap2/tests/ResultTests.cpp b/cmds/idmap2/tests/ResultTests.cpp index d82f0c475ea9..5f4daed521c0 100644 --- a/cmds/idmap2/tests/ResultTests.cpp +++ b/cmds/idmap2/tests/ResultTests.cpp @@ -32,28 +32,28 @@ struct Container { // Tests: Error TEST(ResultTests, ErrorTraits) { - ASSERT_TRUE(std::is_move_constructible<v2::Error>::value); - ASSERT_TRUE(std::is_move_assignable<v2::Error>::value); - ASSERT_TRUE(std::is_copy_constructible<v2::Error>::value); - ASSERT_TRUE(std::is_copy_assignable<v2::Error>::value); + ASSERT_TRUE(std::is_move_constructible<Error>::value); + ASSERT_TRUE(std::is_move_assignable<Error>::value); + ASSERT_TRUE(std::is_copy_constructible<Error>::value); + ASSERT_TRUE(std::is_copy_assignable<Error>::value); } TEST(ResultTests, ErrorCtorFormat) { - v2::Error e("%s=0x%08x", "resid", 0x7f010002); + Error e("%s=0x%08x", "resid", 0x7f010002); ASSERT_EQ(e.GetMessage(), "resid=0x7f010002"); } TEST(ResultTests, ErrorPropagateParent) { - v2::Error e1("foo"); + Error e1("foo"); ASSERT_EQ(e1.GetMessage(), "foo"); - v2::Error e2(e1, "bar"); + Error e2(e1, "bar"); ASSERT_EQ(e2.GetMessage(), "foo -> bar"); - v2::Error e3(e2); // NOLINT(performance-unnecessary-copy-initialization) + Error e3(e2); // NOLINT(performance-unnecessary-copy-initialization) ASSERT_EQ(e3.GetMessage(), "foo -> bar"); - v2::Error e4(e3, "%02d", 1); + Error e4(e3, "%02d", 1); ASSERT_EQ(e4.GetMessage(), "foo -> bar -> 01"); } @@ -61,13 +61,13 @@ TEST(ResultTests, ErrorPropagateParent) { // Result(const Result&) TEST(ResultTests, CopyConstructor) { - v2::Result<uint32_t> r1(42U); + Result<uint32_t> r1(42U); - v2::Result<uint32_t> r2(r1); + Result<uint32_t> r2(r1); ASSERT_TRUE(r2); ASSERT_EQ(*r2, 42U); - v2::Result<uint32_t> r3 = r2; + Result<uint32_t> r3 = r2; ASSERT_TRUE(r3); ASSERT_EQ(*r3, 42U); } @@ -75,23 +75,23 @@ TEST(ResultTests, CopyConstructor) { // Result(const T&) TEST(ResultTests, Constructor) { uint32_t v = 42U; - v2::Result<uint32_t> r1(v); + Result<uint32_t> r1(v); ASSERT_TRUE(r1); ASSERT_EQ(*r1, 42U); - v2::Error e("foo"); - v2::Result<uint32_t> r2(e); + Error e("foo"); + Result<uint32_t> r2(e); ASSERT_FALSE(r2); ASSERT_EQ(r2.GetErrorMessage(), "foo"); } // Result(const T&&) TEST(ResultTests, MoveConstructor) { - v2::Result<uint32_t> r1(42U); + Result<uint32_t> r1(42U); ASSERT_TRUE(r1); ASSERT_EQ(*r1, 42U); - v2::Result<uint32_t> r2(v2::Error("foo")); + Result<uint32_t> r2(Error("foo")); ASSERT_FALSE(r2); ASSERT_EQ(r2.GetErrorMessage(), "foo"); } @@ -99,52 +99,52 @@ TEST(ResultTests, MoveConstructor) { // operator= TEST(ResultTests, CopyAssignmentOperator) { // note: 'Result<...> r2 = r1;' calls the copy ctor - v2::Result<uint32_t> r1(42U); - v2::Result<uint32_t> r2(0U); + Result<uint32_t> r1(42U); + Result<uint32_t> r2(0U); r2 = r1; ASSERT_TRUE(r2); ASSERT_EQ(*r2, 42U); - v2::Result<uint32_t> r3(v2::Error("foo")); + Result<uint32_t> r3(Error("foo")); r2 = r3; ASSERT_FALSE(r2); ASSERT_EQ(r2.GetErrorMessage(), "foo"); } TEST(ResultTests, MoveAssignmentOperator) { - v2::Result<uint32_t> r(0U); - r = v2::Result<uint32_t>(42U); + Result<uint32_t> r(0U); + r = Result<uint32_t>(42U); ASSERT_TRUE(r); ASSERT_EQ(*r, 42U); - r = v2::Result<uint32_t>(v2::Error("foo")); + r = Result<uint32_t>(Error("foo")); ASSERT_FALSE(r); ASSERT_EQ(r.GetErrorMessage(), "foo"); } // operator bool() TEST(ResultTests, BoolOperator) { - v2::Result<uint32_t> r1(42U); + Result<uint32_t> r1(42U); ASSERT_TRUE(r1); ASSERT_EQ(*r1, 42U); - v2::Result<uint32_t> r2(v2::Error("foo")); + Result<uint32_t> r2(Error("foo")); ASSERT_FALSE(r2); ASSERT_EQ(r2.GetErrorMessage(), "foo"); } // operator* TEST(ResultTests, IndirectionOperator) { - const v2::Result<uint32_t> r1(42U); + const Result<uint32_t> r1(42U); ASSERT_TRUE(r1); ASSERT_EQ(*r1, 42U); - const v2::Result<Container> r2(Container{42U}); + const Result<Container> r2(Container{42U}); ASSERT_TRUE(r2); const Container& c = *r2; ASSERT_EQ(c.value, 42U); - v2::Result<Container> r3(Container{42U}); + Result<Container> r3(Container{42U}); ASSERT_TRUE(r3); ASSERT_EQ((*r3).value, 42U); (*r3).value = 0U; @@ -153,11 +153,11 @@ TEST(ResultTests, IndirectionOperator) { // operator-> TEST(ResultTests, DereferenceOperator) { - const v2::Result<Container> r1(Container{42U}); + const Result<Container> r1(Container{42U}); ASSERT_TRUE(r1); ASSERT_EQ(r1->value, 42U); - v2::Result<Container> r2(Container{42U}); + Result<Container> r2(Container{42U}); ASSERT_TRUE(r2); ASSERT_EQ(r2->value, 42U); r2->value = 0U; @@ -167,14 +167,14 @@ TEST(ResultTests, DereferenceOperator) { // Tests: intended use of Result<T> TEST(ResultTests, ResultTraits) { - ASSERT_TRUE(std::is_move_constructible<v2::Result<uint32_t>>::value); - ASSERT_TRUE(std::is_move_assignable<v2::Result<uint32_t>>::value); - ASSERT_TRUE(std::is_copy_constructible<v2::Result<uint32_t>>::value); - ASSERT_TRUE(std::is_copy_assignable<v2::Result<uint32_t>>::value); + ASSERT_TRUE(std::is_move_constructible<Result<uint32_t>>::value); + ASSERT_TRUE(std::is_move_assignable<Result<uint32_t>>::value); + ASSERT_TRUE(std::is_copy_constructible<Result<uint32_t>>::value); + ASSERT_TRUE(std::is_copy_assignable<Result<uint32_t>>::value); } TEST(ResultTests, UnitTypeResult) { - v2::Result<v2::Unit> r(v2::Unit{}); + Result<Unit> r(Unit{}); ASSERT_TRUE(r); } @@ -220,16 +220,16 @@ TEST(ResultTests, ReferenceCount) { ASSERT_FALSE(std::is_copy_assignable<RefCountContainer>::value); RefCountData rc{0, 0, 0, 0}; - { v2::Result<RefCountContainer> r(RefCountContainer{rc}); } + { Result<RefCountContainer> r(RefCountContainer{rc}); } ASSERT_EQ(rc.ctor, 1); ASSERT_EQ(rc.copy_ctor, 1); ASSERT_EQ(rc.move, 0); ASSERT_EQ(rc.dtor, 2); } -v2::Result<Container> CreateContainer(bool succeed) { +Result<Container> CreateContainer(bool succeed) { if (!succeed) { - return v2::Error("foo"); + return Error("foo"); } return Container{42U}; } @@ -245,10 +245,10 @@ TEST(ResultTests, FunctionReturn) { ASSERT_EQ(r2.GetError().GetMessage(), "foo"); } -v2::Result<Container> FailToCreateContainer() { +Result<Container> FailToCreateContainer() { auto container = CreateContainer(false); if (!container) { - return v2::Error(container.GetError(), "bar"); + return Error(container.GetError(), "bar"); } return container; } @@ -264,9 +264,9 @@ struct NoCopyContainer { DISALLOW_COPY_AND_ASSIGN(NoCopyContainer); }; -v2::Result<std::unique_ptr<NoCopyContainer>> CreateNoCopyContainer(bool succeed) { +Result<std::unique_ptr<NoCopyContainer>> CreateNoCopyContainer(bool succeed) { if (!succeed) { - return v2::Error("foo"); + return Error("foo"); } std::unique_ptr<NoCopyContainer> p(new NoCopyContainer{0U}); p->value = 42U; diff --git a/cmds/input/src/com/android/commands/input/Input.java b/cmds/input/src/com/android/commands/input/Input.java index 0c861cfd3e63..a0777459311b 100644 --- a/cmds/input/src/com/android/commands/input/Input.java +++ b/cmds/input/src/com/android/commands/input/Input.java @@ -196,7 +196,7 @@ public class Input extends BaseCommand { int repeatCount = 0; KeyEvent event = new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, repeatCount, - 0 /*metaState*/, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/, 0/*flags*/, + 0 /*metaState*/, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/, 0 /*flags*/, inputSource); event.setDisplayId(displayId); diff --git a/cmds/sm/src/com/android/commands/sm/Sm.java b/cmds/sm/src/com/android/commands/sm/Sm.java index be5a5bf6c35a..4a6f87f09d29 100644 --- a/cmds/sm/src/com/android/commands/sm/Sm.java +++ b/cmds/sm/src/com/android/commands/sm/Sm.java @@ -103,6 +103,8 @@ public final class Sm { runSetVirtualDisk(); } else if ("set-isolated-storage".equals(op)) { runIsolatedStorage(); + } else if ("set-legacy-greylist".equals(op)) { + runLegacyGreylist(); } else { throw new IllegalArgumentException(); } @@ -282,7 +284,7 @@ public final class Sm { StorageManager.DEBUG_VIRTUAL_DISK); } - public void runIsolatedStorage() { + public void runIsolatedStorage() throws RemoteException { final int value; final int mask = StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_ON | StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_OFF; @@ -301,16 +303,13 @@ public final class Sm { default: return; } + mSm.setDebugFlags(value, mask); + } - // Toggling isolated-storage state will result in a device reboot. So to avoid this command - // from erroring out (DeadSystemException), call setDebugFlags() in a separate thread. - new Thread(() -> { - try { - mSm.setDebugFlags(value, mask); - } catch (RemoteException e) { - Log.e(TAG, "Encountered an error!", e); - } - }).start(); + public void runLegacyGreylist() throws RemoteException { + final boolean legacyGreylist = Boolean.parseBoolean(nextArg()); + mSm.setDebugFlags(legacyGreylist ? StorageManager.DEBUG_LEGACY_GREYLIST : 0, + StorageManager.DEBUG_LEGACY_GREYLIST); } public void runIdleMaint() throws RemoteException { diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index f4086557870d..ca48881ca519 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -346,3 +346,9 @@ java_library { javacflags: ["-XepDisableAllChecks"], }, } + +// Filegroup for statsd config proto definition. +filegroup { + name: "statsd-config-proto-def", + srcs: ["src/statsd_config.proto"], +} diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp index 4deb8bd83a9f..69fbf1f9d881 100644 --- a/cmds/statsd/src/StatsService.cpp +++ b/cmds/statsd/src/StatsService.cpp @@ -1200,9 +1200,9 @@ Status StatsService::sendBinaryPushStateChangedAtom(const android::String16& tra userid_t userId = multiuser_get_user_id(uid); - bool requiresStaging = options | IStatsManager::FLAG_REQUIRE_STAGING; - bool rollbackEnabled = options | IStatsManager::FLAG_ROLLBACK_ENABLED; - bool requiresLowLatencyMonitor = options | IStatsManager::FLAG_REQUIRE_LOW_LATENCY_MONITOR; + bool requiresStaging = options & IStatsManager::FLAG_REQUIRE_STAGING; + bool rollbackEnabled = options & IStatsManager::FLAG_ROLLBACK_ENABLED; + bool requiresLowLatencyMonitor = options & IStatsManager::FLAG_REQUIRE_LOW_LATENCY_MONITOR; ProtoOutputStream proto; for (const auto& expId : experimentIds) { diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index d78647eebe1f..b6474001deba 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -45,6 +45,7 @@ import "frameworks/base/core/proto/android/stats/docsui/docsui_enums.proto"; import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy.proto"; import "frameworks/base/core/proto/android/stats/devicepolicy/device_policy_enums.proto"; import "frameworks/base/core/proto/android/stats/launcher/launcher.proto"; +import "frameworks/base/core/proto/android/stats/style/style_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"; @@ -247,6 +248,7 @@ message Atom { AssistGestureProgressReported assist_gesture_progress_reported = 176; TouchGestureClassified touch_gesture_classified = 177; HiddenApiUsed hidden_api_used = 178 [(allow_from_any_uid) = true]; + StyleUIChanged style_ui_changed = 179; } // Pulled events will start at field 10000. @@ -283,7 +285,7 @@ message Atom { CategorySize category_size = 10028; ProcStats proc_stats = 10029; BatteryVoltage battery_voltage = 10030; - NumBiometricsEnrolled num_fingerprints_enrolled = 10031; + NumFingerprintsEnrolled num_fingerprints_enrolled = 10031; DiskIo disk_io = 10032; PowerProfile power_profile = 10033; ProcStatsPkgProc proc_stats_pkg_proc = 10034; @@ -300,7 +302,7 @@ message Atom { BatteryCycleCount battery_cycle_count = 10045; DebugElapsedClock debug_elapsed_clock = 10046; DebugFailingElapsedClock debug_failing_elapsed_clock = 10047; - NumBiometricsEnrolled num_faces_enrolled = 10048; + NumFacesEnrolled num_faces_enrolled = 10048; RoleHolder role_holder = 10049; DangerousPermissionState dangerous_permission_state = 10050; TrainInfo train_info = 10051; @@ -2349,6 +2351,17 @@ message LauncherUIChanged { optional bool is_swipe_up_enabled = 5; } +message StyleUIChanged { + optional android.stats.style.Action action = 1; + optional int32 color_package_hash = 2; + optional int32 font_package_hash = 3; + optional int32 shape_package_hash = 4; + optional int32 clock_package_hash = 5; + optional int32 launcher_grid = 6; + optional int32 wallpaper_category_hash = 7; + optional int32 wallpaper_id_hash = 8; +} + /** * Logs when Settings UI has changed. * @@ -4114,15 +4127,27 @@ message DiskIo { * * Pulled from StatsCompanionService, which queries <Biometric>Manager. */ -message NumBiometricsEnrolled { +message NumFingerprintsEnrolled { // The associated user. Eg: 0 for owners, 10+ for others. // Defined in android/os/UserHandle.java optional int32 user = 1; // Number of fingerprints registered to that user. - optional int32 num_enrolled = 2; + optional int32 num_fingerprints_enrolled = 2; } /** + * Pulls the number of faces for each user. + * + * Pulled from StatsCompanionService, which queries <Biometric>Manager. + */ +message NumFacesEnrolled { + // The associated user. Eg: 0 for owners, 10+ for others. + // Defined in android/os/UserHandle.java + optional int32 user = 1; + // Number of faces registered to that user. + optional int32 num_faces_enrolled = 2; +} +/** * A mapping of role holder -> role */ message RoleHolder { @@ -5455,7 +5480,7 @@ message BubbleUIChanged { HEADER_GO_TO_SETTINGS = 9; PERMISSION_OPT_IN = 10; PERMISSION_OPT_OUT = 11; - PERMISSION_IGNORED = 12; + PERMISSION_DIALOG_SHOWN = 12; SWIPE_LEFT = 13; SWIPE_RIGHT = 14; STACK_EXPANDED = 15; @@ -5503,8 +5528,9 @@ message ScheduledJobConstraintChanged { optional com.android.server.job.ConstraintEnum constraint = 3; enum State { - UNSATISFIED = 0; - SATISFIED = 1; + UNKNOWN = 0; + UNSATISFIED = 1; + SATISFIED = 2; } optional State state = 4; } @@ -5630,7 +5656,7 @@ message TrainInfo { * frameworks/base/packages/SystemUI/ */ message AssistGestureStageReported { - optional android.hardware.sensor.assist.AssistGestureStageEnum gesture_stage = 1; + optional android.hardware.sensor.assist.AssistGestureStageEnum gesture_stage = 1; } /** @@ -5640,8 +5666,8 @@ message AssistGestureStageReported { * frameworks/base/packages/SystemUI/ */ message AssistGestureFeedbackReported { - // Whether or not the gesture was used. - optional android.hardware.sensor.assist.AssistGestureFeedbackEnum feedback_type = 1; + // Whether or not the gesture was used. + optional android.hardware.sensor.assist.AssistGestureFeedbackEnum feedback_type = 1; } /** @@ -5651,8 +5677,8 @@ message AssistGestureFeedbackReported { * frameworks/base/packages/SystemUI/ */ message AssistGestureProgressReported { - // [0,100] progress for the assist gesture. - optional int32 progress = 1; + // [0,100] progress for the assist gesture. + optional int32 progress = 1; } /* diff --git a/cmds/statsd/src/external/PowerStatsPuller.cpp b/cmds/statsd/src/external/PowerStatsPuller.cpp index 71e5fa0c710d..c56f9a27086e 100644 --- a/cmds/statsd/src/external/PowerStatsPuller.cpp +++ b/cmds/statsd/src/external/PowerStatsPuller.cpp @@ -39,17 +39,40 @@ namespace android { namespace os { namespace statsd { -sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHal = nullptr; -std::mutex gPowerStatsHalMutex; -bool gPowerStatsExist = true; // Initialized to ensure making a first attempt. -std::vector<RailInfo> gRailInfo; +static sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHal = nullptr; +static std::mutex gPowerStatsHalMutex; +static bool gPowerStatsExist = true; // Initialized to ensure making a first attempt. +static std::vector<RailInfo> gRailInfo; + +struct PowerStatsPullerDeathRecipient : virtual public hardware::hidl_death_recipient { + virtual void serviceDied(uint64_t cookie, + const wp<android::hidl::base::V1_0::IBase>& who) override { + // The HAL just died. Reset all handles to HAL services. + std::lock_guard<std::mutex> lock(gPowerStatsHalMutex); + gPowerStatsHal = nullptr; + } +}; + +static sp<PowerStatsPullerDeathRecipient> gDeathRecipient = new PowerStatsPullerDeathRecipient(); -bool getPowerStatsHal() { +static bool getPowerStatsHalLocked() { if (gPowerStatsHal == nullptr && gPowerStatsExist) { gPowerStatsHal = android::hardware::power::stats::V1_0::IPowerStats::getService(); if (gPowerStatsHal == nullptr) { ALOGW("Couldn't load power.stats HAL service"); gPowerStatsExist = false; + } else { + // Link death recipient to power.stats service handle + hardware::Return<bool> linked = gPowerStatsHal->linkToDeath(gDeathRecipient, 0); + if (!linked.isOk()) { + ALOGE("Transaction error in linking to power.stats HAL death: %s", + linked.description().c_str()); + gPowerStatsHal = nullptr; + return false; + } else if (!linked) { + ALOGW("Unable to link to power.stats HAL death notifications"); + // We should still continue even though linking failed + } } } return gPowerStatsHal != nullptr; @@ -61,7 +84,7 @@ PowerStatsPuller::PowerStatsPuller() : StatsPuller(android::util::ON_DEVICE_POWE bool PowerStatsPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) { std::lock_guard<std::mutex> lock(gPowerStatsHalMutex); - if (!getPowerStatsHal()) { + if (!getPowerStatsHalLocked()) { ALOGE("power.stats Hal not loaded"); return false; } diff --git a/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp b/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp index d8229599635c..f6a4aeaa3f9e 100644 --- a/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp +++ b/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp @@ -60,41 +60,43 @@ namespace android { namespace os { namespace statsd { -std::function<bool(vector<shared_ptr<LogEvent>>* data)> gPuller = {}; +static std::function<bool(vector<shared_ptr<LogEvent>>* data)> gPuller = {}; -sp<android::hardware::power::V1_0::IPower> gPowerHalV1_0 = nullptr; -sp<android::hardware::power::V1_1::IPower> gPowerHalV1_1 = nullptr; -sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHalV1_0 = nullptr; +static sp<android::hardware::power::V1_0::IPower> gPowerHalV1_0 = nullptr; +static sp<android::hardware::power::V1_1::IPower> gPowerHalV1_1 = nullptr; +static sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHalV1_0 = nullptr; -std::unordered_map<uint32_t, std::string> gEntityNames = {}; -std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>> gStateNames = {}; +static std::unordered_map<uint32_t, std::string> gEntityNames = {}; +static std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>> gStateNames = {}; -std::mutex gPowerHalMutex; +static std::mutex gPowerHalMutex; // The caller must be holding gPowerHalMutex. -void deinitPowerStatsLocked() { +static void deinitPowerStatsLocked() { gPowerHalV1_0 = nullptr; gPowerHalV1_1 = nullptr; gPowerStatsHalV1_0 = nullptr; } -struct PowerHalDeathRecipient : virtual public hardware::hidl_death_recipient { +struct SubsystemSleepStatePullerDeathRecipient : virtual public hardware::hidl_death_recipient { virtual void serviceDied(uint64_t cookie, - const wp<android::hidl::base::V1_0::IBase>& who) override { + const wp<android::hidl::base::V1_0::IBase>& who) override { + // The HAL just died. Reset all handles to HAL services. std::lock_guard<std::mutex> lock(gPowerHalMutex); deinitPowerStatsLocked(); } }; -sp<PowerHalDeathRecipient> gDeathRecipient = new PowerHalDeathRecipient(); +static sp<SubsystemSleepStatePullerDeathRecipient> gDeathRecipient = + new SubsystemSleepStatePullerDeathRecipient(); SubsystemSleepStatePuller::SubsystemSleepStatePuller() : StatsPuller(android::util::SUBSYSTEM_SLEEP_STATE) { } // The caller must be holding gPowerHalMutex. -bool checkResultLocked(const Return<void> &ret, const char* function) { +static bool checkResultLocked(const Return<void> &ret, const char* function) { if (!ret.isOk()) { ALOGE("%s failed: requested HAL service not available. Description: %s", function, ret.description().c_str()); @@ -108,7 +110,7 @@ bool checkResultLocked(const Return<void> &ret, const char* function) { // The caller must be holding gPowerHalMutex. // gPowerStatsHalV1_0 must not be null -bool initializePowerStats() { +static bool initializePowerStats() { using android::hardware::power::stats::V1_0::Status; // Clear out previous content if we are re-initializing @@ -155,7 +157,7 @@ bool initializePowerStats() { } // The caller must be holding gPowerHalMutex. -bool getPowerStatsHalLocked() { +static bool getPowerStatsHalLocked() { if(gPowerStatsHalV1_0 == nullptr) { gPowerStatsHalV1_0 = android::hardware::power::stats::V1_0::IPowerStats::getService(); if (gPowerStatsHalV1_0 == nullptr) { @@ -180,7 +182,7 @@ bool getPowerStatsHalLocked() { } // The caller must be holding gPowerHalMutex. -bool getIPowerStatsDataLocked(vector<shared_ptr<LogEvent>>* data) { +static bool getIPowerStatsDataLocked(vector<shared_ptr<LogEvent>>* data) { using android::hardware::power::stats::V1_0::Status; if(!getPowerStatsHalLocked()) { @@ -225,7 +227,7 @@ bool getIPowerStatsDataLocked(vector<shared_ptr<LogEvent>>* data) { } // The caller must be holding gPowerHalMutex. -bool getPowerHalLocked() { +static bool getPowerHalLocked() { if(gPowerHalV1_0 == nullptr) { gPowerHalV1_0 = android::hardware::power::V1_0::IPower::getService(); if(gPowerHalV1_0 == nullptr) { @@ -250,7 +252,7 @@ bool getPowerHalLocked() { } // The caller must be holding gPowerHalMutex. -bool getIPowerDataLocked(vector<shared_ptr<LogEvent>>* data) { +static bool getIPowerDataLocked(vector<shared_ptr<LogEvent>>* data) { using android::hardware::power::V1_0::Status; if(!getPowerHalLocked()) { diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp index d661ee8651be..71d39fffcb52 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.cpp +++ b/cmds/statsd/src/guardrail/StatsdStats.cpp @@ -493,6 +493,11 @@ void StatsdStats::noteInvalidatedBucket(int64_t metricId) { getAtomMetricStats(metricId).invalidatedBucket++; } +void StatsdStats::noteBucketCount(int64_t metricId) { + lock_guard<std::mutex> lock(mLock); + getAtomMetricStats(metricId).bucketCount++; +} + void StatsdStats::noteBucketBoundaryDelayNs(int64_t metricId, int64_t timeDelayNs) { lock_guard<std::mutex> lock(mLock); AtomMetricStats& pullStats = getAtomMetricStats(metricId); diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h index e039be2b4395..7c2d8462c4e4 100644 --- a/cmds/statsd/src/guardrail/StatsdStats.h +++ b/cmds/statsd/src/guardrail/StatsdStats.h @@ -403,6 +403,11 @@ public: void noteInvalidatedBucket(int64_t metricId); /** + * Tracks the total number of buckets (include skipped/invalid buckets). + */ + void noteBucketCount(int64_t metricId); + + /** * For pulls at bucket boundaries, it represents the misalignment between the real timestamp and * the end of the bucket. */ @@ -464,6 +469,7 @@ public: int64_t minBucketBoundaryDelayNs = 0; int64_t maxBucketBoundaryDelayNs = 0; long bucketUnknownCondition = 0; + long bucketCount = 0; } AtomMetricStats; private: diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp index 96d144765081..254d7d5a38f5 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.cpp +++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp @@ -371,6 +371,7 @@ void CountMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs, } } + StatsdStats::getInstance().noteBucketCount(mMetricId); // Only resets the counters, but doesn't setup the times nor numbers. // (Do not clear since the old one is still referenced in mAnomalyTrackers). mCurrentSlicedCounter = std::make_shared<DimToValMap>(); diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index 5e4594b4e00a..7dc4e2d91533 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -601,6 +601,7 @@ void DurationMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs whatIt++; } } + StatsdStats::getInstance().noteBucketCount(mMetricId); } void DurationMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const { diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp index 7b001b3b8bf4..2b6cac8189f3 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp @@ -576,6 +576,7 @@ void GaugeMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs, } } + StatsdStats::getInstance().noteBucketCount(mMetricId); mCurrentSlicedBucket = std::make_shared<DimToGaugeAtomsMap>(); } diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index 99cb5d4389c7..046f9963b351 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -69,7 +69,7 @@ public: mTimeBaseNs(timeBaseNs), mCurrentBucketStartTimeNs(timeBaseNs), mCurrentBucketNum(0), - mCondition(conditionIndex >= 0 ? ConditionState::kUnknown : ConditionState::kTrue), + mCondition(initialCondition(conditionIndex)), mConditionSliced(false), mWizard(wizard), mConditionTrackerIndex(conditionIndex), @@ -82,6 +82,10 @@ public: virtual ~MetricProducer(){}; + ConditionState initialCondition(const int conditionIndex) const { + return conditionIndex >= 0 ? ConditionState::kUnknown : ConditionState::kTrue; + } + /** * Forces this metric to split into a partial bucket right now. If we're past a full bucket, we * first call the standard flushing code to flush up to the latest full bucket. Then we call diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index e94b75c16fbe..27ee57013fda 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -155,7 +155,7 @@ ValueMetricProducer::ValueMetricProducer( mCurrentBucketStartTimeNs = startTimeNs; // Kicks off the puller immediately if condition is true and diff based. if (mIsPulled && mCondition == ConditionState::kTrue && mUseDiff) { - pullAndMatchEventsLocked(startTimeNs); + pullAndMatchEventsLocked(startTimeNs, mCondition); } VLOG("value metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(), (long long)mBucketSizeNs, (long long)mTimeBaseNs); @@ -174,13 +174,17 @@ void ValueMetricProducer::onSlicedConditionMayChangeLocked(bool overallCondition } void ValueMetricProducer::dropDataLocked(const int64_t dropTimeNs) { - flushIfNeededLocked(dropTimeNs); StatsdStats::getInstance().noteBucketDropped(mMetricId); - mPastBuckets.clear(); + // We are going to flush the data without doing a pull first so we need to invalidte the data. + bool pullNeeded = mIsPulled && mCondition == ConditionState::kTrue; + if (pullNeeded) { + invalidateCurrentBucket(); + } + flushIfNeededLocked(dropTimeNs); + clearPastBucketsLocked(dropTimeNs); } void ValueMetricProducer::clearPastBucketsLocked(const int64_t dumpTimeNs) { - flushIfNeededLocked(dumpTimeNs); mPastBuckets.clear(); mSkippedBuckets.clear(); } @@ -192,7 +196,6 @@ void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, std::set<string> *str_set, ProtoOutputStream* protoOutput) { VLOG("metric %lld dump report now...", (long long)mMetricId); - flushIfNeededLocked(dumpTimeNs); if (include_current_partial_bucket) { // For pull metrics, we need to do a pull at bucket boundaries. If we do not do that the // current bucket will have incomplete data and the next will have the wrong snapshot to do @@ -205,10 +208,10 @@ void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, invalidateCurrentBucket(); break; case NO_TIME_CONSTRAINTS: - pullAndMatchEventsLocked(dumpTimeNs); + pullAndMatchEventsLocked(dumpTimeNs, mCondition); break; } - } + } flushCurrentBucketLocked(dumpTimeNs, dumpTimeNs); } protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_ID, (long long)mMetricId); @@ -325,12 +328,16 @@ void ValueMetricProducer::onDumpReportLocked(const int64_t dumpTimeNs, } } -void ValueMetricProducer::invalidateCurrentBucket() { +void ValueMetricProducer::invalidateCurrentBucketWithoutResetBase() { if (!mCurrentBucketIsInvalid) { // Only report once per invalid bucket. StatsdStats::getInstance().noteInvalidatedBucket(mMetricId); } mCurrentBucketIsInvalid = true; +} + +void ValueMetricProducer::invalidateCurrentBucket() { + invalidateCurrentBucketWithoutResetBase(); resetBase(); } @@ -345,82 +352,112 @@ void ValueMetricProducer::resetBase() { void ValueMetricProducer::onConditionChangedLocked(const bool condition, const int64_t eventTimeNs) { - if (eventTimeNs < mCurrentBucketStartTimeNs) { + bool isEventTooLate = eventTimeNs < mCurrentBucketStartTimeNs; + if (!isEventTooLate) { + if (mCondition == ConditionState::kUnknown) { + // If the condition was unknown, we mark the bucket as invalid since the bucket will + // contain partial data. For instance, the condition change might happen close to the + // end of the bucket and we might miss lots of data. + // + // We still want to pull to set the base. + invalidateCurrentBucket(); + } + + // Pull on condition changes. + ConditionState newCondition = condition ? ConditionState::kTrue : ConditionState::kFalse; + bool conditionChanged = + (mCondition == ConditionState::kTrue && newCondition == ConditionState::kFalse) + || (mCondition == ConditionState::kFalse && newCondition == ConditionState::kTrue); + // We do not need to pull when we go from unknown to false. + // + // We also pull if the condition was already true in order to be able to flush the bucket at + // the end if needed. + // + // onConditionChangedLocked might happen on bucket boundaries if this is called before + // #onDataPulled. + if (mIsPulled && (conditionChanged || condition)) { + pullAndMatchEventsLocked(eventTimeNs, newCondition); + } + + // When condition change from true to false, clear diff base but don't + // reset other counters as we may accumulate more value in the bucket. + if (mUseDiff && mCondition == ConditionState::kTrue + && newCondition == ConditionState::kFalse) { + resetBase(); + } + mCondition = newCondition; + + } else { VLOG("Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs, (long long)mCurrentBucketStartTimeNs); StatsdStats::getInstance().noteConditionChangeInNextBucket(mMetricId); invalidateCurrentBucket(); - return; + // Something weird happened. If we received another event if the future, the condition might + // be wrong. + mCondition = initialCondition(mConditionTrackerIndex); } + // This part should alway be called. flushIfNeededLocked(eventTimeNs); - - // Pull on condition changes. - bool conditionChanged = mCondition != condition; - bool unknownToFalse = mCondition == ConditionState::kUnknown - && condition == ConditionState::kFalse; - // We do not need to pull when we go from unknown to false. - if (mIsPulled && conditionChanged && !unknownToFalse) { - pullAndMatchEventsLocked(eventTimeNs); - } - - // when condition change from true to false, clear diff base but don't - // reset other counters as we may accumulate more value in the bucket. - if (mUseDiff && mCondition == ConditionState::kTrue && condition == ConditionState::kFalse) { - resetBase(); - } - - mCondition = condition ? ConditionState::kTrue : ConditionState::kFalse; } -void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs) { +void ValueMetricProducer::pullAndMatchEventsLocked(const int64_t timestampNs, ConditionState condition) { vector<std::shared_ptr<LogEvent>> allData; if (!mPullerManager->Pull(mPullTagId, &allData)) { - ALOGE("Gauge Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs); + ALOGE("Stats puller failed for tag: %d at %lld", mPullTagId, (long long)timestampNs); invalidateCurrentBucket(); return; } - accumulateEvents(allData, timestampNs, timestampNs); + accumulateEvents(allData, timestampNs, timestampNs, condition); } int64_t ValueMetricProducer::calcPreviousBucketEndTime(const int64_t currentTimeNs) { return mTimeBaseNs + ((currentTimeNs - mTimeBaseNs) / mBucketSizeNs) * mBucketSizeNs; } +// By design, statsd pulls data at bucket boundaries using AlarmManager. These pulls are likely +// to be delayed. Other events like condition changes or app upgrade which are not based on +// AlarmManager might have arrived earlier and close the bucket. void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData, bool pullSuccess, int64_t originalPullTimeNs) { std::lock_guard<std::mutex> lock(mMutex); - if (mCondition == ConditionState::kTrue) { - if (!pullSuccess) { + if (mCondition == ConditionState::kTrue) { // If the pull failed, we won't be able to compute a diff. - invalidateCurrentBucket(); - return; + if (!pullSuccess) { + invalidateCurrentBucket(); + } else { + bool isEventLate = originalPullTimeNs < getCurrentBucketEndTimeNs(); + if (isEventLate) { + // If the event is late, we are in the middle of a bucket. Just + // process the data without trying to snap the data to the nearest bucket. + accumulateEvents(allData, originalPullTimeNs, originalPullTimeNs, mCondition); + } else { + // For scheduled pulled data, the effective event time is snap to the nearest + // bucket end. In the case of waking up from a deep sleep state, we will + // attribute to the previous bucket end. If the sleep was long but not very + // long, we will be in the immediate next bucket. Previous bucket may get a + // larger number as we pull at a later time than real bucket end. + // + // If the sleep was very long, we skip more than one bucket before sleep. In + // this case, if the diff base will be cleared and this new data will serve as + // new diff base. + int64_t bucketEndTime = calcPreviousBucketEndTime(originalPullTimeNs) - 1; + StatsdStats::getInstance().noteBucketBoundaryDelayNs( + mMetricId, originalPullTimeNs - bucketEndTime); + accumulateEvents(allData, originalPullTimeNs, bucketEndTime, mCondition); + } + } } - // For scheduled pulled data, the effective event time is snap to the nearest - // bucket end. In the case of waking up from a deep sleep state, we will - // attribute to the previous bucket end. If the sleep was long but not very long, we - // will be in the immediate next bucket. Previous bucket may get a larger number as - // we pull at a later time than real bucket end. - // If the sleep was very long, we skip more than one bucket before sleep. In this case, - // if the diff base will be cleared and this new data will serve as new diff base. - int64_t bucketEndTime = calcPreviousBucketEndTime(originalPullTimeNs) - 1; - StatsdStats::getInstance().noteBucketBoundaryDelayNs( - mMetricId, originalPullTimeNs - bucketEndTime); - accumulateEvents(allData, originalPullTimeNs, bucketEndTime); - - // We can probably flush the bucket. Since we used bucketEndTime when calling - // #onMatchedLogEventInternalLocked, the current bucket will not have been flushed. - flushIfNeededLocked(originalPullTimeNs); - - } else { - VLOG("No need to commit data on condition false."); - } + // We can probably flush the bucket. Since we used bucketEndTime when calling + // #onMatchedLogEventInternalLocked, the current bucket will not have been flushed. + flushIfNeededLocked(originalPullTimeNs); } -void ValueMetricProducer::accumulateEvents(const std::vector<std::shared_ptr<LogEvent>>& allData, - int64_t originalPullTimeNs, int64_t eventElapsedTimeNs) { +void ValueMetricProducer::accumulateEvents(const std::vector<std::shared_ptr<LogEvent>>& allData, + int64_t originalPullTimeNs, int64_t eventElapsedTimeNs, + ConditionState condition) { bool isEventLate = eventElapsedTimeNs < mCurrentBucketStartTimeNs; if (isEventLate) { VLOG("Skip bucket end pull due to late arrival: %lld vs %lld", @@ -459,7 +496,7 @@ void ValueMetricProducer::accumulateEvents(const std::vector<std::shared_ptr<Log // If the new pulled data does not contains some keys we track in our intervals, we need to // reset the base. for (auto& slice : mCurrentSlicedBucket) { - bool presentInPulledData = mMatchedMetricDimensionKeys.find(slice.first) + bool presentInPulledData = mMatchedMetricDimensionKeys.find(slice.first) != mMatchedMetricDimensionKeys.end(); if (!presentInPulledData) { for (auto& interval : slice.second) { @@ -583,7 +620,10 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIn } mMatchedMetricDimensionKeys.insert(eventKey); - flushIfNeededLocked(eventTimeNs); + if (!mIsPulled) { + // We cannot flush without doing a pull first. + flushIfNeededLocked(eventTimeNs); + } // For pulled data, we already check condition when we decide to pull or // in onDataPulled. So take all of them. @@ -718,26 +758,26 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIn } } +// For pulled metrics, we always need to make sure we do a pull before flushing the bucket +// if mCondition is true! void ValueMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) { int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs(); - if (eventTimeNs < currentBucketEndTimeNs) { VLOG("eventTime is %lld, less than next bucket start time %lld", (long long)eventTimeNs, (long long)(currentBucketEndTimeNs)); return; } - - int64_t numBucketsForward = 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs; + int64_t numBucketsForward = calcBucketsForwardCount(eventTimeNs); int64_t nextBucketStartTimeNs = currentBucketEndTimeNs + (numBucketsForward - 1) * mBucketSizeNs; flushCurrentBucketLocked(eventTimeNs, nextBucketStartTimeNs); +} - mCurrentBucketNum += numBucketsForward; - if (numBucketsForward > 1) { - VLOG("Skipping forward %lld buckets", (long long)numBucketsForward); - StatsdStats::getInstance().noteSkippedForwardBuckets(mMetricId); - // take base again in future good bucket. - resetBase(); +int64_t ValueMetricProducer::calcBucketsForwardCount(const int64_t& eventTimeNs) const { + int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs(); + if (eventTimeNs < currentBucketEndTimeNs) { + return 0; } + return 1 + (eventTimeNs - currentBucketEndTimeNs) / mBucketSizeNs; } void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs, @@ -746,6 +786,16 @@ void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs, StatsdStats::getInstance().noteBucketUnknownCondition(mMetricId); } + int64_t numBucketsForward = calcBucketsForwardCount(eventTimeNs); + mCurrentBucketNum += numBucketsForward; + if (numBucketsForward > 1) { + VLOG("Skipping forward %lld buckets", (long long)numBucketsForward); + StatsdStats::getInstance().noteSkippedForwardBuckets(mMetricId); + // Something went wrong. Maybe the device was sleeping for a long time. It is better + // to mark the current bucket as invalid. The last pull might have been successful through. + invalidateCurrentBucketWithoutResetBase(); + } + VLOG("finalizing bucket for %ld, dumping %d slices", (long)mCurrentBucketStartTimeNs, (int)mCurrentSlicedBucket.size()); int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs(); @@ -769,11 +819,7 @@ void ValueMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs, if (!mCurrentBucketIsInvalid) { appendToFullBucket(eventTimeNs, fullBucketEndTimeNs); } - initCurrentSlicedBucket(); - mCurrentBucketIsInvalid = false; - mCurrentBucketStartTimeNs = nextBucketStartTimeNs; - VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId, - (long long)mCurrentBucketStartTimeNs); + initCurrentSlicedBucket(nextBucketStartTimeNs); } ValueBucket ValueMetricProducer::buildPartialBucket(int64_t bucketEndTime, @@ -800,7 +846,9 @@ ValueBucket ValueMetricProducer::buildPartialBucket(int64_t bucketEndTime, return bucket; } -void ValueMetricProducer::initCurrentSlicedBucket() { +void ValueMetricProducer::initCurrentSlicedBucket(int64_t nextBucketStartTimeNs) { + StatsdStats::getInstance().noteBucketCount(mMetricId); + // Cleanup data structure to aggregate values. for (auto it = mCurrentSlicedBucket.begin(); it != mCurrentSlicedBucket.end();) { bool obsolete = true; for (auto& interval : it->second) { @@ -818,6 +866,16 @@ void ValueMetricProducer::initCurrentSlicedBucket() { it++; } } + + mCurrentBucketIsInvalid = false; + // If we do not have a global base when the condition is true, + // we will have incomplete bucket for the next bucket. + if (mUseDiff && !mHasGlobalBase && mCondition) { + mCurrentBucketIsInvalid = false; + } + mCurrentBucketStartTimeNs = nextBucketStartTimeNs; + VLOG("metric %lld: new bucket start time: %lld", (long long)mMetricId, + (long long)mCurrentBucketStartTimeNs); } void ValueMetricProducer::appendToFullBucket(int64_t eventTimeNs, int64_t fullBucketEndTimeNs) { diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h index 696d4fa7ae45..f317c3768dd9 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.h +++ b/cmds/statsd/src/metrics/ValueMetricProducer.h @@ -39,6 +39,13 @@ struct ValueBucket { std::vector<Value> values; }; + +// Aggregates values within buckets. +// +// There are different events that might complete a bucket +// - a condition change +// - an app upgrade +// - an alarm set to the end of the bucket class ValueMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver { public: ValueMetricProducer(const ConfigKey& key, const ValueMetric& valueMetric, @@ -61,9 +68,8 @@ public: if (!mSplitBucketForAppUpgrade) { return; } - flushIfNeededLocked(eventTimeNs - 1); if (mIsPulled && mCondition) { - pullAndMatchEventsLocked(eventTimeNs - 1); + pullAndMatchEventsLocked(eventTimeNs, mCondition); } flushCurrentBucketLocked(eventTimeNs, eventTimeNs); }; @@ -94,9 +100,12 @@ private: void dumpStatesLocked(FILE* out, bool verbose) const override; - // Util function to flush the old packet. + // For pulled metrics, this method should only be called if a pull has be done. Else we will + // not have complete data for the bucket. void flushIfNeededLocked(const int64_t& eventTime) override; + // For pulled metrics, this method should only be called if a pulled have be done. Else we will + // not have complete data for the bucket. void flushCurrentBucketLocked(const int64_t& eventTimeNs, const int64_t& nextBucketStartTimeNs) override; @@ -105,8 +114,12 @@ private: // Calculate previous bucket end time based on current time. int64_t calcPreviousBucketEndTime(const int64_t currentTimeNs); + // Calculate how many buckets are present between the current bucket and eventTimeNs. + int64_t calcBucketsForwardCount(const int64_t& eventTimeNs) const; + // Mark the data as invalid. void invalidateCurrentBucket(); + void invalidateCurrentBucketWithoutResetBase(); const int mWhatMatcherIndex; @@ -163,14 +176,15 @@ private: bool hitFullBucketGuardRailLocked(const MetricDimensionKey& newKey); - void pullAndMatchEventsLocked(const int64_t timestampNs); + void pullAndMatchEventsLocked(const int64_t timestampNs, ConditionState condition); void accumulateEvents(const std::vector<std::shared_ptr<LogEvent>>& allData, - int64_t originalPullTimeNs, int64_t eventElapsedTimeNs); + int64_t originalPullTimeNs, int64_t eventElapsedTimeNs, + ConditionState condition); ValueBucket buildPartialBucket(int64_t bucketEndTime, const std::vector<Interval>& intervals); - void initCurrentSlicedBucket(); + void initCurrentSlicedBucket(int64_t nextBucketStartTimeNs); void appendToFullBucket(int64_t eventTimeNs, int64_t fullBucketEndTimeNs); // Reset diff base and mHasGlobalBase @@ -214,47 +228,55 @@ private: const bool mSplitBucketForAppUpgrade; + FRIEND_TEST(ValueMetricProducerTest, TestAnomalyDetection); + FRIEND_TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange); + FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundariesOnAppUpgrade); + FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange); + FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition); + FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition); + FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2); + FRIEND_TEST(ValueMetricProducerTest, TestBucketIncludingUnknownConditionIsInvalid); + FRIEND_TEST(ValueMetricProducerTest, TestBucketInvalidIfGlobalBaseIsNotSet); + FRIEND_TEST(ValueMetricProducerTest, TestCalcPreviousBucketEndTime); + FRIEND_TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged); + FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary); + FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged); + FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled); + FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition); + FRIEND_TEST(ValueMetricProducerTest, TestFirstBucket); + FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenGuardRailHit); + FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenInitialPullFailed); + FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenLastPullFailed); + FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenOneConditionFailed); + FRIEND_TEST(ValueMetricProducerTest, TestLateOnDataPulledWithDiff); + FRIEND_TEST(ValueMetricProducerTest, TestLateOnDataPulledWithoutDiff); + FRIEND_TEST(ValueMetricProducerTest, TestPartialBucketCreated); + FRIEND_TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries); FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsNoCondition); - FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering); FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset); FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset); - FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition); - FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade); + FRIEND_TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering); FRIEND_TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade); - FRIEND_TEST(ValueMetricProducerTest, TestPartialBucketCreated); FRIEND_TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse); FRIEND_TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled); - FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition); - FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithCondition); - FRIEND_TEST(ValueMetricProducerTest, TestAnomalyDetection); - FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition); - FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition); - FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2); - FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMin); - FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMax); FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateAvg); + FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMax); + FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateMin); FRIEND_TEST(ValueMetricProducerTest, TestPushedAggregateSum); - FRIEND_TEST(ValueMetricProducerTest, TestFirstBucket); - FRIEND_TEST(ValueMetricProducerTest, TestCalcPreviousBucketEndTime); + FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithCondition); + FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade); + FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition); + FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded); + FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange); + FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfBucket); + FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange); + FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate); FRIEND_TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput); FRIEND_TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue); + FRIEND_TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey); FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBase); FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures); - FRIEND_TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey); - FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange); - FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange); - FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfBucket); - FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate); - FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenOneConditionFailed); - FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenGuardRailHit); - FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenInitialPullFailed); - FRIEND_TEST(ValueMetricProducerTest, TestInvalidBucketWhenLastPullFailed); - FRIEND_TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded); - FRIEND_TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange); - FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled); - FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged); - FRIEND_TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary); - FRIEND_TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries); + friend class ValueMetricProducerTestHelper; }; } // namespace statsd diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto index 623d8bc06e38..166e087b69dc 100644 --- a/cmds/statsd/src/stats_log.proto +++ b/cmds/statsd/src/stats_log.proto @@ -426,6 +426,7 @@ message StatsdStatsReport { optional int64 min_bucket_boundary_delay_ns = 9; optional int64 max_bucket_boundary_delay_ns = 10; optional int64 bucket_unknown_condition = 11; + optional int64 bucket_count = 12; } repeated AtomMetricStats atom_metric_stats = 17; diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp index ca645e186614..31f160d99944 100644 --- a/cmds/statsd/src/stats_log_util.cpp +++ b/cmds/statsd/src/stats_log_util.cpp @@ -85,6 +85,7 @@ const int FIELD_ID_BUCKET_DROPPED = 8; const int FIELD_ID_MIN_BUCKET_BOUNDARY_DELAY_NS = 9; const int FIELD_ID_MAX_BUCKET_BOUNDARY_DELAY_NS = 10; const int FIELD_ID_BUCKET_UNKNOWN_CONDITION = 11; +const int FIELD_ID_BUCKET_COUNT = 12; namespace { @@ -515,6 +516,8 @@ void writeAtomMetricStatsToStream(const std::pair<int64_t, StatsdStats::AtomMetr (long long)pair.second.maxBucketBoundaryDelayNs); protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_UNKNOWN_CONDITION, (long long)pair.second.bucketUnknownCondition); + protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_COUNT, + (long long)pair.second.bucketCount); protoOutput->end(token); } diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp index a9d2c8810adc..e5e453490159 100644 --- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp @@ -52,15 +52,100 @@ const int64_t bucket5StartTimeNs = bucketStartTimeNs + 4 * bucketSizeNs; const int64_t bucket6StartTimeNs = bucketStartTimeNs + 5 * bucketSizeNs; double epsilon = 0.001; +static void assertPastBucketValuesSingleKey( + const std::unordered_map<MetricDimensionKey, std::vector<ValueBucket>>& mPastBuckets, + const std::initializer_list<int>& expectedValuesList) { + std::vector<int> expectedValues(expectedValuesList); + if (expectedValues.size() == 0) { + ASSERT_EQ(0, mPastBuckets.size()); + return; + } + + ASSERT_EQ(1, mPastBuckets.size()); + ASSERT_EQ(expectedValues.size(), mPastBuckets.begin()->second.size()); + + auto buckets = mPastBuckets.begin()->second; + for (int i = 0; i < expectedValues.size(); i++) { + EXPECT_EQ(expectedValues[i], buckets[i].values[0].long_value) + << "Values differ at index " << i; + } +} + + +class ValueMetricProducerTestHelper { + + public: + static shared_ptr<LogEvent> createEvent(int64_t eventTimeNs, int64_t value) { + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, eventTimeNs); + event->write(tagId); + event->write(value); + event->write(value); + event->init(); + return event; + } + + static sp<ValueMetricProducer> createValueProducerNoConditions( + sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric) { + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); + + sp<ValueMetricProducer> valueProducer = new ValueMetricProducer( + kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + logEventMatcherIndex, eventMatcherWizard, tagId, + bucketStartTimeNs, bucketStartTimeNs, pullerManager); + return valueProducer; + } + + static sp<ValueMetricProducer> createValueProducerWithCondition( + sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric) { + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); + + sp<ValueMetricProducer> valueProducer = + new ValueMetricProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, + eventMatcherWizard, tagId, bucketStartTimeNs, + bucketStartTimeNs, pullerManager); + valueProducer->mCondition = ConditionState::kFalse; + return valueProducer; + } + + static ValueMetric createMetric() { + 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); + metric.set_max_pull_delay_sec(INT_MAX); + return metric; + } + + static ValueMetric createMetricWithCondition() { + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); + metric.set_condition(StringToId("SCREEN_ON")); + return metric; + } +}; + + /* * Tests that the first bucket works correctly */ TEST(ValueMetricProducerTest, TestCalcPreviousBucketEndTime) { - 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); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); int64_t startTimeBase = 11; UidMap uidMap; @@ -90,11 +175,7 @@ TEST(ValueMetricProducerTest, TestCalcPreviousBucketEndTime) { * 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); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); UidMap uidMap; SimpleAtomMatcher atomMatcher; @@ -120,23 +201,8 @@ TEST(ValueMetricProducerTest, TestFirstBucket) { * Tests pulled atoms with no conditions */ TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) { - 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); - metric.set_max_pull_delay_sec(INT_MAX); - - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp<EventMatcherWizard> eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); 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, vector<std::shared_ptr<LogEvent>>* data) { data->clear(); @@ -148,9 +214,8 @@ TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) { return true; })); - ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, - logEventMatcherIndex, eventMatcherWizard, tagId, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); vector<shared_ptr<LogEvent>> allData; allData.clear(); @@ -160,17 +225,17 @@ TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) { event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); // has one slice - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(11, curInterval.base.long_value); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(8, curInterval.value.long_value); - EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); - EXPECT_EQ(8, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value); + EXPECT_EQ(1UL, valueProducer->mPastBuckets.size()); + EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value); allData.clear(); event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1); @@ -178,19 +243,19 @@ TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) { event->write(23); event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); + valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); // has one slice - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(23, curInterval.base.long_value); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(12, curInterval.value.long_value); - EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); - EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size()); - EXPECT_EQ(8, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value); - EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value); + EXPECT_EQ(1UL, valueProducer->mPastBuckets.size()); + EXPECT_EQ(2UL, valueProducer->mPastBuckets.begin()->second.size()); + EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value); + EXPECT_EQ(12, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value); allData.clear(); event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1); @@ -198,39 +263,24 @@ TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) { event->write(36); event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(36, curInterval.base.long_value); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(13, curInterval.value.long_value); - EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); - EXPECT_EQ(3UL, valueProducer.mPastBuckets.begin()->second.size()); - EXPECT_EQ(8, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value); - EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second[1].values[0].long_value); - EXPECT_EQ(13, valueProducer.mPastBuckets.begin()->second[2].values[0].long_value); + EXPECT_EQ(1UL, valueProducer->mPastBuckets.size()); + EXPECT_EQ(3UL, valueProducer->mPastBuckets.begin()->second.size()); + EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value); + EXPECT_EQ(12, valueProducer->mPastBuckets.begin()->second[1].values[0].long_value); + EXPECT_EQ(13, valueProducer->mPastBuckets.begin()->second[2].values[0].long_value); } TEST(ValueMetricProducerTest, TestPartialBucketCreated) { - 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); - metric.set_max_pull_delay_sec(INT_MAX); - - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp<EventMatcherWizard> eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); EXPECT_CALL(*pullerManager, Pull(tagId, _)) // Initialize bucket. .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { @@ -253,9 +303,8 @@ TEST(ValueMetricProducerTest, TestPartialBucketCreated) { return true; })); - ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, - logEventMatcherIndex, eventMatcherWizard, tagId, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); // First bucket ends. vector<shared_ptr<LogEvent>> allData; @@ -265,14 +314,14 @@ TEST(ValueMetricProducerTest, TestPartialBucketCreated) { event->write(2); event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData, /** success */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, /** success */ true, bucket2StartTimeNs); // Partial buckets created in 2nd bucket. - valueProducer.notifyAppUpgrade(bucket2StartTimeNs + 2, "com.foo", 10000, 1); + valueProducer->notifyAppUpgrade(bucket2StartTimeNs + 2, "com.foo", 10000, 1); // One full bucket and one partial bucket. - EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); - vector<ValueBucket> buckets = valueProducer.mPastBuckets.begin()->second; + EXPECT_EQ(1UL, valueProducer->mPastBuckets.size()); + vector<ValueBucket> buckets = valueProducer->mPastBuckets.begin()->second; EXPECT_EQ(2UL, buckets.size()); // Full bucket (2 - 1) EXPECT_EQ(1, buckets[0].values[0].long_value); @@ -284,12 +333,7 @@ TEST(ValueMetricProducerTest, TestPartialBucketCreated) { * Tests pulled atoms with filtering */ TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) { - 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); - metric.set_max_pull_delay_sec(INT_MAX); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); UidMap uidMap; SimpleAtomMatcher atomMatcher; @@ -315,9 +359,10 @@ TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) { return true; })); - ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, - logEventMatcherIndex, eventMatcherWizard, tagId, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); + sp<ValueMetricProducer> valueProducer = new ValueMetricProducer( + kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + logEventMatcherIndex, eventMatcherWizard, tagId, + bucketStartTimeNs, bucketStartTimeNs, pullerManager); vector<shared_ptr<LogEvent>> allData; allData.clear(); @@ -327,18 +372,18 @@ TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) { event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); // has one slice - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval curInterval = - valueProducer.mCurrentSlicedBucket.begin()->second[0]; + valueProducer->mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(11, curInterval.base.long_value); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(8, curInterval.value.long_value); - EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); - EXPECT_EQ(8, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value); + EXPECT_EQ(1UL, valueProducer->mPastBuckets.size()); + EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value); allData.clear(); event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1); @@ -346,16 +391,16 @@ TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) { event->write(23); event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); + valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); // No new data seen, so data has been cleared. - EXPECT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size()); + EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(11, curInterval.base.long_value); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(8, curInterval.value.long_value); - EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); - EXPECT_EQ(8, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value); + EXPECT_EQ(1UL, valueProducer->mPastBuckets.size()); + EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value); allData.clear(); event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1); @@ -363,46 +408,30 @@ TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) { event->write(36); event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; // the base was reset EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(36, curInterval.base.long_value); EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); - EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size()); - EXPECT_EQ(8, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value); + EXPECT_EQ(1UL, valueProducer->mPastBuckets.size()); + EXPECT_EQ(1UL, valueProducer->mPastBuckets.begin()->second.size()); + EXPECT_EQ(8, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value); } /* * Tests pulled atoms with no conditions and take absolute value after reset */ TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset) { - 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); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); metric.set_use_absolute_value_on_reset(true); - metric.set_max_pull_delay_sec(INT_MAX); - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp<EventMatcherWizard> eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - 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(true)); - - ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, - logEventMatcherIndex, eventMatcherWizard, tagId, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); vector<shared_ptr<LogEvent>> allData; allData.clear(); @@ -412,15 +441,15 @@ TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset) { event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); // has one slice - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(11, curInterval.base.long_value); EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(0UL, valueProducer->mPastBuckets.size()); allData.clear(); event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1); @@ -428,16 +457,16 @@ TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset) { event->write(10); event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); + valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); // has one slice - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(10, curInterval.base.long_value); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(10, curInterval.value.long_value); - EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); - EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value); + EXPECT_EQ(1UL, valueProducer->mPastBuckets.size()); + EXPECT_EQ(10, valueProducer->mPastBuckets.begin()->second.back().values[0].long_value); allData.clear(); event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1); @@ -445,45 +474,28 @@ TEST(ValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset) { event->write(36); event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(36, curInterval.base.long_value); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(26, curInterval.value.long_value); - EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); - EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size()); - EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value); - EXPECT_EQ(26, valueProducer.mPastBuckets.begin()->second[1].values[0].long_value); + EXPECT_EQ(1UL, valueProducer->mPastBuckets.size()); + EXPECT_EQ(2UL, valueProducer->mPastBuckets.begin()->second.size()); + EXPECT_EQ(10, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value); + EXPECT_EQ(26, valueProducer->mPastBuckets.begin()->second[1].values[0].long_value); } /* * Tests pulled atoms with no conditions and take zero value after reset */ TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) { - 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); - metric.set_max_pull_delay_sec(INT_MAX); - - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp<EventMatcherWizard> eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); 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, - logEventMatcherIndex, eventMatcherWizard, tagId, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); vector<shared_ptr<LogEvent>> allData; allData.clear(); @@ -493,15 +505,15 @@ TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) { event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); // has one slice - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(11, curInterval.base.long_value); EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(0UL, valueProducer->mPastBuckets.size()); allData.clear(); event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1); @@ -509,14 +521,14 @@ TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) { event->write(10); event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); + valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); // has one slice - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(10, curInterval.base.long_value); EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(0UL, valueProducer->mPastBuckets.size()); allData.clear(); event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1); @@ -524,39 +536,24 @@ TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) { event->write(36); event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(36, curInterval.base.long_value); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(26, curInterval.value.long_value); - EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); - EXPECT_EQ(26, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value); + EXPECT_EQ(1UL, valueProducer->mPastBuckets.size()); + EXPECT_EQ(26, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value); } /* * Test pulled event with non sliced condition. */ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) { - 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); - metric.set_condition(StringToId("SCREEN_ON")); - metric.set_max_pull_delay_sec(INT_MAX); + ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp<EventMatcherWizard> eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); EXPECT_CALL(*pullerManager, Pull(tagId, _)) .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { @@ -572,25 +569,25 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) { data->clear(); shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1); event->write(tagId); - event->write(120); + event->write(130); event->init(); data->push_back(event); return true; })); - ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, - eventMatcherWizard, tagId, bucketStartTimeNs, - bucketStartTimeNs, pullerManager); - valueProducer.onConditionChanged(true, bucketStartTimeNs + 8); + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); + + valueProducer->onConditionChanged(true, bucketStartTimeNs + 8); // has one slice - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; // startUpdated:false sum:0 start:100 EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(100, curInterval.base.long_value); EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(0UL, valueProducer->mPastBuckets.size()); vector<shared_ptr<LogEvent>> allData; allData.clear(); @@ -599,34 +596,30 @@ TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) { event->write(110); event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}); // has one slice - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(110, curInterval.base.long_value); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(10, curInterval.value.long_value); - EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); - EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value); - valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1); + valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}); // has one slice - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(true, curInterval.hasValue); - EXPECT_EQ(10, curInterval.value.long_value); + EXPECT_EQ(20, curInterval.value.long_value); EXPECT_EQ(false, curInterval.hasBase); } TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade) { - 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); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); UidMap uidMap; SimpleAtomMatcher atomMatcher; @@ -670,12 +663,7 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade) { } TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade) { - 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); - metric.set_max_pull_delay_sec(INT_MAX); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); UidMap uidMap; SimpleAtomMatcher atomMatcher; @@ -733,12 +721,7 @@ TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade) { } TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled) { - 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); - metric.set_max_pull_delay_sec(INT_MAX); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); metric.set_split_bucket_for_app_upgrade(false); UidMap uidMap; @@ -773,24 +756,9 @@ TEST(ValueMetricProducerTest, TestPulledWithAppUpgradeDisabled) { } TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse) { - 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); - metric.set_condition(StringToId("SCREEN_ON")); - metric.set_max_pull_delay_sec(INT_MAX); + ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp<EventMatcherWizard> eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - 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(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { data->clear(); @@ -810,29 +778,25 @@ TEST(ValueMetricProducerTest, TestPulledValueWithUpgradeWhileConditionFalse) { data->push_back(event); return true; })); - ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, - eventMatcherWizard, tagId, bucketStartTimeNs, - bucketStartTimeNs, pullerManager); - valueProducer.onConditionChanged(true, bucketStartTimeNs + 1); + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); - valueProducer.onConditionChanged(false, bucket2StartTimeNs-100); - EXPECT_FALSE(valueProducer.mCondition); + valueProducer->onConditionChanged(true, bucketStartTimeNs + 1); - valueProducer.notifyAppUpgrade(bucket2StartTimeNs-50, "ANY.APP", 1, 1); + valueProducer->onConditionChanged(false, bucket2StartTimeNs-100); + EXPECT_FALSE(valueProducer->mCondition); + + valueProducer->notifyAppUpgrade(bucket2StartTimeNs-50, "ANY.APP", 1, 1); // Expect one full buckets already done and starting a partial bucket. - EXPECT_EQ(bucket2StartTimeNs-50, valueProducer.mCurrentBucketStartTimeNs); - EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); - EXPECT_EQ(bucketStartTimeNs, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs); - EXPECT_EQ(20L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].values[0].long_value); - EXPECT_FALSE(valueProducer.mCondition); + EXPECT_EQ(bucket2StartTimeNs-50, valueProducer->mCurrentBucketStartTimeNs); + EXPECT_EQ(1UL, valueProducer->mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size()); + EXPECT_EQ(bucketStartTimeNs, valueProducer->mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs); + EXPECT_EQ(20L, valueProducer->mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].values[0].long_value); + EXPECT_FALSE(valueProducer->mCondition); } TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) { - 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); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); UidMap uidMap; SimpleAtomMatcher atomMatcher; @@ -869,18 +833,12 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) { curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(30, curInterval.value.long_value); - valueProducer.flushIfNeededLocked(bucket3StartTimeNs); - EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); - EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size()); - EXPECT_EQ(30, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value); + valueProducer.flushIfNeededLocked(bucket2StartTimeNs); + assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {30}); } TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) { - 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); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); UidMap uidMap; SimpleAtomMatcher atomMatcher; @@ -894,6 +852,7 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) { ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, pullerManager); + valueProducer.mCondition = ConditionState::kFalse; shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); event1->write(1); @@ -939,10 +898,8 @@ TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) { curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(50, curInterval.value.long_value); - valueProducer.flushIfNeededLocked(bucket3StartTimeNs); - EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); - EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size()); - EXPECT_EQ(50, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value); + valueProducer.flushIfNeededLocked(bucket2StartTimeNs); + assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {50}); } TEST(ValueMetricProducerTest, TestAnomalyDetection) { @@ -955,11 +912,7 @@ TEST(ValueMetricProducerTest, TestAnomalyDetection) { const int32_t refPeriodSec = 3; alert.set_refractory_period_secs(refPeriodSec); - 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); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); UidMap uidMap; SimpleAtomMatcher atomMatcher; @@ -1036,28 +989,11 @@ TEST(ValueMetricProducerTest, TestAnomalyDetection) { // Test value metric no condition, the pull on bucket boundary come in time and too late TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) { - 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); - metric.set_max_pull_delay_sec(INT_MAX); - - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp<EventMatcherWizard> eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); 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(true)); - - ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, - logEventMatcherIndex, eventMatcherWizard, tagId, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); vector<shared_ptr<LogEvent>> allData; // pull 1 @@ -1068,16 +1004,16 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) { event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); // has one slice - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; // startUpdated:true sum:0 start:11 EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(11, curInterval.base.long_value); EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(0UL, valueProducer->mPastBuckets.size()); // pull 2 at correct time allData.clear(); @@ -1086,16 +1022,15 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) { event->write(23); event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); + valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); // has one slice - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; // tartUpdated:false sum:12 EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(23, curInterval.base.long_value); EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); - EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}); // pull 3 come late. // The previous bucket gets closed with error. (Has start value 23, no ending) @@ -1107,16 +1042,14 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) { event->write(36); event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData, /** succeed */ true, bucket6StartTimeNs); - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + valueProducer->onDataPulled(allData, /** succeed */ true, bucket6StartTimeNs); + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; // startUpdated:false sum:12 EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(36, curInterval.base.long_value); EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); - EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size()); - EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}); } /* @@ -1124,25 +1057,9 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) { * was delivered late. */ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) { - 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); - metric.set_condition(StringToId("SCREEN_ON")); - metric.set_max_pull_delay_sec(INT_MAX); + ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp<EventMatcherWizard> eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); - EXPECT_CALL(*pullerManager, Pull(tagId, _)) // condition becomes true .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { @@ -1164,44 +1081,35 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) { data->push_back(event); return true; })); + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); - ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, - eventMatcherWizard, tagId, bucketStartTimeNs, - bucketStartTimeNs, pullerManager); - valueProducer.onConditionChanged(true, bucketStartTimeNs + 8); + valueProducer->onConditionChanged(true, bucketStartTimeNs + 8); // has one slice - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(100, curInterval.base.long_value); EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(0UL, valueProducer->mPastBuckets.size()); // pull on bucket boundary come late, condition change happens before it - valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1); + curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}); EXPECT_EQ(false, curInterval.hasBase); - EXPECT_EQ(true, curInterval.hasValue); - EXPECT_EQ(20, curInterval.value.long_value); - EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); // Now the alarm is delivered. // since the condition turned to off before this pull finish, it has no effect vector<shared_ptr<LogEvent>> allData; - allData.clear(); - shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 30); - event->write(1); - event->write(110); - event->init(); - allData.push_back(event); - valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 30, 110)); + valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}); + curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(false, curInterval.hasBase); - EXPECT_EQ(true, curInterval.hasValue); - EXPECT_EQ(20, curInterval.value.long_value); - EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(false, curInterval.hasValue); } /* @@ -1209,25 +1117,9 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) { * change to false, and then true again. This is due to alarm delivered late. */ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) { - 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); - metric.set_condition(StringToId("SCREEN_ON")); - metric.set_max_pull_delay_sec(INT_MAX); + ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp<EventMatcherWizard> eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillRepeatedly(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); - EXPECT_CALL(*pullerManager, Pull(tagId, _)) // condition becomes true .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { @@ -1260,61 +1152,57 @@ TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) { return true; })); - ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, - eventMatcherWizard, tagId, bucketStartTimeNs, - bucketStartTimeNs, pullerManager); - valueProducer.onConditionChanged(true, bucketStartTimeNs + 8); + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); + + valueProducer->onConditionChanged(true, bucketStartTimeNs + 8); // has one slice - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + ValueMetricProducer::Interval curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; // startUpdated:false sum:0 start:100 EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(100, curInterval.base.long_value); EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(0UL, valueProducer->mPastBuckets.size()); // pull on bucket boundary come late, condition change happens before it - valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}); + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(false, curInterval.hasBase); - EXPECT_EQ(true, curInterval.hasValue); - EXPECT_EQ(20, curInterval.value.long_value); - EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(false, curInterval.hasValue); // condition changed to true again, before the pull alarm is delivered - valueProducer.onConditionChanged(true, bucket2StartTimeNs + 25); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + valueProducer->onConditionChanged(true, bucket2StartTimeNs + 25); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}); + curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(130, curInterval.base.long_value); - EXPECT_EQ(true, curInterval.hasValue); - EXPECT_EQ(20, curInterval.value.long_value); - EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(false, curInterval.hasValue); - // Now the alarm is delivered, but it is considered late, the bucket is invalidated. + // Now the alarm is delivered, but it is considered late, the data will be used + // for the new bucket since it was just pulled. vector<shared_ptr<LogEvent>> allData; - allData.clear(); - shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 50); - event->write(1); - event->write(110); - event->init(); - allData.push_back(event); - valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 50, 140)); + valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 50); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; - EXPECT_EQ(false, curInterval.hasBase); - EXPECT_EQ(130, curInterval.base.long_value); + curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; + EXPECT_EQ(true, curInterval.hasBase); + EXPECT_EQ(140, curInterval.base.long_value); EXPECT_EQ(true, curInterval.hasValue); - EXPECT_EQ(20, curInterval.value.long_value); - EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(10, curInterval.value.long_value); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}); + + allData.clear(); + allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket3StartTimeNs, 160)); + valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20, 30}); } TEST(ValueMetricProducerTest, TestPushedAggregateMin) { - 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); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); metric.set_aggregation_type(ValueMetric::MIN); UidMap uidMap; @@ -1352,18 +1240,12 @@ TEST(ValueMetricProducerTest, TestPushedAggregateMin) { curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(10, curInterval.value.long_value); - valueProducer.flushIfNeededLocked(bucket3StartTimeNs); - EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); - EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size()); - EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value); + valueProducer.flushIfNeededLocked(bucket2StartTimeNs); + assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {10}); } TEST(ValueMetricProducerTest, TestPushedAggregateMax) { - 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); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); metric.set_aggregation_type(ValueMetric::MAX); UidMap uidMap; @@ -1402,17 +1284,13 @@ TEST(ValueMetricProducerTest, TestPushedAggregateMax) { EXPECT_EQ(20, curInterval.value.long_value); valueProducer.flushIfNeededLocked(bucket3StartTimeNs); - EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); - EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size()); - EXPECT_EQ(20, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value); + /* EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); */ + /* EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size()); */ + /* EXPECT_EQ(20, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value); */ } TEST(ValueMetricProducerTest, TestPushedAggregateAvg) { - 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); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); metric.set_aggregation_type(ValueMetric::AVG); UidMap uidMap; @@ -1453,18 +1331,14 @@ TEST(ValueMetricProducerTest, TestPushedAggregateAvg) { EXPECT_EQ(25, curInterval.value.long_value); EXPECT_EQ(2, curInterval.sampleSize); - valueProducer.flushIfNeededLocked(bucket3StartTimeNs); + valueProducer.flushIfNeededLocked(bucket2StartTimeNs); EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size()); EXPECT_TRUE(std::abs(valueProducer.mPastBuckets.begin()->second.back().values[0].double_value - 12.5) < epsilon); } TEST(ValueMetricProducerTest, TestPushedAggregateSum) { - 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); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); metric.set_aggregation_type(ValueMetric::SUM); UidMap uidMap; @@ -1502,18 +1376,12 @@ TEST(ValueMetricProducerTest, TestPushedAggregateSum) { curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(25, curInterval.value.long_value); - valueProducer.flushIfNeededLocked(bucket3StartTimeNs); - EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); - EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size()); - EXPECT_EQ(25, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value); + valueProducer.flushIfNeededLocked(bucket2StartTimeNs); + assertPastBucketValuesSingleKey(valueProducer.mPastBuckets, {25}); } TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) { - 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); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); metric.set_aggregation_type(ValueMetric::MIN); metric.set_use_diff(true); @@ -1584,11 +1452,7 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) { } TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) { - 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); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); metric.mutable_value_field()->add_child()->set_field(3); metric.set_aggregation_type(ValueMetric::MIN); metric.set_use_diff(true); @@ -1694,26 +1558,12 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) { * Tests zero default base. */ TEST(ValueMetricProducerTest, TestUseZeroDefaultBase) { - 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); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); metric.mutable_dimensions_in_what()->set_field(tagId); metric.mutable_dimensions_in_what()->add_child()->set_field(1); metric.set_use_zero_default_base(true); - metric.set_max_pull_delay_sec(INT_MAX); - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp<EventMatcherWizard> eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - 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(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { data->clear(); @@ -1725,19 +1575,18 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBase) { return true; })); - ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, - logEventMatcherIndex, eventMatcherWizard, tagId, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - auto iter = valueProducer.mCurrentSlicedBucket.begin(); + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + auto iter = valueProducer->mCurrentSlicedBucket.begin(); auto& interval1 = iter->second[0]; EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); EXPECT_EQ(true, interval1.hasBase); EXPECT_EQ(3, interval1.base.long_value); EXPECT_EQ(false, interval1.hasValue); - EXPECT_EQ(true, valueProducer.mHasGlobalBase); - EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(true, valueProducer->mHasGlobalBase); + EXPECT_EQ(0UL, valueProducer->mPastBuckets.size()); vector<shared_ptr<LogEvent>> allData; allData.clear(); @@ -1752,15 +1601,15 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBase) { allData.push_back(event1); allData.push_back(event2); - valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size()); + valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); EXPECT_EQ(true, interval1.hasBase); EXPECT_EQ(11, interval1.base.long_value); EXPECT_EQ(false, interval1.hasValue); EXPECT_EQ(8, interval1.value.long_value); - auto it = valueProducer.mCurrentSlicedBucket.begin(); - for (; it != valueProducer.mCurrentSlicedBucket.end(); it++) { + auto it = valueProducer->mCurrentSlicedBucket.begin(); + for (; it != valueProducer->mCurrentSlicedBucket.end(); it++) { if (it != iter) { break; } @@ -1773,8 +1622,8 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBase) { EXPECT_EQ(false, interval2.hasValue); EXPECT_EQ(4, interval2.value.long_value); - EXPECT_EQ(2UL, valueProducer.mPastBuckets.size()); - auto iterator = valueProducer.mPastBuckets.begin(); + EXPECT_EQ(2UL, valueProducer->mPastBuckets.size()); + auto iterator = valueProducer->mPastBuckets.begin(); EXPECT_EQ(8, iterator->second[0].values[0].long_value); iterator++; EXPECT_EQ(4, iterator->second[0].values[0].long_value); @@ -1784,26 +1633,12 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBase) { * Tests using zero default base with failed pull. */ TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) { - 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); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); metric.mutable_dimensions_in_what()->set_field(tagId); metric.mutable_dimensions_in_what()->add_child()->set_field(1); metric.set_use_zero_default_base(true); - metric.set_max_pull_delay_sec(INT_MAX); - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp<EventMatcherWizard> eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - 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(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { data->clear(); @@ -1815,19 +1650,18 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) { return true; })); - ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, - logEventMatcherIndex, eventMatcherWizard, tagId, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - auto iter = valueProducer.mCurrentSlicedBucket.begin(); + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + auto iter = valueProducer->mCurrentSlicedBucket.begin(); auto& interval1 = iter->second[0]; EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); EXPECT_EQ(true, interval1.hasBase); EXPECT_EQ(3, interval1.base.long_value); EXPECT_EQ(false, interval1.hasValue); - EXPECT_EQ(true, valueProducer.mHasGlobalBase); - EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(true, valueProducer->mHasGlobalBase); + EXPECT_EQ(0UL, valueProducer->mPastBuckets.size()); vector<shared_ptr<LogEvent>> allData; allData.clear(); @@ -1842,15 +1676,15 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) { allData.push_back(event1); allData.push_back(event2); - valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size()); + valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); EXPECT_EQ(true, interval1.hasBase); EXPECT_EQ(11, interval1.base.long_value); EXPECT_EQ(false, interval1.hasValue); EXPECT_EQ(8, interval1.value.long_value); - auto it = valueProducer.mCurrentSlicedBucket.begin(); - for (; it != valueProducer.mCurrentSlicedBucket.end(); it++) { + auto it = valueProducer->mCurrentSlicedBucket.begin(); + for (; it != valueProducer->mCurrentSlicedBucket.end(); it++) { if (it != iter) { break; } @@ -1862,7 +1696,7 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) { EXPECT_EQ(4, interval2.base.long_value); EXPECT_EQ(false, interval2.hasValue); EXPECT_EQ(4, interval2.value.long_value); - EXPECT_EQ(2UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(2UL, valueProducer->mPastBuckets.size()); // next pull somehow did not happen, skip to end of bucket 3 allData.clear(); @@ -1871,16 +1705,14 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) { event1->write(5); event1->init(); allData.push_back(event1); - valueProducer.onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); + valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); EXPECT_EQ(true, interval2.hasBase); - EXPECT_EQ(4, interval2.base.long_value); + EXPECT_EQ(5, interval2.base.long_value); EXPECT_EQ(false, interval2.hasValue); - EXPECT_EQ(true, interval1.hasBase); - EXPECT_EQ(false, interval1.hasValue); - EXPECT_EQ(true, valueProducer.mHasGlobalBase); - EXPECT_EQ(2UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(true, valueProducer->mHasGlobalBase); + EXPECT_EQ(2UL, valueProducer->mPastBuckets.size()); allData.clear(); event1 = make_shared<LogEvent>(tagId, bucket5StartTimeNs + 1); @@ -1893,44 +1725,32 @@ TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) { event2->write(5); event2->init(); allData.push_back(event2); - valueProducer.onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs); - - EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size()); - EXPECT_EQ(true, interval2.hasBase); - EXPECT_EQ(5, interval2.base.long_value); - EXPECT_EQ(false, interval2.hasValue); - EXPECT_EQ(5, interval2.value.long_value); - EXPECT_EQ(true, interval1.hasBase); - EXPECT_EQ(13, interval1.base.long_value); - EXPECT_EQ(false, interval1.hasValue); - EXPECT_EQ(8, interval1.value.long_value); - EXPECT_EQ(true, valueProducer.mHasGlobalBase); - EXPECT_EQ(2UL, valueProducer.mPastBuckets.size()); + valueProducer->onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs); + + EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); + auto it1 = std::next(valueProducer->mCurrentSlicedBucket.begin())->second[0]; + EXPECT_EQ(true, it1.hasBase); + EXPECT_EQ(13, it1.base.long_value); + EXPECT_EQ(false, it1.hasValue); + EXPECT_EQ(8, it1.value.long_value); + auto it2 = valueProducer->mCurrentSlicedBucket.begin()->second[0]; + EXPECT_EQ(true, it2.hasBase); + EXPECT_EQ(5, it2.base.long_value); + EXPECT_EQ(false, it2.hasValue); + EXPECT_EQ(5, it2.value.long_value); + EXPECT_EQ(true, valueProducer->mHasGlobalBase); + EXPECT_EQ(2UL, valueProducer->mPastBuckets.size()); } /* * Tests trim unused dimension key if no new data is seen in an entire bucket. */ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) { - 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); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); metric.mutable_dimensions_in_what()->set_field(tagId); metric.mutable_dimensions_in_what()->add_child()->set_field(1); - metric.set_max_pull_delay_sec(INT_MAX); - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp<EventMatcherWizard> eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - 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(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { data->clear(); @@ -1942,18 +1762,17 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) { return true; })); - ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, - logEventMatcherIndex, eventMatcherWizard, tagId, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - auto iter = valueProducer.mCurrentSlicedBucket.begin(); + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + auto iter = valueProducer->mCurrentSlicedBucket.begin(); auto& interval1 = iter->second[0]; EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); EXPECT_EQ(true, interval1.hasBase); EXPECT_EQ(3, interval1.base.long_value); EXPECT_EQ(false, interval1.hasValue); - EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(0UL, valueProducer->mPastBuckets.size()); vector<shared_ptr<LogEvent>> allData; allData.clear(); @@ -1968,18 +1787,17 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) { allData.push_back(event1); allData.push_back(event2); - valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size()); + valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); EXPECT_EQ(true, interval1.hasBase); EXPECT_EQ(11, interval1.base.long_value); EXPECT_EQ(false, interval1.hasValue); EXPECT_EQ(8, interval1.value.long_value); EXPECT_FALSE(interval1.seenNewData); - EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); - EXPECT_EQ(8, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}); - auto it = valueProducer.mCurrentSlicedBucket.begin(); - for (; it != valueProducer.mCurrentSlicedBucket.end(); it++) { + auto it = valueProducer->mCurrentSlicedBucket.begin(); + for (; it != valueProducer->mCurrentSlicedBucket.end(); it++) { if (it != iter) { break; } @@ -1991,7 +1809,7 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) { EXPECT_EQ(4, interval2.base.long_value); EXPECT_EQ(false, interval2.hasValue); EXPECT_FALSE(interval2.seenNewData); - EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}); // next pull somehow did not happen, skip to end of bucket 3 allData.clear(); @@ -2000,17 +1818,16 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) { event1->write(5); event1->init(); allData.push_back(event1); - valueProducer.onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); - + valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs); // Only one interval left. One was trimmed. - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - interval2 = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + interval2 = valueProducer->mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value); EXPECT_EQ(true, interval2.hasBase); EXPECT_EQ(5, interval2.base.long_value); EXPECT_EQ(false, interval2.hasValue); EXPECT_FALSE(interval2.seenNewData); - EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}); allData.clear(); event1 = make_shared<LogEvent>(tagId, bucket5StartTimeNs + 1); @@ -2018,40 +1835,24 @@ TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey) { event1->write(14); event1->init(); allData.push_back(event1); - valueProducer.onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs); + valueProducer->onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs); - interval2 = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + interval2 = valueProducer->mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(true, interval2.hasBase); EXPECT_EQ(14, interval2.base.long_value); EXPECT_EQ(false, interval2.hasValue); EXPECT_FALSE(interval2.seenNewData); - EXPECT_EQ(2UL, valueProducer.mPastBuckets.size()); - auto iterator = valueProducer.mPastBuckets.begin(); + ASSERT_EQ(2UL, valueProducer->mPastBuckets.size()); + auto iterator = valueProducer->mPastBuckets.begin(); EXPECT_EQ(9, iterator->second[0].values[0].long_value); iterator++; EXPECT_EQ(8, iterator->second[0].values[0].long_value); } TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfBucket) { - 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); - metric.set_condition(StringToId("SCREEN_ON")); - metric.set_max_pull_delay_sec(INT_MAX); + ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp<EventMatcherWizard> eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); - // Used by onConditionChanged. EXPECT_CALL(*pullerManager, Pull(tagId, _)) .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { @@ -2064,47 +1865,30 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfB return true; })); - ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, - eventMatcherWizard, tagId, bucketStartTimeNs, - bucketStartTimeNs, pullerManager); + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); - valueProducer.onConditionChanged(true, bucketStartTimeNs + 8); + valueProducer->onConditionChanged(true, bucketStartTimeNs + 8); // has one slice - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval& curInterval = - valueProducer.mCurrentSlicedBucket.begin()->second[0]; + valueProducer->mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(100, curInterval.base.long_value); EXPECT_EQ(false, curInterval.hasValue); vector<shared_ptr<LogEvent>> allData; - valueProducer.onDataPulled(allData, /** succeed */ false, bucket2StartTimeNs); - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + valueProducer->onDataPulled(allData, /** succeed */ false, bucket2StartTimeNs); + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); EXPECT_EQ(false, curInterval.hasBase); EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(false, valueProducer.mHasGlobalBase); + EXPECT_EQ(false, valueProducer->mHasGlobalBase); } TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange) { - 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); - metric.set_condition(StringToId("SCREEN_ON")); - metric.set_max_pull_delay_sec(INT_MAX); + ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp<EventMatcherWizard> eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); - EXPECT_CALL(*pullerManager, Pull(tagId, _)) .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { data->clear(); @@ -2117,50 +1901,33 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange) { })) .WillOnce(Return(false)); - ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, - eventMatcherWizard, tagId, bucketStartTimeNs, - bucketStartTimeNs, pullerManager); + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); - valueProducer.onConditionChanged(true, bucketStartTimeNs + 8); + valueProducer->onConditionChanged(true, bucketStartTimeNs + 8); // has one slice - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval& curInterval = - valueProducer.mCurrentSlicedBucket.begin()->second[0]; + valueProducer->mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(100, curInterval.base.long_value); EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(0UL, valueProducer->mPastBuckets.size()); - valueProducer.onConditionChanged(false, bucketStartTimeNs + 20); + valueProducer->onConditionChanged(false, bucketStartTimeNs + 20); // has one slice - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); EXPECT_EQ(false, curInterval.hasValue); EXPECT_EQ(false, curInterval.hasBase); - EXPECT_EQ(false, valueProducer.mHasGlobalBase); + EXPECT_EQ(false, valueProducer->mHasGlobalBase); } TEST(ValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange) { - 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); - metric.set_condition(StringToId("SCREEN_ON")); - metric.set_max_pull_delay_sec(INT_MAX); + ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp<EventMatcherWizard> eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); - EXPECT_CALL(*pullerManager, Pull(tagId, _)) .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { data->clear(); @@ -2172,45 +1939,30 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange) { return true; })); - ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, - eventMatcherWizard, tagId, bucketStartTimeNs, - bucketStartTimeNs, pullerManager); + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); - valueProducer.mCondition = ConditionState::kTrue; + valueProducer->mCondition = ConditionState::kTrue; vector<shared_ptr<LogEvent>> allData; - valueProducer.onDataPulled(allData, /** succeed */ false, bucketStartTimeNs); - EXPECT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size()); + valueProducer->onDataPulled(allData, /** succeed */ false, bucketStartTimeNs); + EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); - valueProducer.onConditionChanged(false, bucketStartTimeNs + 1); - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + valueProducer->onConditionChanged(false, bucketStartTimeNs + 1); + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval& curInterval = - valueProducer.mCurrentSlicedBucket.begin()->second[0]; + valueProducer->mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(false, curInterval.hasBase); EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(false, valueProducer.mHasGlobalBase); + EXPECT_EQ(false, valueProducer->mHasGlobalBase); } TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded) { - 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); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); metric.set_condition(StringToId("SCREEN_ON")); metric.set_max_pull_delay_sec(0); - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp<EventMatcherWizard> eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); - EXPECT_CALL(*pullerManager, Pull(tagId, _)) .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { data->clear(); @@ -2222,25 +1974,18 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullDelayExceeded) { return true; })); - ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, - eventMatcherWizard, tagId, bucketStartTimeNs, - bucketStartTimeNs, pullerManager); + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); - valueProducer.mCondition = ConditionState::kFalse; + valueProducer->mCondition = ConditionState::kFalse; // Max delay is set to 0 so pull will exceed max delay. - valueProducer.onConditionChanged(true, bucketStartTimeNs + 1); - EXPECT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size()); + valueProducer->onConditionChanged(true, bucketStartTimeNs + 1); + EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); } TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate) { - 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); - metric.set_condition(StringToId("SCREEN_ON")); - metric.set_max_pull_delay_sec(INT_MAX); + ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); UidMap uidMap; SimpleAtomMatcher atomMatcher; @@ -2266,24 +2011,9 @@ TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate) { } TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange) { - 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); - metric.set_condition(StringToId("SCREEN_ON")); - metric.set_max_pull_delay_sec(INT_MAX); + ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp<EventMatcherWizard> eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); EXPECT_CALL(*pullerManager, Pull(tagId, _)) .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { data->clear(); @@ -2295,44 +2025,27 @@ TEST(ValueMetricProducerTest, TestBaseSetOnConditionChange) { return true; })); - ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, - eventMatcherWizard, tagId, bucketStartTimeNs, - bucketStartTimeNs, pullerManager); + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); - valueProducer.mCondition = ConditionState::kFalse; - valueProducer.mHasGlobalBase = false; + valueProducer->mCondition = ConditionState::kFalse; + valueProducer->mHasGlobalBase = false; - valueProducer.onConditionChanged(true, bucketStartTimeNs + 1); - valueProducer.mHasGlobalBase = true; - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + valueProducer->onConditionChanged(true, bucketStartTimeNs + 1); + valueProducer->mHasGlobalBase = true; + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval& curInterval = - valueProducer.mCurrentSlicedBucket.begin()->second[0]; + valueProducer->mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(100, curInterval.base.long_value); EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(true, valueProducer.mHasGlobalBase); + EXPECT_EQ(true, valueProducer->mHasGlobalBase); } TEST(ValueMetricProducerTest, TestInvalidBucketWhenOneConditionFailed) { - 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); - metric.set_condition(StringToId("SCREEN_ON")); - metric.set_max_pull_delay_sec(INT_MAX); + ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp<EventMatcherWizard> eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); - EXPECT_CALL(*pullerManager, Pull(tagId, _)) // First onConditionChanged .WillOnce(Return(false)) @@ -2347,11 +2060,10 @@ TEST(ValueMetricProducerTest, TestInvalidBucketWhenOneConditionFailed) { return true; })); - ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, - eventMatcherWizard, tagId, bucketStartTimeNs, - bucketStartTimeNs, pullerManager); + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); - valueProducer.mCondition = ConditionState::kTrue; + valueProducer->mCondition = ConditionState::kTrue; // Bucket start. vector<shared_ptr<LogEvent>> allData; @@ -2361,12 +2073,12 @@ TEST(ValueMetricProducerTest, TestInvalidBucketWhenOneConditionFailed) { event->write(110); event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs); + valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs); // This will fail and should invalidate the whole bucket since we do not have all the data // needed to compute the metric value when the screen was on. - valueProducer.onConditionChanged(false, bucketStartTimeNs + 2); - valueProducer.onConditionChanged(true, bucketStartTimeNs + 3); + valueProducer->onConditionChanged(false, bucketStartTimeNs + 2); + valueProducer->onConditionChanged(true, bucketStartTimeNs + 3); // Bucket end. allData.clear(); @@ -2375,48 +2087,33 @@ TEST(ValueMetricProducerTest, TestInvalidBucketWhenOneConditionFailed) { event2->write(140); event2->init(); allData.push_back(event2); - valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + + valueProducer->flushIfNeededLocked(bucket2StartTimeNs + 1); - valueProducer.flushIfNeededLocked(bucket2StartTimeNs + 1); - - EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(0UL, valueProducer->mPastBuckets.size()); // Contains base from last pull which was successful. - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval& curInterval = - valueProducer.mCurrentSlicedBucket.begin()->second[0]; + valueProducer->mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(140, curInterval.base.long_value); EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(true, valueProducer.mHasGlobalBase); + EXPECT_EQ(true, valueProducer->mHasGlobalBase); } TEST(ValueMetricProducerTest, TestInvalidBucketWhenGuardRailHit) { - 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); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); metric.mutable_dimensions_in_what()->set_field(tagId); metric.mutable_dimensions_in_what()->add_child()->set_field(1); metric.set_condition(StringToId("SCREEN_ON")); - metric.set_max_pull_delay_sec(INT_MAX); - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp<EventMatcherWizard> eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); - EXPECT_CALL(*pullerManager, Pull(tagId, _)) // First onConditionChanged .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { for (int i = 0; i < 2000; i++) { - shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1); event->write(i); event->write(i); event->init(); @@ -2425,36 +2122,19 @@ TEST(ValueMetricProducerTest, TestInvalidBucketWhenGuardRailHit) { return true; })); - ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, - eventMatcherWizard, tagId, bucketStartTimeNs, - bucketStartTimeNs, pullerManager); + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); + valueProducer->mCondition = ConditionState::kFalse; - valueProducer.mCondition = ConditionState::kFalse; - valueProducer.onConditionChanged(true, bucket2StartTimeNs + 2); - EXPECT_EQ(true, valueProducer.mCurrentBucketIsInvalid); - EXPECT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size()); + valueProducer->onConditionChanged(true, bucketStartTimeNs + 2); + EXPECT_EQ(true, valueProducer->mCurrentBucketIsInvalid); + EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); } TEST(ValueMetricProducerTest, TestInvalidBucketWhenInitialPullFailed) { - 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); - metric.set_condition(StringToId("SCREEN_ON")); - metric.set_max_pull_delay_sec(INT_MAX); + ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp<EventMatcherWizard> eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); - EXPECT_CALL(*pullerManager, Pull(tagId, _)) // First onConditionChanged .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { @@ -2477,11 +2157,10 @@ TEST(ValueMetricProducerTest, TestInvalidBucketWhenInitialPullFailed) { return true; })); - ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, - eventMatcherWizard, tagId, bucketStartTimeNs, - bucketStartTimeNs, pullerManager); + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); - valueProducer.mCondition = ConditionState::kTrue; + valueProducer->mCondition = ConditionState::kTrue; // Bucket start. vector<shared_ptr<LogEvent>> allData; @@ -2491,10 +2170,10 @@ TEST(ValueMetricProducerTest, TestInvalidBucketWhenInitialPullFailed) { event->write(110); event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData, /** succeed */ false, bucketStartTimeNs); + valueProducer->onDataPulled(allData, /** succeed */ false, bucketStartTimeNs); - valueProducer.onConditionChanged(false, bucketStartTimeNs + 2); - valueProducer.onConditionChanged(true, bucketStartTimeNs + 3); + valueProducer->onConditionChanged(false, bucketStartTimeNs + 2); + valueProducer->onConditionChanged(true, bucketStartTimeNs + 3); // Bucket end. allData.clear(); @@ -2503,41 +2182,25 @@ TEST(ValueMetricProducerTest, TestInvalidBucketWhenInitialPullFailed) { event2->write(140); event2->init(); allData.push_back(event2); - valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + + valueProducer->flushIfNeededLocked(bucket2StartTimeNs + 1); - valueProducer.flushIfNeededLocked(bucket2StartTimeNs + 1); - - EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(0UL, valueProducer->mPastBuckets.size()); // Contains base from last pull which was successful. - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval& curInterval = - valueProducer.mCurrentSlicedBucket.begin()->second[0]; + valueProducer->mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(140, curInterval.base.long_value); EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(true, valueProducer.mHasGlobalBase); + EXPECT_EQ(true, valueProducer->mHasGlobalBase); } TEST(ValueMetricProducerTest, TestInvalidBucketWhenLastPullFailed) { - 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); - metric.set_condition(StringToId("SCREEN_ON")); - metric.set_max_pull_delay_sec(INT_MAX); + ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp<EventMatcherWizard> eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); - EXPECT_CALL(*pullerManager, Pull(tagId, _)) // First onConditionChanged .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { @@ -2560,11 +2223,10 @@ TEST(ValueMetricProducerTest, TestInvalidBucketWhenLastPullFailed) { return true; })); - ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, - eventMatcherWizard, tagId, bucketStartTimeNs, - bucketStartTimeNs, pullerManager); + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); - valueProducer.mCondition = ConditionState::kTrue; + valueProducer->mCondition = ConditionState::kTrue; // Bucket start. vector<shared_ptr<LogEvent>> allData; @@ -2574,12 +2236,12 @@ TEST(ValueMetricProducerTest, TestInvalidBucketWhenLastPullFailed) { event->write(110); event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs); + valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs); // This will fail and should invalidate the whole bucket since we do not have all the data // needed to compute the metric value when the screen was on. - valueProducer.onConditionChanged(false, bucketStartTimeNs + 2); - valueProducer.onConditionChanged(true, bucketStartTimeNs + 3); + valueProducer->onConditionChanged(false, bucketStartTimeNs + 2); + valueProducer->onConditionChanged(true, bucketStartTimeNs + 3); // Bucket end. allData.clear(); @@ -2588,39 +2250,23 @@ TEST(ValueMetricProducerTest, TestInvalidBucketWhenLastPullFailed) { event2->write(140); event2->init(); allData.push_back(event2); - valueProducer.onDataPulled(allData, /** succeed */ false, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, /** succeed */ false, bucket2StartTimeNs); - valueProducer.flushIfNeededLocked(bucket2StartTimeNs + 1); - - EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); + valueProducer->flushIfNeededLocked(bucket2StartTimeNs + 1); + + EXPECT_EQ(0UL, valueProducer->mPastBuckets.size()); // Last pull failed so based has been reset. - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval& curInterval = - valueProducer.mCurrentSlicedBucket.begin()->second[0]; + valueProducer->mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(false, curInterval.hasBase); EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(false, valueProducer.mHasGlobalBase); + EXPECT_EQ(false, valueProducer->mHasGlobalBase); } TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled) { - 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); - metric.set_max_pull_delay_sec(INT_MAX); - - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp<EventMatcherWizard> eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); - EXPECT_CALL(*pullerManager, Pull(tagId, _)) // Start bucket. .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { @@ -2633,9 +2279,8 @@ TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled) { return true; })); - ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, - logEventMatcherIndex, eventMatcherWizard, tagId, - bucketStartTimeNs, bucketStartTimeNs, pullerManager); + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); // Bucket 2 start. vector<shared_ptr<LogEvent>> allData; @@ -2645,41 +2290,25 @@ TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled) { event->write(110); event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); + valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + EXPECT_EQ(1UL, valueProducer->mPastBuckets.size()); // Bucket 3 empty. allData.clear(); shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1); event2->init(); allData.push_back(event2); - valueProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); + valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs); // Data has been trimmed. - EXPECT_EQ(0UL, valueProducer.mCurrentSlicedBucket.size()); - EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); + EXPECT_EQ(1UL, valueProducer->mPastBuckets.size()); } TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged) { - 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); - metric.set_condition(StringToId("SCREEN_ON")); - metric.set_max_pull_delay_sec(INT_MAX); + ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp<EventMatcherWizard> eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); - EXPECT_CALL(*pullerManager, Pull(tagId, _)) // First onConditionChanged .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { @@ -2696,47 +2325,30 @@ TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged) { return true; })); - ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, - eventMatcherWizard, tagId, bucketStartTimeNs, - bucketStartTimeNs, pullerManager); + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); - valueProducer.onConditionChanged(true, bucketStartTimeNs + 10); - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + valueProducer->onConditionChanged(true, bucketStartTimeNs + 10); + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval& curInterval = - valueProducer.mCurrentSlicedBucket.begin()->second[0]; + valueProducer->mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(true, valueProducer.mHasGlobalBase); + EXPECT_EQ(true, valueProducer->mHasGlobalBase); // Empty pull. - valueProducer.onConditionChanged(false, bucketStartTimeNs + 10); - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + valueProducer->onConditionChanged(false, bucketStartTimeNs + 10); + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(false, curInterval.hasBase); EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(false, valueProducer.mHasGlobalBase); + EXPECT_EQ(false, valueProducer->mHasGlobalBase); } TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary) { - 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); - metric.set_condition(StringToId("SCREEN_ON")); - metric.set_max_pull_delay_sec(INT_MAX); + ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp<EventMatcherWizard> eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); - EXPECT_CALL(*pullerManager, Pull(tagId, _)) // First onConditionChanged .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { @@ -2767,58 +2379,42 @@ TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary) { return true; })); - ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, - eventMatcherWizard, tagId, bucketStartTimeNs, - bucketStartTimeNs, pullerManager); + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); - valueProducer.onConditionChanged(true, bucketStartTimeNs + 10); - valueProducer.onConditionChanged(false, bucketStartTimeNs + 11); - valueProducer.onConditionChanged(true, bucketStartTimeNs + 12); - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + valueProducer->onConditionChanged(true, bucketStartTimeNs + 10); + valueProducer->onConditionChanged(false, bucketStartTimeNs + 11); + valueProducer->onConditionChanged(true, bucketStartTimeNs + 12); + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); ValueMetricProducer::Interval& curInterval = - valueProducer.mCurrentSlicedBucket.begin()->second[0]; + valueProducer->mCurrentSlicedBucket.begin()->second[0]; EXPECT_EQ(true, curInterval.hasBase); EXPECT_EQ(true, curInterval.hasValue); - EXPECT_EQ(true, valueProducer.mHasGlobalBase); + EXPECT_EQ(true, valueProducer->mHasGlobalBase); // End of bucket vector<shared_ptr<LogEvent>> allData; allData.clear(); - valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); - curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; // Data is empty, base should be reset. EXPECT_EQ(false, curInterval.hasBase); EXPECT_EQ(5, curInterval.base.long_value); EXPECT_EQ(false, curInterval.hasValue); - EXPECT_EQ(true, valueProducer.mHasGlobalBase); + EXPECT_EQ(true, valueProducer->mHasGlobalBase); - EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); - EXPECT_EQ(1, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value); + EXPECT_EQ(1UL, valueProducer->mPastBuckets.size()); + EXPECT_EQ(1, valueProducer->mPastBuckets.begin()->second[0].values[0].long_value); } TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries) { - 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); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); metric.mutable_dimensions_in_what()->set_field(tagId); metric.mutable_dimensions_in_what()->add_child()->set_field(1); metric.set_condition(StringToId("SCREEN_ON")); - metric.set_max_pull_delay_sec(INT_MAX); - UidMap uidMap; - SimpleAtomMatcher atomMatcher; - atomMatcher.set_atom_id(tagId); - sp<EventMatcherWizard> eventMatcherWizard = - new EventMatcherWizard({new SimpleLogMatchingTracker( - atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); - sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); - EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); - EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); - EXPECT_CALL(*pullerManager, Pull(tagId, _)) // First onConditionChanged .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { @@ -2832,12 +2428,11 @@ TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries) { return true; })); - ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, logEventMatcherIndex, - eventMatcherWizard, tagId, bucketStartTimeNs, - bucketStartTimeNs, pullerManager); + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); - valueProducer.onConditionChanged(true, bucketStartTimeNs + 10); - EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + valueProducer->onConditionChanged(true, bucketStartTimeNs + 10); + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); // End of bucket vector<shared_ptr<LogEvent>> allData; @@ -2847,11 +2442,11 @@ TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries) { event->write(2); event->init(); allData.push_back(event); - valueProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); // Key 1 should be reset since in not present in the most pull. - EXPECT_EQ(2UL, valueProducer.mCurrentSlicedBucket.size()); - auto iterator = valueProducer.mCurrentSlicedBucket.begin(); + EXPECT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size()); + auto iterator = valueProducer->mCurrentSlicedBucket.begin(); EXPECT_EQ(true, iterator->second[0].hasBase); EXPECT_EQ(2, iterator->second[0].base.long_value); EXPECT_EQ(false, iterator->second[0].hasValue); @@ -2860,7 +2455,244 @@ TEST(ValueMetricProducerTest, TestPartialResetOnBucketBoundaries) { EXPECT_EQ(1, iterator->second[0].base.long_value); EXPECT_EQ(false, iterator->second[0].hasValue); - EXPECT_EQ(true, valueProducer.mHasGlobalBase); + EXPECT_EQ(true, valueProducer->mHasGlobalBase); +} + +TEST(ValueMetricProducerTest, TestBucketIncludingUnknownConditionIsInvalid) { + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); + metric.mutable_dimensions_in_what()->set_field(tagId); + metric.mutable_dimensions_in_what()->add_child()->set_field(1); + metric.set_condition(StringToId("SCREEN_ON")); + + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, Pull(tagId, _)) + // Second onConditionChanged. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs); + event->write(tagId); + event->write(2); + event->write(2); + event->init(); + data->push_back(event); + return true; + })); + + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); + valueProducer->mCondition = ConditionState::kUnknown; + + valueProducer->onConditionChanged(false, bucketStartTimeNs + 10); + valueProducer->onConditionChanged(true, bucketStartTimeNs + 20); + + // End of bucket + vector<shared_ptr<LogEvent>> allData; + allData.clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1); + event->write(4); + event->write(4); + event->init(); + allData.push_back(event); + valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + + // Bucket is incomplete so it is mark as invalid, however the base is fine since the last pull + // succeeded. + EXPECT_EQ(0UL, valueProducer->mPastBuckets.size()); +} + +TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange) { + ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, Pull(tagId, _)) + // Second onConditionChanged. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + data->push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 10, 5)); + return true; + })) + // Third onConditionChanged. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + data->push_back(ValueMetricProducerTestHelper::createEvent(bucket3StartTimeNs + 10, 7)); + return true; + })); + + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); + valueProducer->mCondition = ConditionState::kUnknown; + + valueProducer->onConditionChanged(false, bucketStartTimeNs); + ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); + + // End of first bucket + vector<shared_ptr<LogEvent>> allData; + allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 1, 4)); + valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 1); + ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size()); + + valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10); + ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; + EXPECT_EQ(true, curInterval.hasBase); + EXPECT_EQ(5, curInterval.base.long_value); + EXPECT_EQ(false, curInterval.hasValue); + + valueProducer->onConditionChanged(false, bucket3StartTimeNs + 10); + + // Bucket should have been completed. + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2}); +} + +TEST(ValueMetricProducerTest, TestLateOnDataPulledWithoutDiff) { + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); + metric.set_use_diff(false); + + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); + + vector<shared_ptr<LogEvent>> allData; + allData.push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs + 30, 10)); + valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + 30); + + allData.clear(); + allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs, 20)); + valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + + // Bucket should have been completed. + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}); +} + +TEST(ValueMetricProducerTest, TestLateOnDataPulledWithDiff) { + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); + + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, Pull(tagId, _)) + // Initialization. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + data->push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs, 1)); + return true; + })); + + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); + + vector<shared_ptr<LogEvent>> allData; + allData.push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs + 30, 10)); + valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + 30); + + allData.clear(); + allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs, 20)); + valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + + // Bucket should have been completed. + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {19}); +} + +TEST(ValueMetricProducerTest, TestBucketBoundariesOnAppUpgrade) { + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); + + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, Pull(tagId, _)) + // Initialization. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + data->push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs, 1)); + return true; + })) + // notifyAppUpgrade. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + data->push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs + 2, 10)); + return true; + })); + + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric); + + valueProducer->notifyAppUpgrade(bucket2StartTimeNs + 2, "com.foo", 10000, 1); + + // Bucket should have been completed. + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9}); +} + +TEST(ValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged) { + ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); + + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, Pull(tagId, _)) + // First on condition changed. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + data->push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs, 1)); + return true; + })) + // Second on condition changed. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + data->push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs, 3)); + return true; + })); + + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); + + valueProducer->onConditionChanged(true, bucketStartTimeNs + 8); + valueProducer->onConditionChanged(false, bucketStartTimeNs + 10); + valueProducer->onConditionChanged(false, bucketStartTimeNs + 10); + + EXPECT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size()); + auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0]; + EXPECT_EQ(true, curInterval.hasValue); + EXPECT_EQ(2, curInterval.value.long_value); +} + +TEST(ValueMetricProducerTest, TestBucketInvalidIfGlobalBaseIsNotSet) { + ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition(); + + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + EXPECT_CALL(*pullerManager, Pull(tagId, _)) + // First condition change. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + data->push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs, 1)); + return true; + })) + // 2nd condition change. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + data->push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs, 1)); + return true; + })) + // 3rd condition change. + .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + data->push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs, 1)); + return true; + })); + + sp<ValueMetricProducer> valueProducer = + ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric); + valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10); + + vector<shared_ptr<LogEvent>> allData; + allData.push_back(ValueMetricProducerTestHelper::createEvent(bucketStartTimeNs + 3, 10)); + valueProducer->onDataPulled(allData, /** succeed */ false, bucketStartTimeNs + 3); + + allData.clear(); + allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket2StartTimeNs, 20)); + valueProducer->onDataPulled(allData, /** succeed */ false, bucket2StartTimeNs); + + valueProducer->onConditionChanged(false, bucket2StartTimeNs + 8); + valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10); + + allData.clear(); + allData.push_back(ValueMetricProducerTestHelper::createEvent(bucket3StartTimeNs, 30)); + valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs); + + // There was not global base available so all buckets are invalid. + assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}); } static StatsLogReport outputStreamToProto(ProtoOutputStream* proto) { @@ -2881,12 +2713,7 @@ static StatsLogReport outputStreamToProto(ProtoOutputStream* proto) { } TEST(ValueMetricProducerTest, TestPullNeededFastDump) { - 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); - metric.set_max_pull_delay_sec(INT_MAX); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); UidMap uidMap; SimpleAtomMatcher atomMatcher; @@ -2918,7 +2745,7 @@ TEST(ValueMetricProducerTest, TestPullNeededFastDump) { ProtoOutputStream output; std::set<string> strSet; - valueProducer.onDumpReport(bucketStartTimeNs + 10, + valueProducer.onDumpReport(bucketStartTimeNs + 10, true /* include recent buckets */, true, FAST, &strSet, &output); @@ -2928,12 +2755,7 @@ TEST(ValueMetricProducerTest, TestPullNeededFastDump) { } TEST(ValueMetricProducerTest, TestFastDumpWithoutCurrentBucket) { - 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); - metric.set_max_pull_delay_sec(INT_MAX); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); UidMap uidMap; SimpleAtomMatcher atomMatcher; @@ -2975,7 +2797,7 @@ TEST(ValueMetricProducerTest, TestFastDumpWithoutCurrentBucket) { ProtoOutputStream output; std::set<string> strSet; - valueProducer.onDumpReport(bucket4StartTimeNs, + valueProducer.onDumpReport(bucket4StartTimeNs, false /* include recent buckets */, true, FAST, &strSet, &output); @@ -2986,12 +2808,7 @@ TEST(ValueMetricProducerTest, TestFastDumpWithoutCurrentBucket) { } TEST(ValueMetricProducerTest, TestPullNeededNoTimeConstraints) { - 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); - metric.set_max_pull_delay_sec(INT_MAX); + ValueMetric metric = ValueMetricProducerTestHelper::createMetric(); UidMap uidMap; SimpleAtomMatcher atomMatcher; @@ -3033,7 +2850,7 @@ TEST(ValueMetricProducerTest, TestPullNeededNoTimeConstraints) { ProtoOutputStream output; std::set<string> strSet; - valueProducer.onDumpReport(bucketStartTimeNs + 10, + valueProducer.onDumpReport(bucketStartTimeNs + 10, true /* include recent buckets */, true, NO_TIME_CONSTRAINTS, &strSet, &output); diff --git a/cmds/svc/src/com/android/commands/svc/Svc.java b/cmds/svc/src/com/android/commands/svc/Svc.java index 62225df0d6ae..68fb8e694e6f 100644 --- a/cmds/svc/src/com/android/commands/svc/Svc.java +++ b/cmds/svc/src/com/android/commands/svc/Svc.java @@ -98,5 +98,6 @@ public class Svc { new UsbCommand(), new NfcCommand(), new BluetoothCommand(), + new SystemServerCommand(), }; } diff --git a/cmds/svc/src/com/android/commands/svc/SystemServerCommand.java b/cmds/svc/src/com/android/commands/svc/SystemServerCommand.java new file mode 100644 index 000000000000..b9104d169fa6 --- /dev/null +++ b/cmds/svc/src/com/android/commands/svc/SystemServerCommand.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.commands.svc; + +import android.app.ActivityManager; +import android.os.ParcelFileDescriptor; + +import java.io.FileInputStream; + +public class SystemServerCommand extends Svc.Command { + public SystemServerCommand() { + super("system-server"); + } + + @Override + public String shortHelp() { + return "System server process related command"; + } + + @Override + public String longHelp() { + return shortHelp() + "\n" + + "\n" + + "usage: system-server wait-for-crash\n" + + " Wait until the system server process crashes.\n\n"; + } + + private void waitForCrash() throws Exception { + ParcelFileDescriptor fd = ActivityManager.getService().getLifeMonitor(); + if (fd == null) { + System.err.println("Unable to get life monitor."); + return; + } + System.out.println("Waiting for the system server process to die..."); + new FileInputStream(fd.getFileDescriptor()).read(); + } + + @Override + public void run(String[] args) { + try { + if (args.length > 1) { + switch (args[1]) { + case "wait-for-crash": + waitForCrash(); + return; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + System.err.println(longHelp()); + } +} diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt index 0a26bfb49bf7..d2535c9ba8ee 100644 --- a/config/boot-image-profile.txt +++ b/config/boot-image-profile.txt @@ -32983,7 +32983,7 @@ HSPLandroid/view/IWindowSession$Stub$Proxy;->getDisplayFrame(Landroid/view/IWind HSPLandroid/view/IWindowSession$Stub$Proxy;->getInTouchMode()Z HSPLandroid/view/IWindowSession$Stub$Proxy;->getWindowId(Landroid/os/IBinder;)Landroid/view/IWindowId; HSPLandroid/view/IWindowSession$Stub$Proxy;->onRectangleOnScreenRequested(Landroid/os/IBinder;Landroid/graphics/Rect;)V -HSPLandroid/view/IWindowSession$Stub$Proxy;->performHapticFeedback(Landroid/view/IWindow;IZ)Z +HSPLandroid/view/IWindowSession$Stub$Proxy;->performHapticFeedback(IZ)Z HSPLandroid/view/IWindowSession$Stub$Proxy;->pokeDrawLock(Landroid/os/IBinder;)V HSPLandroid/view/IWindowSession$Stub$Proxy;->relayout(Landroid/view/IWindow;ILandroid/view/WindowManager$LayoutParams;IIIIJLandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/view/DisplayCutout$ParcelableWrapper;Landroid/util/MergedConfiguration;Landroid/view/Surface;)I HSPLandroid/view/IWindowSession$Stub$Proxy;->remove(Landroid/view/IWindow;)V @@ -33007,7 +33007,7 @@ HSPLandroid/view/IWindowSession;->getWindowId(Landroid/os/IBinder;)Landroid/view HSPLandroid/view/IWindowSession;->onRectangleOnScreenRequested(Landroid/os/IBinder;Landroid/graphics/Rect;)V HSPLandroid/view/IWindowSession;->outOfMemory(Landroid/view/IWindow;)Z HSPLandroid/view/IWindowSession;->performDrag(Landroid/view/IWindow;ILandroid/view/SurfaceControl;IFFFFLandroid/content/ClipData;)Landroid/os/IBinder; -HSPLandroid/view/IWindowSession;->performHapticFeedback(Landroid/view/IWindow;IZ)Z +HSPLandroid/view/IWindowSession;->performHapticFeedback(IZ)Z HSPLandroid/view/IWindowSession;->pokeDrawLock(Landroid/os/IBinder;)V HSPLandroid/view/IWindowSession;->prepareToReplaceWindows(Landroid/os/IBinder;Z)V HSPLandroid/view/IWindowSession;->relayout(Landroid/view/IWindow;ILandroid/view/WindowManager$LayoutParams;IIIIJLandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/view/DisplayCutout$ParcelableWrapper;Landroid/util/MergedConfiguration;Landroid/view/Surface;)I diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt index a836e8ef202f..07fba3ba9ce4 100644 --- a/config/hiddenapi-greylist.txt +++ b/config/hiddenapi-greylist.txt @@ -437,7 +437,6 @@ Landroid/content/pm/IShortcutService$Stub;->asInterface(Landroid/os/IBinder;)Lan Landroid/content/res/ConfigurationBoundResourceCache;-><init>()V Landroid/content/res/DrawableCache;-><init>()V Landroid/content/UndoManager;-><init>()V -Landroid/database/BulkCursorProxy;->mRemote:Landroid/os/IBinder; Landroid/database/IContentObserver$Stub;-><init>()V Landroid/database/IContentObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/database/IContentObserver; Landroid/database/IContentObserver;->onChange(ZLandroid/net/Uri;I)V @@ -446,16 +445,13 @@ Landroid/database/sqlite/SQLiteDatabase;->$assertionsDisabled:Z Landroid/filterfw/GraphEnvironment;->addReferences([Ljava/lang/Object;)V Landroid/hardware/camera2/utils/HashCodeHelpers;->hashCode([I)I Landroid/hardware/display/IDisplayManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/display/IDisplayManager; -Landroid/hardware/display/IDisplayManager;->getDisplayInfo(I)Landroid/view/DisplayInfo; Landroid/hardware/fingerprint/IFingerprintService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/hardware/fingerprint/IFingerprintService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/fingerprint/IFingerprintService; Landroid/hardware/ICameraService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/ICameraService; Landroid/hardware/input/IInputManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/hardware/input/IInputManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/input/IInputManager; Landroid/hardware/input/IInputManager$Stub;->TRANSACTION_injectInputEvent:I -Landroid/hardware/input/IInputManager;->injectInputEvent(Landroid/view/InputEvent;I)Z Landroid/hardware/location/IActivityRecognitionHardwareClient$Stub;-><init>()V -Landroid/hardware/location/IActivityRecognitionHardwareClient;->onAvailabilityChanged(ZLandroid/hardware/location/IActivityRecognitionHardware;)V Landroid/hardware/location/IContextHubService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/location/IContextHubService; Landroid/hardware/usb/IUsbManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/hardware/usb/IUsbManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/usb/IUsbManager; @@ -465,60 +461,33 @@ Landroid/location/ICountryDetector$Stub;->asInterface(Landroid/os/IBinder;)Landr Landroid/location/ICountryListener$Stub;-><init>()V Landroid/location/IGeocodeProvider$Stub;-><init>()V Landroid/location/IGeocodeProvider$Stub;->asInterface(Landroid/os/IBinder;)Landroid/location/IGeocodeProvider; -Landroid/location/IGeocodeProvider;->getFromLocation(DDILandroid/location/GeocoderParams;Ljava/util/List;)Ljava/lang/String; -Landroid/location/IGeocodeProvider;->getFromLocationName(Ljava/lang/String;DDDDILandroid/location/GeocoderParams;Ljava/util/List;)Ljava/lang/String; Landroid/location/IGeofenceProvider$Stub;-><init>()V -Landroid/location/IGeofenceProvider;->setGeofenceHardware(Landroid/hardware/location/IGeofenceHardware;)V Landroid/location/ILocationListener$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/location/ILocationListener$Stub$Proxy;->mRemote:Landroid/os/IBinder; Landroid/location/ILocationListener$Stub;-><init>()V Landroid/location/ILocationListener$Stub;->asInterface(Landroid/os/IBinder;)Landroid/location/ILocationListener; -Landroid/location/ILocationListener;->onLocationChanged(Landroid/location/Location;)V -Landroid/location/ILocationListener;->onProviderDisabled(Ljava/lang/String;)V -Landroid/location/ILocationListener;->onProviderEnabled(Ljava/lang/String;)V -Landroid/location/ILocationListener;->onStatusChanged(Ljava/lang/String;ILandroid/os/Bundle;)V Landroid/location/ILocationManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/location/ILocationManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/location/ILocationManager; Landroid/location/ILocationManager$Stub;->TRANSACTION_getAllProviders:I -Landroid/location/ILocationManager;->getAllProviders()Ljava/util/List; Landroid/location/INetInitiatedListener$Stub;-><init>()V -Landroid/location/INetInitiatedListener;->sendNiResponse(II)Z Landroid/location/LocationManager$ListenerTransport;-><init>(Landroid/location/LocationManager;Landroid/location/LocationListener;Landroid/os/Looper;)V Landroid/Manifest$permission;->CAPTURE_SECURE_VIDEO_OUTPUT:Ljava/lang/String; Landroid/Manifest$permission;->CAPTURE_VIDEO_OUTPUT:Ljava/lang/String; Landroid/Manifest$permission;->READ_FRAME_BUFFER:Ljava/lang/String; Landroid/media/effect/SingleFilterEffect;-><init>(Landroid/media/effect/EffectContext;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V -Landroid/media/IAudioFocusDispatcher;->dispatchAudioFocusChange(ILjava/lang/String;)V Landroid/media/IAudioRoutesObserver$Stub;-><init>()V Landroid/media/IAudioService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/media/IAudioService$Stub;-><init>()V Landroid/media/IAudioService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IAudioService; -Landroid/media/IAudioService;->getStreamMaxVolume(I)I -Landroid/media/IAudioService;->getStreamVolume(I)I -Landroid/media/IAudioService;->setStreamVolume(IIILjava/lang/String;)V -Landroid/media/IAudioService;->startWatchingRoutes(Landroid/media/IAudioRoutesObserver;)Landroid/media/AudioRoutesInfo; Landroid/media/IMediaRouterService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IMediaRouterService; Landroid/media/IMediaScannerListener$Stub;-><init>()V Landroid/media/IMediaScannerService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IMediaScannerService; -Landroid/media/IMediaScannerService;->requestScanFile(Ljava/lang/String;Ljava/lang/String;Landroid/media/IMediaScannerListener;)V -Landroid/media/IMediaScannerService;->scanFile(Ljava/lang/String;Ljava/lang/String;)V -Landroid/media/IRemoteDisplayCallback;->onStateChanged(Landroid/media/RemoteDisplayState;)V Landroid/media/IRingtonePlayer;->play(Landroid/os/IBinder;Landroid/net/Uri;Landroid/media/AudioAttributes;FZ)V Landroid/media/IVolumeController$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IVolumeController; Landroid/media/MediaFile;-><init>()V Landroid/media/MediaScanner$MyMediaScannerClient;-><init>(Landroid/media/MediaScanner;)V -Landroid/media/projection/IMediaProjectionManager;->hasProjectionPermission(ILjava/lang/String;)Z Landroid/media/session/ISessionManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/session/ISessionManager; Landroid/media/tv/ITvRemoteProvider$Stub;-><init>()V -Landroid/media/tv/ITvRemoteServiceInput;->clearInputBridge(Landroid/os/IBinder;)V -Landroid/media/tv/ITvRemoteServiceInput;->closeInputBridge(Landroid/os/IBinder;)V -Landroid/media/tv/ITvRemoteServiceInput;->openInputBridge(Landroid/os/IBinder;Ljava/lang/String;III)V -Landroid/media/tv/ITvRemoteServiceInput;->sendKeyDown(Landroid/os/IBinder;I)V -Landroid/media/tv/ITvRemoteServiceInput;->sendKeyUp(Landroid/os/IBinder;I)V -Landroid/media/tv/ITvRemoteServiceInput;->sendPointerDown(Landroid/os/IBinder;III)V -Landroid/media/tv/ITvRemoteServiceInput;->sendPointerSync(Landroid/os/IBinder;)V -Landroid/media/tv/ITvRemoteServiceInput;->sendPointerUp(Landroid/os/IBinder;I)V -Landroid/media/tv/ITvRemoteServiceInput;->sendTimestamp(Landroid/os/IBinder;J)V Landroid/net/ConnectivityManager$PacketKeepaliveCallback;-><init>()V Landroid/net/IConnectivityManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/net/IConnectivityManager$Stub$Proxy;->getActiveLinkProperties()Landroid/net/LinkProperties; @@ -530,41 +499,13 @@ Landroid/net/IConnectivityManager$Stub$Proxy;->getTetherableUsbRegexs()[Ljava/la Landroid/net/IConnectivityManager$Stub$Proxy;->getTetheredIfaces()[Ljava/lang/String; Landroid/net/IConnectivityManager$Stub$Proxy;->mRemote:Landroid/os/IBinder; Landroid/net/IConnectivityManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/IConnectivityManager; -Landroid/net/IConnectivityManager;->getActiveLinkProperties()Landroid/net/LinkProperties; -Landroid/net/IConnectivityManager;->getActiveNetworkInfo()Landroid/net/NetworkInfo; -Landroid/net/IConnectivityManager;->getAllNetworkInfo()[Landroid/net/NetworkInfo; -Landroid/net/IConnectivityManager;->getAllNetworkState()[Landroid/net/NetworkState; -Landroid/net/IConnectivityManager;->getLastTetherError(Ljava/lang/String;)I -Landroid/net/IConnectivityManager;->getTetherableIfaces()[Ljava/lang/String; -Landroid/net/IConnectivityManager;->getTetherableUsbRegexs()[Ljava/lang/String; -Landroid/net/IConnectivityManager;->getTetherableWifiRegexs()[Ljava/lang/String; -Landroid/net/IConnectivityManager;->getTetheredIfaces()[Ljava/lang/String; -Landroid/net/IConnectivityManager;->getTetheringErroredIfaces()[Ljava/lang/String; -Landroid/net/IConnectivityManager;->startLegacyVpn(Lcom/android/internal/net/VpnProfile;)V Landroid/net/INetworkManagementEventObserver$Stub;-><init>()V Landroid/net/INetworkPolicyListener$Stub;-><init>()V Landroid/net/INetworkPolicyManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkPolicyManager; -Landroid/net/INetworkPolicyManager;->getNetworkQuotaInfo(Landroid/net/NetworkState;)Landroid/net/NetworkQuotaInfo; -Landroid/net/INetworkPolicyManager;->getRestrictBackground()Z -Landroid/net/INetworkPolicyManager;->getUidPolicy(I)I -Landroid/net/INetworkPolicyManager;->setNetworkPolicies([Landroid/net/NetworkPolicy;)V -Landroid/net/INetworkPolicyManager;->setRestrictBackground(Z)V -Landroid/net/INetworkPolicyManager;->setUidPolicy(II)V -Landroid/net/INetworkPolicyManager;->snoozeLimit(Landroid/net/NetworkTemplate;)V Landroid/net/INetworkScoreService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkScoreService; Landroid/net/INetworkStatsService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/net/INetworkStatsService$Stub$Proxy;->getMobileIfaces()[Ljava/lang/String; Landroid/net/INetworkStatsService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkStatsService; -Landroid/net/INetworkStatsService;->forceUpdate()V -Landroid/net/INetworkStatsService;->getDataLayerSnapshotForUid(I)Landroid/net/NetworkStats; -Landroid/net/INetworkStatsService;->getMobileIfaces()[Ljava/lang/String; -Landroid/net/INetworkStatsService;->openSession()Landroid/net/INetworkStatsSession; -Landroid/net/INetworkStatsService;->openSessionForUsageStats(ILjava/lang/String;)Landroid/net/INetworkStatsSession; -Landroid/net/INetworkStatsSession;->close()V -Landroid/net/INetworkStatsSession;->getHistoryForNetwork(Landroid/net/NetworkTemplate;I)Landroid/net/NetworkStatsHistory; -Landroid/net/INetworkStatsSession;->getHistoryForUid(Landroid/net/NetworkTemplate;IIII)Landroid/net/NetworkStatsHistory; -Landroid/net/INetworkStatsSession;->getSummaryForAllUid(Landroid/net/NetworkTemplate;JJZ)Landroid/net/NetworkStats; -Landroid/net/INetworkStatsSession;->getSummaryForNetwork(Landroid/net/NetworkTemplate;JJ)Landroid/net/NetworkStats; Landroid/net/InterfaceConfiguration;-><init>()V Landroid/net/LinkProperties$ProvisioningChange;->values()[Landroid/net/LinkProperties$ProvisioningChange; Landroid/net/MobileLinkQualityInfo;-><init>()V @@ -575,22 +516,12 @@ Landroid/net/SntpClient;-><init>()V Landroid/net/wifi/IWifiManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/net/wifi/IWifiManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/wifi/IWifiManager; Landroid/net/wifi/IWifiManager$Stub;->TRANSACTION_getScanResults:I -Landroid/net/wifi/IWifiManager;->getCurrentNetwork()Landroid/net/Network; -Landroid/net/wifi/IWifiManager;->getWifiApConfiguration()Landroid/net/wifi/WifiConfiguration; -Landroid/net/wifi/IWifiManager;->getWifiApEnabledState()I Landroid/net/wifi/IWifiScanner$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/net/wifi/IWifiScanner$Stub$Proxy;->mRemote:Landroid/os/IBinder; Landroid/net/wifi/IWifiScanner$Stub;-><init>()V Landroid/net/wifi/IWifiScanner$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/wifi/IWifiScanner; Landroid/net/wifi/p2p/IWifiP2pManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/wifi/p2p/IWifiP2pManager; Landroid/nfc/INfcAdapter$Stub;->TRANSACTION_enable:I -Landroid/nfc/INfcAdapterExtras;->authenticate(Ljava/lang/String;[B)V -Landroid/nfc/INfcAdapterExtras;->close(Ljava/lang/String;Landroid/os/IBinder;)Landroid/os/Bundle; -Landroid/nfc/INfcAdapterExtras;->getCardEmulationRoute(Ljava/lang/String;)I -Landroid/nfc/INfcAdapterExtras;->getDriverName(Ljava/lang/String;)Ljava/lang/String; -Landroid/nfc/INfcAdapterExtras;->open(Ljava/lang/String;Landroid/os/IBinder;)Landroid/os/Bundle; -Landroid/nfc/INfcAdapterExtras;->setCardEmulationRoute(Ljava/lang/String;I)V -Landroid/nfc/INfcAdapterExtras;->transceive(Ljava/lang/String;[B)Landroid/os/Bundle; Landroid/os/AsyncResult;-><init>(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Throwable;)V Landroid/os/AsyncResult;->exception:Ljava/lang/Throwable; Landroid/os/AsyncResult;->forMessage(Landroid/os/Message;)Landroid/os/AsyncResult; @@ -688,7 +619,6 @@ Landroid/os/Build$VERSION;->ACTIVE_CODENAMES:[Ljava/lang/String; Landroid/os/Build;->getLong(Ljava/lang/String;)J Landroid/os/Build;->getString(Ljava/lang/String;)Ljava/lang/String; Landroid/os/Build;->IS_DEBUGGABLE:Z -Landroid/os/Build;->IS_EMULATOR:Z Landroid/os/Bundle;->filterValues()Landroid/os/Bundle; Landroid/os/Bundle;->forPair(Ljava/lang/String;Ljava/lang/String;)Landroid/os/Bundle; Landroid/os/Bundle;->getIBinder(Ljava/lang/String;)Landroid/os/IBinder; @@ -1361,9 +1291,7 @@ Landroid/R$styleable;->View_visibility:I Landroid/R$styleable;->Window:[I Landroid/R$styleable;->Window_windowBackground:I Landroid/R$styleable;->Window_windowFrame:I -Landroid/security/Credentials;->convertToPem([Ljava/security/cert/Certificate;)[B Landroid/security/IKeyChainService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/security/IKeyChainService; -Landroid/security/IKeyChainService;->requestPrivateKey(Ljava/lang/String;)Ljava/lang/String; Landroid/security/keymaster/KeymasterBlobArgument;-><init>(ILandroid/os/Parcel;)V Landroid/security/keymaster/KeymasterBlobArgument;-><init>(I[B)V Landroid/security/keymaster/KeymasterBlobArgument;->blob:[B @@ -1392,29 +1320,12 @@ Landroid/service/dreams/IDreamManager;->dream()V Landroid/service/dreams/IDreamManager;->getDreamComponents()[Landroid/content/ComponentName; Landroid/service/dreams/IDreamManager;->isDreaming()Z Landroid/service/dreams/IDreamManager;->setDreamComponents([Landroid/content/ComponentName;)V -Landroid/service/euicc/IDeleteSubscriptionCallback;->onComplete(I)V -Landroid/service/euicc/IEraseSubscriptionsCallback;->onComplete(I)V Landroid/service/euicc/IEuiccService$Stub;-><init>()V -Landroid/service/euicc/IGetDefaultDownloadableSubscriptionListCallback;->onComplete(Landroid/service/euicc/GetDefaultDownloadableSubscriptionListResult;)V -Landroid/service/euicc/IGetDownloadableSubscriptionMetadataCallback;->onComplete(Landroid/service/euicc/GetDownloadableSubscriptionMetadataResult;)V -Landroid/service/euicc/IGetEidCallback;->onSuccess(Ljava/lang/String;)V -Landroid/service/euicc/IGetEuiccInfoCallback;->onSuccess(Landroid/telephony/euicc/EuiccInfo;)V -Landroid/service/euicc/IGetEuiccProfileInfoListCallback;->onComplete(Landroid/service/euicc/GetEuiccProfileInfoListResult;)V -Landroid/service/euicc/IRetainSubscriptionsForFactoryResetCallback;->onComplete(I)V -Landroid/service/euicc/ISwitchToSubscriptionCallback;->onComplete(I)V -Landroid/service/euicc/IUpdateSubscriptionNicknameCallback;->onComplete(I)V Landroid/service/notification/INotificationListener$Stub;-><init>()V Landroid/service/persistentdata/IPersistentDataBlockService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/service/persistentdata/IPersistentDataBlockService; Landroid/service/vr/IVrManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/service/vr/IVrManager; -Landroid/service/vr/IVrManager;->getVr2dDisplayId()I -Landroid/service/vr/IVrManager;->getVrModeState()Z Landroid/service/wallpaper/IWallpaperConnection$Stub;-><init>()V -Landroid/service/wallpaper/IWallpaperEngine;->destroy()V -Landroid/service/wallpaper/IWallpaperEngine;->dispatchPointer(Landroid/view/MotionEvent;)V -Landroid/service/wallpaper/IWallpaperEngine;->dispatchWallpaperCommand(Ljava/lang/String;IIILandroid/os/Bundle;)V -Landroid/service/wallpaper/IWallpaperEngine;->setVisibility(Z)V Landroid/service/wallpaper/IWallpaperService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/service/wallpaper/IWallpaperService; -Landroid/speech/IRecognitionListener;->onEvent(ILandroid/os/Bundle;)V Landroid/telecom/Log;->i(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V Landroid/telecom/Log;->w(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V Landroid/telephony/ims/compat/feature/MMTelFeature;-><init>()V @@ -1425,10 +1336,6 @@ Landroid/telephony/JapanesePhoneNumberFormatter;->format(Landroid/text/Editable; Landroid/telephony/mbms/IMbmsStreamingSessionCallback$Stub;-><init>()V Landroid/telephony/mbms/IStreamingServiceCallback$Stub;-><init>()V Landroid/telephony/mbms/vendor/IMbmsStreamingService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/telephony/mbms/vendor/IMbmsStreamingService; -Landroid/telephony/mbms/vendor/IMbmsStreamingService;->getPlaybackUri(ILjava/lang/String;)Landroid/net/Uri; -Landroid/telephony/mbms/vendor/IMbmsStreamingService;->initialize(Landroid/telephony/mbms/IMbmsStreamingSessionCallback;I)I -Landroid/telephony/mbms/vendor/IMbmsStreamingService;->requestUpdateStreamingServices(ILjava/util/List;)I -Landroid/telephony/mbms/vendor/IMbmsStreamingService;->startStreaming(ILjava/lang/String;Landroid/telephony/mbms/IStreamingServiceCallback;)I Landroid/telephony/SmsCbCmasInfo;->getCategory()I Landroid/telephony/SmsCbCmasInfo;->getCertainty()I Landroid/telephony/SmsCbCmasInfo;->getMessageClass()I @@ -1471,17 +1378,8 @@ Landroid/view/autofill/IAutoFillManager$Stub$Proxy;-><init>(Landroid/os/IBinder; Landroid/view/autofill/IAutoFillManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/autofill/IAutoFillManager; Landroid/view/IAppTransitionAnimationSpecsFuture$Stub;-><init>()V Landroid/view/IDockedStackListener$Stub;-><init>()V -Landroid/view/IRecentsAnimationController;->finish(Z)V -Landroid/view/IRecentsAnimationController;->screenshotTask(I)Landroid/app/ActivityManager$TaskSnapshot; -Landroid/view/IRecentsAnimationController;->setAnimationTargetsBehindSystemBars(Z)V -Landroid/view/IRecentsAnimationController;->setInputConsumerEnabled(Z)V Landroid/view/IRecentsAnimationRunner$Stub;-><init>()V -Landroid/view/IRecentsAnimationRunner;->onAnimationCanceled()V -Landroid/view/IRecentsAnimationRunner;->onAnimationStart(Landroid/view/IRecentsAnimationController;[Landroid/view/RemoteAnimationTarget;Landroid/graphics/Rect;Landroid/graphics/Rect;)V -Landroid/view/IRemoteAnimationFinishedCallback;->onAnimationFinished()V Landroid/view/IRemoteAnimationRunner$Stub;-><init>()V -Landroid/view/IRemoteAnimationRunner;->onAnimationCancelled()V -Landroid/view/IRemoteAnimationRunner;->onAnimationStart([Landroid/view/RemoteAnimationTarget;Landroid/view/IRemoteAnimationFinishedCallback;)V Landroid/view/IRotationWatcher$Stub;-><init>()V Landroid/view/IWindow$Stub;-><init>()V Landroid/view/IWindow$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IWindow; @@ -1493,44 +1391,7 @@ Landroid/view/IWindowManager$Stub$Proxy;->hasNavigationBar(I)Z Landroid/view/IWindowManager$Stub$Proxy;->watchRotation(Landroid/view/IRotationWatcher;I)I Landroid/view/IWindowManager$Stub;-><init>()V Landroid/view/IWindowManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IWindowManager; -Landroid/view/IWindowManager;->createInputConsumer(Landroid/os/IBinder;Ljava/lang/String;ILandroid/view/InputChannel;)V -Landroid/view/IWindowManager;->destroyInputConsumer(Ljava/lang/String;I)Z -Landroid/view/IWindowManager;->endProlongedAnimations()V -Landroid/view/IWindowManager;->executeAppTransition()V -Landroid/view/IWindowManager;->freezeRotation(I)V -Landroid/view/IWindowManager;->getAnimationScale(I)F -Landroid/view/IWindowManager;->getAnimationScales()[F -Landroid/view/IWindowManager;->getBaseDisplaySize(ILandroid/graphics/Point;)V -Landroid/view/IWindowManager;->getDockedStackSide()I -Landroid/view/IWindowManager;->getInitialDisplayDensity(I)I -Landroid/view/IWindowManager;->getInitialDisplaySize(ILandroid/graphics/Point;)V -Landroid/view/IWindowManager;->getStableInsets(ILandroid/graphics/Rect;)V -Landroid/view/IWindowManager;->hasNavigationBar(I)Z -Landroid/view/IWindowManager;->isKeyguardLocked()Z -Landroid/view/IWindowManager;->isKeyguardSecure()Z -Landroid/view/IWindowManager;->isSafeModeEnabled()Z -Landroid/view/IWindowManager;->lockNow(Landroid/os/Bundle;)V -Landroid/view/IWindowManager;->overridePendingAppTransitionMultiThumbFuture(Landroid/view/IAppTransitionAnimationSpecsFuture;Landroid/os/IRemoteCallback;ZI)V -Landroid/view/IWindowManager;->overridePendingAppTransitionRemote(Landroid/view/RemoteAnimationAdapter;I)V -Landroid/view/IWindowManager;->registerDockedStackListener(Landroid/view/IDockedStackListener;)V -Landroid/view/IWindowManager;->removeRotationWatcher(Landroid/view/IRotationWatcher;)V -Landroid/view/IWindowManager;->setAnimationScale(IF)V -Landroid/view/IWindowManager;->setAnimationScales([F)V -Landroid/view/IWindowManager;->setNavBarVirtualKeyHapticFeedbackEnabled(Z)V -Landroid/view/IWindowManager;->setShelfHeight(ZI)V -Landroid/view/IWindowManager;->setStrictModeVisualIndicatorPreference(Ljava/lang/String;)V -Landroid/view/IWindowManager;->thawRotation()V Landroid/view/IWindowSession$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IWindowSession; -Landroid/view/IWindowSession;->finishDrawing(Landroid/view/IWindow;)V -Landroid/view/IWindowSession;->getInTouchMode()Z -Landroid/view/IWindowSession;->performDrag(Landroid/view/IWindow;ILandroid/view/SurfaceControl;IFFFFLandroid/content/ClipData;)Landroid/os/IBinder; -Landroid/view/IWindowSession;->performHapticFeedback(Landroid/view/IWindow;IZ)Z -Landroid/view/IWindowSession;->remove(Landroid/view/IWindow;)V -Landroid/view/IWindowSession;->setInTouchMode(Z)V -Landroid/view/IWindowSession;->setTransparentRegion(Landroid/view/IWindow;Landroid/graphics/Region;)V -Landroid/view/IWindowSession;->wallpaperCommandComplete(Landroid/os/IBinder;Landroid/os/Bundle;)V -Landroid/view/IWindowSession;->wallpaperOffsetsComplete(Landroid/os/IBinder;)V -Landroid/view/RenderNodeAnimator;->setDuration(J)Landroid/view/RenderNodeAnimator; Landroid/view/View$AttachInfo$InvalidateInfo;-><init>()V Landroid/view/View$CheckForLongPress;-><init>(Landroid/view/View;)V Landroid/view/View$ListenerInfo;-><init>()V @@ -1539,9 +1400,6 @@ Landroid/webkit/CacheManager$CacheResult;-><init>()V Landroid/webkit/IWebViewUpdateService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/webkit/IWebViewUpdateService$Stub$Proxy;->waitForAndGetProvider()Landroid/webkit/WebViewProviderResponse; Landroid/webkit/IWebViewUpdateService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/webkit/IWebViewUpdateService; -Landroid/webkit/IWebViewUpdateService;->getCurrentWebViewPackageName()Ljava/lang/String; -Landroid/webkit/IWebViewUpdateService;->getValidWebViewPackages()[Landroid/webkit/WebViewProviderInfo; -Landroid/webkit/IWebViewUpdateService;->isFallbackPackage(Ljava/lang/String;)Z Landroid/widget/DigitalClock$FormatChangeObserver;-><init>(Landroid/widget/DigitalClock;)V Landroid/widget/QuickContactBadge$QueryHandler;-><init>(Landroid/widget/QuickContactBadge;Landroid/content/ContentResolver;)V Landroid/widget/RelativeLayout$DependencyGraph$Node;-><init>()V @@ -1551,7 +1409,6 @@ Lcom/android/ims/ImsCall;->isMultiparty()Z Lcom/android/ims/ImsCall;->reject(I)V Lcom/android/ims/ImsCall;->terminate(I)V Lcom/android/ims/ImsConfigListener$Stub;-><init>()V -Lcom/android/ims/ImsConfigListener;->onSetFeatureResponse(IIII)V Lcom/android/ims/ImsEcbm;->exitEmergencyCallbackMode()V Lcom/android/ims/ImsManager;->getConfigInterface()Lcom/android/ims/ImsConfig; Lcom/android/ims/ImsManager;->getInstance(Landroid/content/Context;I)Lcom/android/ims/ImsManager; @@ -1561,104 +1418,17 @@ Lcom/android/ims/ImsManager;->isVolteEnabledByPlatform(Landroid/content/Context; Lcom/android/ims/ImsUtInterface;->queryCallForward(ILjava/lang/String;Landroid/os/Message;)V Lcom/android/ims/internal/IImsCallSession$Stub;-><init>()V Lcom/android/ims/internal/IImsCallSession$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/ims/internal/IImsCallSession; -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionConferenceStateUpdated(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsConferenceState;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHandover(Lcom/android/ims/internal/IImsCallSession;IILandroid/telephony/ims/ImsReasonInfo;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHandoverFailed(Lcom/android/ims/internal/IImsCallSession;IILandroid/telephony/ims/ImsReasonInfo;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHeld(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHoldFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionHoldReceived(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionInviteParticipantsRequestDelivered(Lcom/android/ims/internal/IImsCallSession;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionInviteParticipantsRequestFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMergeComplete(Lcom/android/ims/internal/IImsCallSession;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMergeFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMergeStarted(Lcom/android/ims/internal/IImsCallSession;Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionMultipartyStateChanged(Lcom/android/ims/internal/IImsCallSession;Z)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionProgressing(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsStreamMediaProfile;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionResumed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionResumeFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionResumeReceived(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionStarted(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionStartFailed(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionSuppServiceReceived(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsSuppServiceNotification;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionTerminated(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsReasonInfo;)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionTtyModeReceived(Lcom/android/ims/internal/IImsCallSession;I)V -Lcom/android/ims/internal/IImsCallSessionListener;->callSessionUpdated(Lcom/android/ims/internal/IImsCallSession;Landroid/telephony/ims/ImsCallProfile;)V Lcom/android/ims/internal/IImsConfig$Stub;-><init>()V Lcom/android/ims/internal/IImsEcbm$Stub;-><init>()V -Lcom/android/ims/internal/IImsRegistrationListener;->registrationAssociatedUriChanged([Landroid/net/Uri;)V -Lcom/android/ims/internal/IImsRegistrationListener;->registrationChangeFailed(ILandroid/telephony/ims/ImsReasonInfo;)V -Lcom/android/ims/internal/IImsRegistrationListener;->registrationConnected()V -Lcom/android/ims/internal/IImsRegistrationListener;->registrationConnectedWithRadioTech(I)V -Lcom/android/ims/internal/IImsRegistrationListener;->registrationDisconnected(Landroid/telephony/ims/ImsReasonInfo;)V -Lcom/android/ims/internal/IImsRegistrationListener;->registrationFeatureCapabilityChanged(I[I[I)V -Lcom/android/ims/internal/IImsRegistrationListener;->registrationProgressingWithRadioTech(I)V -Lcom/android/ims/internal/IImsRegistrationListener;->voiceMessageCountUpdate(I)V Lcom/android/ims/internal/IImsService$Stub;-><init>()V Lcom/android/ims/internal/IImsService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/ims/internal/IImsService; Lcom/android/ims/internal/IImsUt$Stub;-><init>()V -Lcom/android/ims/internal/IImsUtListener;->utConfigurationCallBarringQueried(Lcom/android/ims/internal/IImsUt;I[Landroid/telephony/ims/ImsSsInfo;)V -Lcom/android/ims/internal/IImsUtListener;->utConfigurationCallForwardQueried(Lcom/android/ims/internal/IImsUt;I[Landroid/telephony/ims/ImsCallForwardInfo;)V -Lcom/android/ims/internal/IImsUtListener;->utConfigurationCallWaitingQueried(Lcom/android/ims/internal/IImsUt;I[Landroid/telephony/ims/ImsSsInfo;)V -Lcom/android/ims/internal/IImsUtListener;->utConfigurationQueried(Lcom/android/ims/internal/IImsUt;ILandroid/os/Bundle;)V -Lcom/android/ims/internal/IImsUtListener;->utConfigurationQueryFailed(Lcom/android/ims/internal/IImsUt;ILandroid/telephony/ims/ImsReasonInfo;)V -Lcom/android/ims/internal/IImsUtListener;->utConfigurationUpdated(Lcom/android/ims/internal/IImsUt;I)V -Lcom/android/ims/internal/IImsUtListener;->utConfigurationUpdateFailed(Lcom/android/ims/internal/IImsUt;ILandroid/telephony/ims/ImsReasonInfo;)V -Lcom/android/ims/internal/IImsVideoCallCallback;->changeCallDataUsage(J)V -Lcom/android/ims/internal/IImsVideoCallCallback;->changeCameraCapabilities(Landroid/telecom/VideoProfile$CameraCapabilities;)V -Lcom/android/ims/internal/IImsVideoCallCallback;->changePeerDimensions(II)V -Lcom/android/ims/internal/IImsVideoCallCallback;->changeVideoQuality(I)V -Lcom/android/ims/internal/IImsVideoCallCallback;->handleCallSessionEvent(I)V -Lcom/android/ims/internal/IImsVideoCallCallback;->receiveSessionModifyRequest(Landroid/telecom/VideoProfile;)V -Lcom/android/ims/internal/IImsVideoCallCallback;->receiveSessionModifyResponse(ILandroid/telecom/VideoProfile;Landroid/telecom/VideoProfile;)V Lcom/android/ims/internal/IImsVideoCallProvider$Stub;-><init>()V -Lcom/android/ims/internal/IImsVideoCallProvider;->setCallback(Lcom/android/ims/internal/IImsVideoCallCallback;)V Lcom/android/ims/internal/ImsVideoCallProviderWrapper;-><init>(Lcom/android/ims/internal/IImsVideoCallProvider;)V -Lcom/android/ims/internal/uce/options/IOptionsListener;->cmdStatus(Lcom/android/ims/internal/uce/options/OptionsCmdStatus;)V -Lcom/android/ims/internal/uce/options/IOptionsListener;->getVersionCb(Ljava/lang/String;)V -Lcom/android/ims/internal/uce/options/IOptionsListener;->incomingOptions(Ljava/lang/String;Lcom/android/ims/internal/uce/options/OptionsCapInfo;I)V -Lcom/android/ims/internal/uce/options/IOptionsListener;->serviceAvailable(Lcom/android/ims/internal/uce/common/StatusCode;)V -Lcom/android/ims/internal/uce/options/IOptionsListener;->serviceUnavailable(Lcom/android/ims/internal/uce/common/StatusCode;)V -Lcom/android/ims/internal/uce/options/IOptionsListener;->sipResponseReceived(Ljava/lang/String;Lcom/android/ims/internal/uce/options/OptionsSipResponse;Lcom/android/ims/internal/uce/options/OptionsCapInfo;)V Lcom/android/ims/internal/uce/options/IOptionsService$Stub;-><init>()V -Lcom/android/ims/internal/uce/options/IOptionsService;->addListener(ILcom/android/ims/internal/uce/options/IOptionsListener;Lcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode; -Lcom/android/ims/internal/uce/options/IOptionsService;->getContactCap(ILjava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode; -Lcom/android/ims/internal/uce/options/IOptionsService;->getContactListCap(I[Ljava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode; -Lcom/android/ims/internal/uce/options/IOptionsService;->getMyInfo(II)Lcom/android/ims/internal/uce/common/StatusCode; -Lcom/android/ims/internal/uce/options/IOptionsService;->getVersion(I)Lcom/android/ims/internal/uce/common/StatusCode; -Lcom/android/ims/internal/uce/options/IOptionsService;->removeListener(ILcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode; -Lcom/android/ims/internal/uce/options/IOptionsService;->responseIncomingOptions(IIILjava/lang/String;Lcom/android/ims/internal/uce/options/OptionsCapInfo;Z)Lcom/android/ims/internal/uce/common/StatusCode; -Lcom/android/ims/internal/uce/options/IOptionsService;->setMyInfo(ILcom/android/ims/internal/uce/common/CapInfo;I)Lcom/android/ims/internal/uce/common/StatusCode; -Lcom/android/ims/internal/uce/presence/IPresenceListener;->capInfoReceived(Ljava/lang/String;[Lcom/android/ims/internal/uce/presence/PresTupleInfo;)V -Lcom/android/ims/internal/uce/presence/IPresenceListener;->cmdStatus(Lcom/android/ims/internal/uce/presence/PresCmdStatus;)V -Lcom/android/ims/internal/uce/presence/IPresenceListener;->getVersionCb(Ljava/lang/String;)V -Lcom/android/ims/internal/uce/presence/IPresenceListener;->listCapInfoReceived(Lcom/android/ims/internal/uce/presence/PresRlmiInfo;[Lcom/android/ims/internal/uce/presence/PresResInfo;)V -Lcom/android/ims/internal/uce/presence/IPresenceListener;->publishTriggering(Lcom/android/ims/internal/uce/presence/PresPublishTriggerType;)V -Lcom/android/ims/internal/uce/presence/IPresenceListener;->serviceAvailable(Lcom/android/ims/internal/uce/common/StatusCode;)V -Lcom/android/ims/internal/uce/presence/IPresenceListener;->serviceUnAvailable(Lcom/android/ims/internal/uce/common/StatusCode;)V -Lcom/android/ims/internal/uce/presence/IPresenceListener;->sipResponseReceived(Lcom/android/ims/internal/uce/presence/PresSipResponse;)V -Lcom/android/ims/internal/uce/presence/IPresenceListener;->unpublishMessageSent()V Lcom/android/ims/internal/uce/presence/IPresenceService$Stub;-><init>()V -Lcom/android/ims/internal/uce/presence/IPresenceService;->addListener(ILcom/android/ims/internal/uce/presence/IPresenceListener;Lcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode; -Lcom/android/ims/internal/uce/presence/IPresenceService;->getContactCap(ILjava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode; -Lcom/android/ims/internal/uce/presence/IPresenceService;->getContactListCap(I[Ljava/lang/String;I)Lcom/android/ims/internal/uce/common/StatusCode; -Lcom/android/ims/internal/uce/presence/IPresenceService;->getVersion(I)Lcom/android/ims/internal/uce/common/StatusCode; -Lcom/android/ims/internal/uce/presence/IPresenceService;->publishMyCap(ILcom/android/ims/internal/uce/presence/PresCapInfo;I)Lcom/android/ims/internal/uce/common/StatusCode; -Lcom/android/ims/internal/uce/presence/IPresenceService;->reenableService(II)Lcom/android/ims/internal/uce/common/StatusCode; -Lcom/android/ims/internal/uce/presence/IPresenceService;->removeListener(ILcom/android/ims/internal/uce/common/UceLong;)Lcom/android/ims/internal/uce/common/StatusCode; -Lcom/android/ims/internal/uce/presence/IPresenceService;->setNewFeatureTag(ILjava/lang/String;Lcom/android/ims/internal/uce/presence/PresServiceInfo;I)Lcom/android/ims/internal/uce/common/StatusCode; Lcom/android/ims/internal/uce/uceservice/IUceListener$Stub;-><init>()V -Lcom/android/ims/internal/uce/uceservice/IUceListener;->setStatus(I)V Lcom/android/ims/internal/uce/uceservice/IUceService$Stub;-><init>()V -Lcom/android/ims/internal/uce/uceservice/IUceService;->createOptionsService(Lcom/android/ims/internal/uce/options/IOptionsListener;Lcom/android/ims/internal/uce/common/UceLong;)I -Lcom/android/ims/internal/uce/uceservice/IUceService;->createPresenceService(Lcom/android/ims/internal/uce/presence/IPresenceListener;Lcom/android/ims/internal/uce/common/UceLong;)I -Lcom/android/ims/internal/uce/uceservice/IUceService;->destroyOptionsService(I)V -Lcom/android/ims/internal/uce/uceservice/IUceService;->destroyPresenceService(I)V -Lcom/android/ims/internal/uce/uceservice/IUceService;->getOptionsService()Lcom/android/ims/internal/uce/options/IOptionsService; -Lcom/android/ims/internal/uce/uceservice/IUceService;->getPresenceService()Lcom/android/ims/internal/uce/presence/IPresenceService; -Lcom/android/ims/internal/uce/uceservice/IUceService;->getServiceStatus()Z -Lcom/android/ims/internal/uce/uceservice/IUceService;->isServiceStarted()Z -Lcom/android/ims/internal/uce/uceservice/IUceService;->startService(Lcom/android/ims/internal/uce/uceservice/IUceListener;)Z -Lcom/android/ims/internal/uce/uceservice/IUceService;->stopService()Z Lcom/android/internal/app/AlertActivity;-><init>()V Lcom/android/internal/app/AlertActivity;->mAlert:Lcom/android/internal/app/AlertController; Lcom/android/internal/app/AlertActivity;->mAlertParams:Lcom/android/internal/app/AlertController$AlertParams; @@ -1689,22 +1459,12 @@ Lcom/android/internal/app/IAppOpsService$Stub;->TRANSACTION_setUserRestrictions: Lcom/android/internal/app/IAppOpsService$Stub;->TRANSACTION_startOperation:I Lcom/android/internal/app/IAppOpsService$Stub;->TRANSACTION_startWatchingMode:I Lcom/android/internal/app/IAppOpsService$Stub;->TRANSACTION_stopWatchingMode:I -Lcom/android/internal/app/IAppOpsService;->finishOperation(Landroid/os/IBinder;IILjava/lang/String;)V -Lcom/android/internal/app/IAppOpsService;->getOpsForPackage(ILjava/lang/String;[I)Ljava/util/List; -Lcom/android/internal/app/IAppOpsService;->getPackagesForOps([I)Ljava/util/List; -Lcom/android/internal/app/IAppOpsService;->resetAllModes(ILjava/lang/String;)V -Lcom/android/internal/app/IAppOpsService;->setMode(IILjava/lang/String;I)V Lcom/android/internal/app/IBatteryStats$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Lcom/android/internal/app/IBatteryStats$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/app/IBatteryStats; -Lcom/android/internal/app/IBatteryStats;->computeChargeTimeRemaining()J -Lcom/android/internal/app/IBatteryStats;->getAwakeTimeBattery()J -Lcom/android/internal/app/IBatteryStats;->getStatistics()[B -Lcom/android/internal/app/IBatteryStats;->isCharging()Z Lcom/android/internal/app/IMediaContainerService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/app/IMediaContainerService; Lcom/android/internal/app/IntentForwarderActivity;->TAG:Ljava/lang/String; Lcom/android/internal/app/IVoiceInteractionManagerService$Stub$Proxy;->showSessionFromSession(Landroid/os/IBinder;Landroid/os/Bundle;I)Z Lcom/android/internal/app/IVoiceInteractionManagerService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/app/IVoiceInteractionManagerService; -Lcom/android/internal/app/IVoiceInteractionManagerService;->getKeyphraseSoundModel(ILjava/lang/String;)Landroid/hardware/soundtrigger/SoundTrigger$KeyphraseSoundModel; Lcom/android/internal/app/LocaleHelper$LocaleInfoComparator;-><init>(Ljava/util/Locale;Z)V Lcom/android/internal/app/LocaleHelper$LocaleInfoComparator;->compare(Lcom/android/internal/app/LocaleStore$LocaleInfo;Lcom/android/internal/app/LocaleStore$LocaleInfo;)I Lcom/android/internal/app/LocaleHelper;->getDisplayCountry(Ljava/util/Locale;Ljava/util/Locale;)Ljava/lang/String; @@ -1732,10 +1492,6 @@ Lcom/android/internal/app/WindowDecorActionBar;->mTabScrollView:Lcom/android/int Lcom/android/internal/app/WindowDecorActionBar;->setShowHideAnimationEnabled(Z)V Lcom/android/internal/appwidget/IAppWidgetService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/appwidget/IAppWidgetService; Lcom/android/internal/appwidget/IAppWidgetService$Stub;->TRANSACTION_bindAppWidgetId:I -Lcom/android/internal/appwidget/IAppWidgetService;->bindAppWidgetId(Ljava/lang/String;IILandroid/content/ComponentName;Landroid/os/Bundle;)Z -Lcom/android/internal/appwidget/IAppWidgetService;->bindRemoteViewsService(Ljava/lang/String;ILandroid/content/Intent;Landroid/app/IApplicationThread;Landroid/os/IBinder;Landroid/app/IServiceConnection;I)Z -Lcom/android/internal/appwidget/IAppWidgetService;->getAppWidgetIds(Landroid/content/ComponentName;)[I -Lcom/android/internal/appwidget/IAppWidgetService;->getAppWidgetViews(Ljava/lang/String;I)Landroid/widget/RemoteViews; Lcom/android/internal/backup/IBackupTransport$Stub;-><init>()V Lcom/android/internal/content/PackageMonitor;-><init>()V Lcom/android/internal/database/SortCursor;-><init>([Landroid/database/Cursor;Ljava/lang/String;)V @@ -1752,16 +1508,11 @@ Lcom/android/internal/location/GpsNetInitiatedHandler;->handleNiNotification(Lco Lcom/android/internal/location/GpsNetInitiatedHandler;->mIsHexInput:Z Lcom/android/internal/location/ILocationProvider$Stub;-><init>()V Lcom/android/internal/location/ILocationProvider$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/location/ILocationProvider; -Lcom/android/internal/location/ILocationProvider;->getStatus(Landroid/os/Bundle;)I -Lcom/android/internal/location/ILocationProvider;->getStatusUpdateTime()J Lcom/android/internal/location/ILocationProvider;->sendExtraCommand(Ljava/lang/String;Landroid/os/Bundle;)V Lcom/android/internal/location/ILocationProvider;->setLocationProviderManager(Lcom/android/internal/location/ILocationProviderManager;)V Lcom/android/internal/location/ILocationProvider;->setRequest(Lcom/android/internal/location/ProviderRequest;Landroid/os/WorkSource;)V Lcom/android/internal/location/ILocationProviderManager$Stub;-><init>()V Lcom/android/internal/location/ILocationProviderManager$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/location/ILocationProviderManager; -Lcom/android/internal/location/ILocationProviderManager;->onReportLocation(Landroid/location/Location;)V -Lcom/android/internal/location/ILocationProviderManager;->onSetEnabled(Z)V -Lcom/android/internal/location/ILocationProviderManager;->onSetProperties(Lcom/android/internal/location/ProviderProperties;)V Lcom/android/internal/logging/MetricsLogger;-><init>()V Lcom/android/internal/net/LegacyVpnInfo;-><init>()V Lcom/android/internal/net/VpnConfig;-><init>()V @@ -1773,7 +1524,6 @@ Lcom/android/internal/os/BinderInternal;->getContextObject()Landroid/os/IBinder; Lcom/android/internal/os/BinderInternal;->handleGc()V Lcom/android/internal/os/ClassLoaderFactory;->createClassloaderNamespace(Ljava/lang/ClassLoader;ILjava/lang/String;Ljava/lang/String;ZZ)Ljava/lang/String; Lcom/android/internal/os/IDropBoxManagerService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/os/IDropBoxManagerService; -Lcom/android/internal/os/IDropBoxManagerService;->getNextEntry(Ljava/lang/String;JLjava/lang/String;)Landroid/os/DropBoxManager$Entry; Lcom/android/internal/os/ProcessCpuTracker$Stats;->name:Ljava/lang/String; Lcom/android/internal/os/ProcessCpuTracker$Stats;->rel_stime:I Lcom/android/internal/os/ProcessCpuTracker$Stats;->rel_uptime:J @@ -1799,8 +1549,6 @@ Lcom/android/internal/policy/DecorView;->mLastLeftInset:I Lcom/android/internal/policy/DecorView;->mLastRightInset:I Lcom/android/internal/policy/DecorView;->mWindow:Lcom/android/internal/policy/PhoneWindow; Lcom/android/internal/policy/IKeyguardService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/policy/IKeyguardService; -Lcom/android/internal/policy/IKeyguardService;->doKeyguardTimeout(Landroid/os/Bundle;)V -Lcom/android/internal/policy/IKeyguardService;->setKeyguardEnabled(Z)V Lcom/android/internal/policy/IKeyguardStateCallback$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/policy/IKeyguardStateCallback; Lcom/android/internal/policy/PhoneFallbackEventHandler;-><init>(Landroid/content/Context;)V Lcom/android/internal/policy/PhoneFallbackEventHandler;->mContext:Landroid/content/Context; @@ -2254,14 +2002,7 @@ Lcom/android/internal/R$xml;->power_profile:I Lcom/android/internal/statusbar/IStatusBar$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/statusbar/IStatusBar; Lcom/android/internal/statusbar/IStatusBarService$Stub;-><init>()V Lcom/android/internal/statusbar/IStatusBarService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/statusbar/IStatusBarService; -Lcom/android/internal/statusbar/IStatusBarService;->collapsePanels()V -Lcom/android/internal/statusbar/IStatusBarService;->disable(ILandroid/os/IBinder;Ljava/lang/String;)V -Lcom/android/internal/statusbar/IStatusBarService;->expandNotificationsPanel()V -Lcom/android/internal/statusbar/IStatusBarService;->handleSystemKey(I)V -Lcom/android/internal/statusbar/IStatusBarService;->removeIcon(Ljava/lang/String;)V -Lcom/android/internal/statusbar/IStatusBarService;->setIconVisibility(Ljava/lang/String;Z)V Lcom/android/internal/telecom/ITelecomService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telecom/ITelecomService; -Lcom/android/internal/telecom/ITelecomService;->getCallState()I Lcom/android/internal/telephony/BaseCommands;->mCallStateRegistrants:Landroid/os/RegistrantList; Lcom/android/internal/telephony/BaseCommands;->mCallWaitingInfoRegistrants:Landroid/os/RegistrantList; Lcom/android/internal/telephony/BaseCommands;->mCatCallSetUpRegistrant:Landroid/os/Registrant; @@ -2888,7 +2629,6 @@ Lcom/android/internal/telephony/GsmCdmaPhone;->notifyPreciseCallStateChanged()V Lcom/android/internal/telephony/GsmCdmaPhone;->notifyServiceStateChanged(Landroid/telephony/ServiceState;)V Lcom/android/internal/telephony/GsmCdmaPhone;->setOnEcbModeExitResponse(Landroid/os/Handler;ILjava/lang/Object;)V Lcom/android/internal/telephony/GsmCdmaPhone;->syncClirSetting()V -Lcom/android/internal/telephony/ICarrierConfigLoader;->getConfigForSubId(ILjava/lang/String;)Landroid/os/PersistableBundle; Lcom/android/internal/telephony/IccCard;->getState()Lcom/android/internal/telephony/IccCardConstants$State; Lcom/android/internal/telephony/IccCard;->registerForNetworkLocked(Landroid/os/Handler;ILjava/lang/Object;)V Lcom/android/internal/telephony/IccCard;->supplyNetworkDepersonalization(Ljava/lang/String;Landroid/os/Message;)V @@ -2910,15 +2650,8 @@ Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->logd(Ljava/lang/S Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->loge(Ljava/lang/String;)V Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->mAdnCache:Lcom/android/internal/telephony/uicc/AdnRecordCache; Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->mBaseHandler:Landroid/os/Handler; -Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->mCurrentApp:Lcom/android/internal/telephony/uicc/UiccCardApplication; -Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->mIs3gCard:Z -Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->mLock:Ljava/lang/Object; Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->mPhone:Lcom/android/internal/telephony/Phone; -Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->mRecords:Ljava/util/List; -Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->mRecordSize:[I -Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->mSuccess:Z Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->updateEfForIccType(I)I -Lcom/android/internal/telephony/IccPhoneBookInterfaceManager;->waitForResult(Ljava/util/concurrent/atomic/AtomicBoolean;)V Lcom/android/internal/telephony/IccProvider;-><init>()V Lcom/android/internal/telephony/IccProvider;->ADDRESS_BOOK_COLUMN_NAMES:[Ljava/lang/String; Lcom/android/internal/telephony/IccProvider;->DBG:Z @@ -3101,16 +2834,10 @@ Lcom/android/internal/telephony/IPhoneSubInfo$Stub$Proxy;-><init>(Landroid/os/IB Lcom/android/internal/telephony/IPhoneSubInfo$Stub$Proxy;->getDeviceId(Ljava/lang/String;)Ljava/lang/String; Lcom/android/internal/telephony/IPhoneSubInfo$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/IPhoneSubInfo; Lcom/android/internal/telephony/IPhoneSubInfo$Stub;->TRANSACTION_getDeviceId:I -Lcom/android/internal/telephony/IPhoneSubInfo;->getIccSerialNumber(Ljava/lang/String;)Ljava/lang/String; -Lcom/android/internal/telephony/IPhoneSubInfo;->getSubscriberId(Ljava/lang/String;)Ljava/lang/String; Lcom/android/internal/telephony/ISms$Stub;-><init>()V Lcom/android/internal/telephony/ISms$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ISms; Lcom/android/internal/telephony/ISub$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Lcom/android/internal/telephony/ISub$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ISub; -Lcom/android/internal/telephony/ISub;->getActiveSubIdList()[I -Lcom/android/internal/telephony/ISub;->getDefaultDataSubId()I -Lcom/android/internal/telephony/ISub;->getDefaultSubId()I -Lcom/android/internal/telephony/ISub;->setDefaultDataSubId(I)V Lcom/android/internal/telephony/ITelephony$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->getDeviceId(Ljava/lang/String;)Ljava/lang/String; Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->isRadioOn(Ljava/lang/String;)Z @@ -3120,38 +2847,9 @@ Lcom/android/internal/telephony/ITelephony$Stub;->DESCRIPTOR:Ljava/lang/String; Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_call:I Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_dial:I Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_getDeviceId:I -Lcom/android/internal/telephony/ITelephony;->call(Ljava/lang/String;Ljava/lang/String;)V -Lcom/android/internal/telephony/ITelephony;->dial(Ljava/lang/String;)V -Lcom/android/internal/telephony/ITelephony;->disableDataConnectivity()Z -Lcom/android/internal/telephony/ITelephony;->disableLocationUpdates()V -Lcom/android/internal/telephony/ITelephony;->enableDataConnectivity()Z -Lcom/android/internal/telephony/ITelephony;->enableLocationUpdates()V -Lcom/android/internal/telephony/ITelephony;->getActivePhoneType()I -Lcom/android/internal/telephony/ITelephony;->getCallState()I -Lcom/android/internal/telephony/ITelephony;->getDataActivity()I -Lcom/android/internal/telephony/ITelephony;->getDataEnabled(I)Z -Lcom/android/internal/telephony/ITelephony;->getDataState()I -Lcom/android/internal/telephony/ITelephony;->getNetworkType()I -Lcom/android/internal/telephony/ITelephony;->handlePinMmi(Ljava/lang/String;)Z -Lcom/android/internal/telephony/ITelephony;->handlePinMmiForSubscriber(ILjava/lang/String;)Z -Lcom/android/internal/telephony/ITelephony;->hasIccCard()Z -Lcom/android/internal/telephony/ITelephony;->iccCloseLogicalChannel(II)Z -Lcom/android/internal/telephony/ITelephony;->iccTransmitApduLogicalChannel(IIIIIIILjava/lang/String;)Ljava/lang/String; -Lcom/android/internal/telephony/ITelephony;->isRadioOnForSubscriber(ILjava/lang/String;)Z -Lcom/android/internal/telephony/ITelephony;->setRadio(Z)Z -Lcom/android/internal/telephony/ITelephony;->supplyPin(Ljava/lang/String;)Z -Lcom/android/internal/telephony/ITelephony;->toggleRadioOnOff()V -Lcom/android/internal/telephony/ITelephony;->updateServiceLocation()V Lcom/android/internal/telephony/ITelephonyRegistry$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Lcom/android/internal/telephony/ITelephonyRegistry$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ITelephonyRegistry; -Lcom/android/internal/telephony/ITelephonyRegistry;->listen(Ljava/lang/String;Lcom/android/internal/telephony/IPhoneStateListener;IZ)V -Lcom/android/internal/telephony/ITelephonyRegistry;->notifyCallState(ILjava/lang/String;)V -Lcom/android/internal/telephony/ITelephonyRegistry;->notifyCellInfo(Ljava/util/List;)V -Lcom/android/internal/telephony/ITelephonyRegistry;->notifyDataConnectionFailed(Ljava/lang/String;)V Lcom/android/internal/telephony/IWapPushManager$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/IWapPushManager; -Lcom/android/internal/telephony/IWapPushManager;->addPackage(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IZZ)Z -Lcom/android/internal/telephony/IWapPushManager;->deletePackage(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z -Lcom/android/internal/telephony/IWapPushManager;->updatePackage(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IZZ)Z Lcom/android/internal/telephony/MccTable$MccEntry;->mIso:Ljava/lang/String; Lcom/android/internal/telephony/MccTable;->countryCodeForMcc(I)Ljava/lang/String; Lcom/android/internal/telephony/MccTable;->defaultLanguageForMcc(I)Ljava/lang/String; @@ -3946,23 +3644,7 @@ Lcom/android/internal/widget/ActionBarOverlayLayout;-><init>(Landroid/content/Co Lcom/android/internal/widget/ActionBarOverlayLayout;->setWindowCallback(Landroid/view/Window$Callback;)V Lcom/android/internal/widget/EditableInputConnection;-><init>(Landroid/widget/TextView;)V Lcom/android/internal/widget/ILockSettings$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/widget/ILockSettings; -Lcom/android/internal/widget/ILockSettings;->getBoolean(Ljava/lang/String;ZI)Z -Lcom/android/internal/widget/ILockSettings;->getLong(Ljava/lang/String;JI)J -Lcom/android/internal/widget/ILockSettings;->getString(Ljava/lang/String;Ljava/lang/String;I)Ljava/lang/String; -Lcom/android/internal/widget/ILockSettings;->havePassword(I)Z -Lcom/android/internal/widget/ILockSettings;->havePattern(I)Z -Lcom/android/internal/widget/ILockSettings;->setBoolean(Ljava/lang/String;ZI)V -Lcom/android/internal/widget/ILockSettings;->setLong(Ljava/lang/String;JI)V -Lcom/android/internal/widget/ILockSettings;->setString(Ljava/lang/String;Ljava/lang/String;I)V Lcom/android/internal/widget/IRemoteViewsFactory$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/widget/IRemoteViewsFactory; -Lcom/android/internal/widget/IRemoteViewsFactory;->getCount()I -Lcom/android/internal/widget/IRemoteViewsFactory;->getItemId(I)J -Lcom/android/internal/widget/IRemoteViewsFactory;->getLoadingView()Landroid/widget/RemoteViews; -Lcom/android/internal/widget/IRemoteViewsFactory;->getViewAt(I)Landroid/widget/RemoteViews; -Lcom/android/internal/widget/IRemoteViewsFactory;->getViewTypeCount()I -Lcom/android/internal/widget/IRemoteViewsFactory;->hasStableIds()Z -Lcom/android/internal/widget/IRemoteViewsFactory;->isCreated()Z -Lcom/android/internal/widget/IRemoteViewsFactory;->onDataSetChanged()V Lcom/android/internal/widget/LinearLayoutWithDefaultTouchRecepient;-><init>(Landroid/content/Context;)V Lcom/android/internal/widget/LinearLayoutWithDefaultTouchRecepient;->setDefaultTouchRecepient(Landroid/view/View;)V Lcom/android/internal/widget/LockPatternChecker;->checkPassword(Lcom/android/internal/widget/LockPatternUtils;Ljava/lang/String;ILcom/android/internal/widget/LockPatternChecker$OnCheckCallback;)Landroid/os/AsyncTask; diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index d29feddc0963..68b2de425b72 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -127,7 +127,6 @@ import android.view.autofill.AutofillPopupWindow; import android.view.autofill.IAutofillWindowPresenter; import android.view.contentcapture.ContentCaptureContext; import android.view.contentcapture.ContentCaptureManager; -import android.view.contentcapture.ContentCaptureSession; import android.widget.AdapterView; import android.widget.Toast; import android.widget.Toolbar; @@ -1038,15 +1037,15 @@ public class Activity extends ContextThemeWrapper } /** @hide */ private static final int CONTENT_CAPTURE_START = 1; - /** @hide */ private static final int CONTENT_CAPTURE_PAUSE = 2; - /** @hide */ private static final int CONTENT_CAPTURE_RESUME = 3; + /** @hide */ private static final int CONTENT_CAPTURE_RESUME = 2; + /** @hide */ private static final int CONTENT_CAPTURE_PAUSE = 3; /** @hide */ private static final int CONTENT_CAPTURE_STOP = 4; /** @hide */ @IntDef(prefix = { "CONTENT_CAPTURE_" }, value = { CONTENT_CAPTURE_START, - CONTENT_CAPTURE_PAUSE, CONTENT_CAPTURE_RESUME, + CONTENT_CAPTURE_PAUSE, CONTENT_CAPTURE_STOP }) @Retention(RetentionPolicy.SOURCE) @@ -1056,10 +1055,10 @@ public class Activity extends ContextThemeWrapper switch (type) { case CONTENT_CAPTURE_START: return "START"; - case CONTENT_CAPTURE_PAUSE: - return "PAUSE"; case CONTENT_CAPTURE_RESUME: return "RESUME"; + case CONTENT_CAPTURE_PAUSE: + return "PAUSE"; case CONTENT_CAPTURE_STOP: return "STOP"; default: @@ -1086,16 +1085,16 @@ public class Activity extends ContextThemeWrapper & WindowManager.LayoutParams.FLAG_SECURE) != 0) { flags |= ContentCaptureContext.FLAG_DISABLED_BY_FLAG_SECURE; } - cm.onActivityStarted(mToken, getComponentName(), flags); - break; - case CONTENT_CAPTURE_PAUSE: - cm.flush(ContentCaptureSession.FLUSH_REASON_ACTIVITY_PAUSED); + cm.onActivityCreated(mToken, getComponentName(), flags); break; case CONTENT_CAPTURE_RESUME: - cm.flush(ContentCaptureSession.FLUSH_REASON_ACTIVITY_RESUMED); + cm.onActivityResumed(); + break; + case CONTENT_CAPTURE_PAUSE: + cm.onActivityPaused(); break; case CONTENT_CAPTURE_STOP: - cm.onActivityStopped(); + cm.onActivityDestroyed(); break; default: Log.wtf(TAG, "Invalid @ContentCaptureNotificationType: " + type); @@ -1110,7 +1109,7 @@ public class Activity extends ContextThemeWrapper super.attachBaseContext(newBase); if (newBase != null) { newBase.setAutofillClient(this); - newBase.setContentCaptureSupported(true); + newBase.setContentCaptureOptions(getContentCaptureOptions()); } } @@ -1120,12 +1119,6 @@ public class Activity extends ContextThemeWrapper return this; } - /** @hide */ - @Override - public boolean isContentCaptureSupported() { - return true; - } - /** * Register an {@link Application.ActivityLifecycleCallbacks} instance that receives * lifecycle callbacks for only this Activity. @@ -1660,6 +1653,8 @@ public class Activity extends ContextThemeWrapper } mCalled = true; + + notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_START); } /** @@ -1703,7 +1698,6 @@ public class Activity extends ContextThemeWrapper if (mAutoFillResetNeeded) { getAutofillManager().onVisibleForAutofill(); } - notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_START); } /** @@ -1786,8 +1780,10 @@ public class Activity extends ContextThemeWrapper } } } - mCalled = true; + notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_RESUME); + + mCalled = true; } /** @@ -2203,8 +2199,9 @@ public class Activity extends ContextThemeWrapper mAutoFillIgnoreFirstResumePause = false; } } - mCalled = true; + notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_PAUSE); + mCalled = true; } /** @@ -2393,7 +2390,6 @@ public class Activity extends ContextThemeWrapper getAutofillManager().onPendingSaveUi(AutofillManager.PENDING_UI_OPERATION_CANCEL, mIntent.getIBinderExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN)); } - notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_STOP); } mEnterAnimationComplete = false; } @@ -2465,6 +2461,8 @@ public class Activity extends ContextThemeWrapper } dispatchActivityDestroyed(); + + notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_STOP); } /** @@ -3523,6 +3521,12 @@ public class Activity extends ContextThemeWrapper * Default implementation of {@link KeyEvent.Callback#onKeyLongPress(int, KeyEvent) * KeyEvent.Callback.onKeyLongPress()}: always returns false (doesn't handle * the event). + * + * To receive this callback, you must return true from onKeyDown for the current + * event stream. + * + * @see KeyEvent.Callback#onKeyLongPress() + * @see KeyEvent.Callback#onKeyLongPress(int, KeyEvent) */ public boolean onKeyLongPress(int keyCode, KeyEvent event) { return false; @@ -7614,7 +7618,8 @@ public class Activity extends ContextThemeWrapper mWindow.setColorMode(info.colorMode); - setAutofillCompatibilityEnabled(application.isAutofillCompatibilityEnabled()); + setAutofillOptions(application.getAutofillOptions()); + setContentCaptureOptions(application.getContentCaptureOptions()); } private void enableAutofillCompatibilityIfNeeded() { diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 5d4f988c3630..ee7288ffd52c 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -3925,6 +3925,14 @@ public class ActivityManager { /** * @hide */ + @TestApi + public static void resumeAppSwitches() throws RemoteException { + getService().resumeAppSwitches(); + } + + /** + * @hide + */ public static void noteWakeupAlarm(PendingIntent ps, WorkSource workSource, int sourceUid, String sourcePkg, String tag) { try { diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 790863713b19..b18c4de93b40 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -43,9 +43,11 @@ import android.app.servertransaction.PendingTransactionActions; import android.app.servertransaction.PendingTransactionActions.StopInfo; import android.app.servertransaction.TransactionExecutor; import android.app.servertransaction.TransactionExecutorHelper; +import android.content.AutofillOptions; import android.content.BroadcastReceiver; import android.content.ComponentCallbacks2; import android.content.ComponentName; +import android.content.ContentCaptureOptions; import android.content.ContentProvider; import android.content.ContentResolver; import android.content.Context; @@ -744,8 +746,16 @@ public final class ActivityThread extends ClientTransactionHandler { /** Initial values for {@link Profiler}. */ ProfilerInfo initProfilerInfo; - boolean autofillCompatibilityEnabled; + AutofillOptions autofillOptions; + /** + * Content capture options for the application - when null, it means ContentCapture is not + * enabled for the package. + */ + @Nullable + ContentCaptureOptions contentCaptureOptions; + + @Override public String toString() { return "AppBindData{appInfo=" + appInfo + "}"; } @@ -966,8 +976,8 @@ public final class ActivityThread extends ClientTransactionHandler { boolean enableBinderTracking, boolean trackAllocation, boolean isRestrictedBackupMode, boolean persistent, Configuration config, CompatibilityInfo compatInfo, Map services, Bundle coreSettings, - String buildSerial, boolean autofillCompatibilityEnabled) { - + String buildSerial, AutofillOptions autofillOptions, + ContentCaptureOptions contentCaptureOptions) { if (services != null) { if (false) { // Test code to make sure the app could see the passed-in services. @@ -1013,7 +1023,8 @@ public final class ActivityThread extends ClientTransactionHandler { data.compatInfo = compatInfo; data.initProfilerInfo = profilerInfo; data.buildSerial = buildSerial; - data.autofillCompatibilityEnabled = autofillCompatibilityEnabled; + data.autofillOptions = autofillOptions; + data.contentCaptureOptions = contentCaptureOptions; sendMessage(H.BIND_APPLICATION, data); } @@ -6153,7 +6164,10 @@ public final class ActivityThread extends ClientTransactionHandler { app = data.info.makeApplication(data.restrictedBackupMode, null); // Propagate autofill compat state - app.setAutofillCompatibilityEnabled(data.autofillCompatibilityEnabled); + app.setAutofillOptions(data.autofillOptions); + + // Propagate Content Capture options + app.setContentCaptureOptions(data.contentCaptureOptions); mInitialApplication = app; @@ -6940,6 +6954,8 @@ public final class ActivityThread extends ClientTransactionHandler { fd.setInt$(cr.openFileDescriptor(uri, FileUtils.translateModePosixToString(mode)).detachFd()); return fd; + } catch (SecurityException e) { + throw new ErrnoException(e.getMessage(), OsConstants.EACCES); } catch (FileNotFoundException e) { throw new ErrnoException(e.getMessage(), OsConstants.ENOENT); } diff --git a/core/java/android/app/ActivityView.java b/core/java/android/app/ActivityView.java index e0ae4e37765a..2ef085690f8f 100644 --- a/core/java/android/app/ActivityView.java +++ b/core/java/android/app/ActivityView.java @@ -21,7 +21,7 @@ import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_C import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC; import android.annotation.NonNull; -import android.annotation.UnsupportedAppUsage; +import android.annotation.TestApi; import android.app.ActivityManager.StackInfo; import android.content.ComponentName; import android.content.Context; @@ -59,6 +59,7 @@ import java.util.List; * on VirtualDisplays. * @hide */ +@TestApi public class ActivityView extends ViewGroup { private static final String DISPLAY_NAME = "ActivityViewVirtualDisplay"; @@ -92,7 +93,6 @@ public class ActivityView extends ViewGroup { private Insets mForwardedInsets; - @UnsupportedAppUsage public ActivityView(Context context) { this(context, null /* attrs */); } @@ -151,7 +151,7 @@ public class ActivityView extends ViewGroup { * Called when a task is moved to the front of the stack inside the container. * This is a filtered version of {@link TaskStackListener} */ - public void onTaskMovedToFront(ActivityManager.StackInfo stackInfo) { } + public void onTaskMovedToFront(int taskId) { } /** * Called when a task is about to be removed from the stack inside the container. @@ -195,7 +195,6 @@ public class ActivityView extends ViewGroup { * @see StateCallback * @see #startActivity(PendingIntent) */ - @UnsupportedAppUsage public void startActivity(@NonNull Intent intent) { final ActivityOptions options = prepareActivityOptions(); getContext().startActivity(intent, options.toBundle()); @@ -238,7 +237,6 @@ public class ActivityView extends ViewGroup { * @see StateCallback * @see #startActivity(Intent) */ - @UnsupportedAppUsage public void startActivity(@NonNull PendingIntent pendingIntent) { final ActivityOptions options = prepareActivityOptions(); try { @@ -272,7 +270,6 @@ public class ActivityView extends ViewGroup { * * @see StateCallback */ - @UnsupportedAppUsage public void release() { if (mVirtualDisplay == null) { throw new IllegalStateException( @@ -400,7 +397,7 @@ public class ActivityView extends ViewGroup { final IWindowManager wm = WindowManagerGlobal.getWindowManagerService(); mRootSurfaceControl = new SurfaceControl.Builder(surfaceSession) - .setContainerLayer(true) + .setContainerLayer() .setParent(mSurfaceView.getSurfaceControl()) .setName(DISPLAY_NAME) .build(); @@ -524,9 +521,10 @@ public class ActivityView extends ViewGroup { private class TaskStackListenerImpl extends TaskStackListener { @Override - public void onTaskDescriptionChanged(int taskId, ActivityManager.TaskDescription td) + public void onTaskDescriptionChanged(ActivityManager.RunningTaskInfo taskInfo) throws RemoteException { - if (mVirtualDisplay == null) { + if (mVirtualDisplay == null + || taskInfo.displayId != mVirtualDisplay.getDisplay().getDisplayId()) { return; } @@ -536,14 +534,17 @@ public class ActivityView extends ViewGroup { } // Found the topmost stack on target display. Now check if the topmost task's // description changed. - if (taskId == stackInfo.taskIds[stackInfo.taskIds.length - 1]) { - mSurfaceView.setResizeBackgroundColor(td.getBackgroundColor()); + if (taskInfo.taskId == stackInfo.taskIds[stackInfo.taskIds.length - 1]) { + mSurfaceView.setResizeBackgroundColor( + taskInfo.taskDescription.getBackgroundColor()); } } @Override - public void onTaskMovedToFront(int taskId) throws RemoteException { - if (mActivityViewCallback == null) { + public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) + throws RemoteException { + if (mActivityViewCallback == null || mVirtualDisplay == null + || taskInfo.displayId != mVirtualDisplay.getDisplay().getDisplayId()) { return; } @@ -551,14 +552,14 @@ public class ActivityView extends ViewGroup { // if StackInfo was null or unrelated to the "move to front" then there's no use // notifying the callback if (stackInfo != null - && taskId == stackInfo.taskIds[stackInfo.taskIds.length - 1]) { - mActivityViewCallback.onTaskMovedToFront(stackInfo); + && taskInfo.taskId == stackInfo.taskIds[stackInfo.taskIds.length - 1]) { + mActivityViewCallback.onTaskMovedToFront(taskInfo.taskId); } } @Override public void onTaskCreated(int taskId, ComponentName componentName) throws RemoteException { - if (mActivityViewCallback == null) { + if (mActivityViewCallback == null || mVirtualDisplay == null) { return; } @@ -572,17 +573,13 @@ public class ActivityView extends ViewGroup { } @Override - public void onTaskRemovalStarted(int taskId) throws RemoteException { - if (mActivityViewCallback == null) { + public void onTaskRemovalStarted(ActivityManager.RunningTaskInfo taskInfo) + throws RemoteException { + if (mActivityViewCallback == null || mVirtualDisplay == null + || taskInfo.displayId != mVirtualDisplay.getDisplay().getDisplayId()) { return; } - StackInfo stackInfo = getTopMostStackInfo(); - // if StackInfo was null or task is on a different display then there's no use - // notifying the callback - if (stackInfo != null - && taskId == stackInfo.taskIds[stackInfo.taskIds.length - 1]) { - mActivityViewCallback.onTaskRemovalStarted(taskId); - } + mActivityViewCallback.onTaskRemovalStarted(taskInfo.taskId); } private StackInfo getTopMostStackInfo() throws RemoteException { diff --git a/core/java/android/app/AppComponentFactory.java b/core/java/android/app/AppComponentFactory.java index ae632915dd2d..2cec7f0fc323 100644 --- a/core/java/android/app/AppComponentFactory.java +++ b/core/java/android/app/AppComponentFactory.java @@ -27,6 +27,7 @@ import android.content.pm.ApplicationInfo; * * @see #instantiateApplication * @see #instantiateActivity + * @see #instantiateClassLoader * @see #instantiateService * @see #instantiateReceiver * @see #instantiateProvider @@ -39,8 +40,10 @@ public class AppComponentFactory { * a custom class loader hierarchy. * * @param cl The default classloader instantiated by platform. + * @param aInfo Information about the application being loaded. */ - public @NonNull ClassLoader instantiateClassLoader(@NonNull ClassLoader cl) { + public @NonNull ClassLoader instantiateClassLoader(@NonNull ClassLoader cl, + @NonNull ApplicationInfo aInfo) { return cl; } @@ -133,19 +136,6 @@ public class AppComponentFactory { return (ContentProvider) cl.loadClass(className).newInstance(); } - private ApplicationInfo mApplicationInfo = null; - - void setApplicationInfo(ApplicationInfo info) { - mApplicationInfo = info; - } - - /** - * Returns the ApplicationInfo associated with this package. - */ - public ApplicationInfo getApplicationInfo() { - return mApplicationInfo; - } - /** * @hide */ diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 98032dc3d297..d3e350779c6b 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -696,7 +696,9 @@ public class ApplicationPackageManager extends PackageManager { int flagMask, int flagValues, UserHandle user) { try { mPM.updatePermissionFlags(permissionName, packageName, flagMask, - flagValues, user.getIdentifier()); + flagValues, + mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.Q, + user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -2467,6 +2469,33 @@ public class ApplicationPackageManager extends PackageManager { } @Override + public void setAppDetailsActivityEnabled(String packageName, boolean enabled) { + try { + ComponentName componentName = new ComponentName(packageName, + PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME); + mPM.setComponentEnabledSetting(componentName, enabled + ? PackageManager.COMPONENT_ENABLED_STATE_DEFAULT + : PackageManager.COMPONENT_ENABLED_STATE_DISABLED, + PackageManager.DONT_KILL_APP, getUserId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override + public boolean getAppDetailsActivityEnabled(String packageName) { + try { + ComponentName componentName = new ComponentName(packageName, + PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME); + int state = mPM.getComponentEnabledSetting(componentName, getUserId()); + return state == PackageManager.COMPONENT_ENABLED_STATE_ENABLED + || state == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @Override public void setComponentEnabledSetting(ComponentName componentName, int newState, int flags) { try { diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 6908ca27480c..b607f9adebbe 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -21,8 +21,10 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; +import android.content.AutofillOptions; import android.content.BroadcastReceiver; import android.content.ComponentName; +import android.content.ContentCaptureOptions; import android.content.ContentProvider; import android.content.ContentResolver; import android.content.Context; @@ -214,10 +216,10 @@ class ContextImpl extends Context { // The name of the split this Context is representing. May be null. private @Nullable String mSplitName = null; - private AutofillClient mAutofillClient = null; - private boolean mIsAutofillCompatEnabled; + private @Nullable AutofillClient mAutofillClient = null; + private @Nullable AutofillOptions mAutofillOptions; - private boolean mIsContentCaptureSupported = false; + private ContentCaptureOptions mContentCaptureOptions = null; private final Object mSync = new Object(); @@ -2282,8 +2284,8 @@ class ContextImpl extends Context { return (mFlags & Context.CONTEXT_IGNORE_SECURITY) != 0; } + @TestApi @Override - @UnsupportedAppUsage public Display getDisplay() { if (mDisplay == null) { return mResourcesManager.getAdjustedDisplay(Display.DEFAULT_DISPLAY, @@ -2375,27 +2377,26 @@ class ContextImpl extends Context { /** @hide */ @Override - public boolean isAutofillCompatibilityEnabled() { - return mIsAutofillCompatEnabled; + public AutofillOptions getAutofillOptions() { + return mAutofillOptions; } /** @hide */ - @TestApi @Override - public void setAutofillCompatibilityEnabled(boolean autofillCompatEnabled) { - mIsAutofillCompatEnabled = autofillCompatEnabled; + public void setAutofillOptions(AutofillOptions options) { + mAutofillOptions = options; } /** @hide */ @Override - public boolean isContentCaptureSupported() { - return mIsContentCaptureSupported; + public ContentCaptureOptions getContentCaptureOptions() { + return mContentCaptureOptions; } /** @hide */ @Override - public void setContentCaptureSupported(boolean supported) { - mIsContentCaptureSupported = supported; + public void setContentCaptureOptions(ContentCaptureOptions options) { + mContentCaptureOptions = options; } @UnsupportedAppUsage diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 3aa9fa7fedee..5cbb59976c5a 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -204,6 +204,7 @@ interface IActivityManager { void setProcessImportant(in IBinder token, int pid, boolean isForeground, String reason); void setServiceForeground(in ComponentName className, in IBinder token, int id, in Notification notification, int flags, int foregroundServiceType); + int getForegroundServiceType(in ComponentName className, in IBinder token); boolean moveActivityTaskToBack(in IBinder token, boolean nonRoot); void getMemoryInfo(out ActivityManager.MemoryInfo outInfo); List<ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState(); @@ -486,4 +487,13 @@ interface IActivityManager { * started from the shell. */ void stopDelegateShellPermissionIdentity(); + + /** Returns a file descriptor that'll be closed when the system server process dies. */ + ParcelFileDescriptor getLifeMonitor(); + + /** + * Start user, if it us not already running, and bring it to foreground. + * unlockProgressListener can be null if monitoring progress is not necessary. + */ + boolean startUserInForegroundWithListener(int userid, IProgressListener unlockProgressListener); } diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index 497d5ba5fd93..b01cd0e411c7 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -277,12 +277,8 @@ interface IActivityTaskManager { * * @param showingKeyguard True if the keyguard is showing, false otherwise. * @param showingAod True if AOD is showing, false otherwise. - * @param secondaryDisplaysShowing The displayId's of the secondary displays on which the - * keyguard is showing, or {@code null} if there is no such display. Only meaningful if showing - * is {@code true}. */ - void setLockScreenShown(boolean showingKeyguard, boolean showingAod, - in int[] secondaryDisplaysShowing); + void setLockScreenShown(boolean showingKeyguard, boolean showingAod); Bundle getAssistContextExtras(int requestType); boolean launchAssistIntent(in Intent intent, int requestType, in String hint, int userHandle, in Bundle args); diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl index e7a8c0e28bc6..b8af8989170e 100644 --- a/core/java/android/app/IApplicationThread.aidl +++ b/core/java/android/app/IApplicationThread.aidl @@ -21,7 +21,9 @@ import android.app.IUiAutomationConnection; import android.app.ProfilerInfo; import android.app.ResultInfo; import android.app.servertransaction.ClientTransaction; +import android.content.AutofillOptions; import android.content.ComponentName; +import android.content.ContentCaptureOptions; import android.content.IIntentReceiver; import android.content.Intent; import android.content.pm.ActivityInfo; @@ -68,7 +70,8 @@ oneway interface IApplicationThread { int debugMode, boolean enableBinderTracking, boolean trackAllocation, boolean restrictedBackupMode, boolean persistent, in Configuration config, in CompatibilityInfo compatInfo, in Map services, - in Bundle coreSettings, in String buildSerial, boolean isAutofillCompatEnabled); + in Bundle coreSettings, in String buildSerial, in AutofillOptions autofillOptions, + in ContentCaptureOptions contentCaptureOptions); void runIsolatedEntryPoint(in String entryPoint, in String[] entryPointArgs); void scheduleExit(); void scheduleServiceArgs(IBinder token, in ParceledListSlice args); diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl index 2e1e9889eadc..8c85ad134e53 100644 --- a/core/java/android/app/ITaskStackListener.aidl +++ b/core/java/android/app/ITaskStackListener.aidl @@ -73,8 +73,22 @@ oneway interface ITaskStackListener { /** * Called when an activity was requested to be launched on a secondary display but was not * allowed there. + * + * @param taskInfo info about the Activity's task + * @param requestedDisplayId the id of the requested launch display + */ + void onActivityLaunchOnSecondaryDisplayFailed(in ActivityManager.RunningTaskInfo taskInfo, + int requestedDisplayId); + + /** + * Called when an activity was requested to be launched on a secondary display but was rerouted + * to default display. + * + * @param taskInfo info about the Activity's task + * @param requestedDisplayId the id of the requested launch display */ - void onActivityLaunchOnSecondaryDisplayFailed(); + void onActivityLaunchOnSecondaryDisplayRerouted(in ActivityManager.RunningTaskInfo taskInfo, + int requestedDisplayId); /** * Called when a task is added. @@ -94,18 +108,17 @@ oneway interface ITaskStackListener { /** * Called when a task is moved to the front of its stack. * - * @param taskId id of the task. + * @param taskInfo info about the task which moved */ - void onTaskMovedToFront(int taskId); + void onTaskMovedToFront(in ActivityManager.RunningTaskInfo taskInfo); /** * Called when a task’s description is changed due to an activity calling * ActivityManagerService.setTaskDescription * - * @param taskId id of the task. - * @param td the new TaskDescription. + * @param taskInfo info about the task which changed, with {@link TaskInfo#taskDescription} */ - void onTaskDescriptionChanged(int taskId, in ActivityManager.TaskDescription td); + void onTaskDescriptionChanged(in ActivityManager.RunningTaskInfo taskInfo); /** * Called when a activity’s orientation is changed due to it calling @@ -120,8 +133,10 @@ oneway interface ITaskStackListener { * Called when the task is about to be finished but before its surfaces are * removed from the window manager. This allows interested parties to * perform relevant animations before the window disappears. + * + * @param taskInfo info about the task being removed */ - void onTaskRemovalStarted(int taskId); + void onTaskRemovalStarted(in ActivityManager.RunningTaskInfo taskInfo); /** * Called when the task has been put in a locked state because one or more of the diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index 5c4c0052cfbb..5d186a25596f 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -232,7 +232,8 @@ public final class LoadedApk { mResources = Resources.getSystem(); mDefaultClassLoader = ClassLoader.getSystemClassLoader(); mAppComponentFactory = createAppFactory(mApplicationInfo, mDefaultClassLoader); - mClassLoader = mAppComponentFactory.instantiateClassLoader(mDefaultClassLoader); + mClassLoader = mAppComponentFactory.instantiateClassLoader(mDefaultClassLoader, + new ApplicationInfo(mApplicationInfo)); } /** @@ -243,19 +244,15 @@ public final class LoadedApk { mApplicationInfo = info; mDefaultClassLoader = classLoader; mAppComponentFactory = createAppFactory(info, mDefaultClassLoader); - mClassLoader = mAppComponentFactory.instantiateClassLoader(mDefaultClassLoader); + mClassLoader = mAppComponentFactory.instantiateClassLoader(mDefaultClassLoader, + new ApplicationInfo(mApplicationInfo)); } private AppComponentFactory createAppFactory(ApplicationInfo appInfo, ClassLoader cl) { if (appInfo.appComponentFactory != null && cl != null) { try { - AppComponentFactory factory = (AppComponentFactory) cl.loadClass( - appInfo.appComponentFactory).newInstance(); - // Pass a copy of ApplicationInfo to the factory. Copying protects the framework - // from apps which would override the factory and change ApplicationInfo contents. - // ApplicationInfo is used to set up the default class loader. - factory.setApplicationInfo(new ApplicationInfo(appInfo)); - return factory; + return (AppComponentFactory) + cl.loadClass(appInfo.appComponentFactory).newInstance(); } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { Slog.e(TAG, "Unable to instantiate appComponentFactory", e); } @@ -729,8 +726,8 @@ public final class LoadedApk { mDefaultClassLoader = ClassLoader.getSystemClassLoader(); } mAppComponentFactory = createAppFactory(mApplicationInfo, mDefaultClassLoader); - mClassLoader = mAppComponentFactory.instantiateClassLoader(mDefaultClassLoader); - + mClassLoader = mAppComponentFactory.instantiateClassLoader(mDefaultClassLoader, + new ApplicationInfo(mApplicationInfo)); return; } @@ -821,7 +818,8 @@ public final class LoadedApk { } if (mClassLoader == null) { - mClassLoader = mAppComponentFactory.instantiateClassLoader(mDefaultClassLoader); + mClassLoader = mAppComponentFactory.instantiateClassLoader(mDefaultClassLoader, + new ApplicationInfo(mApplicationInfo)); } return; @@ -935,8 +933,10 @@ public final class LoadedApk { // Call AppComponentFactory to select/create the main class loader of this app. // Since this may call code in the app, mDefaultClassLoader must be fully set up // before invoking the factory. + // Invoke with a copy of ApplicationInfo to protect against the app changing it. if (mClassLoader == null) { - mClassLoader = mAppComponentFactory.instantiateClassLoader(mDefaultClassLoader); + mClassLoader = mAppComponentFactory.instantiateClassLoader(mDefaultClassLoader, + new ApplicationInfo(mApplicationInfo)); } } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index dc4f3432b6bd..2e7093db92f0 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -8408,7 +8408,7 @@ public class Notification implements Parcelable public static final class BubbleMetadata implements Parcelable { private PendingIntent mPendingIntent; - private CharSequence mTitle; + private PendingIntent mDeleteIntent; private Icon mIcon; private int mDesiredHeight; private int mFlags; @@ -8436,19 +8436,22 @@ public class Notification implements Parcelable */ private static final int FLAG_SUPPRESS_INITIAL_NOTIFICATION = 0x00000002; - private BubbleMetadata(PendingIntent intent, CharSequence title, Icon icon, int height) { - mPendingIntent = intent; - mTitle = title; + private BubbleMetadata(PendingIntent expandIntent, PendingIntent deleteIntent, + Icon icon, int height) { + mPendingIntent = expandIntent; mIcon = icon; mDesiredHeight = height; + mDeleteIntent = deleteIntent; } private BubbleMetadata(Parcel in) { mPendingIntent = PendingIntent.CREATOR.createFromParcel(in); - mTitle = in.readCharSequence(); mIcon = Icon.CREATOR.createFromParcel(in); mDesiredHeight = in.readInt(); mFlags = in.readInt(); + if (in.readInt() != 0) { + mDeleteIntent = PendingIntent.CREATOR.createFromParcel(in); + } } /** @@ -8459,13 +8462,22 @@ public class Notification implements Parcelable } /** + * @return the pending intent to send when the bubble is dismissed by a user, if one exists. + */ + public PendingIntent getDeleteIntent() { + return mDeleteIntent; + } + + /** * @return the title that will appear along with the app content defined by * {@link #getIntent()} for this bubble. + * + * @deprecated titles are no longer required or shown. */ + @Deprecated public CharSequence getTitle() { - return mTitle; + return ""; } - /** * @return the icon that will be displayed for this bubble when it is collapsed. */ @@ -8521,10 +8533,13 @@ public class Notification implements Parcelable @Override public void writeToParcel(Parcel out, int flags) { mPendingIntent.writeToParcel(out, 0); - out.writeCharSequence(mTitle); mIcon.writeToParcel(out, 0); out.writeInt(mDesiredHeight); out.writeInt(mFlags); + out.writeInt(mDeleteIntent != null ? 1 : 0); + if (mDeleteIntent != null) { + mDeleteIntent.writeToParcel(out, 0); + } } private void setFlags(int flags) { @@ -8537,10 +8552,10 @@ public class Notification implements Parcelable public static class Builder { private PendingIntent mPendingIntent; - private CharSequence mTitle; private Icon mIcon; private int mDesiredHeight; private int mFlags; + private PendingIntent mDeleteIntent; /** * Constructs a new builder object. @@ -8565,12 +8580,11 @@ public class Notification implements Parcelable * * <p>A title is required and should expect to fit on a single line and make sense when * shown with the content defined by {@link #setIntent(PendingIntent)}.</p> + * + * @deprecated titles are no longer required or shown. */ + @Deprecated public BubbleMetadata.Builder setTitle(CharSequence title) { - if (TextUtils.isEmpty(title)) { - throw new IllegalArgumentException("Bubbles require non-null or empty title"); - } - mTitle = title; return this; } @@ -8633,6 +8647,14 @@ public class Notification implements Parcelable } /** + * Sets an optional intent to send when this bubble is explicitly removed by the user. + */ + public BubbleMetadata.Builder setDeleteIntent(PendingIntent deleteIntent) { + mDeleteIntent = deleteIntent; + return this; + } + + /** * Creates the {@link BubbleMetadata} defined by this builder. * <p>Will throw {@link IllegalStateException} if required fields have not been set * on this builder.</p> @@ -8641,14 +8663,11 @@ public class Notification implements Parcelable if (mPendingIntent == null) { throw new IllegalStateException("Must supply pending intent to bubble"); } - if (TextUtils.isEmpty(mTitle)) { - throw new IllegalStateException("Must supply a title for the bubble"); - } if (mIcon == null) { throw new IllegalStateException("Must supply an icon for the bubble"); } - BubbleMetadata data = new BubbleMetadata(mPendingIntent, mTitle, mIcon, - mDesiredHeight); + BubbleMetadata data = new BubbleMetadata(mPendingIntent, mDeleteIntent, + mIcon, mDesiredHeight); data.setFlags(mFlags); return data; } diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java index 4a451509f538..8493fb25b8a5 100644 --- a/core/java/android/app/SearchDialog.java +++ b/core/java/android/app/SearchDialog.java @@ -47,8 +47,10 @@ import android.view.Window; import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import android.widget.AutoCompleteTextView; +import android.widget.Filterable; import android.widget.ImageView; import android.widget.LinearLayout; +import android.widget.ListPopupWindow; import android.widget.SearchView; import android.widget.TextView; @@ -370,7 +372,10 @@ public class SearchDialog extends Dialog { updateSearchAppIcon(); updateSearchBadge(); if (isLandscapeMode(getContext())) { - mSearchAutoComplete.ensureImeVisible(true); + mSearchAutoComplete.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NEEDED); + if (mSearchAutoComplete.isDropDownAlwaysVisible() || enoughToFilter()) { + mSearchAutoComplete.showDropDown(); + } } } } @@ -381,6 +386,15 @@ public class SearchDialog extends Dialog { == Configuration.ORIENTATION_LANDSCAPE; } + private boolean enoughToFilter() { + Filterable filterableAdapter = (Filterable) mSearchAutoComplete.getAdapter(); + if (filterableAdapter == null || filterableAdapter.getFilter() == null) { + return false; + } + + return mSearchAutoComplete.enoughToFilter(); + } + /** * Update the UI according to the info in the current value of {@link #mSearchable}. */ diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java index f116e133e338..1f91b3f431a1 100644 --- a/core/java/android/app/Service.java +++ b/core/java/android/app/Service.java @@ -27,6 +27,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.ContextWrapper; import android.content.Intent; +import android.content.pm.ServiceInfo; import android.content.pm.ServiceInfo.ForegroundServiceType; import android.content.res.Configuration; import android.os.Build; @@ -733,7 +734,7 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac * {@link android.R.attr#foregroundServiceType} flags. * @throws IllegalArgumentException if param foregroundServiceType is not subset of manifest * attribute {@link android.R.attr#foregroundServiceType}. - * @see {@link android.content.pm.ServiceInfo} for the set of FOREGROUND_SERVICE_TYPE flags. + * @see android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_MANIFEST */ public final void startForeground(int id, @NonNull Notification notification, @ForegroundServiceType int foregroundServiceType) { @@ -775,6 +776,30 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac } /** + * If the service has become a foreground service by calling + * {@link #startForeground(int, Notification)} + * or {@link #startForeground(int, Notification, int)}, {@link #getForegroundServiceType()} + * returns the current foreground service type. + * + * <p>If there is no foregroundServiceType specified + * in manifest, {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_NONE} is returned. </p> + * + * <p>If the service is not a foreground service, + * {@link ServiceInfo#FOREGROUND_SERVICE_TYPE_NONE} is returned.</p> + * + * @return current foreground service type flags. + */ + public final @ForegroundServiceType int getForegroundServiceType() { + int ret = ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE; + try { + ret = mActivityManager.getForegroundServiceType( + new ComponentName(this, mClassName), mToken); + } catch (RemoteException ex) { + } + return ret; + } + + /** * Print the Service's state into the given stream. This gets invoked if * you run "adb shell dumpsys activity service <yourservicename>" * (note that for this command to work, the service must be running, and diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index 077652cacc2d..af0366801275 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -17,6 +17,7 @@ package android.app; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.SystemService; @@ -387,6 +388,7 @@ public class StatusBarManager { * @hide */ @SystemApi + @NonNull public DisableInfo getDisableInfo() { try { final int userId = Binder.getCallingUserHandle().getIdentifier(); diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index c12a92f44fa2..1faa2ac76d71 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -41,6 +41,7 @@ import android.bluetooth.BluetoothManager; import android.companion.CompanionDeviceManager; import android.companion.ICompanionDeviceManager; import android.content.ClipboardManager; +import android.content.ContentCaptureOptions; import android.content.Context; import android.content.IRestrictionsManager; import android.content.RestrictionsManager; @@ -1125,16 +1126,19 @@ final class SystemServiceRegistry { throws ServiceNotFoundException { // Get the services without throwing as this is an optional feature Context outerContext = ctx.getOuterContext(); - if (outerContext.isContentCaptureSupported()) { + ContentCaptureOptions options = outerContext.getContentCaptureOptions(); + // Options is null when the service didn't whitelist the activity or package + if (options != null) { IBinder b = ServiceManager .getService(Context.CONTENT_CAPTURE_MANAGER_SERVICE); IContentCaptureManager service = IContentCaptureManager.Stub.asInterface(b); + // Service is null when not provided by OEM or disabled by kill-switch. if (service != null) { - // When feature is disabled, we return a null manager to apps so the - // performance impact is practically zero - return new ContentCaptureManager(outerContext, service); + return new ContentCaptureManager(outerContext, service, options); } } + // When feature is disabled or app / package not whitelisted, we return a null + // manager to apps so the performance impact is practically zero return null; }}); diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java index e23352a533e3..47ad6d737430 100644 --- a/core/java/android/app/TaskStackListener.java +++ b/core/java/android/app/TaskStackListener.java @@ -70,11 +70,27 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { } @Override + public void onActivityLaunchOnSecondaryDisplayFailed(ActivityManager.RunningTaskInfo taskInfo, + int requestedDisplayId) throws RemoteException { + onActivityLaunchOnSecondaryDisplayFailed(); + } + + /** + * @deprecated see {@link + * #onActivityLaunchOnSecondaryDisplayFailed(ActivityManager.RunningTaskInfo, int)} + */ + @Deprecated @UnsupportedAppUsage public void onActivityLaunchOnSecondaryDisplayFailed() throws RemoteException { } @Override + @UnsupportedAppUsage + public void onActivityLaunchOnSecondaryDisplayRerouted(ActivityManager.RunningTaskInfo taskInfo, + int requestedDisplayId) throws RemoteException { + } + + @Override public void onTaskCreated(int taskId, ComponentName componentName) throws RemoteException { } @@ -84,15 +100,42 @@ public abstract class TaskStackListener extends ITaskStackListener.Stub { } @Override + public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) + throws RemoteException { + onTaskMovedToFront(taskInfo.taskId); + } + + /** + * @deprecated see {@link #onTaskMovedToFront(ActivityManager.RunningTaskInfo)} + */ + @Deprecated @UnsupportedAppUsage public void onTaskMovedToFront(int taskId) throws RemoteException { } @Override + public void onTaskRemovalStarted(ActivityManager.RunningTaskInfo taskInfo) + throws RemoteException { + onTaskRemovalStarted(taskInfo.taskId); + } + + /** + * @deprecated see {@link #onTaskRemovalStarted(ActivityManager.RunningTaskInfo)} + */ + @Deprecated public void onTaskRemovalStarted(int taskId) throws RemoteException { } @Override + public void onTaskDescriptionChanged(ActivityManager.RunningTaskInfo taskInfo) + throws RemoteException { + onTaskDescriptionChanged(taskInfo.taskId, taskInfo.taskDescription); + } + + /** + * @deprecated see {@link #onTaskDescriptionChanged(ActivityManager.RunningTaskInfo)} + */ + @Deprecated public void onTaskDescriptionChanged(int taskId, ActivityManager.TaskDescription td) throws RemoteException { } diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java index 31521a369a4c..7e074460bff8 100644 --- a/core/java/android/app/UiAutomationConnection.java +++ b/core/java/android/app/UiAutomationConnection.java @@ -128,7 +128,13 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { : InputManager.INJECT_INPUT_EVENT_MODE_ASYNC; final long identity = Binder.clearCallingIdentity(); try { - return InputManager.getInstance().injectInputEvent(event, mode); + IWindowManager manager = IWindowManager.Stub.asInterface( + ServiceManager.getService(Context.WINDOW_SERVICE)); + try { + return manager.injectInputAfterTransactionsApplied(event, mode); + } catch (RemoteException e) { + } + return false; } finally { Binder.restoreCallingIdentity(identity); } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 3587c68ef234..a32e01fb68e5 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -59,6 +59,7 @@ import android.os.Build; import android.os.Bundle; import android.os.ParcelFileDescriptor; import android.os.Parcelable; +import android.os.ParcelableException; import android.os.PersistableBundle; import android.os.Process; import android.os.RemoteCallback; @@ -115,6 +116,8 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; /** @@ -1576,6 +1579,19 @@ public class DevicePolicyManager { public static final int PERMISSION_POLICY_AUTO_DENY = 2; /** + * Possible policy values for permissions. + * + * @hide + */ + @IntDef(prefix = { "PERMISSION_GRANT_STATE_" }, value = { + PERMISSION_GRANT_STATE_DEFAULT, + PERMISSION_GRANT_STATE_GRANTED, + PERMISSION_GRANT_STATE_DENIED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface PermissionGrantState {} + + /** * Runtime permission state: The user can manage the permission * through the UI. */ @@ -8667,8 +8683,15 @@ public class DevicePolicyManager { * Setting the grant state to {@link #PERMISSION_GRANT_STATE_DEFAULT default} does not revoke * the permission. It retains the previous grant, if any. * <p/> - * Permissions can be granted or revoked only for applications built with a - * {@code targetSdkVersion} of {@link android.os.Build.VERSION_CODES#M} or later. + * Device admins with a {@code targetSdkVersion} < {@link android.os.Build.VERSION_CODES#Q} + * cannot grant and revoke permissions for applications built with a {@code targetSdkVersion} + * < {@link android.os.Build.VERSION_CODES#M}. + * <p/> + * Admins with a {@code targetSdkVersion} ≥ {@link android.os.Build.VERSION_CODES#Q} can + * grant and revoke permissions of all apps. Similar to the user revoking a permission from a + * application built with a {@code targetSdkVersion} < + * {@link android.os.Build.VERSION_CODES#M} the app-op matching the permission is set to + * {@link android.app.AppOpsManager#MODE_IGNORED}, but the permission stays granted. * * @param admin Which profile or device owner this request is associated with. * @param packageName The application to grant or revoke a permission to. @@ -8684,14 +8707,21 @@ public class DevicePolicyManager { * @see #setDelegatedScopes * @see #DELEGATION_PERMISSION_GRANT */ - public boolean setPermissionGrantState(@NonNull ComponentName admin, String packageName, - String permission, int grantState) { + public boolean setPermissionGrantState(@NonNull ComponentName admin, + @NonNull String packageName, @NonNull String permission, + @PermissionGrantState int grantState) { throwIfParentInstance("setPermissionGrantState"); try { - return mService.setPermissionGrantState(admin, mContext.getPackageName(), packageName, - permission, grantState); + CompletableFuture<Boolean> result = new CompletableFuture<>(); + + mService.setPermissionGrantState(admin, mContext.getPackageName(), packageName, + permission, grantState, new RemoteCallback((b) -> result.complete(b != null))); + + return result.get(); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); } } @@ -8719,8 +8749,8 @@ public class DevicePolicyManager { * @see #setDelegatedScopes * @see #DELEGATION_PERMISSION_GRANT */ - public int getPermissionGrantState(@Nullable ComponentName admin, String packageName, - String permission) { + public @PermissionGrantState int getPermissionGrantState(@Nullable ComponentName admin, + @NonNull String packageName, @NonNull String permission) { throwIfParentInstance("getPermissionGrantState"); try { return mService.getPermissionGrantState(admin, mContext.getPackageName(), packageName, diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 5790fda718a7..9478a3c83d23 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -311,8 +311,8 @@ interface IDevicePolicyManager { void setPermissionPolicy(in ComponentName admin, in String callerPackage, int policy); int getPermissionPolicy(in ComponentName admin); - boolean setPermissionGrantState(in ComponentName admin, in String callerPackage, String packageName, - String permission, int grantState); + void setPermissionGrantState(in ComponentName admin, in String callerPackage, String packageName, + String permission, int grantState, in RemoteCallback resultReceiver); int getPermissionGrantState(in ComponentName admin, in String callerPackage, String packageName, String permission); boolean isProvisioningAllowed(String action, String packageName); int checkProvisioningPreCondition(String action, String packageName); diff --git a/core/java/android/app/admin/PasswordMetrics.java b/core/java/android/app/admin/PasswordMetrics.java index e5df2c70eeec..a6bf5012d9e5 100644 --- a/core/java/android/app/admin/PasswordMetrics.java +++ b/core/java/android/app/admin/PasswordMetrics.java @@ -221,7 +221,10 @@ public class PasswordMetrics implements Parcelable { } }; - public static PasswordMetrics computeForPassword(@NonNull String password) { + /** + * Returns the {@code PasswordMetrics} for a given password + */ + public static PasswordMetrics computeForPassword(@NonNull byte[] password) { // Analyse the characters used int letters = 0; int upperCase = 0; @@ -229,9 +232,9 @@ public class PasswordMetrics implements Parcelable { int numeric = 0; int symbols = 0; int nonLetter = 0; - final int length = password.length(); + final int length = password.length; for (int i = 0; i < length; i++) { - switch (categoryChar(password.charAt(i))) { + switch (categoryChar((char) password[i])) { case CHAR_LOWER_CASE: letters++; lowerCase++; @@ -296,7 +299,7 @@ public class PasswordMetrics implements Parcelable { return false; } - /* + /** * Returns the maximum length of a sequential characters. A sequence is defined as * monotonically increasing characters with a constant interval or the same character repeated. * @@ -310,19 +313,19 @@ public class PasswordMetrics implements Parcelable { * maxLengthSequence(";;;;") == 4 (anything that repeats) * maxLengthSequence(":;<=>") == 1 (ordered, but not composed of alphas or digits) * - * @param string the pass + * @param bytes the pass * @return the number of sequential letters or digits */ - public static int maxLengthSequence(@NonNull String string) { - if (string.length() == 0) return 0; - char previousChar = string.charAt(0); + public static int maxLengthSequence(@NonNull byte[] bytes) { + if (bytes.length == 0) return 0; + char previousChar = (char) bytes[0]; @CharacterCatagory int category = categoryChar(previousChar); //current sequence category int diff = 0; //difference between two consecutive characters boolean hasDiff = false; //if we are currently targeting a sequence int maxLength = 0; //maximum length of a sequence already found int startSequence = 0; //where the current sequence started - for (int current = 1; current < string.length(); current++) { - char currentChar = string.charAt(current); + for (int current = 1; current < bytes.length; current++) { + char currentChar = (char) bytes[current]; @CharacterCatagory int categoryCurrent = categoryChar(currentChar); int currentDiff = (int) currentChar - (int) previousChar; if (categoryCurrent != category || Math.abs(currentDiff) > maxDiffCategory(category)) { @@ -341,7 +344,7 @@ public class PasswordMetrics implements Parcelable { } previousChar = currentChar; } - maxLength = Math.max(maxLength, string.length() - startSequence); + maxLength = Math.max(maxLength, bytes.length - startSequence); return maxLength; } diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java index 868fbfeeee39..bcc4974e4e64 100644 --- a/core/java/android/app/backup/BackupManager.java +++ b/core/java/android/app/backup/BackupManager.java @@ -762,6 +762,7 @@ public class BackupManager { */ @Nullable public UserHandle getUserForAncestralSerialNumber(long ancestralSerialNumber) { + checkServiceBinder(); if (sService != null) { try { return sService.getUserForAncestralSerialNumber(ancestralSerialNumber); @@ -782,6 +783,7 @@ public class BackupManager { @SystemApi @RequiresPermission(android.Manifest.permission.BACKUP) public void setAncestralSerialNumber(long ancestralSerialNumber) { + checkServiceBinder(); if (sService != null) { try { sService.setAncestralSerialNumber(ancestralSerialNumber); @@ -802,6 +804,7 @@ public class BackupManager { @TestApi @RequiresPermission(android.Manifest.permission.BACKUP) public Intent getConfigurationIntent(String transportName) { + checkServiceBinder(); if (sService != null) { try { return sService.getConfigurationIntentForUser(mContext.getUserId(), transportName); @@ -823,6 +826,7 @@ public class BackupManager { @TestApi @RequiresPermission(android.Manifest.permission.BACKUP) public String getDestinationString(String transportName) { + checkServiceBinder(); if (sService != null) { try { return sService.getDestinationStringForUser(mContext.getUserId(), transportName); @@ -844,6 +848,7 @@ public class BackupManager { @TestApi @RequiresPermission(android.Manifest.permission.BACKUP) public Intent getDataManagementIntent(String transportName) { + checkServiceBinder(); if (sService != null) { try { return sService.getDataManagementIntentForUser(mContext.getUserId(), transportName); @@ -867,6 +872,7 @@ public class BackupManager { @TestApi @RequiresPermission(android.Manifest.permission.BACKUP) public String getDataManagementLabel(String transportName) { + checkServiceBinder(); if (sService != null) { try { return sService.getDataManagementLabelForUser(mContext.getUserId(), transportName); diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java index edd3ef983945..7ec21f637e0c 100644 --- a/core/java/android/app/role/RoleManager.java +++ b/core/java/android/app/role/RoleManager.java @@ -71,10 +71,8 @@ public final class RoleManager { /** * The name of the assistant app role. * - * @hide + * @see android.service.voice.VoiceInteractionService */ - @SystemApi - @TestApi public static final String ROLE_ASSISTANT = "android.app.role.ASSISTANT"; /** @@ -149,9 +147,6 @@ public final class RoleManager { * place the call through a call redirection service. * * @see android.telecom.CallRedirectionService - * - * TODO: STOPSHIP: Make name of required roles public API - * @hide */ public static final String ROLE_CALL_REDIRECTION = "android.app.role.CALL_REDIRECTION"; @@ -159,9 +154,6 @@ public final class RoleManager { * The name of the call screening and caller id role. * * @see android.telecom.CallScreeningService - * - * TODO: STOPSHIP: Make name of required roles public API - * @hide */ public static final String ROLE_CALL_SCREENING = "android.app.role.CALL_SCREENING"; diff --git a/core/java/android/app/timezone/RulesState.java b/core/java/android/app/timezone/RulesState.java index e86d348a85fa..38dd1ebf1c45 100644 --- a/core/java/android/app/timezone/RulesState.java +++ b/core/java/android/app/timezone/RulesState.java @@ -33,7 +33,7 @@ import java.lang.annotation.RetentionPolicy; * * <p>The following properties are included: * <dl> - * <dt>systemRulesVersion</dt> + * <dt>baseRulesVersion</dt> * <dd>the IANA rules version that shipped with the OS. Always present. e.g. "2017a".</dd> * <dt>distroFormatVersionSupported</dt> * <dd>the distro format version supported by this device. Always present.</dd> @@ -98,7 +98,7 @@ public final class RulesState implements Parcelable { private static final byte BYTE_FALSE = 0; private static final byte BYTE_TRUE = 1; - private final String mSystemRulesVersion; + private final String mBaseRulesVersion; private final DistroFormatVersion mDistroFormatVersionSupported; private final boolean mOperationInProgress; @StagedOperationType private final int mStagedOperationType; @@ -106,13 +106,13 @@ public final class RulesState implements Parcelable { @DistroStatus private final int mDistroStatus; @Nullable private final DistroRulesVersion mInstalledDistroRulesVersion; - public RulesState(String systemRulesVersion, DistroFormatVersion distroFormatVersionSupported, + public RulesState(String baseRulesVersion, DistroFormatVersion distroFormatVersionSupported, boolean operationInProgress, @StagedOperationType int stagedOperationType, @Nullable DistroRulesVersion stagedDistroRulesVersion, @DistroStatus int distroStatus, @Nullable DistroRulesVersion installedDistroRulesVersion) { - this.mSystemRulesVersion = validateRulesVersion("systemRulesVersion", systemRulesVersion); + this.mBaseRulesVersion = validateRulesVersion("baseRulesVersion", baseRulesVersion); this.mDistroFormatVersionSupported = validateNotNull("distroFormatVersionSupported", distroFormatVersionSupported); this.mOperationInProgress = operationInProgress; @@ -132,8 +132,8 @@ public final class RulesState implements Parcelable { "installedDistroRulesVersion", installedDistroRulesVersion); } - public String getSystemRulesVersion() { - return mSystemRulesVersion; + public String getBaseRulesVersion() { + return mBaseRulesVersion; } public boolean isOperationInProgress() { @@ -172,14 +172,14 @@ public final class RulesState implements Parcelable { } /** - * Returns true if the system image data files contain IANA rules data that are newer than the + * Returns true if the base data files contain IANA rules data that are newer than the * distro IANA rules version supplied, i.e. true when the version specified would be "worse" - * than the one that is in the system image. Returns false if the system image version is the + * than the one that is in the base data. Returns false if the base version is the * same or older, i.e. false when the version specified would be "better" than the one that is - * in the system image. + * in the base set. */ - public boolean isSystemVersionNewerThan(DistroRulesVersion distroRulesVersion) { - return mSystemRulesVersion.compareTo(distroRulesVersion.getRulesVersion()) > 0; + public boolean isBaseVersionNewerThan(DistroRulesVersion distroRulesVersion) { + return mBaseRulesVersion.compareTo(distroRulesVersion.getRulesVersion()) > 0; } public static final Parcelable.Creator<RulesState> CREATOR = @@ -194,14 +194,14 @@ public final class RulesState implements Parcelable { }; private static RulesState createFromParcel(Parcel in) { - String systemRulesVersion = in.readString(); + String baseRulesVersion = in.readString(); DistroFormatVersion distroFormatVersionSupported = in.readParcelable(null); boolean operationInProgress = in.readByte() == BYTE_TRUE; int distroStagedState = in.readByte(); DistroRulesVersion stagedDistroRulesVersion = in.readParcelable(null); int installedDistroStatus = in.readByte(); DistroRulesVersion installedDistroRulesVersion = in.readParcelable(null); - return new RulesState(systemRulesVersion, distroFormatVersionSupported, operationInProgress, + return new RulesState(baseRulesVersion, distroFormatVersionSupported, operationInProgress, distroStagedState, stagedDistroRulesVersion, installedDistroStatus, installedDistroRulesVersion); } @@ -213,7 +213,7 @@ public final class RulesState implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { - out.writeString(mSystemRulesVersion); + out.writeString(mBaseRulesVersion); out.writeParcelable(mDistroFormatVersionSupported, 0); out.writeByte(mOperationInProgress ? BYTE_TRUE : BYTE_FALSE); out.writeByte((byte) mStagedOperationType); @@ -242,7 +242,7 @@ public final class RulesState implements Parcelable { if (mDistroStatus != that.mDistroStatus) { return false; } - if (!mSystemRulesVersion.equals(that.mSystemRulesVersion)) { + if (!mBaseRulesVersion.equals(that.mBaseRulesVersion)) { return false; } if (!mDistroFormatVersionSupported.equals(that.mDistroFormatVersionSupported)) { @@ -259,7 +259,7 @@ public final class RulesState implements Parcelable { @Override public int hashCode() { - int result = mSystemRulesVersion.hashCode(); + int result = mBaseRulesVersion.hashCode(); result = 31 * result + mDistroFormatVersionSupported.hashCode(); result = 31 * result + (mOperationInProgress ? 1 : 0); result = 31 * result + mStagedOperationType; @@ -275,7 +275,7 @@ public final class RulesState implements Parcelable { @Override public String toString() { return "RulesState{" - + "mSystemRulesVersion='" + mSystemRulesVersion + '\'' + + "mBaseRulesVersion='" + mBaseRulesVersion + '\'' + ", mDistroFormatVersionSupported=" + mDistroFormatVersionSupported + ", mOperationInProgress=" + mOperationInProgress + ", mStagedOperationType=" + mStagedOperationType diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java index eaf2cc12a4ac..d34e6d3836b8 100644 --- a/core/java/android/app/usage/UsageStatsManager.java +++ b/core/java/android/app/usage/UsageStatsManager.java @@ -781,7 +781,7 @@ public final class UsageStatsManager { android.Manifest.permission.SUSPEND_APPS, android.Manifest.permission.OBSERVE_APP_USAGE}) public void registerAppUsageLimitObserver(int observerId, @NonNull String[] observedEntities, - long timeLimit, @NonNull TimeUnit timeUnit, PendingIntent callbackIntent) { + long timeLimit, @NonNull TimeUnit timeUnit, @Nullable PendingIntent callbackIntent) { try { mService.registerAppUsageLimitObserver(observerId, observedEntities, timeUnit.toMillis(timeLimit), callbackIntent, mContext.getOpPackageName()); diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index 3e9dd2882600..85f0e2342412 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -30,7 +30,6 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.graphics.Color; import android.graphics.Rect; -import android.os.Build; import android.os.Bundle; import android.os.CancellationSignal; import android.os.Parcelable; @@ -178,40 +177,29 @@ public class AppWidgetHostView extends FrameLayout { */ public static Rect getDefaultPaddingForWidget(Context context, ComponentName component, Rect padding) { - ApplicationInfo appInfo = null; - try { - appInfo = context.getPackageManager().getApplicationInfo(component.getPackageName(), 0); - } catch (NameNotFoundException e) { - // if we can't find the package, ignore - } - return getDefaultPaddingForWidget(context, appInfo, padding); + return getDefaultPaddingForWidget(context, padding); } - @UnsupportedAppUsage - private static Rect getDefaultPaddingForWidget(Context context, ApplicationInfo appInfo, - Rect padding) { + private static Rect getDefaultPaddingForWidget(Context context, Rect padding) { if (padding == null) { padding = new Rect(0, 0, 0, 0); } else { padding.set(0, 0, 0, 0); } - if (appInfo != null && appInfo.targetSdkVersion >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { - Resources r = context.getResources(); - padding.left = r.getDimensionPixelSize(com.android.internal. - R.dimen.default_app_widget_padding_left); - padding.right = r.getDimensionPixelSize(com.android.internal. - R.dimen.default_app_widget_padding_right); - padding.top = r.getDimensionPixelSize(com.android.internal. - R.dimen.default_app_widget_padding_top); - padding.bottom = r.getDimensionPixelSize(com.android.internal. - R.dimen.default_app_widget_padding_bottom); - } + Resources r = context.getResources(); + padding.left = r.getDimensionPixelSize( + com.android.internal.R.dimen.default_app_widget_padding_left); + padding.right = r.getDimensionPixelSize( + com.android.internal.R.dimen.default_app_widget_padding_right); + padding.top = r.getDimensionPixelSize( + com.android.internal.R.dimen.default_app_widget_padding_top); + padding.bottom = r.getDimensionPixelSize( + com.android.internal.R.dimen.default_app_widget_padding_bottom); return padding; } private Rect getDefaultPadding() { - return getDefaultPaddingForWidget(mContext, - mInfo == null ? null : mInfo.providerInfo.applicationInfo, null); + return getDefaultPaddingForWidget(mContext, null); } public int getAppWidgetId() { diff --git a/core/java/android/content/AutofillOptions.aidl b/core/java/android/content/AutofillOptions.aidl new file mode 100644 index 000000000000..7e4fed20957f --- /dev/null +++ b/core/java/android/content/AutofillOptions.aidl @@ -0,0 +1,19 @@ +/* +** Copyright 2019, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.content; + +parcelable AutofillOptions; diff --git a/core/java/android/content/AutofillOptions.java b/core/java/android/content/AutofillOptions.java new file mode 100644 index 000000000000..fd7e52aaebcd --- /dev/null +++ b/core/java/android/content/AutofillOptions.java @@ -0,0 +1,130 @@ +/* + * 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.content; + +import android.annotation.NonNull; +import android.annotation.TestApi; +import android.app.ActivityThread; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; +import android.view.autofill.AutofillManager; + +import java.io.PrintWriter; + +/** + * Autofill options for a given package. + * + * <p>This object is created by the Autofill System Service and passed back to the app when the + * application is created. + * + * @hide + */ +@TestApi +public final class AutofillOptions implements Parcelable { + + private static final String TAG = AutofillOptions.class.getSimpleName(); + + /** + * Logging level for {@code logcat} statements. + */ + public final int loggingLevel; + + /** + * Whether compatibility mode is enabled for the package. + */ + public final boolean compatModeEnabled; + + /** + * Whether package is whitelisted for augmented autofill. + */ + public boolean augmentedEnabled; + // TODO(b/123100824): add (optional) list of activities + + public AutofillOptions(int loggingLevel, boolean compatModeEnabled) { + this.loggingLevel = loggingLevel; + this.compatModeEnabled = compatModeEnabled; + } + + /** + * @hide + */ + @TestApi + public static AutofillOptions forWhitelistingItself() { + final ActivityThread at = ActivityThread.currentActivityThread(); + if (at == null) { + throw new IllegalStateException("No ActivityThread"); + } + + final String packageName = at.getApplication().getPackageName(); + + if (!"android.autofillservice.cts".equals(packageName)) { + Log.e(TAG, "forWhitelistingItself(): called by " + packageName); + throw new SecurityException("Thou shall not pass!"); + } + + final AutofillOptions options = new AutofillOptions( + AutofillManager.FLAG_ADD_CLIENT_VERBOSE, /* compatModeAllowed= */ true); + options.augmentedEnabled = true; + // Always log, as it's used by test only + Log.i(TAG, "forWhitelistingItself(" + packageName + "): " + options); + + return options; + } + + @Override + public String toString() { + return "AutofillOptions [loggingLevel=" + loggingLevel + ", compatMode=" + + compatModeEnabled + ", augmentedEnabled=" + augmentedEnabled + "]"; + } + + /** @hide */ + public void dumpShort(@NonNull PrintWriter pw) { + pw.print("logLvl="); pw.print(loggingLevel); + pw.print(", compatMode="); pw.print(compatModeEnabled); + pw.print(", augmented="); pw.print(augmentedEnabled); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeInt(loggingLevel); + parcel.writeBoolean(compatModeEnabled); + parcel.writeBoolean(augmentedEnabled); + } + + public static final Parcelable.Creator<AutofillOptions> CREATOR = + new Parcelable.Creator<AutofillOptions>() { + + @Override + public AutofillOptions createFromParcel(Parcel parcel) { + final int loggingLevel = parcel.readInt(); + final boolean compatMode = parcel.readBoolean(); + final AutofillOptions options = new AutofillOptions(loggingLevel, compatMode); + options.augmentedEnabled = parcel.readBoolean(); + return options; + } + + @Override + public AutofillOptions[] newArray(int size) { + return new AutofillOptions[size]; + } + }; +} diff --git a/core/java/android/content/ContentCaptureOptions.aidl b/core/java/android/content/ContentCaptureOptions.aidl new file mode 100644 index 000000000000..82ffac4205d7 --- /dev/null +++ b/core/java/android/content/ContentCaptureOptions.aidl @@ -0,0 +1,19 @@ +/* +** Copyright 2019, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.content; + +parcelable ContentCaptureOptions; diff --git a/core/java/android/content/ContentCaptureOptions.java b/core/java/android/content/ContentCaptureOptions.java new file mode 100644 index 000000000000..2fe9f14c9dc9 --- /dev/null +++ b/core/java/android/content/ContentCaptureOptions.java @@ -0,0 +1,176 @@ +/* + * 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.content; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.TestApi; +import android.app.ActivityThread; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.ArraySet; +import android.util.Log; +import android.view.contentcapture.ContentCaptureManager; + +import java.io.PrintWriter; + +/** + * Content capture options for a given package. + * + * <p>This object is created by the Content Capture System Service and passed back to the app when + * the application is created. + * + * @hide + */ +@TestApi +public final class ContentCaptureOptions implements Parcelable { + + private static final String TAG = ContentCaptureOptions.class.getSimpleName(); + + /** + * Logging level for {@code logcat} statements. + */ + public final int loggingLevel; + + /** + * Maximum number of events that are buffered before sent to the app. + */ + public final int maxBufferSize; + + /** + * Frequency the buffer is flushed if idle. + */ + public final int idleFlushingFrequencyMs; + + /** + * Frequency the buffer is flushed if last event is a text change. + */ + public final int textChangeFlushingFrequencyMs; + + /** + * Size of events that are logging on {@code dump}. + */ + public final int logHistorySize; + + /** + * List of activities explicitly whitelisted for content capture (or {@code null} if whitelisted + * for all acitivites in the package). + */ + @Nullable + public final ArraySet<ComponentName> whitelistedComponents; + + public ContentCaptureOptions(int loggingLevel, int maxBufferSize, int idleFlushingFrequencyMs, + int textChangeFlushingFrequencyMs, int logHistorySize, + @Nullable ArraySet<ComponentName> whitelistedComponents) { + this.loggingLevel = loggingLevel; + this.maxBufferSize = maxBufferSize; + this.idleFlushingFrequencyMs = idleFlushingFrequencyMs; + this.textChangeFlushingFrequencyMs = textChangeFlushingFrequencyMs; + this.logHistorySize = logHistorySize; + this.whitelistedComponents = whitelistedComponents; + } + + /** + * @hide + */ + @TestApi + public static ContentCaptureOptions forWhitelistingItself() { + final ActivityThread at = ActivityThread.currentActivityThread(); + if (at == null) { + throw new IllegalStateException("No ActivityThread"); + } + + final String packageName = at.getApplication().getPackageName(); + + if (!"android.contentcaptureservice.cts".equals(packageName)) { + Log.e(TAG, "forWhitelistingItself(): called by " + packageName); + throw new SecurityException("Thou shall not pass!"); + } + + final ContentCaptureOptions options = new ContentCaptureOptions( + ContentCaptureManager.LOGGING_LEVEL_VERBOSE, + ContentCaptureManager.DEFAULT_MAX_BUFFER_SIZE, + ContentCaptureManager.DEFAULT_IDLE_FLUSHING_FREQUENCY_MS, + ContentCaptureManager.DEFAULT_TEXT_CHANGE_FLUSHING_FREQUENCY_MS, + ContentCaptureManager.DEFAULT_LOG_HISTORY_SIZE, + /* whitelistedComponents= */ null); + // Always log, as it's used by test only + Log.i(TAG, "forWhitelistingItself(" + packageName + "): " + options); + + return options; + } + + @Override + public String toString() { + return "ContentCaptureOptions [loggingLevel=" + loggingLevel + ", maxBufferSize=" + + maxBufferSize + ", idleFlushingFrequencyMs=" + idleFlushingFrequencyMs + + ", textChangeFlushingFrequencyMs=" + textChangeFlushingFrequencyMs + + ", logHistorySize=" + logHistorySize + ", whitelistedComponents=" + + whitelistedComponents + "]"; + } + + /** @hide */ + public void dumpShort(@NonNull PrintWriter pw) { + pw.print("logLvl="); pw.print(loggingLevel); + pw.print(", bufferSize="); pw.print(maxBufferSize); + pw.print(", idle="); pw.print(idleFlushingFrequencyMs); + pw.print(", textIdle="); pw.print(textChangeFlushingFrequencyMs); + pw.print(", logSize="); pw.print(logHistorySize); + if (whitelistedComponents != null) { + pw.print(", whitelisted="); pw.print(whitelistedComponents); + } + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeInt(loggingLevel); + parcel.writeInt(maxBufferSize); + parcel.writeInt(idleFlushingFrequencyMs); + parcel.writeInt(textChangeFlushingFrequencyMs); + parcel.writeInt(logHistorySize); + parcel.writeArraySet(whitelistedComponents); + } + + public static final Parcelable.Creator<ContentCaptureOptions> CREATOR = + new Parcelable.Creator<ContentCaptureOptions>() { + + @Override + public ContentCaptureOptions createFromParcel(Parcel parcel) { + final int loggingLevel = parcel.readInt(); + final int maxBufferSize = parcel.readInt(); + final int idleFlushingFrequencyMs = parcel.readInt(); + final int textChangeFlushingFrequencyMs = parcel.readInt(); + final int logHistorySize = parcel.readInt(); + @SuppressWarnings("unchecked") + final ArraySet<ComponentName> whitelistedComponents = + (ArraySet<ComponentName>) parcel.readArraySet(null); + return new ContentCaptureOptions(loggingLevel, maxBufferSize, + idleFlushingFrequencyMs, textChangeFlushingFrequencyMs, logHistorySize, + whitelistedComponents); + } + + @Override + public ContentCaptureOptions[] newArray(int size) { + return new ContentCaptureOptions[size]; + } + + }; +} diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 0e11d4e12f3d..1e4b1e7b45c6 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -42,6 +42,7 @@ import android.graphics.ImageDecoder.ImageInfo; import android.graphics.ImageDecoder.Source; import android.graphics.Point; import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; import android.net.Uri; import android.os.Bundle; import android.os.CancellationSignal; @@ -3158,6 +3159,7 @@ public abstract class ContentResolver implements ContentInterface { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.CACHE_CONTENT) public void putCache(@NonNull Uri key, @Nullable Bundle value) { try { getContentService().putCache(mContext.getPackageName(), key, value, @@ -3177,6 +3179,7 @@ public abstract class ContentResolver implements ContentInterface { * @hide */ @SystemApi + @RequiresPermission(android.Manifest.permission.CACHE_CONTENT) public @Nullable Bundle getCache(@NonNull Uri key) { try { final Bundle bundle = getContentService().getCache(mContext.getPackageName(), key, @@ -3365,16 +3368,68 @@ public abstract class ContentResolver implements ContentInterface { return mContext.getUserId(); } + /** {@hide} */ + @Deprecated + public Drawable getTypeDrawable(String mimeType) { + return getTypeInfo(mimeType).getIcon().loadDrawable(mContext); + } + /** - * Get the system drawable of the mime type. + * Return a detailed description of the given MIME type, including an icon + * and label that describe the type. * - * @param mimeType the requested mime type - * @return the matched drawable - * @hide + * @param mimeType Valid, concrete MIME type. */ - @SystemApi - public Drawable getTypeDrawable(String mimeType) { - return MimeIconUtils.loadMimeIcon(mContext, mimeType); + public final @NonNull TypeInfo getTypeInfo(@NonNull String mimeType) { + Objects.requireNonNull(mimeType); + return MimeIconUtils.getTypeInfo(mimeType); + } + + /** + * Detailed description of a specific MIME type, including an icon and label + * that describe the type. + */ + public static final class TypeInfo { + private final Icon mIcon; + private final CharSequence mLabel; + private final CharSequence mContentDescription; + + /** {@hide} */ + public TypeInfo(@NonNull Icon icon, @NonNull CharSequence label, + @NonNull CharSequence contentDescription) { + mIcon = Objects.requireNonNull(icon); + mLabel = Objects.requireNonNull(label); + mContentDescription = Objects.requireNonNull(contentDescription); + } + + /** + * Return a visual representation of this MIME type. This can be styled + * using {@link Icon#setTint(int)} to match surrounding UI. + * + * @see Icon#loadDrawable(Context) + * @see android.widget.ImageView#setImageDrawable(Drawable) + */ + public @NonNull Icon getIcon() { + return mIcon; + } + + /** + * Return a textual representation of this MIME type. + * + * @see android.widget.TextView#setText(CharSequence) + */ + public @NonNull CharSequence getLabel() { + return mLabel; + } + + /** + * Return a content description for this MIME type. + * + * @see android.view.View#setContentDescription(CharSequence) + */ + public @NonNull CharSequence getContentDescription() { + return mContentDescription; + } } /** diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 25bfba26256f..29added9aa51 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -5241,9 +5241,10 @@ public abstract class Context { public abstract DisplayAdjustments getDisplayAdjustments(int displayId); /** + * @return Returns the {@link Display} object this context is associated with. * @hide */ - @UnsupportedAppUsage + @TestApi public abstract Display getDisplay(); /** @@ -5340,35 +5341,42 @@ public abstract class Context { /** * @hide */ - public boolean isAutofillCompatibilityEnabled() { - return false; + public final boolean isAutofillCompatibilityEnabled() { + final AutofillOptions options = getAutofillOptions(); + return options != null && options.compatModeEnabled; + } + + /** + * @hide + */ + @Nullable + public AutofillOptions getAutofillOptions() { + return null; } /** * @hide */ @TestApi - public void setAutofillCompatibilityEnabled( - @SuppressWarnings("unused") boolean autofillCompatEnabled) { + public void setAutofillOptions(@SuppressWarnings("unused") @Nullable AutofillOptions options) { } /** - * Checks whether this context supports content capture. + * Gets the Content Capture options for this context, or {@code null} if it's not whitelisted. * * @hide */ - // NOTE: for now we just need to check if it's supported so we can optimize calls that can be - // skipped when it isn't. Eventually, we might need a full - // ContentCaptureManager.ContentCaptureClient interface (as it's done with AutofillClient). - // - public boolean isContentCaptureSupported() { - return false; + @Nullable + public ContentCaptureOptions getContentCaptureOptions() { + return null; } /** * @hide */ - public void setContentCaptureSupported(@SuppressWarnings("unused") boolean supported) { + @TestApi + public void setContentCaptureOptions( + @SuppressWarnings("unused") @Nullable ContentCaptureOptions options) { } /** diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index 26ed3b736f80..40559d31d631 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -919,11 +919,9 @@ public class ContextWrapper extends Context { return mBase.getDisplayAdjustments(displayId); } - /** - * @hide - */ + /** @hide */ + @TestApi @Override - @UnsupportedAppUsage public Display getDisplay() { return mBase.getDisplay(); } @@ -1031,22 +1029,17 @@ public class ContextWrapper extends Context { mBase.setAutofillClient(client); } - /** - * @hide - */ + /** @hide */ @Override - public boolean isAutofillCompatibilityEnabled() { - return mBase != null && mBase.isAutofillCompatibilityEnabled(); + public AutofillOptions getAutofillOptions() { + return mBase == null ? null : mBase.getAutofillOptions(); } - /** - * @hide - */ - @TestApi + /** @hide */ @Override - public void setAutofillCompatibilityEnabled(boolean autofillCompatEnabled) { + public void setAutofillOptions(AutofillOptions options) { if (mBase != null) { - mBase.setAutofillCompatibilityEnabled(autofillCompatEnabled); + mBase.setAutofillOptions(options); } } @@ -1054,15 +1047,18 @@ public class ContextWrapper extends Context { * @hide */ @Override - public boolean isContentCaptureSupported() { - return mBase.isContentCaptureSupported(); + public ContentCaptureOptions getContentCaptureOptions() { + return mBase == null ? null : mBase.getContentCaptureOptions(); } /** * @hide */ + @TestApi @Override - public void setContentCaptureSupported(boolean supported) { - mBase.setContentCaptureSupported(supported); + public void setContentCaptureOptions(ContentCaptureOptions options) { + if (mBase != null) { + mBase.setContentCaptureOptions(options); + } } } diff --git a/core/java/android/content/pm/AndroidTestBaseUpdater.java b/core/java/android/content/pm/AndroidTestBaseUpdater.java index 2aaac0280a0e..6a1778cfd3f6 100644 --- a/core/java/android/content/pm/AndroidTestBaseUpdater.java +++ b/core/java/android/content/pm/AndroidTestBaseUpdater.java @@ -19,11 +19,12 @@ import static android.content.pm.SharedLibraryNames.ANDROID_TEST_BASE; import static android.content.pm.SharedLibraryNames.ANDROID_TEST_RUNNER; import android.content.pm.PackageParser.Package; +import android.os.Build; import com.android.internal.annotations.VisibleForTesting; /** - * Updates a package to ensure that if it targets < P that the android.test.base library is + * Updates a package to ensure that if it targets <= P that the android.test.base library is * included by default. * * <p>This is separated out so that it can be conditionally included at build time depending on @@ -37,12 +38,17 @@ import com.android.internal.annotations.VisibleForTesting; @VisibleForTesting public class AndroidTestBaseUpdater extends PackageSharedLibraryUpdater { + private static boolean apkTargetsApiLevelLessThanOrEqualToP(Package pkg) { + int targetSdkVersion = pkg.applicationInfo.targetSdkVersion; + return targetSdkVersion <= Build.VERSION_CODES.P; + } + @Override public void updatePackage(Package pkg) { - // Packages targeted at <= O_MR1 expect the classes in the android.test.base library + // Packages targeted at <= P expect the classes in the android.test.base library // to be accessible so this maintains backward compatibility by adding the // android.test.base library to those packages. - if (apkTargetsApiLevelLessThanOrEqualToOMR1(pkg)) { + if (apkTargetsApiLevelLessThanOrEqualToP(pkg)) { prefixRequiredLibrary(pkg, ANDROID_TEST_BASE); } else { // If a package already depends on android.test.runner then add a dependency on diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 6c6fcb2ea558..dad1a1a22fb6 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -662,6 +662,14 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { */ public static final int PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE = 1 << 26; + /** + * Value for {@link #privateFlags}: true if the application allows its audio playback + * to be captured by other apps. + * + * @hide + */ + public static final int PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE = 1 << 27; + /** @hide */ @IntDef(flag = true, prefix = { "PRIVATE_FLAG_" }, value = { PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE, @@ -688,7 +696,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { PRIVATE_FLAG_VENDOR, PRIVATE_FLAG_VIRTUAL_PRELOAD, PRIVATE_FLAG_HAS_FRAGILE_USER_DATA, - PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE + PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE, + PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE }) @Retention(RetentionPolicy.SOURCE) public @interface ApplicationInfoPrivateFlags {} @@ -1342,6 +1351,8 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { } pw.println(prefix + "HiddenApiEnforcementPolicy=" + getHiddenApiEnforcementPolicy()); pw.println(prefix + "usesNonSdkApi=" + usesNonSdkApi()); + pw.println(prefix + "allowsPlaybackCapture=" + + (isAudioPlaybackCaptureAllowed() ? "true" : "false")); } super.dumpBack(pw, prefix); } @@ -1790,6 +1801,17 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { return (privateFlags & PRIVATE_FLAG_HAS_FRAGILE_USER_DATA) != 0; } + /** + * Whether an app allows its playback audio to be captured by other apps. + * + * @return {@code true} if the app indicates that its audio can be captured by other apps. + * + * @hide + */ + public boolean isAudioPlaybackCaptureAllowed() { + return (privateFlags & PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE) != 0; + } + private boolean isAllowedToUseHiddenApis() { if (isSignedWithPlatformKey()) { return true; diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl index a251c0036a78..0cf83fd4655b 100644 --- a/core/java/android/content/pm/IPackageInstaller.aidl +++ b/core/java/android/content/pm/IPackageInstaller.aidl @@ -50,5 +50,8 @@ interface IPackageInstaller { void uninstall(in VersionedPackage versionedPackage, String callerPackageName, int flags, in IntentSender statusReceiver, int userId); + void installExistingPackage(String packageName, int installFlags, int installReason, + in IntentSender statusReceiver, int userId); + void setPermissionsResult(int sessionId, boolean accepted); } diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 14e77258c65e..dcbb4ac2d5f3 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -111,7 +111,7 @@ interface IPackageManager { int getPermissionFlags(String permissionName, String packageName, int userId); void updatePermissionFlags(String permissionName, String packageName, int flagMask, - int flagValues, int userId); + int flagValues, boolean checkAdjustPolicyFlagPermission, int userId); void updatePermissionFlagsForAllApps(int flagMask, int flagValues, int userId); diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index b0d16cdace21..bf556ba0b668 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -514,7 +514,8 @@ public class LauncherApps { /** * Retrieves a list of launchable activities that match {@link Intent#ACTION_MAIN} and - * {@link Intent#CATEGORY_LAUNCHER}, for a specified user. + * {@link Intent#CATEGORY_LAUNCHER}, for a specified user. Result may include + * synthesized activities like app details Activity injected by system. * * @param packageName The specific package to query. If null, it checks all installed packages * in the profile. @@ -794,7 +795,8 @@ public class LauncherApps { * @throws SecurityException when the caller is not the active launcher. */ @Nullable - public LauncherApps.AppUsageLimit getAppUsageLimit(String packageName, UserHandle user) { + public LauncherApps.AppUsageLimit getAppUsageLimit(@NonNull String packageName, + @NonNull UserHandle user) { try { return mService.getAppUsageLimit(mContext.getPackageName(), packageName, user); } catch (RemoteException re) { diff --git a/core/java/android/content/pm/OrgApacheHttpLegacyUpdater.java b/core/java/android/content/pm/OrgApacheHttpLegacyUpdater.java index 7790067b03de..707443b19679 100644 --- a/core/java/android/content/pm/OrgApacheHttpLegacyUpdater.java +++ b/core/java/android/content/pm/OrgApacheHttpLegacyUpdater.java @@ -18,6 +18,7 @@ package android.content.pm; import static android.content.pm.SharedLibraryNames.ORG_APACHE_HTTP_LEGACY; import android.content.pm.PackageParser.Package; +import android.os.Build; import com.android.internal.annotations.VisibleForTesting; @@ -30,6 +31,11 @@ import com.android.internal.annotations.VisibleForTesting; @VisibleForTesting public class OrgApacheHttpLegacyUpdater extends PackageSharedLibraryUpdater { + private static boolean apkTargetsApiLevelLessThanOrEqualToOMR1(Package pkg) { + int targetSdkVersion = pkg.applicationInfo.targetSdkVersion; + return targetSdkVersion < Build.VERSION_CODES.P; + } + @Override public void updatePackage(Package pkg) { // Packages targeted at <= O_MR1 expect the classes in the org.apache.http.legacy library diff --git a/core/java/android/content/pm/PackageBackwardCompatibility.java b/core/java/android/content/pm/PackageBackwardCompatibility.java index b19196a9b636..4331bd4ac4d4 100644 --- a/core/java/android/content/pm/PackageBackwardCompatibility.java +++ b/core/java/android/content/pm/PackageBackwardCompatibility.java @@ -116,7 +116,7 @@ public class PackageBackwardCompatibility extends PackageSharedLibraryUpdater { private final PackageSharedLibraryUpdater[] mPackageUpdaters; - public PackageBackwardCompatibility( + private PackageBackwardCompatibility( boolean bootClassPathContainsATB, PackageSharedLibraryUpdater[] packageUpdaters) { this.mBootClassPathContainsATB = bootClassPathContainsATB; this.mPackageUpdaters = packageUpdaters; diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 80954731bffb..0304f19cc5b4 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -590,6 +590,30 @@ public class PackageInstaller { } } + /** + * Install the given package, which already exists on the device, for the user for which this + * installer was created. + * + * @param packageName The package to install. + * @param installReason Reason for install. + * @param statusReceiver Where to deliver the result. + */ + @RequiresPermission(allOf = { + Manifest.permission.INSTALL_PACKAGES, + Manifest.permission.INSTALL_EXISTING_PACKAGES}) + public void installExistingPackage(@NonNull String packageName, + @InstallReason int installReason, + @Nullable IntentSender statusReceiver) { + Preconditions.checkNotNull(packageName, "packageName cannot be null"); + try { + mInstaller.installExistingPackage(packageName, 0, installReason, statusReceiver, + mUserId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** {@hide} */ @SystemApi @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) @@ -1545,7 +1569,11 @@ public class PackageInstaller { /** * Set this session to be installing an APEX package. + * + * {@hide} */ + @SystemApi + @RequiresPermission(Manifest.permission.INSTALL_PACKAGES) public void setInstallAsApex() { installFlags |= PackageManager.INSTALL_APEX; } @@ -1715,11 +1743,11 @@ public class PackageInstaller { public int[] childSessionIds = NO_SESSIONS; /** {@hide} */ - public boolean isSessionApplied; + public boolean isStagedSessionApplied; /** {@hide} */ - public boolean isSessionReady; + public boolean isStagedSessionReady; /** {@hide} */ - public boolean isSessionFailed; + public boolean isStagedSessionFailed; private int mStagedSessionErrorCode; private String mStagedSessionErrorMessage; @@ -1758,9 +1786,9 @@ public class PackageInstaller { if (childSessionIds == null) { childSessionIds = NO_SESSIONS; } - isSessionApplied = source.readBoolean(); - isSessionReady = source.readBoolean(); - isSessionFailed = source.readBoolean(); + isStagedSessionApplied = source.readBoolean(); + isStagedSessionReady = source.readBoolean(); + isStagedSessionFailed = source.readBoolean(); mStagedSessionErrorCode = source.readInt(); mStagedSessionErrorMessage = source.readString(); } @@ -2054,36 +2082,46 @@ public class PackageInstaller { return childSessionIds; } + private void checkSessionIsStaged() { + if (!isStaged) { + throw new IllegalStateException("Session is not marked as staged."); + } + } + /** * Whether the staged session has been applied successfully, meaning that all of its * packages have been activated and no further action is required. * Only meaningful if {@code isStaged} is true. */ - public boolean isSessionApplied() { - return isSessionApplied; + public boolean isStagedSessionApplied() { + checkSessionIsStaged(); + return isStagedSessionApplied; } /** * Whether the staged session is ready to be applied at next reboot. Only meaningful if * {@code isStaged} is true. */ - public boolean isSessionReady() { - return isSessionReady; + public boolean isStagedSessionReady() { + checkSessionIsStaged(); + return isStagedSessionReady; } /** * Whether something went wrong and the staged session is declared as failed, meaning that * it will be ignored at next reboot. Only meaningful if {@code isStaged} is true. */ - public boolean isSessionFailed() { - return isSessionFailed; + public boolean isStagedSessionFailed() { + checkSessionIsStaged(); + return isStagedSessionFailed; } /** * If something went wrong with a staged session, clients can check this error code to * understand which kind of failure happened. Only meaningful if {@code isStaged} is true. */ - public int getStagedSessionErrorCode() { + public @StagedSessionErrorCode int getStagedSessionErrorCode() { + checkSessionIsStaged(); return mStagedSessionErrorCode; } @@ -2092,6 +2130,7 @@ public class PackageInstaller { * empty string if no error was encountered. */ public String getStagedSessionErrorMessage() { + checkSessionIsStaged(); return mStagedSessionErrorMessage; } @@ -2134,9 +2173,9 @@ public class PackageInstaller { dest.writeBoolean(isStaged); dest.writeInt(parentSessionId); dest.writeIntArray(childSessionIds); - dest.writeBoolean(isSessionApplied); - dest.writeBoolean(isSessionReady); - dest.writeBoolean(isSessionFailed); + dest.writeBoolean(isStagedSessionApplied); + dest.writeBoolean(isStagedSessionReady); + dest.writeBoolean(isStagedSessionFailed); dest.writeInt(mStagedSessionErrorCode); dest.writeString(mStagedSessionErrorMessage); } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 00419212544a..a5464c2137af 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -33,6 +33,7 @@ import android.annotation.UnsupportedAppUsage; import android.annotation.UserIdInt; import android.annotation.XmlRes; import android.app.ActivityManager; +import android.app.AppDetailsActivity; import android.app.PackageDeleteObserver; import android.app.PackageInstallObserver; import android.app.admin.DevicePolicyManager; @@ -2564,6 +2565,14 @@ public abstract class PackageManager { public static final String FEATURE_PC = "android.hardware.type.pc"; /** + * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: This is a foldable device. Properties such as + * the display size may change in response to being folded. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_FOLDABLE = "android.hardware.type.foldable"; + + /** * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: * The device supports printing. */ @@ -3030,6 +3039,13 @@ public abstract class PackageManager { public static final int MASK_PERMISSION_FLAGS = 0xFF; /** + * Injected activity in app that forwards user to setting activity of that app. + * + * @hide + */ + public static final String APP_DETAILS_ACTIVITY_CLASS_NAME = AppDetailsActivity.class.getName(); + + /** * This is a library that contains components apps can invoke. For * example, a services for apps to bind to, or standard chooser UI, * etc. This library is versioned and backwards compatible. Clients @@ -5114,7 +5130,10 @@ public abstract class PackageManager { * If there is already an application with the given package name installed * on the system for other users, also install it for the calling user. * @hide + * + * @deprecated use {@link PackageInstaller#installExistingPackage()} instead. */ + @Deprecated @SystemApi public abstract int installExistingPackage(String packageName) throws NameNotFoundException; @@ -5122,7 +5141,10 @@ public abstract class PackageManager { * If there is already an application with the given package name installed * on the system for other users, also install it for the calling user. * @hide + * + * @deprecated use {@link PackageInstaller#installExistingPackage()} instead. */ + @Deprecated @SystemApi public abstract int installExistingPackage(String packageName, @InstallReason int installReason) throws NameNotFoundException; @@ -5131,7 +5153,10 @@ public abstract class PackageManager { * If there is already an application with the given package name installed * on the system for other users, also install it for the specified user. * @hide + * + * @deprecated use {@link PackageInstaller#installExistingPackage()} instead. */ + @Deprecated @RequiresPermission(anyOf = { Manifest.permission.INSTALL_EXISTING_PACKAGES, Manifest.permission.INSTALL_PACKAGES, @@ -5792,6 +5817,37 @@ public abstract class PackageManager { @NonNull ComponentName componentName); /** + * Set the enabled setting for a package app settings activity. + * + * @param packageName The package name of the app + * @param enabled The new enabled state for app details activity + * + * @hide + */ + @RequiresPermission(value = android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE, + conditional = true) + @SystemApi + public void setAppDetailsActivityEnabled(@NonNull String packageName, boolean enabled) { + throw new UnsupportedOperationException( + "setAppDetailsActivityEnabled not implemented"); + } + + + /** + * Return the enabled setting for a package app settings activity. + * + * @param packageName The package name of the app + * @return Returns the current enabled state for app settings activity. + * + * @hide + */ + @SystemApi + public boolean getAppDetailsActivityEnabled(@NonNull String packageName) { + throw new UnsupportedOperationException( + "getAppDetailsActivityEnabled not implemented"); + } + + /** * Set the enabled setting for an application * This setting will override any enabled state which may have been set by the application in * its manifest. It also overrides the enabled state set in the manifest for any of the diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 0f67262bf901..f899800f4e93 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -48,7 +48,6 @@ import android.annotation.Nullable; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.app.ActivityTaskManager; -import android.app.AppDetailsActivity; import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; @@ -725,6 +724,9 @@ public class PackageParser { for (int i = 0; i < N; i++) { final Activity a = p.activities.get(i); if (state.isMatch(a.info, flags)) { + if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(a.className)) { + continue; + } res[num++] = generateActivityInfo(a, flags, state, userId); } } @@ -3754,6 +3756,12 @@ public class PackageParser { ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE; } + if (sa.getBoolean( + R.styleable.AndroidManifestApplication_allowAudioPlaybackCapture, + owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q)) { + ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE; + } + ai.maxAspectRatio = sa.getFloat(R.styleable.AndroidManifestApplication_maxAspectRatio, 0); ai.minAspectRatio = sa.getFloat(R.styleable.AndroidManifestApplication_minAspectRatio, 0); @@ -4311,7 +4319,7 @@ public class PackageParser { } else { String outInfoName = buildClassName(owner.applicationInfo.packageName, name, outError); - if (AppDetailsActivity.class.getName().equals(outInfoName)) { + if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(outInfoName)) { outError[0] = tag + " invalid android:name"; return false; } @@ -4364,13 +4372,14 @@ public class PackageParser { boolean hardwareAccelerated) { // Build custom App Details activity info instead of parsing it from xml - Activity a = new Activity(owner, AppDetailsActivity.class.getName(), new ActivityInfo()); + Activity a = new Activity(owner, PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME, + new ActivityInfo()); a.owner = owner; a.setPackageName(owner.packageName); a.info.theme = android.R.style.Theme_NoDisplay; a.info.exported = true; - a.info.name = AppDetailsActivity.class.getName(); + a.info.name = PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME; a.info.processName = owner.applicationInfo.processName; a.info.uiOptions = a.info.applicationInfo.uiOptions; a.info.taskAffinity = buildTaskAffinityName(owner.packageName, owner.packageName, diff --git a/core/java/android/content/pm/PackageSharedLibraryUpdater.java b/core/java/android/content/pm/PackageSharedLibraryUpdater.java index b14b321a2481..1565d9ce77d4 100644 --- a/core/java/android/content/pm/PackageSharedLibraryUpdater.java +++ b/core/java/android/content/pm/PackageSharedLibraryUpdater.java @@ -17,7 +17,6 @@ package android.content.pm; import android.annotation.NonNull; import android.annotation.Nullable; -import android.os.Build; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; @@ -60,11 +59,6 @@ public abstract class PackageSharedLibraryUpdater { || ArrayUtils.contains(usesOptionalLibraries, apacheHttpLegacy); } - static boolean apkTargetsApiLevelLessThanOrEqualToOMR1(PackageParser.Package pkg) { - int targetSdkVersion = pkg.applicationInfo.targetSdkVersion; - return targetSdkVersion < Build.VERSION_CODES.P; - } - /** * Add an implicit dependency. * diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java index b1553250d638..e674b8b7fd3d 100644 --- a/core/java/android/content/pm/ShortcutInfo.java +++ b/core/java/android/content/pm/ShortcutInfo.java @@ -18,6 +18,7 @@ package android.content.pm; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.annotation.UserIdInt; @@ -1482,6 +1483,7 @@ public final class ShortcutInfo implements Parcelable { * @hide */ @Nullable + @SystemApi public Person[] getPersons() { return clonePersons(mPersons); } diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java index 849fd03eacb3..bd327b0be874 100644 --- a/core/java/android/content/pm/ShortcutManager.java +++ b/core/java/android/content/pm/ShortcutManager.java @@ -644,6 +644,7 @@ public class ShortcutManager { * @return True if the package has any share target definitions, False otherwise. * @hide */ + @SystemApi public boolean hasShareTargets(@NonNull String packageName) { try { return mService.hasShareTargets(mContext.getPackageName(), packageName, diff --git a/core/java/android/content/rollback/PackageRollbackInfo.java b/core/java/android/content/rollback/PackageRollbackInfo.java index 1d0ab5ad2679..7e5532c8e9bb 100644 --- a/core/java/android/content/rollback/PackageRollbackInfo.java +++ b/core/java/android/content/rollback/PackageRollbackInfo.java @@ -108,6 +108,11 @@ public final class PackageRollbackInfo implements Parcelable { } /** @hide */ + public void addPendingBackup(int userId) { + mPendingBackups.add(userId); + } + + /** @hide */ public IntArray getPendingBackups() { return mPendingBackups; } @@ -154,6 +159,19 @@ public final class PackageRollbackInfo implements Parcelable { } /** @hide */ + public void removePendingBackup(int userId) { + int idx = mPendingBackups.indexOf(userId); + if (idx != -1) { + mPendingBackups.remove(idx); + } + } + + /** @hide */ + public void removePendingRestoreInfo(int userId) { + removeRestoreInfo(getRestoreInfo(userId)); + } + + /** @hide */ public PackageRollbackInfo(VersionedPackage packageRolledBackFrom, VersionedPackage packageRolledBackTo, @NonNull IntArray pendingBackups, @NonNull ArrayList<RestoreInfo> pendingRestores, diff --git a/core/java/android/content/rollback/RollbackInfo.java b/core/java/android/content/rollback/RollbackInfo.java index 0cde6ba38099..9459ad366f76 100644 --- a/core/java/android/content/rollback/RollbackInfo.java +++ b/core/java/android/content/rollback/RollbackInfo.java @@ -17,12 +17,10 @@ package android.content.rollback; import android.annotation.SystemApi; -import android.content.pm.PackageInstaller; import android.content.pm.VersionedPackage; import android.os.Parcel; import android.os.Parcelable; -import java.util.Collections; import java.util.List; /** @@ -44,13 +42,7 @@ public final class RollbackInfo implements Parcelable { private final List<VersionedPackage> mCausePackages; private final boolean mIsStaged; - private final int mCommittedSessionId; - - /** @hide */ - public RollbackInfo(int rollbackId, List<PackageRollbackInfo> packages, boolean isStaged) { - this(rollbackId, packages, isStaged, Collections.emptyList(), - PackageInstaller.SessionInfo.INVALID_ID); - } + private int mCommittedSessionId; /** @hide */ public RollbackInfo(int rollbackId, List<PackageRollbackInfo> packages, boolean isStaged, @@ -101,6 +93,14 @@ public final class RollbackInfo implements Parcelable { } /** + * Sets the session ID for the committed rollback for staged rollbacks. + * @hide + */ + public void setCommittedSessionId(int sessionId) { + mCommittedSessionId = sessionId; + } + + /** * Gets the list of package versions that motivated this rollback. * As provided to {@link #commitRollback} when the rollback was committed. * This is only applicable for rollbacks that have been committed. diff --git a/core/java/android/database/BulkCursorNative.java b/core/java/android/database/BulkCursorNative.java index d3c11e785d7f..77a13cf80073 100644 --- a/core/java/android/database/BulkCursorNative.java +++ b/core/java/android/database/BulkCursorNative.java @@ -16,6 +16,7 @@ package android.database; +import android.annotation.UnsupportedAppUsage; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; @@ -138,6 +139,7 @@ public abstract class BulkCursorNative extends Binder implements IBulkCursor final class BulkCursorProxy implements IBulkCursor { + @UnsupportedAppUsage private IBinder mRemote; private Bundle mExtras; diff --git a/core/java/android/database/sqlite/SQLiteCompatibilityWalFlags.java b/core/java/android/database/sqlite/SQLiteCompatibilityWalFlags.java index 8ea1db25a9a7..a2690728d9bf 100644 --- a/core/java/android/database/sqlite/SQLiteCompatibilityWalFlags.java +++ b/core/java/android/database/sqlite/SQLiteCompatibilityWalFlags.java @@ -40,8 +40,7 @@ public class SQLiteCompatibilityWalFlags { private static final String TAG = "SQLiteCompatibilityWalFlags"; private static volatile boolean sInitialized; - private static volatile boolean sFlagsSet; - private static volatile boolean sCompatibilityWalSupported; + private static volatile boolean sLegacyCompatibilityWalEnabled; private static volatile String sWALSyncMode; private static volatile long sTruncateSize = -1; // This flag is used to avoid recursive initialization due to circular dependency on Settings @@ -54,18 +53,9 @@ public class SQLiteCompatibilityWalFlags { * @hide */ @VisibleForTesting - public static boolean areFlagsSet() { + public static boolean isLegacyCompatibilityWalEnabled() { initIfNeeded(); - return sFlagsSet; - } - - /** - * @hide - */ - @VisibleForTesting - public static boolean isCompatibilityWalSupported() { - initIfNeeded(); - return sCompatibilityWalSupported; + return sLegacyCompatibilityWalEnabled; } /** @@ -74,6 +64,14 @@ public class SQLiteCompatibilityWalFlags { @VisibleForTesting public static String getWALSyncMode() { initIfNeeded(); + // The configurable WAL sync mode should only ever be used if the legacy compatibility + // WAL is enabled. It should *not* have any effect if app developers explicitly turn on + // WAL for their database using setWriteAheadLoggingEnabled. Throwing an exception here + // adds an extra layer of checking that we never use it in the wrong place. + if (!sLegacyCompatibilityWalEnabled) { + throw new IllegalStateException("isLegacyCompatibilityWalEnabled() == false"); + } + return sWALSyncMode; } @@ -131,13 +129,12 @@ public class SQLiteCompatibilityWalFlags { sInitialized = true; return; } - sCompatibilityWalSupported = parser.getBoolean("compatibility_wal_supported", - SQLiteGlobal.isCompatibilityWalSupported()); + sLegacyCompatibilityWalEnabled = parser.getBoolean( + "legacy_compatibility_wal_enabled", false); sWALSyncMode = parser.getString("wal_syncmode", SQLiteGlobal.getWALSyncMode()); sTruncateSize = parser.getInt("truncate_size", -1); - Log.i(TAG, "Read compatibility WAL flags: compatibility_wal_supported=" - + sCompatibilityWalSupported + ", wal_syncmode=" + sWALSyncMode); - sFlagsSet = true; + Log.i(TAG, "Read compatibility WAL flags: legacy_compatibility_wal_enabled=" + + sLegacyCompatibilityWalEnabled + ", wal_syncmode=" + sWALSyncMode); sInitialized = true; } @@ -148,8 +145,7 @@ public class SQLiteCompatibilityWalFlags { @TestApi public static void reset() { sInitialized = false; - sFlagsSet = false; - sCompatibilityWalSupported = false; + sLegacyCompatibilityWalEnabled = false; sWALSyncMode = null; } } diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java index 3c8e236a7893..3844794da980 100644 --- a/core/java/android/database/sqlite/SQLiteConnection.java +++ b/core/java/android/database/sqlite/SQLiteConnection.java @@ -300,12 +300,13 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen (mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0; // Use compatibility WAL unless an app explicitly set journal/synchronous mode // or DISABLE_COMPATIBILITY_WAL flag is set - final boolean useCompatibilityWal = mConfiguration.useCompatibilityWal(); - if (walEnabled || useCompatibilityWal) { + final boolean isCompatibilityWalEnabled = + mConfiguration.isLegacyCompatibilityWalEnabled(); + if (walEnabled || isCompatibilityWalEnabled) { setJournalMode("WAL"); if (mConfiguration.syncMode != null) { setSyncMode(mConfiguration.syncMode); - } else if (useCompatibilityWal && SQLiteCompatibilityWalFlags.areFlagsSet()) { + } else if (isCompatibilityWalEnabled) { setSyncMode(SQLiteCompatibilityWalFlags.getWALSyncMode()); } else { setSyncMode(SQLiteGlobal.getWALSyncMode()); @@ -504,7 +505,7 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen != mConfiguration.foreignKeyConstraintsEnabled; boolean walModeChanged = ((configuration.openFlags ^ mConfiguration.openFlags) & (SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING - | SQLiteDatabase.DISABLE_COMPATIBILITY_WAL)) != 0; + | SQLiteDatabase.ENABLE_LEGACY_COMPATIBILITY_WAL)) != 0; boolean localeChanged = !configuration.locale.equals(mConfiguration.locale); // Update configuration parameters. diff --git a/core/java/android/database/sqlite/SQLiteConnectionPool.java b/core/java/android/database/sqlite/SQLiteConnectionPool.java index dbc176614daf..852f8f2a4204 100644 --- a/core/java/android/database/sqlite/SQLiteConnectionPool.java +++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java @@ -321,7 +321,7 @@ public final class SQLiteConnectionPool implements Closeable { // We should do in-place switching when transitioning from compatibility WAL // to rollback journal. Otherwise transient connection state will be lost boolean onlyCompatWalChanged = (mConfiguration.openFlags ^ configuration.openFlags) - == SQLiteDatabase.DISABLE_COMPATIBILITY_WAL; + == SQLiteDatabase.ENABLE_LEGACY_COMPATIBILITY_WAL; if (!onlyCompatWalChanged && mConfiguration.openFlags != configuration.openFlags) { // If we are changing open flags and WAL mode at the same time, then @@ -1113,19 +1113,18 @@ public final class SQLiteConnectionPool implements Closeable { if (directories != null) { directories.add(new File(mConfiguration.path).getParent()); } + boolean isCompatibilityWalEnabled = mConfiguration.isLegacyCompatibilityWalEnabled(); printer.println("Connection pool for " + mConfiguration.path + ":"); printer.println(" Open: " + mIsOpen); printer.println(" Max connections: " + mMaxConnectionPoolSize); printer.println(" Total execution time: " + mTotalExecutionTimeCounter); printer.println(" Configuration: openFlags=" + mConfiguration.openFlags - + ", useCompatibilityWal=" + mConfiguration.useCompatibilityWal() + + ", isLegacyCompatibilityWalEnabled=" + isCompatibilityWalEnabled + ", journalMode=" + TextUtils.emptyIfNull(mConfiguration.journalMode) + ", syncMode=" + TextUtils.emptyIfNull(mConfiguration.syncMode)); - if (SQLiteCompatibilityWalFlags.areFlagsSet()) { - printer.println(" Compatibility WAL settings: compatibility_wal_supported=" - + SQLiteCompatibilityWalFlags - .isCompatibilityWalSupported() + ", wal_syncmode=" + if (isCompatibilityWalEnabled) { + printer.println(" Compatibility WAL enabled: wal_syncmode=" + SQLiteCompatibilityWalFlags.getWALSyncMode()); } if (mConfiguration.isLookasideConfigSet()) { diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java index ae456afdc252..dffbd89c33d2 100644 --- a/core/java/android/database/sqlite/SQLiteDatabase.java +++ b/core/java/android/database/sqlite/SQLiteDatabase.java @@ -266,12 +266,17 @@ public final class SQLiteDatabase extends SQLiteClosable { */ public static final int ENABLE_WRITE_AHEAD_LOGGING = 0x20000000; + + // Note: The below value was only used on Android Pie. + // public static final int DISABLE_COMPATIBILITY_WAL = 0x40000000; + /** - * Open flag: Flag for {@link #openDatabase} to disable Compatibility WAL when opening database. + * Open flag: Flag for {@link #openDatabase} to enable the legacy Compatibility WAL when opening + * database. * * @hide */ - public static final int DISABLE_COMPATIBILITY_WAL = 0x40000000; + public static final int ENABLE_LEGACY_COMPATIBILITY_WAL = 0x80000000; /** * Absolute max value that can be set by {@link #setMaxSqlCacheSize(int)}. @@ -309,10 +314,8 @@ public final class SQLiteDatabase extends SQLiteClosable { mConfigurationLocked.idleConnectionTimeoutMs = effectiveTimeoutMs; mConfigurationLocked.journalMode = journalMode; mConfigurationLocked.syncMode = syncMode; - if (!SQLiteGlobal.isCompatibilityWalSupported() || ( - SQLiteCompatibilityWalFlags.areFlagsSet() && !SQLiteCompatibilityWalFlags - .isCompatibilityWalSupported())) { - mConfigurationLocked.openFlags |= DISABLE_COMPATIBILITY_WAL; + if (SQLiteCompatibilityWalFlags.isLegacyCompatibilityWalEnabled()) { + mConfigurationLocked.openFlags |= ENABLE_LEGACY_COMPATIBILITY_WAL; } } @@ -2123,15 +2126,18 @@ public final class SQLiteDatabase extends SQLiteClosable { throwIfNotOpenLocked(); final int oldFlags = mConfigurationLocked.openFlags; - final boolean walDisabled = (oldFlags & ENABLE_WRITE_AHEAD_LOGGING) == 0; - final boolean compatibilityWalDisabled = (oldFlags & DISABLE_COMPATIBILITY_WAL) != 0; - if (walDisabled && compatibilityWalDisabled) { + final boolean walEnabled = (oldFlags & ENABLE_WRITE_AHEAD_LOGGING) != 0; + final boolean compatibilityWalEnabled = + (oldFlags & ENABLE_LEGACY_COMPATIBILITY_WAL) != 0; + // WAL was never enabled for this database, so there's nothing left to do. + if (!walEnabled && !compatibilityWalEnabled) { return; } + // If an app explicitly disables WAL, it takes priority over any directive + // to use the legacy "compatibility WAL" mode. mConfigurationLocked.openFlags &= ~ENABLE_WRITE_AHEAD_LOGGING; - // If an app explicitly disables WAL, compatibility mode should be disabled too - mConfigurationLocked.openFlags |= DISABLE_COMPATIBILITY_WAL; + mConfigurationLocked.openFlags &= ~ENABLE_LEGACY_COMPATIBILITY_WAL; try { mConnectionPoolLocked.reconfigure(mConfigurationLocked); diff --git a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java index 48f10219921b..fcdaf0abafcb 100644 --- a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java +++ b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java @@ -17,6 +17,7 @@ package android.database.sqlite; import android.annotation.UnsupportedAppUsage; + import java.util.ArrayList; import java.util.Locale; import java.util.regex.Pattern; @@ -197,9 +198,9 @@ public final class SQLiteDatabaseConfiguration { return path.equalsIgnoreCase(MEMORY_DB_PATH); } - boolean useCompatibilityWal() { + boolean isLegacyCompatibilityWalEnabled() { return journalMode == null && syncMode == null - && (openFlags & SQLiteDatabase.DISABLE_COMPATIBILITY_WAL) == 0; + && (openFlags & SQLiteDatabase.ENABLE_LEGACY_COMPATIBILITY_WAL) != 0; } private static String stripPathForLogs(String path) { diff --git a/core/java/android/database/sqlite/SQLiteGlobal.java b/core/java/android/database/sqlite/SQLiteGlobal.java index ff286fdb42df..d796003395f5 100644 --- a/core/java/android/database/sqlite/SQLiteGlobal.java +++ b/core/java/android/database/sqlite/SQLiteGlobal.java @@ -91,16 +91,6 @@ public final class SQLiteGlobal { } /** - * Returns true if compatibility WAL mode is supported. In this mode, only - * database journal mode is changed. Connection pool will use at most one connection. - */ - public static boolean isCompatibilityWalSupported() { - return SystemProperties.getBoolean("debug.sqlite.compatibility_wal_supported", - Resources.getSystem().getBoolean( - com.android.internal.R.bool.db_compatibility_wal_supported)); - } - - /** * Gets the journal size limit in bytes. */ public static int getJournalSizeLimit() { diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java index 0e869c8c19fd..8163c4d412a7 100644 --- a/core/java/android/database/sqlite/SQLiteOpenHelper.java +++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java @@ -202,8 +202,9 @@ public abstract class SQLiteOpenHelper implements AutoCloseable { } mOpenParamsBuilder.setWriteAheadLoggingEnabled(enabled); } + // Compatibility WAL is disabled if an app disables or enables WAL - mOpenParamsBuilder.addOpenFlags(SQLiteDatabase.DISABLE_COMPATIBILITY_WAL); + mOpenParamsBuilder.removeOpenFlags(SQLiteDatabase.ENABLE_LEGACY_COMPATIBILITY_WAL); } } diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java index baf972b26573..8629210c2a7b 100644 --- a/core/java/android/hardware/biometrics/BiometricPrompt.java +++ b/core/java/android/hardware/biometrics/BiometricPrompt.java @@ -616,8 +616,15 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan mExecutor = executor; mAuthenticationCallback = callback; final long sessionId = crypto != null ? crypto.getOpId() : 0; - mService.authenticate(mToken, sessionId, userId, mBiometricServiceReceiver, - mContext.getOpPackageName(), mBundle); + if (BiometricManager.hasBiometrics(mContext)) { + mService.authenticate(mToken, sessionId, userId, mBiometricServiceReceiver, + mContext.getOpPackageName(), mBundle); + } else { + mExecutor.execute(() -> { + callback.onAuthenticationError(BiometricPrompt.BIOMETRIC_ERROR_HW_NOT_PRESENT, + mContext.getString(R.string.biometric_error_hw_unavailable)); + }); + } } catch (RemoteException e) { Log.e(TAG, "Remote exception while authenticating", e); mExecutor.execute(() -> { diff --git a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java b/core/java/android/hardware/display/AmbientDisplayConfiguration.java index e19a32e72a98..b122f199bc6f 100644 --- a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java +++ b/core/java/android/hardware/display/AmbientDisplayConfiguration.java @@ -11,11 +11,12 @@ * 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 + * limitations under the License. */ -package com.android.internal.hardware; +package android.hardware.display; +import android.annotation.TestApi; import android.content.Context; import android.os.Build; import android.os.SystemProperties; @@ -24,16 +25,25 @@ import android.text.TextUtils; import com.android.internal.R; +/** + * AmbientDisplayConfiguration encapsulates reading access to the configuration of ambient display. + * + * {@hide} + */ +@TestApi public class AmbientDisplayConfiguration { private final Context mContext; private final boolean mAlwaysOnByDefault; + /** {@hide} */ + @TestApi public AmbientDisplayConfiguration(Context context) { mContext = context; mAlwaysOnByDefault = mContext.getResources().getBoolean(R.bool.config_dozeAlwaysOnEnabled); } + /** {@hide} */ public boolean enabled(int user) { return pulseOnNotificationEnabled(user) || pulseOnLongPressEnabled(user) @@ -41,63 +51,83 @@ public class AmbientDisplayConfiguration { || wakeScreenGestureEnabled(user); } + /** {@hide} */ public boolean pulseOnNotificationEnabled(int user) { - return boolSettingDefaultOn(Settings.Secure.DOZE_ENABLED, user) && pulseOnNotificationAvailable(); + return boolSettingDefaultOn(Settings.Secure.DOZE_ENABLED, user) + && pulseOnNotificationAvailable(); } + /** {@hide} */ public boolean pulseOnNotificationAvailable() { return ambientDisplayAvailable(); } + /** {@hide} */ public boolean pickupGestureEnabled(int user) { return boolSettingDefaultOn(Settings.Secure.DOZE_PICK_UP_GESTURE, user) && dozePickupSensorAvailable(); } + /** {@hide} */ public boolean dozePickupSensorAvailable() { return mContext.getResources().getBoolean(R.bool.config_dozePulsePickup); } + /** {@hide} */ public boolean tapGestureEnabled(int user) { return boolSettingDefaultOn(Settings.Secure.DOZE_TAP_SCREEN_GESTURE, user) && tapSensorAvailable(); } + /** {@hide} */ public boolean tapSensorAvailable() { return !TextUtils.isEmpty(tapSensorType()); } + /** {@hide} */ public boolean doubleTapGestureEnabled(int user) { return boolSettingDefaultOn(Settings.Secure.DOZE_DOUBLE_TAP_GESTURE, user) && doubleTapSensorAvailable(); } + /** {@hide} */ public boolean doubleTapSensorAvailable() { return !TextUtils.isEmpty(doubleTapSensorType()); } + /** {@hide} */ public boolean wakeScreenGestureAvailable() { return mContext.getResources() .getBoolean(R.bool.config_dozeWakeLockScreenSensorAvailable); } + /** {@hide} */ public boolean wakeScreenGestureEnabled(int user) { return boolSettingDefaultOn(Settings.Secure.DOZE_WAKE_SCREEN_GESTURE, user) && wakeScreenGestureAvailable(); } + /** {@hide} */ + public long getWakeLockScreenDebounce() { + return mContext.getResources().getInteger(R.integer.config_dozeWakeLockScreenDebounce); + } + + /** {@hide} */ public String doubleTapSensorType() { return mContext.getResources().getString(R.string.config_dozeDoubleTapSensorType); } + /** {@hide} */ public String tapSensorType() { return mContext.getResources().getString(R.string.config_dozeTapSensorType); } + /** {@hide} */ public String longPressSensorType() { return mContext.getResources().getString(R.string.config_dozeLongPressSensorType); } + /** {@hide} */ public boolean pulseOnLongPressEnabled(int user) { return pulseOnLongPressAvailable() && boolSettingDefaultOff( Settings.Secure.DOZE_PULSE_ON_LONG_PRESS, user); @@ -107,28 +137,49 @@ public class AmbientDisplayConfiguration { return !TextUtils.isEmpty(longPressSensorType()); } + /** + * Returns if Always-on-Display functionality is enabled on the display for a specified user. + * + * {@hide} + */ + @TestApi public boolean alwaysOnEnabled(int user) { return boolSetting(Settings.Secure.DOZE_ALWAYS_ON, user, mAlwaysOnByDefault ? 1 : 0) && alwaysOnAvailable() && !accessibilityInversionEnabled(user); } + /** + * Returns if Always-on-Display functionality is available on the display. + * + * {@hide} + */ + @TestApi public boolean alwaysOnAvailable() { return (alwaysOnDisplayDebuggingEnabled() || alwaysOnDisplayAvailable()) && ambientDisplayAvailable(); } + /** + * Returns if Always-on-Display functionality is available on the display for a specified user. + * + * {@hide} + */ + @TestApi public boolean alwaysOnAvailableForUser(int user) { return alwaysOnAvailable() && !accessibilityInversionEnabled(user); } + /** {@hide} */ public String ambientDisplayComponent() { return mContext.getResources().getString(R.string.config_dozeComponent); } + /** {@hide} */ public boolean accessibilityInversionEnabled(int user) { return boolSettingDefaultOff(Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, user); } + /** {@hide} */ public boolean ambientDisplayAvailable() { return !TextUtils.isEmpty(ambientDisplayComponent()); } @@ -141,7 +192,6 @@ public class AmbientDisplayConfiguration { return SystemProperties.getBoolean("debug.doze.aod", false) && Build.IS_DEBUGGABLE; } - private boolean boolSettingDefaultOn(String name, int user) { return boolSetting(name, user, 1); } diff --git a/core/java/android/hardware/display/BrightnessConfiguration.java b/core/java/android/hardware/display/BrightnessConfiguration.java index 8c74ddc7698c..ed8a97c7e263 100644 --- a/core/java/android/hardware/display/BrightnessConfiguration.java +++ b/core/java/android/hardware/display/BrightnessConfiguration.java @@ -16,6 +16,7 @@ package android.hardware.display; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; @@ -93,7 +94,7 @@ public final class BrightnessConfiguration implements Parcelable { * */ @Nullable - public BrightnessCorrection getCorrectionByPackageName(String packageName) { + public BrightnessCorrection getCorrectionByPackageName(@NonNull String packageName) { return mCorrectionsByPackageName.get(packageName); } @@ -106,7 +107,7 @@ public final class BrightnessConfiguration implements Parcelable { * @return The matching brightness correction, or null. */ @Nullable - public BrightnessCorrection getCorrectionByCategory(int category) { + public BrightnessCorrection getCorrectionByCategory(@ApplicationInfo.Category int category) { return mCorrectionsByCategory.get(category); } @@ -238,7 +239,7 @@ public final class BrightnessConfiguration implements Parcelable { * * @hide */ - public void saveToXml(XmlSerializer serializer) throws IOException { + public void saveToXml(@NonNull XmlSerializer serializer) throws IOException { serializer.startTag(null, TAG_BRIGHTNESS_CURVE); if (mDescription != null) { serializer.attribute(null, ATTR_DESCRIPTION, mDescription); @@ -284,7 +285,7 @@ public final class BrightnessConfiguration implements Parcelable { * * @hide */ - public static BrightnessConfiguration loadFromXml(XmlPullParser parser) + public static BrightnessConfiguration loadFromXml(@NonNull XmlPullParser parser) throws IOException, XmlPullParserException { String description = null; List<Float> luxList = new ArrayList<>(); @@ -443,8 +444,10 @@ public final class BrightnessConfiguration implements Parcelable { * {@link #getMaxCorrectionsByPackageName}). * */ - public Builder addCorrectionByPackageName(String packageName, - BrightnessCorrection correction) { + public Builder addCorrectionByPackageName(@NonNull String packageName, + @NonNull BrightnessCorrection correction) { + Objects.requireNonNull(packageName, "packageName must not be null"); + Objects.requireNonNull(correction, "correction must not be null"); if (mCorrectionsByPackageName.size() >= getMaxCorrectionsByPackageName()) { throw new IllegalArgumentException("Too many corrections by package name"); } @@ -470,7 +473,8 @@ public final class BrightnessConfiguration implements Parcelable { * */ public Builder addCorrectionByCategory(@ApplicationInfo.Category int category, - BrightnessCorrection correction) { + @NonNull BrightnessCorrection correction) { + Objects.requireNonNull(correction, "correction must not be null"); if (mCorrectionsByCategory.size() >= getMaxCorrectionsByCategory()) { throw new IllegalArgumentException("Too many corrections by category"); } diff --git a/core/java/android/hardware/display/BrightnessCorrection.java b/core/java/android/hardware/display/BrightnessCorrection.java index 6a073ffaaa5b..ee8d8467d827 100644 --- a/core/java/android/hardware/display/BrightnessCorrection.java +++ b/core/java/android/hardware/display/BrightnessCorrection.java @@ -16,6 +16,7 @@ package android.hardware.display; +import android.annotation.FloatRange; import android.annotation.NonNull; import android.annotation.SystemApi; import android.annotation.TestApi; @@ -59,15 +60,15 @@ public final class BrightnessCorrection implements Parcelable { /** * Creates a BrightnessCorrection that given {@code brightness}, corrects it to be - * {@code exp(scale * log(brightness) + translate)}. + * {@code exp(scale * ln(brightness) + translate)}. * * @param scale - * How much to scale the log brightness. + * How much to scale the log (base e) brightness. * @param translate - * How much to translate the log brightness. + * How much to translate the log (base e) brightness. * * @return A BrightnessCorrection that given {@code brightness}, corrects it to be - * {@code exp(scale * log(brightness) + translate)}. + * {@code exp(scale * ln(brightness) + translate)}. * * @throws IllegalArgumentException * - scale or translate are NaN. @@ -87,7 +88,8 @@ public final class BrightnessCorrection implements Parcelable { * * @return The corrected brightness. */ - public float apply(float brightness) { + @FloatRange(from = 0.0) + public float apply(@FloatRange(from = 0.0) float brightness) { return mImplementation.apply(brightness); } @@ -202,7 +204,7 @@ public final class BrightnessCorrection implements Parcelable { /** * A BrightnessCorrection that given {@code brightness}, corrects it to be - * {@code exp(scale * log(brightness) + translate)}. + * {@code exp(scale * ln(brightness) + translate)}. */ private static class ScaleAndTranslateLog implements BrightnessCorrectionImplementation { private static final float MIN_SCALE = 0.5f; diff --git a/core/java/android/hardware/display/Curve.java b/core/java/android/hardware/display/Curve.java index ac28fdd634ac..41f66f55b01e 100644 --- a/core/java/android/hardware/display/Curve.java +++ b/core/java/android/hardware/display/Curve.java @@ -59,4 +59,18 @@ public final class Curve implements Parcelable { public int describeContents() { return 0; } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("["); + final int size = mX.length; + for (int i = 0; i < size; i++) { + if (i != 0) { + sb.append(", "); + } + sb.append("(").append(mX[i]).append(", ").append(mY[i]).append(")"); + } + sb.append("]"); + return sb.toString(); + } } diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index 5ea8bd67ce75..edd20519bb53 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -30,6 +30,7 @@ import android.view.Surface; /** @hide */ interface IDisplayManager { + @UnsupportedAppUsage DisplayInfo getDisplayInfo(int displayId); int[] getDisplayIds(); diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl index 64448fd98bf3..2923bbf14c30 100644 --- a/core/java/android/hardware/input/IInputManager.aidl +++ b/core/java/android/hardware/input/IInputManager.aidl @@ -45,6 +45,7 @@ interface IInputManager { // Injects an input event into the system. To inject into windows owned by other // applications, the caller must have the INJECT_EVENTS permission. + @UnsupportedAppUsage boolean injectInputEvent(in InputEvent ev, int mode); // Calibrate input device position diff --git a/core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl b/core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl index 3fe645c59a30..2dfaf601c6f5 100644 --- a/core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl +++ b/core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl @@ -32,5 +32,6 @@ oneway interface IActivityRecognitionHardwareClient { * @param isSupported whether the platform has hardware support for the feature * @param instance the available instance to provide access to the feature */ + @UnsupportedAppUsage void onAvailabilityChanged(in boolean isSupported, in IActivityRecognitionHardware instance); } diff --git a/core/java/android/metrics/LogMaker.java b/core/java/android/metrics/LogMaker.java index 19848ee6d415..5496e17206d9 100644 --- a/core/java/android/metrics/LogMaker.java +++ b/core/java/android/metrics/LogMaker.java @@ -16,6 +16,7 @@ package android.metrics; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.content.ComponentName; import android.util.Log; import android.util.SparseArray; @@ -31,6 +32,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; * @hide */ @SystemApi +@TestApi public class LogMaker { private static final String TAG = "LogBuilder"; diff --git a/core/java/android/metrics/MetricsReader.java b/core/java/android/metrics/MetricsReader.java index 5f356ca00d88..27f9a5dbf51c 100644 --- a/core/java/android/metrics/MetricsReader.java +++ b/core/java/android/metrics/MetricsReader.java @@ -16,6 +16,7 @@ package android.metrics; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.util.EventLog; import com.android.internal.annotations.VisibleForTesting; @@ -35,6 +36,7 @@ import java.util.concurrent.TimeUnit; * @hide */ @SystemApi +@TestApi public class MetricsReader { private Queue<LogMaker> mPendingQueue = new LinkedList<>(); private Queue<LogMaker> mSeenQueue = new LinkedList<>(); diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 6d195ae47449..64ed32266217 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -3147,9 +3147,9 @@ public class ConnectivityManager { /** * Called if no network is found in the timeout time specified in - * {@link #requestNetwork(NetworkRequest, NetworkCallback, int)} call. This callback is not - * called for the version of {@link #requestNetwork(NetworkRequest, NetworkCallback)} - * without timeout. When this callback is invoked the associated + * {@link #requestNetwork(NetworkRequest, NetworkCallback, int)} call or if the + * requested network request cannot be fulfilled (whether or not a timeout was + * specified). When this callback is invoked the associated * {@link NetworkRequest} will have already been removed and released, as if * {@link #unregisterNetworkCallback(NetworkCallback)} had been called. */ @@ -3684,7 +3684,8 @@ public class ConnectivityManager { /** * Registers to receive notifications about all networks which satisfy the given * {@link NetworkRequest}. The callbacks will continue to be called until - * either the application exits or link #unregisterNetworkCallback(NetworkCallback)} is called. + * either the application exits or {@link #unregisterNetworkCallback(NetworkCallback)} is + * called. * * @param request {@link NetworkRequest} describing this request. * @param networkCallback The {@link NetworkCallback} that the system will call as suitable @@ -3700,7 +3701,8 @@ public class ConnectivityManager { /** * Registers to receive notifications about all networks which satisfy the given * {@link NetworkRequest}. The callbacks will continue to be called until - * either the application exits or link #unregisterNetworkCallback(NetworkCallback)} is called. + * either the application exits or {@link #unregisterNetworkCallback(NetworkCallback)} is + * called. * * @param request {@link NetworkRequest} describing this request. * @param networkCallback The {@link NetworkCallback} that the system will call as suitable diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index 87c62d2f10a7..bd2b4eb3230f 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -47,10 +47,12 @@ interface IConnectivityManager { Network getActiveNetwork(); Network getActiveNetworkForUid(int uid, boolean ignoreBlocked); + @UnsupportedAppUsage NetworkInfo getActiveNetworkInfo(); NetworkInfo getActiveNetworkInfoForUid(int uid, boolean ignoreBlocked); NetworkInfo getNetworkInfo(int networkType); NetworkInfo getNetworkInfoForUid(in Network network, int uid, boolean ignoreBlocked); + @UnsupportedAppUsage NetworkInfo[] getAllNetworkInfo(); Network getNetworkForType(int networkType); Network[] getAllNetworks(); @@ -58,12 +60,14 @@ interface IConnectivityManager boolean isNetworkSupported(int networkType); + @UnsupportedAppUsage LinkProperties getActiveLinkProperties(); LinkProperties getLinkPropertiesForType(int networkType); LinkProperties getLinkProperties(in Network network); NetworkCapabilities getNetworkCapabilities(in Network network); + @UnsupportedAppUsage NetworkState[] getAllNetworkState(); NetworkQuotaInfo getActiveNetworkQuotaInfo(); @@ -75,6 +79,7 @@ interface IConnectivityManager int untether(String iface, String callerPkg); + @UnsupportedAppUsage int getLastTetherError(String iface); boolean isTetheringSupported(String callerPkg); @@ -84,16 +89,21 @@ interface IConnectivityManager void stopTethering(int type, String callerPkg); + @UnsupportedAppUsage String[] getTetherableIfaces(); + @UnsupportedAppUsage String[] getTetheredIfaces(); + @UnsupportedAppUsage String[] getTetheringErroredIfaces(); String[] getTetheredDhcpRanges(); + @UnsupportedAppUsage String[] getTetherableUsbRegexs(); + @UnsupportedAppUsage String[] getTetherableWifiRegexs(); String[] getTetherableBluetoothRegexs(); @@ -118,6 +128,7 @@ interface IConnectivityManager VpnConfig getVpnConfig(int userId); + @UnsupportedAppUsage void startLegacyVpn(in VpnProfile profile); LegacyVpnInfo getLegacyVpnInfo(int userId); diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl index e92302a939d8..385cb1d68b57 100644 --- a/core/java/android/net/INetworkPolicyManager.aidl +++ b/core/java/android/net/INetworkPolicyManager.aidl @@ -31,9 +31,11 @@ import android.telephony.SubscriptionPlan; interface INetworkPolicyManager { /** Control UID policies. */ + @UnsupportedAppUsage void setUidPolicy(int uid, int policy); void addUidPolicy(int uid, int policy); void removeUidPolicy(int uid, int policy); + @UnsupportedAppUsage int getUidPolicy(int uid); int[] getUidsWithPolicy(int policy); @@ -41,14 +43,18 @@ interface INetworkPolicyManager { void unregisterListener(INetworkPolicyListener listener); /** Control network policies atomically. */ + @UnsupportedAppUsage void setNetworkPolicies(in NetworkPolicy[] policies); NetworkPolicy[] getNetworkPolicies(String callingPackage); /** Snooze limit on policy matching given template. */ + @UnsupportedAppUsage void snoozeLimit(in NetworkTemplate template); /** Control if background data is restricted system-wide. */ + @UnsupportedAppUsage void setRestrictBackground(boolean restrictBackground); + @UnsupportedAppUsage boolean getRestrictBackground(); /** Callback used to change internal state on tethering */ @@ -64,6 +70,7 @@ interface INetworkPolicyManager { void setDeviceIdleMode(boolean enabled); void setWifiMeteredOverride(String networkId, int meteredOverride); + @UnsupportedAppUsage NetworkQuotaInfo getNetworkQuotaInfo(in NetworkState state); SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage); diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl index 8e6f27238846..92b685c4b517 100644 --- a/core/java/android/net/INetworkStatsService.aidl +++ b/core/java/android/net/INetworkStatsService.aidl @@ -29,6 +29,7 @@ import android.os.Messenger; interface INetworkStatsService { /** Start a statistics query session. */ + @UnsupportedAppUsage INetworkStatsSession openSession(); /** Start a statistics query session. If calling package is profile or device owner then it is @@ -37,9 +38,11 @@ interface INetworkStatsService { * PACKAGE_USAGE_STATS permission is always checked. If PACKAGE_USAGE_STATS is not granted * READ_NETWORK_USAGE_STATS is checked for. */ + @UnsupportedAppUsage INetworkStatsSession openSessionForUsageStats(int flags, String callingPackage); /** Return data layer snapshot of UID network usage. */ + @UnsupportedAppUsage NetworkStats getDataLayerSnapshotForUid(int uid); /** Get a detailed snapshot of stats since boot for all UIDs. @@ -52,6 +55,7 @@ interface INetworkStatsService { NetworkStats getDetailedUidStats(in String[] requiredIfaces); /** Return set of any ifaces associated with mobile networks since boot. */ + @UnsupportedAppUsage String[] getMobileIfaces(); /** Increment data layer count of operations performed for UID and tag. */ @@ -60,6 +64,7 @@ interface INetworkStatsService { /** Force update of ifaces. */ void forceUpdateIfaces(in Network[] defaultNetworks); /** Force update of statistics. */ + @UnsupportedAppUsage void forceUpdate(); /** Registers a callback on data usage. */ diff --git a/core/java/android/net/INetworkStatsSession.aidl b/core/java/android/net/INetworkStatsSession.aidl index 5229a3b3b9f6..f13f2cb664ba 100644 --- a/core/java/android/net/INetworkStatsSession.aidl +++ b/core/java/android/net/INetworkStatsSession.aidl @@ -27,13 +27,17 @@ interface INetworkStatsSession { NetworkStats getDeviceSummaryForNetwork(in NetworkTemplate template, long start, long end); /** Return network layer usage summary for traffic that matches template. */ + @UnsupportedAppUsage NetworkStats getSummaryForNetwork(in NetworkTemplate template, long start, long end); /** Return historical network layer stats for traffic that matches template. */ + @UnsupportedAppUsage NetworkStatsHistory getHistoryForNetwork(in NetworkTemplate template, int fields); /** Return network layer usage summary per UID for traffic that matches template. */ + @UnsupportedAppUsage NetworkStats getSummaryForAllUid(in NetworkTemplate template, long start, long end, boolean includeTags); /** Return historical network layer stats for specific UID traffic that matches template. */ + @UnsupportedAppUsage NetworkStatsHistory getHistoryForUid(in NetworkTemplate template, int uid, int set, int tag, int fields); /** Return historical network layer stats for specific UID traffic that matches template. */ NetworkStatsHistory getHistoryIntervalForUid(in NetworkTemplate template, int uid, int set, int tag, int fields, long start, long end); @@ -41,6 +45,7 @@ interface INetworkStatsSession { /** Return array of uids that have stats and are accessible to the calling user */ int[] getRelevantUids(); + @UnsupportedAppUsage void close(); } diff --git a/core/java/android/net/NetworkFactory.java b/core/java/android/net/NetworkFactory.java index 0dfe7a495738..5b1d12c603b4 100644 --- a/core/java/android/net/NetworkFactory.java +++ b/core/java/android/net/NetworkFactory.java @@ -27,11 +27,13 @@ import android.util.Log; import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.AsyncChannel; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Protocol; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.concurrent.atomic.AtomicInteger; /** @@ -113,7 +115,16 @@ public class NetworkFactory extends Handler { */ private static final int CMD_SET_FILTER = BASE + 3; + /** + * Sent by NetworkFactory to ConnectivityService to indicate that a request is + * unfulfillable. + * @see #releaseRequestAsUnfulfillableByAnyFactory(NetworkRequest). + */ + public static final int EVENT_UNFULFILLABLE_REQUEST = BASE + 4; + private final Context mContext; + private final ArrayList<Message> mPreConnectedQueue = new ArrayList<Message>(); + private AsyncChannel mAsyncChannel; private final String LOG_TAG; private final SparseArray<NetworkRequestInfo> mNetworkRequests = @@ -155,6 +166,36 @@ public class NetworkFactory extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { + case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: { + if (mAsyncChannel != null) { + log("Received new connection while already connected!"); + break; + } + if (VDBG) log("NetworkFactory fully connected"); + AsyncChannel ac = new AsyncChannel(); + ac.connected(null, this, msg.replyTo); + ac.replyToMessage(msg, AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED, + AsyncChannel.STATUS_SUCCESSFUL); + mAsyncChannel = ac; + for (Message m : mPreConnectedQueue) { + ac.sendMessage(m); + } + mPreConnectedQueue.clear(); + break; + } + case AsyncChannel.CMD_CHANNEL_DISCONNECT: { + if (VDBG) log("CMD_CHANNEL_DISCONNECT"); + if (mAsyncChannel != null) { + mAsyncChannel.disconnect(); + mAsyncChannel = null; + } + break; + } + case AsyncChannel.CMD_CHANNEL_DISCONNECTED: { + if (DBG) log("NetworkFactory channel lost"); + mAsyncChannel = null; + break; + } case CMD_REQUEST_NETWORK: { handleAddRequest((NetworkRequest) msg.obj, msg.arg1, msg.arg2); break; @@ -355,6 +396,27 @@ public class NetworkFactory extends Handler { }); } + /** + * Can be called by a factory to release a request as unfulfillable: the request will be + * removed, and the caller will get a + * {@link ConnectivityManager.NetworkCallback#onUnavailable()} callback after this function + * returns. + * + * Note: this should only be called by factory which KNOWS that it is the ONLY factory which + * is able to fulfill this request! + */ + protected void releaseRequestAsUnfulfillableByAnyFactory(NetworkRequest r) { + post(() -> { + if (DBG) log("releaseRequestAsUnfulfillableByAnyFactory: " + r); + Message msg = obtainMessage(EVENT_UNFULFILLABLE_REQUEST, r); + if (mAsyncChannel != null) { + mAsyncChannel.sendMessage(msg); + } else { + mPreConnectedQueue.add(msg); + } + }); + } + // override to do simple mode (request independent) protected void startNetwork() { } protected void stopNetwork() { } diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java index 90dccb5b82d5..45860b3858ce 100644 --- a/core/java/android/net/SSLCertificateSocketFactory.java +++ b/core/java/android/net/SSLCertificateSocketFactory.java @@ -23,8 +23,7 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.RoSystemProperties; -import com.android.org.conscrypt.Conscrypt; -import com.android.org.conscrypt.OpenSSLContextImpl; +import com.android.org.conscrypt.ClientSessionContext; import com.android.org.conscrypt.OpenSSLSocketImpl; import com.android.org.conscrypt.SSLClientSessionCache; @@ -33,6 +32,8 @@ import java.net.InetAddress; import java.net.Socket; import java.net.SocketException; import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; import java.security.PrivateKey; import java.security.cert.X509Certificate; @@ -40,6 +41,7 @@ import javax.net.SocketFactory; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; import javax.net.ssl.SSLException; import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLSession; @@ -251,11 +253,12 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { private SSLSocketFactory makeSocketFactory( KeyManager[] keyManagers, TrustManager[] trustManagers) { try { - OpenSSLContextImpl sslContext = (OpenSSLContextImpl) Conscrypt.newPreferredSSLContextSpi(); - sslContext.engineInit(keyManagers, trustManagers, null); - sslContext.engineGetClientSessionContext().setPersistentCache(mSessionCache); - return sslContext.engineGetSocketFactory(); - } catch (KeyManagementException e) { + SSLContext sslContext = SSLContext.getInstance("TLS", "AndroidOpenSSL"); + sslContext.init(keyManagers, trustManagers, null); + ((ClientSessionContext) sslContext.getClientSessionContext()) + .setPersistentCache(mSessionCache); + return sslContext.getSocketFactory(); + } catch (KeyManagementException | NoSuchAlgorithmException | NoSuchProviderException e) { Log.wtf(TAG, e); return (SSLSocketFactory) SSLSocketFactory.getDefault(); // Fallback } diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java index 784f23311103..ebb1ae4bb795 100644 --- a/core/java/android/net/VpnService.java +++ b/core/java/android/net/VpnService.java @@ -19,6 +19,7 @@ package android.net; import static android.system.OsConstants.AF_INET; import static android.system.OsConstants.AF_INET6; +import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.UnsupportedAppUsage; @@ -512,7 +513,7 @@ public class VpnService extends Service { * Sets an HTTP proxy for the VPN network. This proxy is only a recommendation * and it is possible that some apps will ignore it. */ - public Builder setHttpProxy(ProxyInfo proxyInfo) { + public Builder setHttpProxy(@NonNull ProxyInfo proxyInfo) { mConfig.proxyInfo = proxyInfo; return this; } diff --git a/core/java/android/net/metrics/ValidationProbeEvent.java b/core/java/android/net/metrics/ValidationProbeEvent.java index 42e8aa6dc248..a43dc60cec84 100644 --- a/core/java/android/net/metrics/ValidationProbeEvent.java +++ b/core/java/android/net/metrics/ValidationProbeEvent.java @@ -79,7 +79,7 @@ public final class ValidationProbeEvent implements IpConnectivityLog.Event { } /** - * Utility to create an instance of {@link ApfProgramEvent}. + * Utility to create an instance of {@link ValidationProbeEvent}. */ public static class Builder { private long mDurationMs; diff --git a/core/java/android/nfc/INfcAdapterExtras.aidl b/core/java/android/nfc/INfcAdapterExtras.aidl index 41ebf636c89e..dd260bc6f2f6 100644 --- a/core/java/android/nfc/INfcAdapterExtras.aidl +++ b/core/java/android/nfc/INfcAdapterExtras.aidl @@ -23,11 +23,18 @@ import android.os.Bundle; * {@hide} */ interface INfcAdapterExtras { + @UnsupportedAppUsage Bundle open(in String pkg, IBinder b); + @UnsupportedAppUsage Bundle close(in String pkg, IBinder b); + @UnsupportedAppUsage Bundle transceive(in String pkg, in byte[] data_in); + @UnsupportedAppUsage int getCardEmulationRoute(in String pkg); + @UnsupportedAppUsage void setCardEmulationRoute(in String pkg, int route); + @UnsupportedAppUsage void authenticate(in String pkg, in byte[] token); + @UnsupportedAppUsage String getDriverName(in String pkg); } diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java index 27f7e2296e7f..684369a6f720 100644 --- a/core/java/android/os/BugreportManager.java +++ b/core/java/android/os/BugreportManager.java @@ -59,7 +59,8 @@ public class BugreportManager { BUGREPORT_ERROR_INVALID_INPUT, BUGREPORT_ERROR_RUNTIME, BUGREPORT_ERROR_USER_DENIED_CONSENT, - BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT + BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT, + BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS }) /** Possible error codes taking a bugreport can encounter */ @@ -81,6 +82,10 @@ public class BugreportManager { public static final int BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT = IDumpstateListener.BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT; + /** There is currently a bugreport running. The caller should try again later. */ + public static final int BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS = + IDumpstateListener.BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS; + /** * Called when there is a progress update. * @param progress the progress in [0.0, 100.0] @@ -96,6 +101,9 @@ public class BugreportManager { * <p>If {@code BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT} is passed, then the consent timed * out, but the bugreport could be available in the internal directory of dumpstate for * manual retrieval. + * + * <p> If {@code BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS} is passed, then the + * caller should try later, as only one bugreport can be in progress at a time. */ public void onError(@BugreportErrorCode int errorCode) {} diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 83a7654d494b..0425c6234a04 100755 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -107,6 +107,7 @@ public class Build { * Whether this build was for an emulator device. * @hide */ + @TestApi public static final boolean IS_EMULATOR = getString("ro.kernel.qemu").equals("1"); /** diff --git a/core/java/android/os/FileObserver.java b/core/java/android/os/FileObserver.java index da0389578e15..330bde541d7d 100644 --- a/core/java/android/os/FileObserver.java +++ b/core/java/android/os/FileObserver.java @@ -16,11 +16,14 @@ package android.os; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.util.Log; import java.io.File; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; import java.util.Arrays; import java.util.HashMap; @@ -45,6 +48,24 @@ import java.util.List; * keep a reference to the FileObserver instance from some other live object.</p> */ public abstract class FileObserver { + /** @hide */ + @IntDef(flag = true, value = { + ACCESS, + MODIFY, + ATTRIB, + CLOSE_WRITE, + CLOSE_NOWRITE, + OPEN, + MOVED_FROM, + MOVED_TO, + CREATE, + DELETE, + DELETE_SELF, + MOVE_SELF + }) + @Retention(RetentionPolicy.SOURCE) + public @interface NotifyEventType {} + /** Event type: Data was read from a file */ public static final int ACCESS = 0x00000001; /** Event type: Data was written to a file */ @@ -71,6 +92,7 @@ public abstract class FileObserver { public static final int MOVE_SELF = 0x00000800; /** Event mask: All valid event types, combined */ + @NotifyEventType public static final int ALL_EVENTS = ACCESS | MODIFY | ATTRIB | CLOSE_WRITE | CLOSE_NOWRITE | OPEN | MOVED_FROM | MOVED_TO | DELETE | CREATE | DELETE_SELF | MOVE_SELF; @@ -90,7 +112,8 @@ public abstract class FileObserver { observe(m_fd); } - public int[] startWatching(List<File> files, int mask, FileObserver observer) { + public int[] startWatching(List<File> files, + @NotifyEventType int mask, FileObserver observer) { final int count = files.size(); final String[] paths = new String[count]; for (int i = 0; i < count; ++i) { @@ -118,7 +141,7 @@ public abstract class FileObserver { stopWatching(m_fd, descriptors); } - public void onEvent(int wfd, int mask, String path) { + public void onEvent(int wfd, @NotifyEventType int mask, String path) { // look up our observer, fixing up the map if necessary... FileObserver observer = null; @@ -144,7 +167,8 @@ public abstract class FileObserver { private native int init(); private native void observe(int fd); - private native void startWatching(int fd, String[] paths, int mask, int[] wfds); + private native void startWatching(int fd, String[] paths, + @NotifyEventType int mask, int[] wfds); private native void stopWatching(int fd, int[] wfds); } @@ -197,7 +221,7 @@ public abstract class FileObserver { * @deprecated use {@link #FileObserver(File, int)} instead. */ @Deprecated - public FileObserver(String path, int mask) { + public FileObserver(String path, @NotifyEventType int mask) { this(new File(path), mask); } @@ -209,7 +233,7 @@ public abstract class FileObserver { * @param file The file or directory to monitor * @param mask The event or events (added together) to watch for */ - public FileObserver(@NonNull File file, int mask) { + public FileObserver(@NonNull File file, @NotifyEventType int mask) { this(Arrays.asList(file), mask); } @@ -220,7 +244,7 @@ public abstract class FileObserver { * @param files The files or directories to monitor * @param mask The event or events (added together) to watch for */ - public FileObserver(@NonNull List<File> files, int mask) { + public FileObserver(@NonNull List<File> files, @NotifyEventType int mask) { mFiles = files; mMask = mask; } diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index 4a14eced7d87..650d21785ec2 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -60,7 +60,8 @@ public class GraphicsEnvironment { private static final String SYSTEM_DRIVER_VERSION_NAME = ""; private static final long SYSTEM_DRIVER_VERSION_CODE = 0; private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0"; - private static final String PROPERTY_GFX_DRIVER_BUILD_DATE = "ro.gfx.driver.build_date"; + private static final String PROPERTY_GFX_DRIVER_BUILD_TIME = "ro.gfx.driver_build_time"; + private static final String METADATA_DRIVER_BUILD_TIME = "driver_build_time"; private static final String ANGLE_RULES_FILE = "a4a_rules.json"; private static final String ANGLE_TEMP_RULES = "debug.angle.rules"; private static final String ACTION_ANGLE_FOR_ANDROID = "android.app.action.ANGLE_FOR_ANDROID"; @@ -79,9 +80,8 @@ public class GraphicsEnvironment { setupGpuLayers(context, coreSettings, pm, packageName); setupAngle(context, coreSettings, pm, packageName); if (!chooseDriver(context, coreSettings, pm, packageName)) { - final String driverBuildDate = SystemProperties.get(PROPERTY_GFX_DRIVER_BUILD_DATE); setGpuStats(SYSTEM_DRIVER_NAME, SYSTEM_DRIVER_VERSION_NAME, SYSTEM_DRIVER_VERSION_CODE, - driverBuildDate == null ? "" : driverBuildDate, packageName); + SystemProperties.getLong(PROPERTY_GFX_DRIVER_BUILD_TIME, 0), packageName); } } @@ -664,14 +664,28 @@ public class GraphicsEnvironment { .append(abi); final String paths = sb.toString(); - if (DEBUG) Log.v(TAG, "gfx driver package libs: " + paths); - setDriverPath(paths); + final String sphalLibraries = + coreSettings.getString(Settings.Global.GAME_DRIVER_SPHAL_LIBRARIES); - final String driverBuildDate = driverAppInfo.metaData == null - ? "" - : driverAppInfo.metaData.getString("driver_build_date"); + if (DEBUG) { + Log.v(TAG, + "gfx driver package search path: " + paths + + ", required sphal libraries: " + sphalLibraries); + } + setDriverPathAndSphalLibraries(paths, sphalLibraries); + + if (driverAppInfo.metaData == null) { + throw new NullPointerException("apk's meta-data cannot be null"); + } + + final String driverBuildTime = driverAppInfo.metaData.getString(METADATA_DRIVER_BUILD_TIME); + if (driverBuildTime == null || driverBuildTime.isEmpty()) { + throw new IllegalArgumentException("driver_build_time meta-data is not set"); + } + // driver_build_time in the meta-data is in "L<Unix epoch timestamp>" format. e.g. L123456. + // Long.parseLong will throw if the meta-data "driver_build_time" is not set properly. setGpuStats(driverPackageName, driverPackageInfo.versionName, driverAppInfo.longVersionCode, - driverBuildDate == null ? "" : driverBuildDate, packageName); + Long.parseLong(driverBuildTime.substring(1)), packageName); return true; } @@ -693,9 +707,9 @@ public class GraphicsEnvironment { private static native void setLayerPaths(ClassLoader classLoader, String layerPaths); private static native void setDebugLayers(String layers); private static native void setDebugLayersGLES(String layers); - private static native void setDriverPath(String path); + private static native void setDriverPathAndSphalLibraries(String path, String sphalLibraries); private static native void setGpuStats(String driverPackageName, String driverVersionName, - long driverVersionCode, String driverBuildDate, String appPackageName); + long driverVersionCode, long driverBuildTime, String appPackageName); private static native void setAngleInfo(String path, String appPackage, String devOptIn, FileDescriptor rulesFd, long rulesOffset, long rulesLength); private static native boolean getShouldUseAngle(String packageName); diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 6bd1f2b59fad..91ddf82b5400 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -1569,7 +1569,7 @@ public final class PowerManager { @LocationPowerSaveMode public int getLocationPowerSaveMode() { final PowerSaveState powerSaveState = getPowerSaveState(ServiceType.LOCATION); - if (!powerSaveState.globalBatterySaverEnabled) { + if (!powerSaveState.batterySaverEnabled) { return LOCATION_MODE_NO_CHANGE; } return powerSaveState.locationMode; diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 9e97e375753c..cd43b42c8ca5 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -505,7 +505,6 @@ public class Process { * @param invokeWith null-ok the command to invoke with. * @param packageName null-ok the name of the package this process belongs to. * @param packagesForUid null-ok all the packages with the same uid as this process. - * @param visibleVols null-ok storage volumes that can be accessed by this process. * @param zygoteArgs Additional arguments to supply to the zygote process. * * @return An object that describes the result of the attempt to start the process. @@ -525,13 +524,12 @@ public class Process { @Nullable String invokeWith, @Nullable String packageName, @Nullable String[] packagesForUid, - @Nullable String[] visibleVols, @Nullable String sandboxId, @Nullable String[] zygoteArgs) { return ZYGOTE_PROCESS.start(processClass, niceName, uid, gid, gids, runtimeFlags, mountExternal, targetSdkVersion, seInfo, abi, instructionSet, appDataDir, invokeWith, packageName, - packagesForUid, visibleVols, sandboxId, /*useBlastulaPool=*/ true, zygoteArgs); + packagesForUid, sandboxId, /*useBlastulaPool=*/ true, zygoteArgs); } /** @hide */ @@ -547,13 +545,12 @@ public class Process { @Nullable String invokeWith, @Nullable String packageName, @Nullable String[] packagesForUid, - @Nullable String[] visibleVols, @Nullable String sandboxId, @Nullable String[] zygoteArgs) { return WebViewZygote.getProcess().start(processClass, niceName, uid, gid, gids, runtimeFlags, mountExternal, targetSdkVersion, seInfo, abi, instructionSet, appDataDir, invokeWith, packageName, - packagesForUid, visibleVols, sandboxId, /*useBlastulaPool=*/ false, zygoteArgs); + packagesForUid, sandboxId, /*useBlastulaPool=*/ false, zygoteArgs); } /** diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index e2b5730e10f4..067375590c99 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -2697,6 +2697,19 @@ public class UserManager { } /** + * Updates the calling user's name. + * Requires {@link android.Manifest.permission#MANAGE_USERS} permission. + * + * @param name the new name for the user + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + public void setUserName(String name) { + setUserName(getUserHandle(), name); + } + + /** * Sets the user's photo. * @param userHandle the user for whom to change the photo. * @param icon the bitmap to set as the photo. @@ -2711,6 +2724,19 @@ public class UserManager { } /** + * Sets the calling user's photo. + * Requires {@link android.Manifest.permission#MANAGE_USERS} permission. + * + * @param icon the bitmap to set as the photo. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + public void setUserIcon(Bitmap icon) { + setUserIcon(getUserHandle(), icon); + } + + /** * Returns a file descriptor for the user's photo. PNG data can be read from this file. * @param userHandle the user whose photo we want to read. * @return a {@link Bitmap} of the user's photo, or null if there's no photo. @@ -2737,6 +2763,20 @@ public class UserManager { } /** + * Returns a Bitmap for the calling user's photo. + * Requires {@link android.Manifest.permission#MANAGE_USERS} permission. + * + * @return a {@link Bitmap} of the user's photo, or null if there's no photo. + * @see com.android.internal.util.UserIcons#getDefaultUserIcon for a default. + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + public Bitmap getUserIcon() { + return getUserIcon(getUserHandle()); + } + + /** * Returns the maximum number of users that can be created on this device. A return value * of 1 means that it is a single user device. * @hide diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java index 99fb608b80dc..471ae30a4058 100644 --- a/core/java/android/os/VibrationEffect.java +++ b/core/java/android/os/VibrationEffect.java @@ -240,7 +240,7 @@ public abstract class VibrationEffect implements Parcelable { * * @return The desired effect. */ - public static VibrationEffect createPrebaked(@EffectType int effectId) { + public static VibrationEffect createPredefined(@EffectType int effectId) { return get(effectId, true); } diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java index de378b0cd1bf..e49b65e63c77 100644 --- a/core/java/android/os/ZygoteProcess.java +++ b/core/java/android/os/ZygoteProcess.java @@ -27,7 +27,6 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.Zygote; -import com.android.internal.util.Preconditions; import java.io.BufferedWriter; import java.io.DataInputStream; @@ -122,8 +121,9 @@ public class ZygoteProcess { new LocalSocketAddress(Zygote.BLASTULA_POOL_SECONDARY_SOCKET_NAME, LocalSocketAddress.Namespace.RESERVED); - // TODO (chriswailes): Uncomment when the blastula pool can be enabled. -// fetchBlastulaPoolEnabledProp(); + if (fetchBlastulaPoolEnabledProp()) { + informZygotesOfBlastulaPoolStatus(); + } } public ZygoteProcess(LocalSocketAddress primarySocketAddress, @@ -305,7 +305,6 @@ public class ZygoteProcess { * @param invokeWith null-ok the command to invoke with. * @param packageName null-ok the name of the package this process belongs to. * @param packagesForUid null-ok all the packages with the same uid as this process. - * @param visibleVols null-ok storage volumes that can be accessed by this process. * @param zygoteArgs Additional arguments to supply to the zygote process. * * @return An object that describes the result of the attempt to start the process. @@ -323,23 +322,19 @@ public class ZygoteProcess { @Nullable String invokeWith, @Nullable String packageName, @Nullable String[] packagesForUid, - @Nullable String[] visibleVols, @Nullable String sandboxId, boolean useBlastulaPool, @Nullable String[] zygoteArgs) { - if (fetchBlastulaPoolEnabledProp()) { - // TODO (chriswailes): Send the appropriate command to the zygotes - Log.i(LOG_TAG, "Blastula pool enabled property set to: " + mBlastulaPoolEnabled); - - // This can't be enabled yet, but we do want to test this code path. - mBlastulaPoolEnabled = false; + // TODO (chriswailes): Is there a better place to check this value? + if (fetchBlastulaPoolEnabledPropWithMinInterval()) { + informZygotesOfBlastulaPoolStatus(); } try { return startViaZygote(processClass, niceName, uid, gid, gids, runtimeFlags, mountExternal, targetSdkVersion, seInfo, abi, instructionSet, appDataDir, invokeWith, /*startChildZygote=*/false, - packageName, packagesForUid, visibleVols, sandboxId, + packageName, packagesForUid, sandboxId, useBlastulaPool, zygoteArgs); } catch (ZygoteStartFailedEx ex) { Log.e(LOG_TAG, @@ -387,7 +382,7 @@ public class ZygoteProcess { * @throws ZygoteStartFailedEx if process start failed for any reason */ @GuardedBy("mLock") - private static Process.ProcessStartResult zygoteSendArgsAndGetResult( + private Process.ProcessStartResult zygoteSendArgsAndGetResult( ZygoteState zygoteState, boolean useBlastulaPool, ArrayList<String> args) throws ZygoteStartFailedEx { // Throw early if any of the arguments are malformed. This means we can @@ -415,7 +410,7 @@ public class ZygoteProcess { Process.ProcessStartResult result = new Process.ProcessStartResult(); // TODO (chriswailes): Move branch body into separate function. - if (useBlastulaPool && isValidBlastulaCommand(args)) { + if (useBlastulaPool && mBlastulaPoolEnabled && isValidBlastulaCommand(args)) { LocalSocket blastulaSessionSocket = null; try { @@ -444,7 +439,7 @@ public class ZygoteProcess { // If there was an IOException using the blastula pool we will log the error and // attempt to start the process through the Zygote. Log.e(LOG_TAG, "IO Exception while communicating with blastula pool - " - + ex.toString()); + + ex.getMessage()); } finally { try { blastulaSessionSocket.close(); @@ -531,7 +526,6 @@ public class ZygoteProcess { * that has its state cloned from this zygote process. * @param packageName null-ok the name of the package this process belongs to. * @param packagesForUid null-ok all the packages with the same uid as this process. - * @param visibleVols null-ok storage volumes that can be accessed by this process. * @param extraArgs Additional arguments to supply to the zygote process. * @return An object that describes the result of the attempt to start the process. * @throws ZygoteStartFailedEx if process start failed for any reason @@ -550,7 +544,6 @@ public class ZygoteProcess { boolean startChildZygote, @Nullable String packageName, @Nullable String[] packagesForUid, - @Nullable String[] visibleVols, @Nullable String sandboxId, boolean useBlastulaPool, @Nullable String[] extraArgs) @@ -638,19 +631,6 @@ public class ZygoteProcess { argsForZygote.add(sb.toString()); } - if (visibleVols != null && visibleVols.length > 0) { - final StringBuilder sb = new StringBuilder(); - sb.append("--visible-vols="); - - for (int i = 0; i < visibleVols.length; ++i) { - if (i != 0) { - sb.append(','); - } - sb.append(visibleVols[i]); - } - argsForZygote.add(sb.toString()); - } - if (sandboxId != null) { argsForZygote.add("--sandbox-id=" + sandboxId); } @@ -665,7 +645,7 @@ public class ZygoteProcess { synchronized(mLock) { return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), - useBlastulaPool && mBlastulaPoolEnabled, + useBlastulaPool, argsForZygote); } } @@ -685,6 +665,10 @@ public class ZygoteProcess { Boolean.parseBoolean(BLASTULA_POOL_ENABLED_DEFAULT)); } + if (origVal != mBlastulaPoolEnabled) { + Log.i(LOG_TAG, "blastulaPoolEnabled = " + mBlastulaPoolEnabled); + } + return origVal != mBlastulaPoolEnabled; } @@ -847,49 +831,57 @@ public class ZygoteProcess { } /** - * Tries to open a session socket to a Zygote process with a compatible ABI if one is not - * already open. If a compatible session socket is already open that session socket is returned. - * This function may block and may have to try connecting to multiple Zygotes to find the - * appropriate one. Requires that mLock be held. + * Creates a ZygoteState for the primary zygote if it doesn't exist or has been disconnected. */ @GuardedBy("mLock") - private ZygoteState openZygoteSocketIfNeeded(String abi) - throws ZygoteStartFailedEx { - - Preconditions.checkState(Thread.holdsLock(mLock), "ZygoteProcess lock not held"); - + private void attemptConnectionToPrimaryZygote() throws IOException { if (primaryZygoteState == null || primaryZygoteState.isClosed()) { - try { - primaryZygoteState = + primaryZygoteState = ZygoteState.connect(mZygoteSocketAddress, mBlastulaPoolSocketAddress); - } catch (IOException ioe) { - throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe); - } maybeSetApiBlacklistExemptions(primaryZygoteState, false); maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState); } + } - if (primaryZygoteState.matches(abi)) { - return primaryZygoteState; - } - - // The primary zygote didn't match. Try the secondary. + /** + * Creates a ZygoteState for the secondary zygote if it doesn't exist or has been disconnected. + */ + @GuardedBy("mLock") + private void attemptConnectionToSecondaryZygote() throws IOException { if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) { - try { - secondaryZygoteState = + secondaryZygoteState = ZygoteState.connect(mZygoteSecondarySocketAddress, - mBlastulaPoolSecondarySocketAddress); - } catch (IOException ioe) { - throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe); - } + mBlastulaPoolSecondarySocketAddress); maybeSetApiBlacklistExemptions(secondaryZygoteState, false); maybeSetHiddenApiAccessLogSampleRate(secondaryZygoteState); } + } + + /** + * Tries to open a session socket to a Zygote process with a compatible ABI if one is not + * already open. If a compatible session socket is already open that session socket is returned. + * This function may block and may have to try connecting to multiple Zygotes to find the + * appropriate one. Requires that mLock be held. + */ + @GuardedBy("mLock") + private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx { + try { + attemptConnectionToPrimaryZygote(); + + if (primaryZygoteState.matches(abi)) { + return primaryZygoteState; + } + + // The primary zygote didn't match. Try the secondary. + attemptConnectionToSecondaryZygote(); - if (secondaryZygoteState.matches(abi)) { - return secondaryZygoteState; + if (secondaryZygoteState.matches(abi)) { + return secondaryZygoteState; + } + } catch (IOException ioe) { + throw new ZygoteStartFailedEx("Error connecting to zygote", ioe); } throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi); @@ -1015,6 +1007,57 @@ public class ZygoteProcess { } /** + * Sends messages to the zygotes telling them to change the status of their blastula pools. If + * this notification fails the ZygoteProcess will fall back to the previous behavior. + */ + private void informZygotesOfBlastulaPoolStatus() { + final String command = "1\n--blastula-pool-enabled=" + mBlastulaPoolEnabled + "\n"; + + synchronized (mLock) { + try { + attemptConnectionToPrimaryZygote(); + + primaryZygoteState.mZygoteOutputWriter.write(command); + primaryZygoteState.mZygoteOutputWriter.flush(); + } catch (IOException ioe) { + mBlastulaPoolEnabled = !mBlastulaPoolEnabled; + Log.w(LOG_TAG, "Failed to inform zygotes of blastula pool status: " + + ioe.getMessage()); + return; + } + + try { + attemptConnectionToSecondaryZygote(); + + try { + secondaryZygoteState.mZygoteOutputWriter.write(command); + secondaryZygoteState.mZygoteOutputWriter.flush(); + + // Wait for the secondary Zygote to finish its work. + secondaryZygoteState.mZygoteInputStream.readInt(); + } catch (IOException ioe) { + throw new IllegalStateException( + "Blastula pool state change cause an irrecoverable error", + ioe); + } + } catch (IOException ioe) { + // No secondary zygote present. This is expected on some devices. + } + + // Wait for the response from the primary zygote here so the primary/secondary zygotes + // can work concurrently. + try { + // Wait for the primary zygote to finish its work. + primaryZygoteState.mZygoteInputStream.readInt(); + } catch (IOException ioe) { + throw new IllegalStateException( + "Blastula pool state change cause an irrecoverable error", + ioe); + } + } + } + + /** * Starts a new zygote process as a child of this zygote. This is used to create * secondary zygotes that inherit data from the zygote that this object * communicates with. This returns a new ZygoteProcess representing a connection @@ -1061,7 +1104,7 @@ public class ZygoteProcess { gids, runtimeFlags, 0 /* mountExternal */, 0 /* targetSdkVersion */, seInfo, abi, instructionSet, null /* appDataDir */, null /* invokeWith */, true /* startChildZygote */, null /* packageName */, - null /* packagesForUid */, null /* visibleVolumes */, null /* sandboxId */, + null /* packagesForUid */, null /* sandboxId */, false /* useBlastulaPool */, extraArgs); } catch (ZygoteStartFailedEx ex) { throw new RuntimeException("Starting child-zygote through Zygote failed", ex); diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 90a5f7660e0d..27e391423d21 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -136,6 +136,8 @@ public class StorageManager { public static final String PROP_ISOLATED_STORAGE = "persist.sys.isolated_storage"; /** {@hide} */ public static final String PROP_ISOLATED_STORAGE_SNAPSHOT = "sys.isolated_storage_snapshot"; + /** {@hide} */ + public static final String PROP_LEGACY_GREYLIST = "persist.sys.legacy_greylist"; /** {@hide} */ public static final String PROP_FORCE_AUDIO = "persist.fw.force_audio"; @@ -233,6 +235,8 @@ public class StorageManager { public static final int DEBUG_ISOLATED_STORAGE_FORCE_ON = 1 << 6; /** {@hide} */ public static final int DEBUG_ISOLATED_STORAGE_FORCE_OFF = 1 << 7; + /** {@hide} */ + public static final int DEBUG_LEGACY_GREYLIST = 1 << 8; /** {@hide} */ public static final int FLAG_STORAGE_DE = IInstalld.FLAG_STORAGE_DE; diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java index 03b2c2c4c6f2..f1c313838731 100644 --- a/core/java/android/os/storage/StorageManagerInternal.java +++ b/core/java/android/os/storage/StorageManagerInternal.java @@ -109,11 +109,6 @@ public abstract class StorageManagerInternal { @Nullable String sharedUserId, int userId); /** - * @return Labels of storage volumes that are visible to the given userId. - */ - public abstract String[] getVisibleVolumesForUser(int userId); - - /** * A listener for reset events in the StorageManagerService. */ public interface ResetListener { diff --git a/core/java/android/permission/IPermissionController.aidl b/core/java/android/permission/IPermissionController.aidl index 7e9ba5d4b628..cb2517e956df 100644 --- a/core/java/android/permission/IPermissionController.aidl +++ b/core/java/android/permission/IPermissionController.aidl @@ -40,4 +40,6 @@ oneway interface IPermissionController { void getPermissionUsages(boolean countSystem, long numMillis, in RemoteCallback callback); void isApplicationQualifiedForRole(String roleName, String packageName, in RemoteCallback callback); + void setRuntimePermissionGrantStateByDeviceAdmin(String callerPackageName, String packageName, + String permission, int grantState, in RemoteCallback callback); } diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java index d62bc6c5a872..9d58064ab6d0 100644 --- a/core/java/android/permission/PermissionControllerManager.java +++ b/core/java/android/permission/PermissionControllerManager.java @@ -16,8 +16,12 @@ package android.permission; +import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT; +import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED; +import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED; import static android.permission.PermissionControllerService.SERVICE_INTERFACE; +import static com.android.internal.util.Preconditions.checkArgument; import static com.android.internal.util.Preconditions.checkArgumentNonnegative; import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull; import static com.android.internal.util.Preconditions.checkFlagsArgument; @@ -35,6 +39,7 @@ import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; +import android.app.admin.DevicePolicyManager.PermissionGrantState; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -268,6 +273,40 @@ public final class PermissionControllerManager { } /** + * Set the runtime permission state from a device admin. + * + * @param callerPackageName The package name of the admin requesting the change + * @param packageName Package the permission belongs to + * @param permission Permission to change + * @param grantState State to set the permission into + * @param executor Executor to run the {@code callback} on + * @param callback The callback + * + * @hide + */ + @RequiresPermission(allOf = {Manifest.permission.GRANT_RUNTIME_PERMISSIONS, + Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, + Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY}, + conditional = true) + public void setRuntimePermissionGrantStateByDeviceAdmin(@NonNull String callerPackageName, + @NonNull String packageName, @NonNull String permission, + @PermissionGrantState int grantState, @NonNull @CallbackExecutor Executor executor, + @NonNull Consumer<Boolean> callback) { + checkStringNotEmpty(callerPackageName); + checkStringNotEmpty(packageName); + checkStringNotEmpty(permission); + checkArgument(grantState == PERMISSION_GRANT_STATE_GRANTED + || grantState == PERMISSION_GRANT_STATE_DENIED + || grantState == PERMISSION_GRANT_STATE_DEFAULT); + checkNotNull(executor); + checkNotNull(callback); + + mRemoteService.scheduleRequest(new PendingSetRuntimePermissionGrantStateByDeviceAdmin( + mRemoteService, callerPackageName, packageName, permission, grantState, executor, + callback)); + } + + /** * Create a backup of the runtime permissions. * * @param user The user to be backed up @@ -480,7 +519,7 @@ public final class PermissionControllerManager { } @Override - public void scheduleRequest(@NonNull PendingRequest<RemoteService, + public void scheduleRequest(@NonNull BasePendingRequest<RemoteService, IPermissionController> pendingRequest) { super.scheduleRequest(pendingRequest); } @@ -797,6 +836,67 @@ public final class PermissionControllerManager { } /** + * Request for {@link #getRuntimePermissionBackup} + */ + private static final class PendingSetRuntimePermissionGrantStateByDeviceAdmin extends + AbstractRemoteService.PendingRequest<RemoteService, IPermissionController> { + private final @NonNull String mCallerPackageName; + private final @NonNull String mPackageName; + private final @NonNull String mPermission; + private final @PermissionGrantState int mGrantState; + + private final @NonNull Executor mExecutor; + private final @NonNull Consumer<Boolean> mCallback; + private final @NonNull RemoteCallback mRemoteCallback; + + private PendingSetRuntimePermissionGrantStateByDeviceAdmin(@NonNull RemoteService service, + @NonNull String callerPackageName, @NonNull String packageName, + @NonNull String permission, @PermissionGrantState int grantState, + @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) { + super(service); + + mCallerPackageName = callerPackageName; + mPackageName = packageName; + mPermission = permission; + mGrantState = grantState; + mExecutor = executor; + mCallback = callback; + + mRemoteCallback = new RemoteCallback(result -> executor.execute(() -> { + long token = Binder.clearCallingIdentity(); + try { + callback.accept(result.getBoolean(KEY_RESULT, false)); + } finally { + Binder.restoreCallingIdentity(token); + + finish(); + } + }), null); + } + + @Override + protected void onTimeout(RemoteService remoteService) { + long token = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mCallback.accept(false)); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override + public void run() { + try { + getService().getServiceInterface().setRuntimePermissionGrantStateByDeviceAdmin( + mCallerPackageName, mPackageName, mPermission, mGrantState, mRemoteCallback); + } catch (RemoteException e) { + Log.e(TAG, "Error setting permissions state for device admin " + mPackageName, + e); + } + } + } + + /** * Request for {@link #restoreRuntimePermissionBackup} */ private static final class PendingRestoreRuntimePermissionBackup implements diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java index fb6c061c536f..e883d25ab0bd 100644 --- a/core/java/android/permission/PermissionControllerService.java +++ b/core/java/android/permission/PermissionControllerService.java @@ -16,6 +16,9 @@ package android.permission; +import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT; +import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED; +import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED; import static android.permission.PermissionControllerManager.COUNT_ONLY_WHEN_GRANTED; import static android.permission.PermissionControllerManager.COUNT_WHEN_SYSTEM; @@ -32,6 +35,7 @@ import android.annotation.BinderThread; import android.annotation.NonNull; import android.annotation.SystemApi; import android.app.Service; +import android.app.admin.DevicePolicyManager.PermissionGrantState; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; @@ -180,6 +184,18 @@ public abstract class PermissionControllerService extends Service { public abstract boolean onIsApplicationQualifiedForRole(@NonNull String roleName, @NonNull String packageName); + /** + * Set the runtime permission state from a device admin. + * + * @param callerPackageName The package name of the admin requesting the change + * @param packageName Package the permission belongs to + * @param permission Permission to change + * @param grantState State to set the permission into + */ + public abstract boolean onSetRuntimePermissionGrantStateByDeviceAdmin( + @NonNull String callerPackageName, @NonNull String packageName, + @NonNull String permission, @PermissionGrantState int grantState); + @Override public final IBinder onBind(Intent intent) { return new IPermissionController.Stub() { @@ -326,6 +342,35 @@ public abstract class PermissionControllerService extends Service { PermissionControllerService::isApplicationQualifiedForRole, PermissionControllerService.this, roleName, packageName, callback)); } + + @Override + public void setRuntimePermissionGrantStateByDeviceAdmin(String callerPackageName, + String packageName, String permission, int grantState, + RemoteCallback callback) { + checkStringNotEmpty(callerPackageName); + checkStringNotEmpty(packageName); + checkStringNotEmpty(permission); + checkArgument(grantState == PERMISSION_GRANT_STATE_GRANTED + || grantState == PERMISSION_GRANT_STATE_DENIED + || grantState == PERMISSION_GRANT_STATE_DEFAULT); + checkNotNull(callback); + + if (grantState == PERMISSION_GRANT_STATE_DENIED) { + enforceCallingPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS, null); + } + + if (grantState == PERMISSION_GRANT_STATE_DENIED) { + enforceCallingPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, null); + } + + enforceCallingPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, + null); + + mHandler.sendMessage(obtainMessage( + PermissionControllerService::setRuntimePermissionGrantStateByDeviceAdmin, + PermissionControllerService.this, callerPackageName, packageName, + permission, grantState, callback)); + } }; } @@ -399,4 +444,15 @@ public abstract class PermissionControllerService extends Service { result.putBoolean(PermissionControllerManager.KEY_RESULT, qualified); callback.sendResult(result); } + + private void setRuntimePermissionGrantStateByDeviceAdmin(@NonNull String callerPackageName, + @NonNull String packageName, @NonNull String permission, + @PermissionGrantState int grantState, @NonNull RemoteCallback callback) { + boolean wasSet = onSetRuntimePermissionGrantStateByDeviceAdmin(callerPackageName, + packageName, permission, grantState); + + Bundle result = new Bundle(); + result.putBoolean(PermissionControllerManager.KEY_RESULT, wasSet); + callback.sendResult(result); + } } diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java index f63c0adbdf4b..44adc1c5cba4 100644 --- a/core/java/android/provider/CallLog.java +++ b/core/java/android/provider/CallLog.java @@ -783,7 +783,7 @@ public class CallLog { String postDialDigits, String viaNumber, int presentation, int callType, int features, PhoneAccountHandle accountHandle, long start, int duration, Long dataUsage, boolean addForAllUsers, UserHandle userToBeInsertedTo, - boolean isRead, int callBlockReason, String callScreeningAppName, + boolean isRead, int callBlockReason, CharSequence callScreeningAppName, String callScreeningComponentName, CallIdentification callIdentification) { if (VERBOSE_LOG) { Log.v(LOG_TAG, String.format("Add call: number=%s, user=%s, for all=%s", @@ -836,15 +836,19 @@ public class CallLog { } values.put(BLOCK_REASON, callBlockReason); - values.put(CALL_SCREENING_APP_NAME, callScreeningAppName); + values.put(CALL_SCREENING_APP_NAME, charSequenceToString(callScreeningAppName)); values.put(CALL_SCREENING_COMPONENT_NAME, callScreeningComponentName); if (callIdentification != null) { values.put(CALL_ID_PACKAGE_NAME, callIdentification.getCallScreeningPackageName()); - values.put(CALL_ID_APP_NAME, callIdentification.getCallScreeningAppName()); - values.put(CALL_ID_NAME, callIdentification.getName()); - values.put(CALL_ID_DESCRIPTION, callIdentification.getDescription()); - values.put(CALL_ID_DETAILS, callIdentification.getDetails()); + values.put(CALL_ID_APP_NAME, + charSequenceToString(callIdentification.getCallScreeningAppName())); + values.put(CALL_ID_NAME, + charSequenceToString(callIdentification.getName())); + values.put(CALL_ID_DESCRIPTION, + charSequenceToString(callIdentification.getDescription())); + values.put(CALL_ID_DETAILS, + charSequenceToString(callIdentification.getDetails())); values.put(CALL_ID_NUISANCE_CONFIDENCE, callIdentification.getNuisanceConfidence()); } else { values.putNull(CALL_ID_PACKAGE_NAME); @@ -987,6 +991,10 @@ public class CallLog { return result; } + private static String charSequenceToString(CharSequence sequence) { + return sequence == null ? null : sequence.toString(); + } + /** @hide */ public static boolean shouldHaveSharedCallLogEntries(Context context, UserManager userManager, int userId) { diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index f6a8388a7d40..fc89e2ca07e3 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -55,6 +55,15 @@ public final class DeviceConfig { public static final Uri CONTENT_URI = Uri.parse("content://" + Settings.AUTHORITY + "/config"); /** + * Namespace for activity manager related features. These features will be applied + * immediately upon change. + * + * @hide + */ + @SystemApi + public static final String NAMESPACE_ACTIVITY_MANAGER = "activity_manager"; + + /** * Namespace for all Game Driver features. * * @hide @@ -100,27 +109,12 @@ public final class DeviceConfig { public static final String NAMESPACE_NETD_NATIVE = "netd_native"; /** - * Namespace for features related to the ExtServices Notification Assistant. - * These features are applied immediately. + * Namespace for System UI related features. * * @hide */ @SystemApi - public interface NotificationAssistant { - String NAMESPACE = "notification_assistant"; - /** - * Whether the Notification Assistant should generate replies for notifications. - */ - String GENERATE_REPLIES = "generate_replies"; - /** - * Whether the Notification Assistant should generate contextual actions for notifications. - */ - String GENERATE_ACTIONS = "generate_actions"; - - String MAX_MESSAGES_TO_EXTRACT = "max_messages_to_extract"; - - String MAX_SUGGESTIONS = "max_suggestions"; - } + public static final String NAMESPACE_SYSTEMUI = "systemui"; /** * Namespace for all runtime related features. @@ -169,7 +163,7 @@ public final class DeviceConfig { * * @hide for internal use only */ - String BLASTULA_POOL_SIZE_MIN = "blastula_pool_size_max"; + String BLASTULA_POOL_SIZE_MIN = "blastula_pool_size_min"; /** * The threshold used to determine if the pool should be refilled. @@ -197,6 +191,7 @@ public final class DeviceConfig { */ @SystemApi public interface MediaNative { + /** The flag namespace for media native features. */ String NAMESPACE = "media_native"; } @@ -295,35 +290,6 @@ public final class DeviceConfig { } /** - * Namespace for activity manager related features. These features will be applied - * immediately upon change. - * - * @hide - */ - @SystemApi - public interface ActivityManager { - String NAMESPACE = "activity_manager"; - - /** - * App compaction flags. See {@link com.android.server.am.AppCompactor}. - */ - String KEY_USE_COMPACTION = "use_compaction"; - String KEY_COMPACT_ACTION_1 = "compact_action_1"; - String KEY_COMPACT_ACTION_2 = "compact_action_2"; - String KEY_COMPACT_THROTTLE_1 = "compact_throttle_1"; - String KEY_COMPACT_THROTTLE_2 = "compact_throttle_2"; - String KEY_COMPACT_THROTTLE_3 = "compact_throttle_3"; - String KEY_COMPACT_THROTTLE_4 = "compact_throttle_4"; - String KEY_COMPACT_STATSD_SAMPLE_RATE = "compact_statsd_sample_rate"; - - /** - * Maximum number of cached processes. See - * {@link com.android.server.am.ActivityManagerConstants}. - */ - String KEY_MAX_CACHED_PROCESSES = "max_cached_processes"; - } - - /** * Namespace for {@link AttentionManagerService} related features. * * @hide @@ -426,6 +392,100 @@ public final class DeviceConfig { } /** + * Look up the String value of a property for a particular namespace. + * + * @param namespace The namespace containing the property to look up. + * @param name The name of the property to look up. + * @return the corresponding value, or defaultValue if none exists. + * @hide + */ + @SystemApi + @TestApi + @RequiresPermission(READ_DEVICE_CONFIG) + public static String getString(String namespace, String name, String defaultValue) { + String value = getProperty(namespace, name); + return value != null ? value : defaultValue; + } + + /** + * Look up the boolean value of a property for a particular namespace. + * + * @param namespace The namespace containing the property to look up. + * @param name The name of the property to look up. + * @return the corresponding value, or defaultValue if none exists. + * @hide + */ + @SystemApi + @TestApi + @RequiresPermission(READ_DEVICE_CONFIG) + public static boolean getBoolean(String namespace, String name, boolean defaultValue) { + String value = getProperty(namespace, name); + return value != null ? Boolean.parseBoolean(value) : defaultValue; + } + + /** + * Look up the int value of a property for a particular namespace. + * + * @param namespace The namespace containing the property to look up. + * @param name The name of the property to look up. + * @return the corresponding value, or defaultValue if either none exists or it does not parse. + * @hide + */ + @SystemApi + @TestApi + @RequiresPermission(READ_DEVICE_CONFIG) + public static int getInt(String namespace, String name, int defaultValue) { + String value = getProperty(namespace, name); + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + /** + * Look up the long value of a property for a particular namespace. + * + * @param namespace The namespace containing the property to look up. + * @param name The name of the property to look up. + * @return the corresponding value, or defaultValue if either none exists or it does not parse. + * @hide + */ + @SystemApi + @TestApi + @RequiresPermission(READ_DEVICE_CONFIG) + public static long getLong(String namespace, String name, long defaultValue) { + String value = getProperty(namespace, name); + try { + return Long.parseLong(value); + } catch (NumberFormatException e) { + return defaultValue; + } + } + + /** + * Look up the float value of a property for a particular namespace. + * + * @param namespace The namespace containing the property to look up. + * @param name The name of the property to look up. + * @return the corresponding value, or defaultValue if either none exists or it does not parse. + * @hide + */ + @SystemApi + @TestApi + @RequiresPermission(READ_DEVICE_CONFIG) + public static float getFloat(String namespace, String name, float defaultValue) { + String value = getProperty(namespace, name); + try { + return Float.parseFloat(value); + } catch (NumberFormatException e) { + return defaultValue; + } catch (NullPointerException e) { + return defaultValue; + } + } + + /** * Create a new property with the the provided name and value in the provided namespace, or * update the value of such a property if it already exists. The same name can exist in multiple * namespaces and might have different values in any or all namespaces. diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index 643307eb89ae..758db4925e26 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -529,7 +529,6 @@ public final class MediaStore { * * @see MediaColumns#IS_PENDING * @see MediaStore#setIncludePending(Uri) - * @see MediaStore#createPending(Context, PendingParams) */ public static @NonNull Uri setIncludePending(@NonNull Uri uri) { return setIncludePending(uri.buildUpon()).build(); @@ -586,7 +585,9 @@ public final class MediaStore { * @see MediaColumns#IS_PENDING * @see MediaStore#setIncludePending(Uri) * @see MediaStore#createPending(Context, PendingParams) + * @removed */ + @Deprecated public static @NonNull Uri createPending(@NonNull Context context, @NonNull PendingParams params) { return context.getContentResolver().insert(params.insertUri, params.insertValues); @@ -599,14 +600,19 @@ public final class MediaStore { * * @param uri token which was previously returned from * {@link #createPending(Context, PendingParams)}. + * @removed */ + @Deprecated public static @NonNull PendingSession openPending(@NonNull Context context, @NonNull Uri uri) { return new PendingSession(context, uri); } /** * Parameters that describe a pending media item. + * + * @removed */ + @Deprecated public static class PendingParams { /** {@hide} */ public final Uri insertUri; @@ -711,7 +717,10 @@ public final class MediaStore { * expected to have a short lifetime, and owners should either * {@link PendingSession#publish()} or {@link PendingSession#abandon()} a * pending item within a few hours after first creating it. + * + * @removed */ + @Deprecated public static class PendingSession implements AutoCloseable { /** {@hide} */ private final Context mContext; @@ -976,9 +985,7 @@ public final class MediaStore { * Flag indicating if a media item is pending, and still being inserted * by its owner. * - * @see MediaColumns#IS_PENDING * @see MediaStore#setIncludePending(Uri) - * @see MediaStore#createPending(Context, PendingParams) */ @Column(Cursor.FIELD_TYPE_INTEGER) public static final String IS_PENDING = "is_pending"; @@ -998,12 +1005,8 @@ public final class MediaStore { /** * The time the media item should be considered expired. Typically only - * meaningful in the context of {@link #IS_PENDING} or - * {@link #IS_TRASHED}. - * - * @removed + * meaningful in the context of {@link #IS_PENDING}. */ - @Deprecated @CurrentTimeSecondsLong @Column(Cursor.FIELD_TYPE_INTEGER) public static final String DATE_EXPIRES = "date_expires"; @@ -1030,8 +1033,6 @@ public final class MediaStore { /** * The primary directory name this media exists under. The value may be * {@code NULL} if the media doesn't have a primary directory name. - * - * @see PendingParams#setPrimaryDirectory(String) */ @Column(Cursor.FIELD_TYPE_STRING) public static final String PRIMARY_DIRECTORY = "primary_directory"; @@ -1039,8 +1040,6 @@ public final class MediaStore { /** * The secondary directory name this media exists under. The value may * be {@code NULL} if the media doesn't have a secondary directory name. - * - * @see PendingParams#setSecondaryDirectory(String) */ @Column(Cursor.FIELD_TYPE_STRING) public static final String SECONDARY_DIRECTORY = "secondary_directory"; @@ -1292,12 +1291,7 @@ public final class MediaStore { } /** - * Container for downloaded files. - * - * <p> - * Querying for downloads from this table will return files contributed via - * {@link PendingSession} and also ones which were downloaded using - * {@link android.app.DownloadManager} APIs. + * Collection of downloaded items. */ public static final class Downloads implements DownloadColumns { private Downloads() {} @@ -1620,9 +1614,9 @@ public final class MediaStore { * @param name The name of the image * @param description The description of the image * @return The URL to the newly created image - * @deprecated inserting of images should be performed through - * {@link MediaStore#createPending(Context, PendingParams)}, - * which offers richer control over lifecycle. + * @deprecated inserting of images should be performed using + * {@link MediaColumns#IS_PENDING}, which offers richer + * control over lifecycle. */ @Deprecated public static final String insertImage(ContentResolver cr, String imagePath, @@ -1651,9 +1645,9 @@ public final class MediaStore { * @param description The description of the image * @return The URL to the newly created image, or <code>null</code> if the image failed to be stored * for any reason. - * @deprecated inserting of images should be performed through - * {@link MediaStore#createPending(Context, PendingParams)}, - * which offers richer control over lifecycle. + * @deprecated inserting of images should be performed using + * {@link MediaColumns#IS_PENDING}, which offers richer + * control over lifecycle. */ @Deprecated public static final String insertImage(ContentResolver cr, Bitmap source, diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index a465b3271407..0d7e00f708a3 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1204,6 +1204,21 @@ public final class Settings { public static final String ACTION_DREAM_SETTINGS = "android.settings.DREAM_SETTINGS"; /** + * Activity Action: Show Notification assistant settings. + * <p> + * In some cases, a matching Activity may not exist, so ensure you + * safeguard against this. + * <p> + * Input: Nothing. + * <p> + * Output: Nothing. + * @see android.service.notification.NotificationAssistantService + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_NOTIFICATION_ASSISTANT_SETTINGS = + "android.settings.NOTIFICATION_ASSISTANT_SETTINGS"; + + /** * Activity Action: Show Notification listener settings. * <p> * In some cases, a matching Activity may not exist, so ensure you @@ -7468,6 +7483,7 @@ public final class Settings { * @hide */ @SystemApi + @TestApi public static final String DOZE_ALWAYS_ON = "doze_always_on"; private static final Validator DOZE_ALWAYS_ON_VALIDATOR = BOOLEAN_VALIDATOR; @@ -8131,6 +8147,7 @@ public final class Settings { * * @hide */ + @TestApi public static final String ENABLED_VR_LISTENERS = "enabled_vr_listeners"; private static final Validator ENABLED_VR_LISTENERS_VALIDATOR = @@ -9028,7 +9045,6 @@ public final class Settings { * Whether applying ramping ringer on incoming phone call ringtone. * <p>1 = apply ramping ringer * <p>0 = do not apply ramping ringer - * @hide */ public static final String APPLY_RAMPING_RINGER = "apply_ramping_ringer"; @@ -10780,6 +10796,7 @@ public final class Settings { * * @hide */ + @TestApi public static final String OVERLAY_DISPLAY_DEVICES = "overlay_display_devices"; /** @@ -11400,7 +11417,7 @@ public final class Settings { /** * Feature flag to enable or disable the activity starts logging feature. * Type: int (0 for false, 1 for true) - * Default: 0 + * Default: 1 * @hide */ public static final String ACTIVITY_STARTS_LOGGING_ENABLED @@ -11506,6 +11523,7 @@ public final class Settings { * @hide * @see com.android.server.power.batterysaver.BatterySaverPolicy */ + @TestApi public static final String BATTERY_SAVER_CONSTANTS = "battery_saver_constants"; /** @@ -11929,6 +11947,7 @@ public final class Settings { * bcast_deferral (long) * bcast_deferral_decay_factor (float) * bcast_deferral_floor (long) + * bcast_allow_bg_activity_start_timeout (long) * </pre> * * @hide @@ -13045,6 +13064,15 @@ public final class Settings { */ public static final String LTE_SERVICE_FORCED = "lte_service_forced"; + + /** + * Specifies the behaviour the lid triggers when closed + * <p> + * See WindowManagerPolicy.WindowManagerFuncs + * @hide + */ + public static final String LID_BEHAVIOR = "lid_behavior"; + /** * Ephemeral app cookie max size in bytes. * <p> @@ -14096,17 +14124,16 @@ public final class Settings { public static final String SHOW_TEMPERATURE_WARNING = "show_temperature_warning"; /** - * Temperature at which the high temperature warning notification should be shown. + * Whether to show the usb high temperature alarm notification. * @hide */ - public static final String WARNING_TEMPERATURE = "warning_temperature"; - + public static final String SHOW_USB_TEMPERATURE_ALARM = "show_usb_temperature_alarm"; /** - * USB Temperature at which the high temperature alarm notification should be shown. + * Temperature at which the high temperature warning notification should be shown. * @hide */ - public static final String USB_ALARM_TEMPERATURE = "usb_alarm_temperature"; + public static final String WARNING_TEMPERATURE = "warning_temperature"; /** * Whether the diskstats logging task is enabled/disabled. @@ -14174,10 +14201,24 @@ public final class Settings { * Configuration flags for SQLite Compatibility WAL. Encoded as a key-value list, separated * by commas. E.g.: compatibility_wal_supported=true, wal_syncmode=OFF * - * Supported keys: - * compatibility_wal_supported (boolean) - * wal_syncmode (String) - * truncate_size (int) + * Supported keys:<br/> + * <li> + * <ul> {@code legacy_compatibility_wal_enabled} : A {code boolean} flag that determines + * whether or not "compatibility WAL" mode is enabled by default. This is a legacy flag + * and is honoured on Android Q and higher. This flag will be removed in a future release. + * </ul> + * <ul> {@code wal_syncmode} : A {@code String} representing the synchronization mode to use + * when WAL is enabled, either via {@code legacy_compatibility_wal_enabled} or using the + * obsolete {@code compatibility_wal_supported} flag. + * </ul> + * <ul> {@code truncate_size} : A {@code int} flag that specifies the truncate size of the + * WAL journal. + * </ul> + * <ul> {@code compatibility_wal_supported} : A {code boolean} flag that specifies whether + * the legacy "compatibility WAL" mode is enabled by default. This flag is obsolete and is + * only supported on Android Pie. + * </ul> + * </li> * * @hide */ diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java index 1d0c987403db..cbd0cd94c0e3 100644 --- a/core/java/android/service/autofill/FillRequest.java +++ b/core/java/android/service/autofill/FillRequest.java @@ -71,6 +71,14 @@ public final class FillRequest implements Parcelable { */ public static final int FLAG_COMPATIBILITY_MODE_REQUEST = 0x2; + // Private flags below start from the highest-significative bit (0x80000000) + /** + * Request was only triggered for augmented autofill. + * + * @hide + */ + public static final int FLAG_AUGMENTED_AUTOFILL_REQUEST = 0x80000000; + /** @hide */ public static final int INVALID_REQUEST_ID = Integer.MIN_VALUE; @@ -115,7 +123,8 @@ public final class FillRequest implements Parcelable { /** * Gets the flags associated with this request. * - * @see #FLAG_MANUAL_REQUEST + * @return any combination of {@link #FLAG_MANUAL_REQUEST} and + * {@link #FLAG_COMPATIBILITY_MODE_REQUEST}. */ public @RequestFlags int getFlags() { return mFlags; diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java index ce83a57bf68b..463eae681f05 100644 --- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java +++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java @@ -85,6 +85,18 @@ public abstract class AugmentedAutofillService extends Service { private final IAugmentedAutofillService mInterface = new IAugmentedAutofillService.Stub() { @Override + public void onConnected() { + mHandler.sendMessage(obtainMessage(AugmentedAutofillService::handleOnConnected, + AugmentedAutofillService.this)); + } + + @Override + public void onDisconnected() { + mHandler.sendMessage(obtainMessage(AugmentedAutofillService::handleOnDisconnected, + AugmentedAutofillService.this)); + } + + @Override public void onFillRequest(int sessionId, IBinder client, int taskId, ComponentName componentName, AutofillId focusedId, AutofillValue focusedValue, long requestTime, IFillCallback callback) { @@ -174,6 +186,14 @@ public abstract class AugmentedAutofillService extends Service { public void onDisconnected() { } + private void handleOnConnected() { + onConnected(); + } + + private void handleOnDisconnected() { + onDisconnected(); + } + private void handleOnFillRequest(int sessionId, @NonNull IBinder client, int taskId, @NonNull ComponentName componentName, @NonNull AutofillId focusedId, @Nullable AutofillValue focusedValue, long requestTime, @@ -189,7 +209,7 @@ public abstract class AugmentedAutofillService extends Service { } else { // TODO(b/123099468): figure out if it's ok to reuse the proxy; add logging if (DEBUG) Log.d(TAG, "Reusing proxy for session " + sessionId); - proxy.update(focusedId, focusedValue); + proxy.update(focusedId, focusedValue, callback); } // TODO(b/123101711): set cancellation signal final CancellationSignal cancellationSignal = null; @@ -279,13 +299,14 @@ public abstract class AugmentedAutofillService extends Service { private final Object mLock = new Object(); private final IAugmentedAutofillManagerClient mClient; private final int mSessionId; - private final IFillCallback mCallback; public final int taskId; public final ComponentName componentName; @GuardedBy("mLock") private AutofillId mFocusedId; @GuardedBy("mLock") private AutofillValue mFocusedValue; + @GuardedBy("mLock") + private IFillCallback mCallback; /** * Id of the last field that cause the Autofill UI to be shown. @@ -296,8 +317,8 @@ public abstract class AugmentedAutofillService extends Service { private AutofillId mLastShownId; // Objects used to log metrics - private final long mRequestTime; - private long mOnSuccessTime; + private final long mFirstRequestTime; + private long mFirstOnSuccessTime; private long mUiFirstShownTime; private long mUiFirstDestroyedTime; @@ -318,7 +339,7 @@ public abstract class AugmentedAutofillService extends Service { this.componentName = componentName; this.mFocusedId = focusedId; this.mFocusedValue = focusedValue; - this.mRequestTime = requestTime; + this.mFirstRequestTime = requestTime; // TODO(b/123099468): linkToDeath } @@ -380,11 +401,18 @@ public abstract class AugmentedAutofillService extends Service { mClient.requestHideFillUi(mSessionId, mFocusedId); } - private void update(@NonNull AutofillId focusedId, @NonNull AutofillValue focusedValue) { + private void update(@NonNull AutofillId focusedId, @NonNull AutofillValue focusedValue, + @NonNull IFillCallback callback) { synchronized (mLock) { // TODO(b/123099468): should we close the popupwindow if the focused id changed? mFocusedId = focusedId; mFocusedValue = focusedValue; + if (mCallback != null) { + // TODO(b/123101711): we need to check whether the previous request was + // completed or not, and if not, cancel it first. + Slog.d(TAG, "mCallback is updated."); + } + mCallback = callback; } } @@ -406,11 +434,11 @@ public abstract class AugmentedAutofillService extends Service { public void report(@ReportEvent int event) { switch (event) { case REPORT_EVENT_ON_SUCCESS: - if (mOnSuccessTime == 0) { - mOnSuccessTime = SystemClock.elapsedRealtime(); + if (mFirstOnSuccessTime == 0) { + mFirstOnSuccessTime = SystemClock.elapsedRealtime(); if (DEBUG) { - Slog.d(TAG, "Service responsed in " - + TimeUtils.formatDuration(mOnSuccessTime - mRequestTime)); + Slog.d(TAG, "Service responded in " + TimeUtils.formatDuration( + mFirstOnSuccessTime - mFirstRequestTime)); } } try { @@ -423,8 +451,8 @@ public abstract class AugmentedAutofillService extends Service { if (mUiFirstShownTime == 0) { mUiFirstShownTime = SystemClock.elapsedRealtime(); if (DEBUG) { - Slog.d(TAG, "UI shown in " - + TimeUtils.formatDuration(mUiFirstShownTime - mRequestTime)); + Slog.d(TAG, "UI shown in " + TimeUtils.formatDuration( + mUiFirstShownTime - mFirstRequestTime)); } } break; @@ -432,9 +460,8 @@ public abstract class AugmentedAutofillService extends Service { if (mUiFirstDestroyedTime == 0) { mUiFirstDestroyedTime = SystemClock.elapsedRealtime(); if (DEBUG) { - Slog.d(TAG, "UI destroyed in " - + TimeUtils.formatDuration( - mUiFirstDestroyedTime - mRequestTime)); + Slog.d(TAG, "UI destroyed in " + TimeUtils.formatDuration( + mUiFirstDestroyedTime - mFirstRequestTime)); } } break; @@ -466,20 +493,20 @@ public abstract class AugmentedAutofillService extends Service { pw.print(prefix); pw.println("smartSuggestion:"); mSmartSuggestion.dump(prefix2, pw); } - if (mOnSuccessTime > 0) { - final long responseTime = mOnSuccessTime - mRequestTime; + if (mFirstOnSuccessTime > 0) { + final long responseTime = mFirstOnSuccessTime - mFirstRequestTime; pw.print(prefix); pw.print("response time: "); TimeUtils.formatDuration(responseTime, pw); pw.println(); } if (mUiFirstShownTime > 0) { - final long uiRenderingTime = mUiFirstShownTime - mRequestTime; + final long uiRenderingTime = mUiFirstShownTime - mFirstRequestTime; pw.print(prefix); pw.print("UI rendering time: "); TimeUtils.formatDuration(uiRenderingTime, pw); pw.println(); } if (mUiFirstDestroyedTime > 0) { - final long uiTotalTime = mUiFirstDestroyedTime - mRequestTime; + final long uiTotalTime = mUiFirstDestroyedTime - mFirstRequestTime; pw.print(prefix); pw.print("UI life time: "); TimeUtils.formatDuration(uiTotalTime, pw); pw.println(); } @@ -490,6 +517,7 @@ public abstract class AugmentedAutofillService extends Service { if (mFillWindow != null) { if (DEBUG) Log.d(TAG, "destroying window"); mFillWindow.destroy(); + mFillWindow = null; } } } diff --git a/core/java/android/service/autofill/augmented/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java index 6e06754e7b8a..bd532a32035c 100644 --- a/core/java/android/service/autofill/augmented/FillWindow.java +++ b/core/java/android/service/autofill/augmented/FillWindow.java @@ -82,6 +82,8 @@ public final class FillWindow implements AutoCloseable { private Rect mBounds; @GuardedBy("mLock") + private boolean mUpdateCalled; + @GuardedBy("mLock") private boolean mDestroyed; private AutofillProxy mProxy; @@ -103,6 +105,7 @@ public final class FillWindow implements AutoCloseable { } // TODO(b/123100712): add test case for null Preconditions.checkNotNull(area); + Preconditions.checkNotNull(area.proxy); Preconditions.checkNotNull(rootView); // TODO(b/123100712): must check the area is a valid object returned by // SmartSuggestionParams, throw IAE if not @@ -149,6 +152,7 @@ public final class FillWindow implements AutoCloseable { if (DEBUG) { Log.d(TAG, "Created FillWindow: params= " + smartSuggestion + " view=" + rootView); } + mUpdateCalled = true; mDestroyed = false; mProxy.setFillWindow(this); return true; @@ -237,8 +241,10 @@ public final class FillWindow implements AutoCloseable { } synchronized (mLock) { if (mDestroyed) return; - hide(); - mProxy.report(AutofillProxy.REPORT_EVENT_UI_DESTROYED); + if (mUpdateCalled) { + hide(); + mProxy.report(AutofillProxy.REPORT_EVENT_UI_DESTROYED); + } mDestroyed = true; mCloseGuard.close(); } @@ -266,6 +272,7 @@ public final class FillWindow implements AutoCloseable { public void dump(@NonNull String prefix, @NonNull PrintWriter pw) { synchronized (this) { pw.print(prefix); pw.print("destroyed: "); pw.println(mDestroyed); + pw.print(prefix); pw.print("updateCalled: "); pw.println(mUpdateCalled); if (mFillView != null) { pw.print(prefix); pw.print("fill window: "); pw.println(mShowing ? "shown" : "hidden"); diff --git a/core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl b/core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl index fb6912ac6752..509681113c1f 100644 --- a/core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl +++ b/core/java/android/service/autofill/augmented/IAugmentedAutofillService.aidl @@ -31,7 +31,8 @@ import java.util.List; * @hide */ oneway interface IAugmentedAutofillService { - + void onConnected(); + void onDisconnected(); void onFillRequest(int sessionId, in IBinder autofillManagerClient, int taskId, in ComponentName activityComponent, in AutofillId focusedId, in AutofillValue focusedValue, long requestTime, in IFillCallback callback); diff --git a/core/java/android/service/carrier/CarrierMessagingClientService.java b/core/java/android/service/carrier/CarrierMessagingClientService.java index 13f4fc4b0dcb..767c1d1a49c6 100644 --- a/core/java/android/service/carrier/CarrierMessagingClientService.java +++ b/core/java/android/service/carrier/CarrierMessagingClientService.java @@ -15,6 +15,8 @@ */ package android.service.carrier; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.Service; import android.content.ComponentName; import android.content.Intent; @@ -71,7 +73,8 @@ public class CarrierMessagingClientService extends Service { } @Override - public final IBinder onBind(Intent intent) { + @NonNull + public final IBinder onBind(@Nullable Intent intent) { return mImpl.asBinder(); } diff --git a/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java b/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java deleted file mode 100644 index bbcf7a9c97b1..000000000000 --- a/core/java/android/service/contentcapture/ContentCaptureEventsRequest.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package android.service.contentcapture; - -import android.annotation.NonNull; -import android.annotation.SystemApi; -import android.annotation.TestApi; -import android.os.Parcel; -import android.os.Parcelable; -import android.view.contentcapture.ContentCaptureEvent; - -import java.util.Arrays; -import java.util.List; - -/** - * Not needed anymore... - * - * @deprecated ContentCaptureService should use - * {@code #onContentCaptureEvent(ContentCaptureSessionId, ContentCaptureEvent)} instead. - * - * @hide - */ -@SystemApi -@TestApi -@Deprecated -public final class ContentCaptureEventsRequest implements Parcelable { -// TODO(b/121051220): remove .java and .aidl once service implementation doesn't use it anymore - - private final ContentCaptureEvent mEvent; - - /** @hide */ - public ContentCaptureEventsRequest(@NonNull ContentCaptureEvent event) { - mEvent = event; - } - - /** - * Gets the events. - */ - @NonNull - public List<ContentCaptureEvent> getEvents() { - return Arrays.asList(mEvent); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel parcel, int flags) { - parcel.writeParcelable(mEvent, flags); - } - - public static final Parcelable.Creator<ContentCaptureEventsRequest> CREATOR = - new Parcelable.Creator<ContentCaptureEventsRequest>() { - - @Override - public ContentCaptureEventsRequest createFromParcel(Parcel parcel) { - return new ContentCaptureEventsRequest(parcel.readParcelable(null)); - } - - @Override - public ContentCaptureEventsRequest[] newArray(int size) { - return new ContentCaptureEventsRequest[size]; - } - }; -} diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java index d361a2c50935..9c4669f8dcd3 100644 --- a/core/java/android/service/contentcapture/ContentCaptureService.java +++ b/core/java/android/service/contentcapture/ContentCaptureService.java @@ -15,6 +15,9 @@ */ package android.service.contentcapture; +import static android.view.contentcapture.ContentCaptureHelper.sDebug; +import static android.view.contentcapture.ContentCaptureHelper.sVerbose; + import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import android.annotation.CallSuper; @@ -66,10 +69,6 @@ public abstract class ContentCaptureService extends Service { private static final String TAG = ContentCaptureService.class.getSimpleName(); - // TODO(b/121044306): STOPSHIP use dynamic value, or change to false - static final boolean DEBUG = true; - static final boolean VERBOSE = false; - /** * The {@link Intent} that must be declared as handled by the service. * @@ -89,7 +88,9 @@ public abstract class ContentCaptureService extends Service { private final IContentCaptureService mServerInterface = new IContentCaptureService.Stub() { @Override - public void onConnected(IBinder callback) { + public void onConnected(IBinder callback, boolean verbose, boolean debug) { + sVerbose = verbose; + sDebug = debug; mHandler.sendMessage(obtainMessage(ContentCaptureService::handleOnConnected, ContentCaptureService.this, callback)); } @@ -227,23 +228,12 @@ public abstract class ContentCaptureService extends Service { */ public void onCreateContentCaptureSession(@NonNull ContentCaptureContext context, @NonNull ContentCaptureSessionId sessionId) { - if (VERBOSE) { + if (sVerbose) { Log.v(TAG, "onCreateContentCaptureSession(id=" + sessionId + ", ctx=" + context + ")"); } } /** - * - * @deprecated use {@link #onContentCaptureEvent(ContentCaptureSessionId, ContentCaptureEvent)} - * instead. - */ - @Deprecated - public void onContentCaptureEventsRequest(@NonNull ContentCaptureSessionId sessionId, - @NonNull ContentCaptureEventsRequest request) { - if (VERBOSE) Log.v(TAG, "onContentCaptureEventsRequest(id=" + sessionId + ")"); - } - - /** * Notifies the service of {@link ContentCaptureEvent events} associated with a content capture * session. * @@ -252,8 +242,7 @@ public abstract class ContentCaptureService extends Service { */ public void onContentCaptureEvent(@NonNull ContentCaptureSessionId sessionId, @NonNull ContentCaptureEvent event) { - if (VERBOSE) Log.v(TAG, "onContentCaptureEventsRequest(id=" + sessionId + ")"); - onContentCaptureEventsRequest(sessionId, new ContentCaptureEventsRequest(event)); + if (sVerbose) Log.v(TAG, "onContentCaptureEventsRequest(id=" + sessionId + ")"); } /** @@ -262,7 +251,7 @@ public abstract class ContentCaptureService extends Service { * @param request the user data requested to be removed */ public void onUserDataRemovalRequest(@NonNull UserDataRemovalRequest request) { - if (VERBOSE) Log.v(TAG, "onUserDataRemovalRequest()"); + if (sVerbose) Log.v(TAG, "onUserDataRemovalRequest()"); } /** @@ -280,14 +269,14 @@ public abstract class ContentCaptureService extends Service { * @param sessionId the id of the session to destroy * */ public void onDestroyContentCaptureSession(@NonNull ContentCaptureSessionId sessionId) { - if (VERBOSE) Log.v(TAG, "onDestroyContentCaptureSession(id=" + sessionId + ")"); + if (sVerbose) Log.v(TAG, "onDestroyContentCaptureSession(id=" + sessionId + ")"); } /** * Disables the Content Capture service for the given user. */ public final void disableContentCaptureServices() { - if (DEBUG) Log.d(TAG, "disableContentCaptureServices()"); + if (sDebug) Log.d(TAG, "disableContentCaptureServices()"); final IContentCaptureServiceCallback callback = mCallback; if (callback == null) { @@ -313,6 +302,7 @@ public abstract class ContentCaptureService extends Service { @Override @CallSuper protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.print("Debug: "); pw.print(sDebug); pw.print(" Verbose: "); pw.println(sVerbose); final int size = mSessionUids.size(); pw.print("Number sessions: "); pw.println(size); if (size > 0) { @@ -422,7 +412,7 @@ public abstract class ContentCaptureService extends Service { } final Integer rightUid = mSessionUids.get(sessionId); if (rightUid == null) { - if (VERBOSE) { + if (sVerbose) { Log.v(TAG, "handleIsRightCallerFor(" + event + "): no session for " + sessionId + ": " + mSessionUids); } diff --git a/core/java/android/service/contentcapture/IContentCaptureService.aidl b/core/java/android/service/contentcapture/IContentCaptureService.aidl index d92fb3bed679..eb650324a942 100644 --- a/core/java/android/service/contentcapture/IContentCaptureService.aidl +++ b/core/java/android/service/contentcapture/IContentCaptureService.aidl @@ -31,7 +31,7 @@ import java.util.List; * @hide */ oneway interface IContentCaptureService { - void onConnected(IBinder callback); + void onConnected(IBinder callback, boolean verbose, boolean debug); void onDisconnected(); void onSessionStarted(in ContentCaptureContext context, String sessionId, int uid, in IResultReceiver clientReceiver); diff --git a/core/java/android/service/contentsuggestions/ContentSuggestionsService.java b/core/java/android/service/contentsuggestions/ContentSuggestionsService.java index 333f4bec8f7e..40333bf7709e 100644 --- a/core/java/android/service/contentsuggestions/ContentSuggestionsService.java +++ b/core/java/android/service/contentsuggestions/ContentSuggestionsService.java @@ -60,11 +60,17 @@ public abstract class ContentSuggestionsService extends Service { @Override public void provideContextImage(int taskId, GraphicBuffer contextImage, Bundle imageContextRequestExtras) { + + Bitmap wrappedBuffer = null; + if (contextImage != null) { + wrappedBuffer = Bitmap.wrapHardwareBuffer( + HardwareBuffer.createFromGraphicBuffer(contextImage), null); + } + mHandler.sendMessage( obtainMessage(ContentSuggestionsService::processContextImage, ContentSuggestionsService.this, taskId, - Bitmap.wrapHardwareBuffer( - HardwareBuffer.createFromGraphicBuffer(contextImage), null), + wrappedBuffer, imageContextRequestExtras)); } diff --git a/core/java/android/service/euicc/IDeleteSubscriptionCallback.aidl b/core/java/android/service/euicc/IDeleteSubscriptionCallback.aidl index 4667066df866..aff8f1b7b346 100644 --- a/core/java/android/service/euicc/IDeleteSubscriptionCallback.aidl +++ b/core/java/android/service/euicc/IDeleteSubscriptionCallback.aidl @@ -18,5 +18,6 @@ package android.service.euicc; /** @hide */ oneway interface IDeleteSubscriptionCallback { + @UnsupportedAppUsage void onComplete(int result); }
\ No newline at end of file diff --git a/core/java/android/service/euicc/IEraseSubscriptionsCallback.aidl b/core/java/android/service/euicc/IEraseSubscriptionsCallback.aidl index c975f1894aa2..34b53cc71dfb 100644 --- a/core/java/android/service/euicc/IEraseSubscriptionsCallback.aidl +++ b/core/java/android/service/euicc/IEraseSubscriptionsCallback.aidl @@ -18,5 +18,6 @@ package android.service.euicc; /** @hide */ oneway interface IEraseSubscriptionsCallback { + @UnsupportedAppUsage void onComplete(int result); }
\ No newline at end of file diff --git a/core/java/android/service/euicc/IGetDefaultDownloadableSubscriptionListCallback.aidl b/core/java/android/service/euicc/IGetDefaultDownloadableSubscriptionListCallback.aidl index 0c5a0c692cb5..ad69ef132428 100644 --- a/core/java/android/service/euicc/IGetDefaultDownloadableSubscriptionListCallback.aidl +++ b/core/java/android/service/euicc/IGetDefaultDownloadableSubscriptionListCallback.aidl @@ -20,5 +20,6 @@ import android.service.euicc.GetDefaultDownloadableSubscriptionListResult; /** @hide */ oneway interface IGetDefaultDownloadableSubscriptionListCallback { + @UnsupportedAppUsage void onComplete(in GetDefaultDownloadableSubscriptionListResult result); }
\ No newline at end of file diff --git a/core/java/android/service/euicc/IGetDownloadableSubscriptionMetadataCallback.aidl b/core/java/android/service/euicc/IGetDownloadableSubscriptionMetadataCallback.aidl index 3353061ad93e..01f187ed11e2 100644 --- a/core/java/android/service/euicc/IGetDownloadableSubscriptionMetadataCallback.aidl +++ b/core/java/android/service/euicc/IGetDownloadableSubscriptionMetadataCallback.aidl @@ -20,5 +20,6 @@ import android.service.euicc.GetDownloadableSubscriptionMetadataResult; /** @hide */ oneway interface IGetDownloadableSubscriptionMetadataCallback { + @UnsupportedAppUsage void onComplete(in GetDownloadableSubscriptionMetadataResult result); }
\ No newline at end of file diff --git a/core/java/android/service/euicc/IGetEidCallback.aidl b/core/java/android/service/euicc/IGetEidCallback.aidl index 35ee9c25a48e..e405a981c85a 100644 --- a/core/java/android/service/euicc/IGetEidCallback.aidl +++ b/core/java/android/service/euicc/IGetEidCallback.aidl @@ -18,5 +18,6 @@ package android.service.euicc; /** @hide */ oneway interface IGetEidCallback { + @UnsupportedAppUsage void onSuccess(String eid); }
\ No newline at end of file diff --git a/core/java/android/service/euicc/IGetEuiccInfoCallback.aidl b/core/java/android/service/euicc/IGetEuiccInfoCallback.aidl index 6d281487602a..c0611825ff0f 100644 --- a/core/java/android/service/euicc/IGetEuiccInfoCallback.aidl +++ b/core/java/android/service/euicc/IGetEuiccInfoCallback.aidl @@ -20,5 +20,6 @@ import android.telephony.euicc.EuiccInfo; /** @hide */ oneway interface IGetEuiccInfoCallback { + @UnsupportedAppUsage void onSuccess(in EuiccInfo euiccInfo); }
\ No newline at end of file diff --git a/core/java/android/service/euicc/IGetEuiccProfileInfoListCallback.aidl b/core/java/android/service/euicc/IGetEuiccProfileInfoListCallback.aidl index 761ec1f01324..0485f7be29d3 100644 --- a/core/java/android/service/euicc/IGetEuiccProfileInfoListCallback.aidl +++ b/core/java/android/service/euicc/IGetEuiccProfileInfoListCallback.aidl @@ -20,5 +20,6 @@ import android.service.euicc.GetEuiccProfileInfoListResult; /** @hide */ oneway interface IGetEuiccProfileInfoListCallback { + @UnsupportedAppUsage void onComplete(in GetEuiccProfileInfoListResult result); }
\ No newline at end of file diff --git a/core/java/android/service/euicc/IRetainSubscriptionsForFactoryResetCallback.aidl b/core/java/android/service/euicc/IRetainSubscriptionsForFactoryResetCallback.aidl index 127683059c02..340401fe89cb 100644 --- a/core/java/android/service/euicc/IRetainSubscriptionsForFactoryResetCallback.aidl +++ b/core/java/android/service/euicc/IRetainSubscriptionsForFactoryResetCallback.aidl @@ -18,5 +18,6 @@ package android.service.euicc; /** @hide */ oneway interface IRetainSubscriptionsForFactoryResetCallback { + @UnsupportedAppUsage void onComplete(int result); }
\ No newline at end of file diff --git a/core/java/android/service/euicc/ISwitchToSubscriptionCallback.aidl b/core/java/android/service/euicc/ISwitchToSubscriptionCallback.aidl index 0f91a6b29e26..b8f984d1c28b 100644 --- a/core/java/android/service/euicc/ISwitchToSubscriptionCallback.aidl +++ b/core/java/android/service/euicc/ISwitchToSubscriptionCallback.aidl @@ -18,5 +18,6 @@ package android.service.euicc; /** @hide */ oneway interface ISwitchToSubscriptionCallback { + @UnsupportedAppUsage void onComplete(int result); }
\ No newline at end of file diff --git a/core/java/android/service/euicc/IUpdateSubscriptionNicknameCallback.aidl b/core/java/android/service/euicc/IUpdateSubscriptionNicknameCallback.aidl index 66669330c8c0..0aa66978bb91 100644 --- a/core/java/android/service/euicc/IUpdateSubscriptionNicknameCallback.aidl +++ b/core/java/android/service/euicc/IUpdateSubscriptionNicknameCallback.aidl @@ -18,5 +18,6 @@ package android.service.euicc; /** @hide */ oneway interface IUpdateSubscriptionNicknameCallback { + @UnsupportedAppUsage void onComplete(int result); }
\ No newline at end of file diff --git a/core/java/android/service/vr/IVrManager.aidl b/core/java/android/service/vr/IVrManager.aidl index b0269e3325bf..a8293b47db30 100644 --- a/core/java/android/service/vr/IVrManager.aidl +++ b/core/java/android/service/vr/IVrManager.aidl @@ -57,6 +57,7 @@ interface IVrManager { * * @return {@code true} if VR mode is enabled. */ + @UnsupportedAppUsage boolean getVrModeState(); /** @@ -93,6 +94,7 @@ interface IVrManager { * @return {@link android.view.Display.INVALID_DISPLAY} if there is no virtual display * currently, else return the display id of the virtual display */ + @UnsupportedAppUsage int getVr2dDisplayId(); /** diff --git a/core/java/android/service/wallpaper/IWallpaperEngine.aidl b/core/java/android/service/wallpaper/IWallpaperEngine.aidl index ebce4846c16b..00e0b7c170be 100644 --- a/core/java/android/service/wallpaper/IWallpaperEngine.aidl +++ b/core/java/android/service/wallpaper/IWallpaperEngine.aidl @@ -26,11 +26,15 @@ import android.os.Bundle; oneway interface IWallpaperEngine { void setDesiredSize(int width, int height); void setDisplayPadding(in Rect padding); + @UnsupportedAppUsage void setVisibility(boolean visible); void setInAmbientMode(boolean inAmbientDisplay, long animationDuration); + @UnsupportedAppUsage void dispatchPointer(in MotionEvent event); + @UnsupportedAppUsage void dispatchWallpaperCommand(String action, int x, int y, int z, in Bundle extras); void requestWallpaperColors(); + @UnsupportedAppUsage void destroy(); } diff --git a/core/java/android/speech/IRecognitionListener.aidl b/core/java/android/speech/IRecognitionListener.aidl index 3d3c44bf1efe..e77851b0e405 100644 --- a/core/java/android/speech/IRecognitionListener.aidl +++ b/core/java/android/speech/IRecognitionListener.aidl @@ -83,5 +83,6 @@ oneway interface IRecognitionListener { * @param eventType the type of the occurred event * @param params a Bundle containing the passed parameters */ + @UnsupportedAppUsage void onEvent(in int eventType, in Bundle params); } diff --git a/core/java/android/text/style/DynamicDrawableSpan.java b/core/java/android/text/style/DynamicDrawableSpan.java index 575401458691..1a508a1f6c6d 100644 --- a/core/java/android/text/style/DynamicDrawableSpan.java +++ b/core/java/android/text/style/DynamicDrawableSpan.java @@ -16,6 +16,9 @@ package android.text.style; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; @@ -25,6 +28,7 @@ import android.graphics.Paint; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import java.lang.annotation.Retention; import java.lang.ref.WeakReference; /** @@ -80,10 +84,21 @@ public abstract class DynamicDrawableSpan extends ReplacementSpan { /** * A constant indicating that this span should be vertically centered between * the top and the lowest descender. - * @hide */ public static final int ALIGN_CENTER = 2; + /** + * Defines acceptable alignment types. + * @hide + */ + @Retention(SOURCE) + @IntDef(prefix = { "ALIGN_" }, value = { + ALIGN_BOTTOM, + ALIGN_BASELINE, + ALIGN_CENTER + }) + public @interface AlignmentType {} + protected final int mVerticalAlignment; @UnsupportedAppUsage @@ -100,17 +115,18 @@ public abstract class DynamicDrawableSpan extends ReplacementSpan { /** * Creates a {@link DynamicDrawableSpan} based on a vertical alignment.\ * - * @param verticalAlignment one of {@link #ALIGN_BOTTOM} or {@link #ALIGN_BASELINE} + * @param verticalAlignment one of {@link #ALIGN_BOTTOM}, {@link #ALIGN_BASELINE} or + * {@link #ALIGN_CENTER} */ - protected DynamicDrawableSpan(int verticalAlignment) { + protected DynamicDrawableSpan(@AlignmentType int verticalAlignment) { mVerticalAlignment = verticalAlignment; } /** - * Returns the vertical alignment of this span, one of {@link #ALIGN_BOTTOM} or - * {@link #ALIGN_BASELINE}. + * Returns the vertical alignment of this span, one of {@link #ALIGN_BOTTOM}, + * {@link #ALIGN_BASELINE} or {@link #ALIGN_CENTER}. */ - public int getVerticalAlignment() { + public @AlignmentType int getVerticalAlignment() { return mVerticalAlignment; } diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index 3e3a623ca8ef..fac699ece642 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -45,13 +45,13 @@ public class FeatureFlagUtils { static { DEFAULT_FLAGS = new HashMap<>(); DEFAULT_FLAGS.put("settings_audio_switcher", "true"); - DEFAULT_FLAGS.put("settings_dynamic_homepage", "true"); DEFAULT_FLAGS.put("settings_mobile_network_v2", "true"); DEFAULT_FLAGS.put("settings_network_and_internet_v2", "false"); DEFAULT_FLAGS.put("settings_slice_injection", "true"); DEFAULT_FLAGS.put("settings_systemui_theme", "true"); DEFAULT_FLAGS.put("settings_wifi_mac_randomization", "true"); DEFAULT_FLAGS.put("settings_mainline_module", "false"); + DEFAULT_FLAGS.put("settings_dynamic_android", "false"); DEFAULT_FLAGS.put(SEAMLESS_TRANSFER, "false"); DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false"); DEFAULT_FLAGS.put(SAFETY_HUB, "false"); diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java index e4c8eeb3b9b0..f8b38e9d215d 100644 --- a/core/java/android/util/TimeUtils.java +++ b/core/java/android/util/TimeUtils.java @@ -18,6 +18,7 @@ package android.util; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.SystemClock; @@ -302,6 +303,7 @@ public class TimeUtils { } /** @hide Just for debugging; not internationalized. */ + @TestApi public static String formatDuration(long duration) { synchronized (sFormatSync) { int len = formatDurationLocked(duration, 0); diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index cb5100a4d57a..94a9a1c0ff30 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -21,6 +21,7 @@ import static android.Manifest.permission.CONFIGURE_DISPLAY_COLOR_MODE; import android.annotation.IntDef; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.app.KeyguardManager; import android.content.res.CompatibilityInfo; @@ -911,6 +912,7 @@ public final class Display { * @see #FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS * @hide */ + @TestApi // TODO (b/114338689): Remove the method and use IWindowManager#shouldShowSystemDecors public boolean supportsSystemDecorations() { return (mDisplayInfo.flags & FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0; diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java index 3e8002f4634d..60daddd663d5 100644 --- a/core/java/android/view/DisplayEventReceiver.java +++ b/core/java/android/view/DisplayEventReceiver.java @@ -156,6 +156,17 @@ public abstract class DisplayEventReceiver { } /** + * Called when a display config changed event is received. + * + * @param timestampNanos The timestamp of the event, in the {@link System#nanoTime()} + * timebase. + * @param physicalDisplayId Stable display ID that uniquely describes a (display, port) pair. + * @param configId The new config Id + */ + public void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId) { + } + + /** * Schedules a single vertical sync pulse to be delivered when the next * display frame begins. */ @@ -182,4 +193,11 @@ public abstract class DisplayEventReceiver { private void dispatchHotplug(long timestampNanos, long physicalDisplayId, boolean connected) { onHotplug(timestampNanos, physicalDisplayId, connected); } + + // Called from native code. + @SuppressWarnings("unused") + private void dispatchConfigChanged(long timestampNanos, long physicalDisplayId, int configId) { + onConfigChanged(timestampNanos, physicalDisplayId, configId); + } + } diff --git a/core/java/android/view/GestureExclusionTracker.java b/core/java/android/view/GestureExclusionTracker.java new file mode 100644 index 000000000000..8eccc04fa647 --- /dev/null +++ b/core/java/android/view/GestureExclusionTracker.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.graphics.Rect; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +/** + * Used by {@link ViewRootImpl} to track system gesture exclusion rects reported by views. + */ +class GestureExclusionTracker { + private boolean mGestureExclusionViewsChanged = false; + private List<GestureExclusionViewInfo> mGestureExclusionViewInfos = new ArrayList<>(); + private List<Rect> mGestureExclusionRects = Collections.emptyList(); + + public void updateRectsForView(@NonNull View view) { + boolean found = false; + final Iterator<GestureExclusionViewInfo> i = mGestureExclusionViewInfos.iterator(); + while (i.hasNext()) { + final GestureExclusionViewInfo info = i.next(); + final View v = info.getView(); + if (v == null || !v.isAttachedToWindow()) { + mGestureExclusionViewsChanged = true; + i.remove(); + continue; + } + if (v == view) { + found = true; + info.mDirty = true; + break; + } + } + if (!found && view.isAttachedToWindow()) { + mGestureExclusionViewInfos.add(new GestureExclusionViewInfo(view)); + mGestureExclusionViewsChanged = true; + } + } + + @Nullable + public List<Rect> computeChangedRects() { + boolean changed = false; + final Iterator<GestureExclusionViewInfo> i = mGestureExclusionViewInfos.iterator(); + final List<Rect> rects = new ArrayList<>(); + while (i.hasNext()) { + final GestureExclusionViewInfo info = i.next(); + switch (info.update()) { + case GestureExclusionViewInfo.CHANGED: + changed = true; + // Deliberate fall-through + case GestureExclusionViewInfo.UNCHANGED: + rects.addAll(info.mExclusionRects); + break; + case GestureExclusionViewInfo.GONE: + mGestureExclusionViewsChanged = true; + i.remove(); + break; + } + } + if (changed || mGestureExclusionViewsChanged) { + mGestureExclusionViewsChanged = false; + if (!mGestureExclusionRects.equals(rects)) { + mGestureExclusionRects = rects; + return rects; + } + } + return null; + } + + private static class GestureExclusionViewInfo { + public static final int CHANGED = 0; + public static final int UNCHANGED = 1; + public static final int GONE = 2; + + private final WeakReference<View> mView; + boolean mDirty = true; + List<Rect> mExclusionRects = Collections.emptyList(); + + GestureExclusionViewInfo(View view) { + mView = new WeakReference<>(view); + } + + public View getView() { + return mView.get(); + } + + public int update() { + final View excludedView = getView(); + if (excludedView == null || !excludedView.isAttachedToWindow()) return GONE; + final List<Rect> localRects = excludedView.getSystemGestureExclusionRects(); + final List<Rect> newRects = new ArrayList<>(localRects.size()); + for (Rect src : localRects) { + Rect mappedRect = new Rect(src); + ViewParent p = excludedView.getParent(); + if (p != null && p.getChildVisibleRect(excludedView, mappedRect, null)) { + newRects.add(mappedRect); + } + } + + if (mExclusionRects.equals(localRects)) return UNCHANGED; + mExclusionRects = newRects; + return CHANGED; + } + } +} diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl index 94b9bc099d56..b1f934a44cdb 100644 --- a/core/java/android/view/IRecentsAnimationController.aidl +++ b/core/java/android/view/IRecentsAnimationController.aidl @@ -33,6 +33,7 @@ interface IRecentsAnimationController { * Takes a screenshot of the task associated with the given {@param taskId}. Only valid for the * current set of task ids provided to the handler. */ + @UnsupportedAppUsage ActivityManager.TaskSnapshot screenshotTask(int taskId); /** @@ -41,6 +42,7 @@ interface IRecentsAnimationController { * the home activity should be moved to the top. Otherwise, the home activity is hidden and the * user is returned to the app. */ + @UnsupportedAppUsage void finish(boolean moveHomeToTop); /** @@ -50,6 +52,7 @@ interface IRecentsAnimationController { * may register the recents animation input consumer prior to starting the recents animation * and then enable it mid-animation to start receiving touch events. */ + @UnsupportedAppUsage void setInputConsumerEnabled(boolean enabled); /** @@ -58,6 +61,7 @@ interface IRecentsAnimationController { * they can control the SystemUI flags, otherwise the SystemUI flags from home activity will be * taken. */ + @UnsupportedAppUsage void setAnimationTargetsBehindSystemBars(boolean behindSystemBars); /** diff --git a/core/java/android/view/IRecentsAnimationRunner.aidl b/core/java/android/view/IRecentsAnimationRunner.aidl index 4cdf66441514..6e382f416b62 100644 --- a/core/java/android/view/IRecentsAnimationRunner.aidl +++ b/core/java/android/view/IRecentsAnimationRunner.aidl @@ -33,6 +33,7 @@ oneway interface IRecentsAnimationRunner { * wallpaper not drawing in time, or the handler not finishing the animation within a predefined * amount of time. */ + @UnsupportedAppUsage void onAnimationCanceled() = 1; /** @@ -42,6 +43,7 @@ oneway interface IRecentsAnimationRunner { * @param minimizedHomeBounds Specifies the bounds of the minimized home app, will be * {@code null} if the device is not currently in split screen */ + @UnsupportedAppUsage void onAnimationStart(in IRecentsAnimationController controller, in RemoteAnimationTarget[] apps, in Rect homeContentInsets, in Rect minimizedHomeBounds) = 2; diff --git a/core/java/android/view/IRemoteAnimationFinishedCallback.aidl b/core/java/android/view/IRemoteAnimationFinishedCallback.aidl index ae58b226ec03..a99162b6c857 100644 --- a/core/java/android/view/IRemoteAnimationFinishedCallback.aidl +++ b/core/java/android/view/IRemoteAnimationFinishedCallback.aidl @@ -23,5 +23,6 @@ package android.view; * {@hide} */ interface IRemoteAnimationFinishedCallback { + @UnsupportedAppUsage void onAnimationFinished(); } diff --git a/core/java/android/view/IRemoteAnimationRunner.aidl b/core/java/android/view/IRemoteAnimationRunner.aidl index 1350ebf10a4f..73b023c99665 100644 --- a/core/java/android/view/IRemoteAnimationRunner.aidl +++ b/core/java/android/view/IRemoteAnimationRunner.aidl @@ -33,6 +33,7 @@ oneway interface IRemoteAnimationRunner { * @param apps The list of apps to animate. * @param finishedCallback The callback to invoke when the animation is finished. */ + @UnsupportedAppUsage void onAnimationStart(in RemoteAnimationTarget[] apps, in IRemoteAnimationFinishedCallback finishedCallback); @@ -40,5 +41,6 @@ oneway interface IRemoteAnimationRunner { * Called when the animation was cancelled. From this point on, any updates onto the leashes * won't have any effect anymore. */ + @UnsupportedAppUsage void onAnimationCancelled(); } diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 2ef7c4b16d9d..e32c4e136a6e 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -74,10 +74,13 @@ interface IWindowManager IWindowSession openSession(in IWindowSessionCallback callback); + @UnsupportedAppUsage void getInitialDisplaySize(int displayId, out Point size); + @UnsupportedAppUsage void getBaseDisplaySize(int displayId, out Point size); void setForcedDisplaySize(int displayId, int width, int height); void clearForcedDisplaySize(int displayId); + @UnsupportedAppUsage int getInitialDisplayDensity(int displayId); int getBaseDisplayDensity(int displayId); void setForcedDisplayDensityForUser(int displayId, int density, int userId); @@ -97,17 +100,21 @@ interface IWindowManager * used for recents, where generating the thumbnails of the specs takes a non-trivial amount of * time, so we want to move that off the critical path for starting the new activity. */ + @UnsupportedAppUsage void overridePendingAppTransitionMultiThumbFuture( IAppTransitionAnimationSpecsFuture specsFuture, IRemoteCallback startedCallback, boolean scaleUp, int displayId); + @UnsupportedAppUsage void overridePendingAppTransitionRemote(in RemoteAnimationAdapter remoteAnimationAdapter, int displayId); + @UnsupportedAppUsage void executeAppTransition(); /** * Used by system ui to report that recents has shown itself. * @deprecated to be removed once prebuilts are updated */ + @UnsupportedAppUsage void endProlongedAnimations(); void startFreezingScreen(int exitAnim, int enterAnim); @@ -119,7 +126,9 @@ interface IWindowManager /** @deprecated use Activity.setShowWhenLocked instead. */ void reenableKeyguard(IBinder token, int userId); void exitKeyguardSecurely(IOnKeyguardExitResult callback); + @UnsupportedAppUsage boolean isKeyguardLocked(); + @UnsupportedAppUsage boolean isKeyguardSecure(); void dismissKeyguard(IKeyguardDismissCallback callback, CharSequence message); @@ -129,9 +138,13 @@ interface IWindowManager void closeSystemDialogs(String reason); // These can only be called with the SET_ANIMATON_SCALE permission. + @UnsupportedAppUsage float getAnimationScale(int which); + @UnsupportedAppUsage float[] getAnimationScales(); + @UnsupportedAppUsage void setAnimationScale(int which, float scale); + @UnsupportedAppUsage void setAnimationScales(in float[] scales); float getCurrentAnimatorScale(); @@ -150,6 +163,7 @@ interface IWindowManager // should be enabled. The 'enabled' value is null or blank for // the system default (differs per build variant) or any valid // boolean string as parsed by SystemProperties.getBoolean(). + @UnsupportedAppUsage void setStrictModeVisualIndicatorPreference(String enabled); /** @@ -188,6 +202,7 @@ interface IWindowManager * Remove a rotation watcher set using watchRotation. * @hide */ + @UnsupportedAppUsage void removeRotationWatcher(IRotationWatcher watcher); /** @@ -203,12 +218,14 @@ interface IWindowManager * Equivalent to calling {@link #freezeDisplayRotation(int, int)} with {@link * android.view.Display#DEFAULT_DISPLAY} and given rotation. */ + @UnsupportedAppUsage void freezeRotation(int rotation); /** * Equivalent to calling {@link #thawDisplayRotation(int)} with {@link * android.view.Display#DEFAULT_DISPLAY}. */ + @UnsupportedAppUsage void thawRotation(); /** @@ -286,11 +303,13 @@ interface IWindowManager /** * Called by System UI to notify of changes to the visibility and height of the shelf. */ + @UnsupportedAppUsage void setShelfHeight(boolean visible, int shelfHeight); /** * Called by System UI to enable or disable haptic feedback on the navigation bar buttons. */ + @UnsupportedAppUsage void setNavBarVirtualKeyHapticFeedbackEnabled(boolean enabled); /** @@ -298,6 +317,7 @@ interface IWindowManager * * @param displayId the id of display to check if there is a software navigation bar. */ + @UnsupportedAppUsage boolean hasNavigationBar(int displayId); /** @@ -308,11 +328,13 @@ interface IWindowManager /** * Lock the device immediately with the specified options (can be null). */ + @UnsupportedAppUsage void lockNow(in Bundle options); /** * Device is in safe mode. */ + @UnsupportedAppUsage boolean isSafeModeEnabled(); /** @@ -340,6 +362,7 @@ interface IWindowManager * @return the dock side the current docked stack is at; must be one of the * WindowManagerGlobal.DOCKED_* values */ + @UnsupportedAppUsage int getDockedStackSide(); /** @@ -352,6 +375,7 @@ interface IWindowManager * Registers a listener that will be called when the dock divider changes its visibility or when * the docked stack gets added/removed. */ + @UnsupportedAppUsage void registerDockedStackListener(IDockedStackListener listener); /** @@ -378,6 +402,7 @@ interface IWindowManager /** * Retrieves the current stable insets from the primary display. */ + @UnsupportedAppUsage void getStableInsets(int displayId, out Rect outInsets); /** @@ -400,6 +425,7 @@ interface IWindowManager /** * Create an input consumer by name and display id. */ + @UnsupportedAppUsage void createInputConsumer(IBinder token, String name, int displayId, out InputChannel inputChannel); @@ -407,6 +433,7 @@ interface IWindowManager * Destroy an input consumer by name and display id. * This method will also dispose the input channels associated with that InputConsumer. */ + @UnsupportedAppUsage boolean destroyInputConsumer(String name, int displayId); /** @@ -582,4 +609,13 @@ interface IWindowManager * display should be re-parented to. */ void reparentDisplayContent(int displayId, in SurfaceControl sc); + + /** + * Waits for transactions to get applied before injecting input. + * This includes waiting for the input windows to get sent to InputManager. + * + * This is needed for testing since the system add windows and injects input + * quick enough that the windows don't have time to get sent to InputManager. + */ + boolean injectInputAfterTransactionsApplied(in InputEvent ev, int mode); } diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 658f06ad21f9..1fcd4321cdde 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -46,6 +46,7 @@ interface IWindowSession { int addToDisplayWithoutInputChannel(IWindow window, int seq, in WindowManager.LayoutParams attrs, in int viewVisibility, in int layerStackId, out Rect outContentInsets, out Rect outStableInsets, out InsetsState insetsState); + @UnsupportedAppUsage void remove(IWindow window); /** @@ -122,6 +123,7 @@ interface IWindowSession { * completely transparent, allowing it to work with the surface flinger * to optimize compositing of this part of the window. */ + @UnsupportedAppUsage void setTransparentRegion(IWindow window, in Region region); /** @@ -143,12 +145,16 @@ interface IWindowSession { */ void getDisplayFrame(IWindow window, out Rect outDisplayFrame); + @UnsupportedAppUsage void finishDrawing(IWindow window); + @UnsupportedAppUsage void setInTouchMode(boolean showFocus); + @UnsupportedAppUsage boolean getInTouchMode(); - boolean performHapticFeedback(IWindow window, int effectId, boolean always); + @UnsupportedAppUsage + boolean performHapticFeedback(int effectId, boolean always); /** * Initiate the drag operation itself @@ -166,6 +172,7 @@ interface IWindowSession { * @param data Data transferred by drag and drop * @return Token of drag operation which will be passed to cancelDragAndDrop. */ + @UnsupportedAppUsage IBinder performDrag(IWindow window, int flags, in SurfaceControl surface, int touchSource, float touchX, float touchY, float thumbCenterX, float thumbCenterY, in ClipData data); @@ -199,6 +206,7 @@ interface IWindowSession { */ void setWallpaperPosition(IBinder windowToken, float x, float y, float xstep, float ystep); + @UnsupportedAppUsage void wallpaperOffsetsComplete(IBinder window); /** @@ -209,6 +217,7 @@ interface IWindowSession { Bundle sendWallpaperCommand(IBinder window, String action, int x, int y, int z, in Bundle extras, boolean sync); + @UnsupportedAppUsage void wallpaperCommandComplete(IBinder window, in Bundle result); /** diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java index dd88e3c34d30..50ef91f90997 100644 --- a/core/java/android/view/InsetsAnimationControlImpl.java +++ b/core/java/android/view/InsetsAnimationControlImpl.java @@ -20,6 +20,7 @@ import static android.view.InsetsState.INSET_SIDE_BOTTOM; import static android.view.InsetsState.INSET_SIDE_LEFT; import static android.view.InsetsState.INSET_SIDE_RIGHT; import static android.view.InsetsState.INSET_SIDE_TOP; +import static android.view.InsetsState.toPublicType; import android.annotation.Nullable; import android.graphics.Insets; @@ -33,6 +34,7 @@ import android.util.SparseSetArray; import android.view.InsetsState.InsetSide; import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams; import android.view.WindowInsets.Type.InsetType; +import android.view.WindowInsetsAnimationListener.InsetsAnimation; import android.view.WindowManager.LayoutParams; import com.android.internal.annotations.VisibleForTesting; @@ -66,8 +68,12 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll private final Supplier<SyncRtSurfaceTransactionApplier> mTransactionApplierSupplier; private final InsetsController mController; private final WindowInsetsAnimationListener.InsetsAnimation mAnimation; + private final Rect mFrame; private Insets mCurrentInsets; private Insets mPendingInsets; + private boolean mFinished; + private boolean mCancelled; + private int mFinishedShownTypes; @VisibleForTesting public InsetsAnimationControlImpl(SparseArray<InsetsSourceConsumer> consumers, Rect frame, @@ -86,6 +92,7 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll null /* typeSideMap */); mShownInsets = calculateInsets(mInitialInsetsState, frame, consumers, true /* shown */, mTypeSideMap); + mFrame = new Rect(frame); buildTypeSourcesMap(mTypeSideMap, mSideSourceMap, mConsumers); // TODO: Check for controllability first and wait for IME if needed. @@ -119,12 +126,26 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll @Override public void changeInsets(Insets insets) { + if (mFinished) { + throw new IllegalStateException( + "Can't change insets on an animation that is finished."); + } + if (mCancelled) { + throw new IllegalStateException( + "Can't change insets on an animation that is cancelled."); + } mPendingInsets = sanitize(insets); mController.scheduleApplyChangeInsets(); } @VisibleForTesting - public void applyChangeInsets(InsetsState state) { + /** + * @return Whether the finish callback of this animation should be invoked. + */ + public boolean applyChangeInsets(InsetsState state) { + if (mCancelled) { + return false; + } final Insets offset = Insets.subtract(mShownInsets, mPendingInsets); ArrayList<SurfaceParams> params = new ArrayList<>(); if (offset.left != 0) { @@ -144,13 +165,40 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll SyncRtSurfaceTransactionApplier applier = mTransactionApplierSupplier.get(); applier.scheduleApply(params.toArray(new SurfaceParams[params.size()])); mCurrentInsets = mPendingInsets; + if (mFinished) { + mController.notifyFinished(this, mFinishedShownTypes); + } + return mFinished; } @Override public void finish(int shownTypes) { - // TODO + if (mCancelled) { + return; + } + InsetsState state = new InsetsState(mController.getState()); + for (int i = mConsumers.size() - 1; i >= 0; i--) { + InsetsSourceConsumer consumer = mConsumers.valueAt(i); + boolean visible = (shownTypes & toPublicType(consumer.getType())) != 0; + state.getSource(consumer.getType()).setVisible(visible); + } + Insets insets = getInsetsFromState(state, mFrame, null /* typeSideMap */); + changeInsets(insets); + mFinished = true; + mFinishedShownTypes = shownTypes; + } + + @VisibleForTesting + public void onCancelled() { + if (mFinished) { + return; + } + mCancelled = true; + mListener.onCancelled(); + } - mController.dispatchAnimationFinished(mAnimation); + InsetsAnimation getAnimation() { + return mAnimation; } private Insets calculateInsets(InsetsState state, Rect frame, @@ -225,4 +273,3 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll } } } - diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 258600019e71..08f2e8d87352 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -17,6 +17,8 @@ package android.view; import static android.view.InsetsState.TYPE_IME; +import static android.view.InsetsState.toPublicType; +import static android.view.WindowInsets.Type.all; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -99,6 +101,7 @@ public class InsetsController implements WindowInsetsController { private final SparseArray<InsetsSourceControl> mTmpControlArray = new SparseArray<>(); private final ArrayList<InsetsAnimationControlImpl> mAnimationControls = new ArrayList<>(); + private final ArrayList<InsetsAnimationControlImpl> mTmpFinishedControls = new ArrayList<>(); private WindowInsets mLastInsets; private boolean mAnimCallbackScheduled; @@ -107,7 +110,6 @@ public class InsetsController implements WindowInsetsController { private final Rect mLastLegacyContentInsets = new Rect(); private final Rect mLastLegacyStableInsets = new Rect(); - private ObjectAnimator mAnimator; private @AnimationDirection int mAnimationDirection; private int mPendingTypesToShow; @@ -122,19 +124,29 @@ public class InsetsController implements WindowInsetsController { return; } + mTmpFinishedControls.clear(); InsetsState state = new InsetsState(mState, true /* copySources */); for (int i = mAnimationControls.size() - 1; i >= 0; i--) { - mAnimationControls.get(i).applyChangeInsets(state); + InsetsAnimationControlImpl control = mAnimationControls.get(i); + if (mAnimationControls.get(i).applyChangeInsets(state)) { + mTmpFinishedControls.add(control); + } } + WindowInsets insets = state.calculateInsets(mFrame, mLastInsets.isRound(), mLastInsets.shouldAlwaysConsumeNavBar(), mLastInsets.getDisplayCutout(), mLastLegacyContentInsets, mLastLegacyStableInsets, mLastLegacySoftInputMode, null /* typeSideMap */); mViewRoot.mView.dispatchWindowInsetsAnimationProgress(insets); + + for (int i = mTmpFinishedControls.size() - 1; i >= 0; i--) { + dispatchAnimationFinished(mTmpFinishedControls.get(i).getAnimation()); + } }; } - void onFrameChanged(Rect frame) { + @VisibleForTesting + public void onFrameChanged(Rect frame) { if (mFrame.equals(frame)) { return; } @@ -279,7 +291,8 @@ public class InsetsController implements WindowInsetsController { // nothing to animate. return; } - // TODO: Check whether we already have a controller. + cancelExistingControllers(types); + final ArraySet<Integer> internalTypes = mState.toInternalType(types); final SparseArray<InsetsSourceConsumer> consumers = new SparseArray<>(); @@ -321,7 +334,7 @@ public class InsetsController implements WindowInsetsController { // Show request switch(consumer.requestShow(fromIme)) { case ShowResult.SHOW_IMMEDIATELY: - typesReady |= InsetsState.toPublicType(TYPE_IME); + typesReady |= InsetsState.toPublicType(consumer.getType()); break; case ShowResult.SHOW_DELAYED: isReady = false; @@ -365,6 +378,36 @@ public class InsetsController implements WindowInsetsController { return typesReady; } + private void cancelExistingControllers(@InsetType int types) { + for (int i = mAnimationControls.size() - 1; i >= 0; i--) { + InsetsAnimationControlImpl control = mAnimationControls.get(i); + if ((control.getTypes() & types) != 0) { + cancelAnimation(control); + } + } + } + + @VisibleForTesting + public void notifyFinished(InsetsAnimationControlImpl controller, int shownTypes) { + mAnimationControls.remove(controller); + hideDirectly(controller.getTypes() & ~shownTypes); + showDirectly(controller.getTypes() & shownTypes); + } + + void notifyControlRevoked(InsetsSourceConsumer consumer) { + for (int i = mAnimationControls.size() - 1; i >= 0; i--) { + InsetsAnimationControlImpl control = mAnimationControls.get(i); + if ((control.getTypes() & toPublicType(consumer.getType())) != 0) { + cancelAnimation(control); + } + } + } + + private void cancelAnimation(InsetsAnimationControlImpl control) { + control.onCancelled(); + mAnimationControls.remove(control); + } + private void applyLocalVisibilityOverride() { for (int i = mSourceConsumers.size() - 1; i >= 0; i--) { final InsetsSourceConsumer controller = mSourceConsumers.valueAt(i); @@ -455,8 +498,13 @@ public class InsetsController implements WindowInsetsController { } WindowInsetsAnimationControlListener listener = new WindowInsetsAnimationControlListener() { + + private WindowInsetsAnimationController mController; + private ObjectAnimator mAnimator; + @Override public void onReady(WindowInsetsAnimationController controller, int types) { + mController = controller; if (show) { showDirectly(types); } else { @@ -474,10 +522,6 @@ public class InsetsController implements WindowInsetsController { : ANIMATION_DURATION_HIDE_MS); mAnimator.setInterpolator(INTERPOLATOR); mAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationCancel(Animator animation) { - onAnimationFinish(); - } @Override public void onAnimationEnd(Animator animation) { @@ -488,15 +532,15 @@ public class InsetsController implements WindowInsetsController { } @Override - public void onCancelled() {} + public void onCancelled() { + mAnimator.cancel(); + } private void onAnimationFinish() { mAnimationDirection = DIRECTION_NONE; + mController.finish(show ? types : 0); } }; - // TODO: Instead of clearing this here, properly wire up - // InsetsAnimationControlImpl.finish() to remove this from mAnimationControls. - mAnimationControls.clear(); // Show/hide animations always need to be relative to the display frame, in order that shown // and hidden state insets are correct. @@ -522,10 +566,7 @@ public class InsetsController implements WindowInsetsController { */ @VisibleForTesting public void cancelExistingAnimation() { - mAnimationDirection = DIRECTION_NONE; - if (mAnimator != null) { - mAnimator.cancel(); - } + cancelExistingControllers(all()); } void dump(String prefix, PrintWriter pw) { diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java index eab83ce34708..1383463ef72f 100644 --- a/core/java/android/view/InsetsSourceConsumer.java +++ b/core/java/android/view/InsetsSourceConsumer.java @@ -77,6 +77,9 @@ public class InsetsSourceConsumer { if (applyLocalVisibilityOverride()) { mController.notifyVisibilityChanged(); } + if (mSourceControl == null) { + mController.notifyControlRevoked(this); + } } @VisibleForTesting diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java index 23f2b8a0a3ca..7ab9534941c7 100644 --- a/core/java/android/view/RenderNodeAnimator.java +++ b/core/java/android/view/RenderNodeAnimator.java @@ -336,6 +336,7 @@ public class RenderNodeAnimator extends Animator { return mUnscaledStartDelay; } + @UnsupportedAppUsage @Override public RenderNodeAnimator setDuration(long duration) { checkMutable(); diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java index c24b8b2a5ee3..7c69cfde2b9e 100644 --- a/core/java/android/view/ScaleGestureDetector.java +++ b/core/java/android/view/ScaleGestureDetector.java @@ -201,7 +201,7 @@ public class ScaleGestureDetector { mListener = listener; final ViewConfiguration viewConfiguration = ViewConfiguration.get(context); mSpanSlop = viewConfiguration.getScaledTouchSlop() * 2; - mMinSpan = viewConfiguration.getScaledMinScalingSpan(); + mMinSpan = viewConfiguration.getScaledMinimumScalingSpan(); mHandler = handler; // Quick scale is enabled by default after JB_MR2 final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion; diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java index d0194f990148..a15a20a58abb 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -922,7 +922,7 @@ public class Surface implements Parcelable { if (mCanvas != null) { throw new IllegalStateException("Surface was already locked!"); } - mCanvas = mRenderNode.start(width, height); + mCanvas = mRenderNode.beginRecording(width, height); return mCanvas; } @@ -931,7 +931,7 @@ public class Surface implements Parcelable { throw new IllegalArgumentException("canvas object must be the same instance that " + "was previously returned by lockCanvas"); } - mRenderNode.end(mCanvas); + mRenderNode.endRecording(); mCanvas = null; nHwuiDraw(mHwuiRenderer); } diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 1212df0c397a..3768acaaaad5 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -88,7 +88,8 @@ public final class SurfaceControl implements Parcelable { private static native void nativeDisconnect(long nativeObject); private static native GraphicBuffer nativeScreenshot(IBinder displayToken, - Rect sourceCrop, int width, int height, boolean useIdentityTransform, int rotation); + Rect sourceCrop, int width, int height, boolean useIdentityTransform, int rotation, + boolean captureSecureLayers); private static native GraphicBuffer nativeCaptureLayers(IBinder layerHandleToken, Rect sourceCrop, float frameScale); @@ -157,6 +158,9 @@ public final class SurfaceControl implements Parcelable { IBinder displayToken, long numFrames, long timestamp); private static native int nativeGetActiveConfig(IBinder displayToken); private static native boolean nativeSetActiveConfig(IBinder displayToken, int id); + private static native boolean nativeSetAllowedDisplayConfigs(IBinder displayToken, + int[] allowedConfigs); + private static native int[] nativeGetAllowedDisplayConfigs(IBinder displayToken); private static native int[] nativeGetDisplayColorModes(IBinder displayToken); private static native SurfaceControl.DisplayPrimaries nativeGetDisplayNativePrimaries( IBinder displayToken); @@ -189,6 +193,7 @@ public final class SurfaceControl implements Parcelable { IBinder toToken); private static native boolean nativeGetProtectedContentSupport(); private static native void nativeSetMetadata(long transactionObj, int key, Parcel data); + private static native void nativeSyncInputWindows(long transactionObj); private final CloseGuard mCloseGuard = CloseGuard.get(); private String mName; @@ -489,7 +494,16 @@ public final class SurfaceControl implements Parcelable { } mWidth = width; mHeight = height; - return this; + // set this as a buffer layer since we are specifying a buffer size. + return setFlags(FX_SURFACE_NORMAL, FX_SURFACE_MASK); + } + + /** + * Set the initial size of the controlled surface's buffers in pixels. + */ + private void unsetBufferSize() { + mWidth = 0; + mHeight = 0; } /** @@ -607,16 +621,11 @@ public final class SurfaceControl implements Parcelable { * Color layers will not have an associated BufferQueue and will instead always render a * solid color (that is, solid before plane alpha). Currently that color is black. * - * @param isColorLayer Whether to create a color layer. * @hide */ - public Builder setColorLayer(boolean isColorLayer) { - if (isColorLayer) { - mFlags |= FX_SURFACE_DIM; - } else { - mFlags &= ~FX_SURFACE_DIM; - } - return this; + public Builder setColorLayer() { + unsetBufferSize(); + return setFlags(FX_SURFACE_DIM, FX_SURFACE_MASK); } private boolean isColorLayerSet() { @@ -629,16 +638,11 @@ public final class SurfaceControl implements Parcelable { * Container layers will not be rendered in any fashion and instead are used * as a parent of renderable layers. * - * @param isContainerLayer Whether to create a container layer. * @hide */ - public Builder setContainerLayer(boolean isContainerLayer) { - if (isContainerLayer) { - mFlags |= FX_SURFACE_CONTAINER; - } else { - mFlags &= ~FX_SURFACE_CONTAINER; - } - return this; + public Builder setContainerLayer() { + unsetBufferSize(); + return setFlags(FX_SURFACE_CONTAINER, FX_SURFACE_MASK); } private boolean isContainerLayerSet() { @@ -646,7 +650,7 @@ public final class SurfaceControl implements Parcelable { } /** - * Set 'Surface creation flags' such as {@link HIDDEN}, {@link SECURE}. + * Set 'Surface creation flags' such as {@link #HIDDEN}, {@link #SECURE}. * * TODO: Finish conversion to individual builder methods? * @param flags The combined flags @@ -656,6 +660,11 @@ public final class SurfaceControl implements Parcelable { mFlags = flags; return this; } + + private Builder setFlags(int flags, int mask) { + mFlags = (mFlags & ~mask) | flags; + return this; + } } /** @@ -1516,6 +1525,30 @@ public final class SurfaceControl implements Parcelable { /** * @hide */ + public static boolean setAllowedDisplayConfigs(IBinder displayToken, int[] allowedConfigs) { + if (displayToken == null) { + throw new IllegalArgumentException("displayToken must not be null"); + } + if (allowedConfigs == null) { + throw new IllegalArgumentException("allowedConfigs must not be null"); + } + + return nativeSetAllowedDisplayConfigs(displayToken, allowedConfigs); + } + + /** + * @hide + */ + public static int[] getAllowedDisplayConfigs(IBinder displayToken) { + if (displayToken == null) { + throw new IllegalArgumentException("displayToken must not be null"); + } + return nativeGetAllowedDisplayConfigs(displayToken); + } + + /** + * @hide + */ public static int[] getDisplayColorModes(IBinder displayToken) { if (displayToken == null) { throw new IllegalArgumentException("displayToken must not be null"); @@ -1848,7 +1881,29 @@ public final class SurfaceControl implements Parcelable { throw new IllegalArgumentException("displayToken must not be null"); } - return nativeScreenshot(display, sourceCrop, width, height, useIdentityTransform, rotation); + return nativeScreenshot(display, sourceCrop, width, height, useIdentityTransform, rotation, + false /* captureSecureLayers */); + } + + /** + * Like screenshotToBuffer, but if the caller is AID_SYSTEM, allows + * for the capture of secure layers. This is used for the screen rotation + * animation where the system server takes screenshots but does + * not persist them or allow them to leave the server. However in other + * cases in the system server, we mostly want to omit secure layers + * like when we take a screenshot on behalf of the assistant. + * + * @hide + */ + public static GraphicBuffer screenshotToBufferWithSecureLayersUnsafe(IBinder display, + Rect sourceCrop, int width, int height, boolean useIdentityTransform, + int rotation) { + if (display == null) { + throw new IllegalArgumentException("displayToken must not be null"); + } + + return nativeScreenshot(display, sourceCrop, width, height, useIdentityTransform, rotation, + true /* captureSecureLayers */); } private static void rotateCropForSF(Rect crop, int rot) { @@ -2105,6 +2160,17 @@ public final class SurfaceControl implements Parcelable { } /** + * Waits until any changes to input windows have been sent from SurfaceFlinger to + * InputFlinger before returning. + * + * @hide + */ + public Transaction syncInputWindows() { + nativeSyncInputWindows(mNativeObject); + return this; + } + + /** * Specify how the buffer assosciated with this Surface is mapped in to the * parent coordinate space. The source frame will be scaled to fit the destination * frame, after being rotated according to the orientation parameter. diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 9f0800f11721..ee8d66313ea2 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -33,7 +33,6 @@ import android.graphics.Region; import android.graphics.RenderNode; import android.os.Build; import android.os.Handler; -import android.os.IBinder; import android.os.Looper; import android.os.SystemClock; import android.util.AttributeSet; @@ -211,7 +210,7 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb public SurfaceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); - mRenderNode.requestPositionUpdates(mPositionListener); + mRenderNode.addPositionUpdateListener(mPositionListener); setWillNotDraw(true); } @@ -588,7 +587,7 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb mBackgroundControl = new SurfaceControl.Builder(mSurfaceSession) .setName("Background for -" + name) .setOpaque(true) - .setColorLayer(true) + .setColorLayer() .setParent(mSurfaceControl) .build(); diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 20978127f510..3d3d5dc7db32 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -590,7 +590,7 @@ public final class ThreadedRenderer extends HardwareRenderer { } if (mRootNodeNeedsUpdate || !mRootNode.hasDisplayList()) { - RecordingCanvas canvas = mRootNode.startRecording(mSurfaceWidth, mSurfaceHeight); + RecordingCanvas canvas = mRootNode.beginRecording(mSurfaceWidth, mSurfaceHeight); try { final int saveCount = canvas.save(); canvas.translate(mInsetLeft, mInsetTop); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index a78f2b073a3a..cc585b0294c7 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -40,6 +40,7 @@ import android.annotation.StyleRes; import android.annotation.TestApi; import android.annotation.UiThread; import android.annotation.UnsupportedAppUsage; +import android.content.AutofillOptions; import android.content.ClipData; import android.content.Context; import android.content.ContextWrapper; @@ -880,12 +881,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private static boolean sAlwaysRemeasureExactly = false; /** - * Relax constraints around whether setLayoutParams() must be called after - * modifying the layout params. - */ - private static boolean sLayoutParamsAlwaysChanged = false; - - /** * Allow setForeground/setBackground to be called (and ignored) on a textureview, * without throwing */ @@ -4602,6 +4597,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private ArrayList<OnUnhandledKeyEventListener> mUnhandledKeyListeners; private WindowInsetsAnimationListener mWindowInsetsAnimationListener; + + /** + * This lives here since it's only valid for interactive views. + */ + private List<Rect> mSystemGestureExclusionRects; + + /** + * Used to track {@link #mSystemGestureExclusionRects} + */ + public RenderNode.PositionUpdateListener mPositionUpdateListener; } @UnsupportedAppUsage @@ -5169,11 +5174,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // modes, so we always need to run an additional EXACTLY pass. sAlwaysRemeasureExactly = targetSdkVersion <= Build.VERSION_CODES.M; - // Prior to N, layout params could change without requiring a - // subsequent call to setLayoutParams() and they would usually - // work. Partial layout breaks this assumption. - sLayoutParamsAlwaysChanged = targetSdkVersion <= Build.VERSION_CODES.M; - // Prior to N, TextureView would silently ignore calls to setBackground/setForeground. // On N+, we throw, but that breaks compatibility with apps that use these methods. sTextureViewIgnoresDrawableSetters = targetSdkVersion <= Build.VERSION_CODES.M; @@ -9378,7 +9378,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, AttachInfo ai = mAttachInfo; // First check if context has client, so it saves a service lookup when it doesn't - if (!mContext.isContentCaptureSupported()) return; + if (mContext.getContentCaptureOptions() == null) return; // Then check if it's enabled in the context... final ContentCaptureManager ccm = ai != null ? ai.getContentCaptureManager(mContext) @@ -9408,20 +9408,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } setNotifiedContentCaptureAppeared(); - // TODO(b/123307965): instead of post, we should queue it on AttachInfo and then - // dispatch on RootImpl, as we're doing with the removed ones (in that case, we should - // merge the delayNotifyContentCaptureDisappeared() into a more generic method that - // takes a session and a command, where the command is either view added or removed - - // The code below doesn't take much for a unique view, but it's called for all views - // the first time the view hiearchy is laid off, which could acccumulative delay the - // initial layout. Hence, we're postponing it to a later stage - it might still cost a - // lost frame (or more), but that jank cost would only happen after the 1st layout. - Choreographer.getInstance().postCallback(Choreographer.CALLBACK_COMMIT, () -> { - final ViewStructure structure = session.newViewStructure(this); - onProvideContentCaptureStructure(structure, /* flags= */ 0); - session.notifyViewAppeared(structure); - }, /* token= */ null); + if (ai != null) { + ai.delayNotifyContentCaptureEvent(session, this, appeared); + } else { + if (DEBUG_CONTENT_CAPTURE) { + Log.w(CONTENT_CAPTURE_LOG_TAG, "no AttachInfo on appeared for " + this); + } + } } else { if ((mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) == 0 || (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0) { @@ -9440,13 +9433,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mPrivateFlags4 &= ~PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED; if (ai != null) { - ai.delayNotifyContentCaptureDisappeared(session, getAutofillId()); + ai.delayNotifyContentCaptureEvent(session, this, appeared); } else { if (DEBUG_CONTENT_CAPTURE) { - Log.v(CONTENT_CAPTURE_LOG_TAG, "no AttachInfo on gone for " + this); + Log.v(CONTENT_CAPTURE_LOG_TAG, "no AttachInfo on disappeared for " + this); } - Choreographer.getInstance().postCallback(Choreographer.CALLBACK_COMMIT, - () -> session.notifyViewDisappeared(getAutofillId()), /* token= */ null); } } } @@ -9533,8 +9524,22 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } private boolean isAutofillable() { - return getAutofillType() != AUTOFILL_TYPE_NONE && isImportantForAutofill() - && getAutofillViewId() > LAST_APP_AUTOFILL_ID; + if (getAutofillType() == AUTOFILL_TYPE_NONE) return false; + + if (!isImportantForAutofill()) { + // View is not important for "regular" autofill, so we must check if Augmented Autofill + // is enabled for the activity + final AutofillOptions options = mContext.getAutofillOptions(); + if (options == null || !options.augmentedEnabled) { + // TODO(b/123100824): should also check if activity is whitelisted + return false; + } + final AutofillManager afm = getAutofillManager(); + if (afm == null) return false; + afm.notifyViewEnteredForAugmentedAutofill(this); + } + + return getAutofillViewId() > LAST_APP_AUTOFILL_ID; } /** @hide */ @@ -9703,13 +9708,19 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @hide */ - public void dispatchInitialProvideContentCaptureStructure(@NonNull ContentCaptureManager ccm) { + public void dispatchInitialProvideContentCaptureStructure() { AttachInfo ai = mAttachInfo; if (ai == null) { Log.w(CONTENT_CAPTURE_LOG_TAG, "dispatchProvideContentCaptureStructure(): no AttachInfo for " + this); return; } + ContentCaptureManager ccm = ai.mContentCaptureManager; + if (ccm == null) { + Log.w(CONTENT_CAPTURE_LOG_TAG, "dispatchProvideContentCaptureStructure(): " + + "no ContentCaptureManager for " + this); + return; + } // We must set it before checkign if the view itself is important, because it might // initially not be (for example, if it's empty), although that might change later (for @@ -9735,11 +9746,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return; } - session.internalNotifyViewHierarchyEvent(/* started= */ true); + session.internalNotifyViewTreeEvent(/* started= */ true); try { dispatchProvideContentCaptureStructure(); } finally { - session.internalNotifyViewHierarchyEvent(/* started= */ false); + session.internalNotifyViewTreeEvent(/* started= */ false); } } @@ -10956,12 +10967,101 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } void dispatchWindowInsetsAnimationFinished(InsetsAnimation animation) { - if (mListenerInfo != null && mListenerInfo.mOnApplyWindowInsetsListener != null) { + if (mListenerInfo != null && mListenerInfo.mWindowInsetsAnimationListener != null) { mListenerInfo.mWindowInsetsAnimationListener.onFinished(animation); } } /** + * Sets a list of areas within this view's post-layout coordinate space where the system + * should not intercept touch or other pointing device gestures. <em>This method should + * be called by {@link #onLayout(boolean, int, int, int, int)} or {@link #onDraw(Canvas)}.</em> + * + * <p>Use this to tell the system which specific sub-areas of a view need to receive gesture + * input in order to function correctly in the presence of global system gestures that may + * conflict. For example, if the system wishes to capture swipe-in-from-screen-edge gestures + * to provide system-level navigation functionality, a view such as a navigation drawer + * container can mark the left (or starting) edge of itself as requiring gesture capture + * priority using this API. The system may then choose to relax its own gesture recognition + * to allow the app to consume the user's gesture. It is not necessary for an app to register + * exclusion rects for broadly spanning regions such as the entirety of a + * <code>ScrollView</code> or for simple press and release click targets such as + * <code>Button</code>. Mark an exclusion rect when interacting with a view requires + * a precision touch gesture in a small area in either the X or Y dimension, such as + * an edge swipe or dragging a <code>SeekBar</code> thumb.</p> + * + * <p>Do not modify the provided list after this method is called.</p> + * + * @param rects A list of precision gesture regions that this view needs to function correctly + */ + public void setSystemGestureExclusionRects(@NonNull List<Rect> rects) { + if (rects.isEmpty() && mListenerInfo == null) return; + + final ListenerInfo info = getListenerInfo(); + if (rects.isEmpty()) { + info.mSystemGestureExclusionRects = null; + if (info.mPositionUpdateListener != null) { + mRenderNode.removePositionUpdateListener(info.mPositionUpdateListener); + } + } else { + info.mSystemGestureExclusionRects = rects; + if (info.mPositionUpdateListener == null) { + info.mPositionUpdateListener = new RenderNode.PositionUpdateListener() { + @Override + public void positionChanged(long n, int l, int t, int r, int b) { + postUpdateSystemGestureExclusionRects(); + } + + @Override + public void positionLost(long frameNumber) { + postUpdateSystemGestureExclusionRects(); + } + }; + mRenderNode.addPositionUpdateListener(info.mPositionUpdateListener); + } + } + postUpdateSystemGestureExclusionRects(); + } + + /** + * WARNING: this can be called by a hwui worker thread, not just the UI thread! + */ + void postUpdateSystemGestureExclusionRects() { + // Potentially racey from a background thread. It's ok if it's not perfect. + final Handler h = getHandler(); + if (h != null) { + h.postAtFrontOfQueue(this::updateSystemGestureExclusionRects); + } + } + + void updateSystemGestureExclusionRects() { + final AttachInfo ai = mAttachInfo; + if (ai != null) { + ai.mViewRootImpl.updateSystemGestureExclusionRectsForView(this); + } + } + + /** + * Retrieve the list of areas within this view's post-layout coordinate space where the system + * should not intercept touch or other pointing device gestures. + * + * <p>Do not modify the returned list.</p> + * + * @return the list set by {@link #setSystemGestureExclusionRects(List)} + */ + @NonNull + public List<Rect> getSystemGestureExclusionRects() { + final ListenerInfo info = mListenerInfo; + if (info != null) { + final List<Rect> list = info.mSystemGestureExclusionRects; + if (list != null) { + return list; + } + } + return Collections.emptyList(); + } + + /** * Compute the view's coordinate within the surface. * * <p>Computes the coordinates of this view in its surface. The argument @@ -16043,7 +16143,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @ViewDebug.ExportedProperty(category = "drawing") @InspectableProperty public float getRotation() { - return mRenderNode.getRotation(); + return mRenderNode.getRotationZ(); } /** @@ -16064,7 +16164,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (rotation != getRotation()) { // Double-invalidation is necessary to capture view's old and new areas invalidateViewProperty(true, false); - mRenderNode.setRotation(rotation); + mRenderNode.setRotationZ(rotation); invalidateViewProperty(false, true); invalidateParentIfNeededAndWasQuickRejected(); @@ -17976,21 +18076,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Utility method to transform a given Rect by the current matrix of this view. - */ - void transformRect(final Rect rect) { - if (!getMatrix().isIdentity()) { - RectF boundingRect = mAttachInfo.mTmpTransformRect; - boundingRect.set(rect); - getMatrix().mapRect(boundingRect); - rect.set((int) Math.floor(boundingRect.left), - (int) Math.floor(boundingRect.top), - (int) Math.ceil(boundingRect.right), - (int) Math.ceil(boundingRect.bottom)); - } - } - - /** * Used to indicate that the parent of this view should clear its caches. This functionality * is used to force the parent to rebuild its display list (when hardware-accelerated), * which is necessary when various parent-managed properties of the view change, such as @@ -20578,7 +20663,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, int height = mBottom - mTop; int layerType = getLayerType(); - final RecordingCanvas canvas = renderNode.startRecording(width, height); + final RecordingCanvas canvas = renderNode.beginRecording(width, height); try { if (layerType == LAYER_TYPE_SOFTWARE) { @@ -21233,7 +21318,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } else { mClipBounds = null; } - mRenderNode.setClipBounds(mClipBounds); + mRenderNode.setClipRect(mClipBounds); invalidateViewProperty(false, false); } @@ -21978,7 +22063,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, final Rect bounds = drawable.getBounds(); final int width = bounds.width(); final int height = bounds.height(); - final RecordingCanvas canvas = renderNode.startRecording(width, height); + final RecordingCanvas canvas = renderNode.beginRecording(width, height); // Reverse left/top translation done by drawable canvas, which will // instead be applied by rendernode's LTRB bounds below. This way, the @@ -28358,11 +28443,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, boolean mReadyForContentCaptureUpdates; /** - * Map of ids (per session) that need to be notified after as gone the view hierchy is - * traversed. + * Map(keyed by session) of content capture events that need to be notified after the view + * hierarchy is traversed: value is either the view itself for appearead events, or its + * autofill id for disappeared. */ // TODO(b/121197119): use SparseArray once session id becomes integer - ArrayMap<String, ArrayList<AutofillId>> mContentCaptureRemovedIds; + ArrayMap<String, ArrayList<Object>> mContentCaptureEvents; /** * Cached reference to the {@link ContentCaptureManager}. @@ -28388,24 +28474,24 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mTreeObserver = new ViewTreeObserver(context); } - private void delayNotifyContentCaptureDisappeared(@NonNull ContentCaptureSession session, - @NonNull AutofillId id) { - if (mContentCaptureRemovedIds == null) { + private void delayNotifyContentCaptureEvent(@NonNull ContentCaptureSession session, + @NonNull View view, boolean appeared) { + if (mContentCaptureEvents == null) { // Most of the time there will be just one session, so intial capacity is 1 - mContentCaptureRemovedIds = new ArrayMap<>(1); + mContentCaptureEvents = new ArrayMap<>(1); } String sessionId = session.getId(); // TODO: life would be much easier if we provided a MultiMap implementation somwhere... - ArrayList<AutofillId> ids = mContentCaptureRemovedIds.get(sessionId); - if (ids == null) { - ids = new ArrayList<>(); - mContentCaptureRemovedIds.put(sessionId, ids); + ArrayList<Object> events = mContentCaptureEvents.get(sessionId); + if (events == null) { + events = new ArrayList<>(); + mContentCaptureEvents.put(sessionId, events); } - ids.add(id); + events.add(appeared ? view : view.getAutofillId()); } @Nullable - private ContentCaptureManager getContentCaptureManager(@NonNull Context context) { + ContentCaptureManager getContentCaptureManager(@NonNull Context context) { if (mContentCaptureManager != null) { return mContentCaptureManager; } diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index bb29ed6c5339..81e9c1372aed 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -928,8 +928,8 @@ public class ViewConfiguration { } /** - * If a MotionEvent has CLASSIFICATION_AMBIGUOUS_GESTURE set, then certain actions, such as - * scrolling, will be inhibited. + * If a MotionEvent has {@link android.view.MotionEvent#CLASSIFICATION_AMBIGUOUS_GESTURE} set, + * then certain actions, such as scrolling, will be inhibited. * However, to account for the possibility of incorrect classification, * the default scrolling will only be inhibited if the pointer travels less than * (getScaledTouchSlop() * this factor). @@ -979,7 +979,7 @@ public class ViewConfiguration { * @throws IllegalStateException if this method is called on a ViewConfiguration that was * instantiated using a constructor with no Context parameter. */ - public int getScaledMinScalingSpan() { + public int getScaledMinimumScalingSpan() { if (!mConstructedWithContext) { throw new IllegalStateException("Min scaling span cannot be determined when this " + "method is called on a ViewConfiguration that was instantiated using a " diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java index 6f9ee4b554f3..a390db2e9644 100644 --- a/core/java/android/view/ViewDebug.java +++ b/core/java/android/view/ViewDebug.java @@ -611,11 +611,11 @@ public class ViewDebug { } if (view.isHardwareAccelerated()) { - RecordingCanvas canvas = node.start(dm.widthPixels, dm.heightPixels); + RecordingCanvas canvas = node.beginRecording(dm.widthPixels, dm.heightPixels); try { return profileViewOperation(view, () -> view.draw(canvas)); } finally { - node.end(canvas); + node.endRecording(); } } else { Bitmap bitmap = Bitmap.createBitmap( diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java index 68c0d9ec465b..afee1e5cf7f4 100644 --- a/core/java/android/view/ViewPropertyAnimator.java +++ b/core/java/android/view/ViewPropertyAnimator.java @@ -985,7 +985,7 @@ public class ViewPropertyAnimator { renderNode.setTranslationZ(value); break; case ROTATION: - renderNode.setRotation(value); + renderNode.setRotationZ(value); break; case ROTATION_X: renderNode.setRotationX(value); @@ -1031,7 +1031,7 @@ public class ViewPropertyAnimator { case TRANSLATION_Z: return node.getTranslationZ(); case ROTATION: - return node.getRotation(); + return node.getRotationZ(); case ROTATION_X: return node.getRotationX(); case ROTATION_Y: diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index b1fee2d17079..65b1abd715d9 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -29,6 +29,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY; import android.Manifest; import android.animation.LayoutTransition; +import android.annotation.AnyThread; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; @@ -107,6 +108,7 @@ import android.view.animation.Interpolator; import android.view.autofill.AutofillId; import android.view.autofill.AutofillManager; import android.view.contentcapture.ContentCaptureManager; +import android.view.contentcapture.ContentCaptureSession; import android.view.contentcapture.MainContentCaptureSession; import android.view.inputmethod.InputMethodManager; import android.widget.Scroller; @@ -222,10 +224,25 @@ public final class ViewRootImpl implements ViewParent, */ static final int MAX_TRACKBALL_DELAY = 250; + /** + * Initial value for {@link #mContentCaptureEnabled}. + */ + private static final int CONTENT_CAPTURE_ENABLED_NOT_CHECKED = 0; + + /** + * Value for {@link #mContentCaptureEnabled} when it was checked and set to {@code true}. + */ + private static final int CONTENT_CAPTURE_ENABLED_TRUE = 1; + + /** + * Value for {@link #mContentCaptureEnabled} when it was checked and set to {@code false}. + */ + private static final int CONTENT_CAPTURE_ENABLED_FALSE = 2; + @UnsupportedAppUsage static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>(); - static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList(); + static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList<>(); static boolean sFirstDrawComplete = false; /** @@ -417,7 +434,11 @@ public final class ViewRootImpl implements ViewParent, boolean mApplyInsetsRequested; boolean mLayoutRequested; boolean mFirst; + + @Nullable + int mContentCaptureEnabled = CONTENT_CAPTURE_ENABLED_NOT_CHECKED; boolean mPerformContentCapture; + boolean mReportNextDraw; boolean mFullRedrawNeeded; boolean mNewSurfaceNeeded; @@ -584,6 +605,8 @@ public final class ViewRootImpl implements ViewParent, private final InsetsController mInsetsController = new InsetsController(this); + private final GestureExclusionTracker mGestureExclusionTracker = new GestureExclusionTracker(); + static final class SystemUiVisibilityInfo { int seq; int globalVisibility; @@ -1041,10 +1064,22 @@ public final class ViewRootImpl implements ViewParent, return mHeight; } + /** + * Destroys hardware rendering resources for this ViewRootImpl + * + * May be called on any thread + */ + @AnyThread void destroyHardwareResources() { - if (mAttachInfo.mThreadedRenderer != null) { - mAttachInfo.mThreadedRenderer.destroyHardwareResources(mView); - mAttachInfo.mThreadedRenderer.destroy(); + final ThreadedRenderer renderer = mAttachInfo.mThreadedRenderer; + if (renderer != null) { + // This is called by WindowManagerGlobal which may or may not be on the right thread + if (Looper.myLooper() != mAttachInfo.mHandler.getLooper()) { + mAttachInfo.mHandler.postAtFrontOfQueue(this::destroyHardwareResources); + return; + } + renderer.destroyHardwareResources(mView); + renderer.destroy(); } } @@ -2763,25 +2798,54 @@ public final class ViewRootImpl implements ViewParent, } } - if (mAttachInfo.mContentCaptureRemovedIds != null) { + if (mAttachInfo.mContentCaptureEvents != null) { + notifyContentCatpureEvents(); + } + + mIsInTraversal = false; + } + + private void notifyContentCatpureEvents() { + Trace.traceBegin(Trace.TRACE_TAG_VIEW, "notifyContentCaptureEvents"); + try { MainContentCaptureSession mainSession = mAttachInfo.mContentCaptureManager .getMainContentCaptureSession(); - Trace.traceBegin(Trace.TRACE_TAG_VIEW, "notifyContentCaptureViewsGone"); - try { - for (int i = 0; i < mAttachInfo.mContentCaptureRemovedIds.size(); i++) { - String sessionId = mAttachInfo.mContentCaptureRemovedIds - .keyAt(i); - ArrayList<AutofillId> ids = mAttachInfo.mContentCaptureRemovedIds - .valueAt(i); - mainSession.notifyViewsDisappeared(sessionId, ids); - } - mAttachInfo.mContentCaptureRemovedIds = null; - } finally { - Trace.traceEnd(Trace.TRACE_TAG_VIEW); + for (int i = 0; i < mAttachInfo.mContentCaptureEvents.size(); i++) { + String sessionId = mAttachInfo.mContentCaptureEvents + .keyAt(i); + mainSession.notifyViewTreeEvent(sessionId, /* started= */ true); + ArrayList<Object> events = mAttachInfo.mContentCaptureEvents + .valueAt(i); + for_each_event: for (int j = 0; j < events.size(); j++) { + Object event = events.get(j); + if (event instanceof AutofillId) { + mainSession.notifyViewDisappeared(sessionId, (AutofillId) event); + } else if (event instanceof View) { + View view = (View) event; + ContentCaptureSession session = view.getContentCaptureSession(); + if (session == null) { + Log.w(mTag, "no content capture session on view: " + view); + continue for_each_event; + } + String actualId = session.getId().toString(); + if (!actualId.equals(sessionId)) { + Log.w(mTag, "content capture session mismatch for view (" + view + + "): was " + sessionId + " before, it's " + actualId + " now"); + continue for_each_event; + } + ViewStructure structure = session.newViewStructure(view); + view.onProvideContentCaptureStructure(structure, /* flags= */ 0); + session.notifyViewAppeared(structure); + } else { + Log.w(mTag, "invalid content capture event: " + event); + } + } + mainSession.notifyViewTreeEvent(sessionId, /* started= */ false); } + mAttachInfo.mContentCaptureEvents = null; + } finally { + Trace.traceEnd(Trace.TRACE_TAG_VIEW); } - - mIsInTraversal = false; } private void notifySurfaceDestroyed() { @@ -2914,6 +2978,13 @@ public final class ViewRootImpl implements ViewParent, } } mFirstInputStage.onWindowFocusChanged(hasWindowFocus); + + // NOTE: there's no view visibility (appeared / disapparead) events when the windows focus + // is lost, so we don't need to to force a flush - there might be other events such as + // text changes, but these should be flushed independently. + if (hasWindowFocus) { + handleContentCaptureFlush(); + } } private void fireAccessibilityFocusEventIfHasFocusedNode() { @@ -3481,31 +3552,82 @@ public final class ViewRootImpl implements ViewParent, } } if (mPerformContentCapture) { - performContentCapture(); + performContentCaptureInitialReport(); + } + } + + /** + * Checks (and caches) if content capture is enabled for this context. + */ + private boolean isContentCaptureEnabled() { + switch (mContentCaptureEnabled) { + case CONTENT_CAPTURE_ENABLED_TRUE: + return true; + case CONTENT_CAPTURE_ENABLED_FALSE: + return false; + case CONTENT_CAPTURE_ENABLED_NOT_CHECKED: + final boolean reallyEnabled = isContentCaptureReallyEnabled(); + mContentCaptureEnabled = reallyEnabled ? CONTENT_CAPTURE_ENABLED_TRUE + : CONTENT_CAPTURE_ENABLED_FALSE; + return reallyEnabled; + default: + Log.w(TAG, "isContentCaptureEnabled(): invalid state " + mContentCaptureEnabled); + return false; } + } - private void performContentCapture() { + /** + * Checks (without caching) if content capture is enabled for this context. + */ + private boolean isContentCaptureReallyEnabled() { + // First check if context supports it, so it saves a service lookup when it doesn't + if (mContext.getContentCaptureOptions() == null) return false; + + final ContentCaptureManager ccm = mAttachInfo.getContentCaptureManager(mContext); + // Then check if it's enabled in the contex itself. + if (ccm == null || !ccm.isContentCaptureEnabled()) return false; + + return true; + } + + private void performContentCaptureInitialReport() { mPerformContentCapture = false; // One-time offer! final View rootView = mView; if (DEBUG_CONTENT_CAPTURE) { - Log.v(mTag, "dispatchContentCapture() on " + rootView); + Log.v(mTag, "performContentCaptureInitialReport() on " + rootView); } if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "dispatchContentCapture() for " + getClass().getSimpleName()); } try { - // First check if context supports it, so it saves a service lookup when it doesn't - if (!mContext.isContentCaptureSupported()) return; - - // Then check if it's enabled in the contex itself. - final ContentCaptureManager ccm = mContext - .getSystemService(ContentCaptureManager.class); - if (ccm == null || !ccm.isContentCaptureEnabled()) return; + if (!isContentCaptureEnabled()) return; // Content capture is a go! - rootView.dispatchInitialProvideContentCaptureStructure(ccm); + rootView.dispatchInitialProvideContentCaptureStructure(); + } finally { + Trace.traceEnd(Trace.TRACE_TAG_VIEW); + } + } + + private void handleContentCaptureFlush() { + if (DEBUG_CONTENT_CAPTURE) { + Log.v(mTag, "handleContentCaptureFlush()"); + } + if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { + Trace.traceBegin(Trace.TRACE_TAG_VIEW, "flushContentCapture for " + + getClass().getSimpleName()); + } + try { + if (!isContentCaptureEnabled()) return; + + final ContentCaptureManager ccm = mAttachInfo.mContentCaptureManager; + if (ccm == null) { + Log.w(TAG, "No ContentCapture on AttachInfo"); + return; + } + ccm.flush(ContentCaptureSession.FLUSH_REASON_VIEW_ROOT_ENTERED); } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); } @@ -3857,6 +3979,20 @@ public final class ViewRootImpl implements ViewParent, return mAttachInfo.mAccessibilityFocusDrawable; } + void updateSystemGestureExclusionRectsForView(View view) { + mGestureExclusionTracker.updateRectsForView(view); + mHandler.sendEmptyMessage(MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED); + } + + void systemGestureExclusionChanged() { + final List<Rect> rectsForWindowManager = mGestureExclusionTracker.computeChangedRects(); + if (rectsForWindowManager != null) { + // TODO Send to WM + mAttachInfo.mTreeObserver + .dispatchOnSystemGestureExclusionRectsChanged(rectsForWindowManager); + } + } + /** * Requests that the root render node is invalidated next time we perform a draw, such that * {@link WindowCallbacks#onPostDraw} gets called. @@ -4313,35 +4449,36 @@ public final class ViewRootImpl implements ViewParent, } } - private final static int MSG_INVALIDATE = 1; - private final static int MSG_INVALIDATE_RECT = 2; - private final static int MSG_DIE = 3; - private final static int MSG_RESIZED = 4; - private final static int MSG_RESIZED_REPORT = 5; - private final static int MSG_WINDOW_FOCUS_CHANGED = 6; - private final static int MSG_DISPATCH_INPUT_EVENT = 7; - private final static int MSG_DISPATCH_APP_VISIBILITY = 8; - private final static int MSG_DISPATCH_GET_NEW_SURFACE = 9; - private final static int MSG_DISPATCH_KEY_FROM_IME = 11; - private final static int MSG_DISPATCH_KEY_FROM_AUTOFILL = 12; - private final static int MSG_CHECK_FOCUS = 13; - private final static int MSG_CLOSE_SYSTEM_DIALOGS = 14; - private final static int MSG_DISPATCH_DRAG_EVENT = 15; - private final static int MSG_DISPATCH_DRAG_LOCATION_EVENT = 16; - private final static int MSG_DISPATCH_SYSTEM_UI_VISIBILITY = 17; - private final static int MSG_UPDATE_CONFIGURATION = 18; - private final static int MSG_PROCESS_INPUT_EVENTS = 19; - private final static int MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST = 21; - private final static int MSG_INVALIDATE_WORLD = 22; - private final static int MSG_WINDOW_MOVED = 23; - private final static int MSG_SYNTHESIZE_INPUT_EVENT = 24; - private final static int MSG_DISPATCH_WINDOW_SHOWN = 25; - private final static int MSG_REQUEST_KEYBOARD_SHORTCUTS = 26; - private final static int MSG_UPDATE_POINTER_ICON = 27; - private final static int MSG_POINTER_CAPTURE_CHANGED = 28; - private final static int MSG_DRAW_FINISHED = 29; - private final static int MSG_INSETS_CHANGED = 30; - private final static int MSG_INSETS_CONTROL_CHANGED = 31; + private static final int MSG_INVALIDATE = 1; + private static final int MSG_INVALIDATE_RECT = 2; + private static final int MSG_DIE = 3; + private static final int MSG_RESIZED = 4; + private static final int MSG_RESIZED_REPORT = 5; + private static final int MSG_WINDOW_FOCUS_CHANGED = 6; + private static final int MSG_DISPATCH_INPUT_EVENT = 7; + private static final int MSG_DISPATCH_APP_VISIBILITY = 8; + private static final int MSG_DISPATCH_GET_NEW_SURFACE = 9; + private static final int MSG_DISPATCH_KEY_FROM_IME = 11; + private static final int MSG_DISPATCH_KEY_FROM_AUTOFILL = 12; + private static final int MSG_CHECK_FOCUS = 13; + private static final int MSG_CLOSE_SYSTEM_DIALOGS = 14; + private static final int MSG_DISPATCH_DRAG_EVENT = 15; + private static final int MSG_DISPATCH_DRAG_LOCATION_EVENT = 16; + private static final int MSG_DISPATCH_SYSTEM_UI_VISIBILITY = 17; + private static final int MSG_UPDATE_CONFIGURATION = 18; + private static final int MSG_PROCESS_INPUT_EVENTS = 19; + private static final int MSG_CLEAR_ACCESSIBILITY_FOCUS_HOST = 21; + private static final int MSG_INVALIDATE_WORLD = 22; + private static final int MSG_WINDOW_MOVED = 23; + private static final int MSG_SYNTHESIZE_INPUT_EVENT = 24; + private static final int MSG_DISPATCH_WINDOW_SHOWN = 25; + private static final int MSG_REQUEST_KEYBOARD_SHORTCUTS = 26; + private static final int MSG_UPDATE_POINTER_ICON = 27; + private static final int MSG_POINTER_CAPTURE_CHANGED = 28; + private static final int MSG_DRAW_FINISHED = 29; + private static final int MSG_INSETS_CHANGED = 30; + private static final int MSG_INSETS_CONTROL_CHANGED = 31; + private static final int MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED = 32; final class ViewRootHandler extends Handler { @Override @@ -4399,6 +4536,10 @@ public final class ViewRootImpl implements ViewParent, return "MSG_DRAW_FINISHED"; case MSG_INSETS_CHANGED: return "MSG_INSETS_CHANGED"; + case MSG_INSETS_CONTROL_CHANGED: + return "MSG_INSETS_CONTROL_CHANGED"; + case MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED: + return "MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED"; } return super.getMessageName(message); } @@ -4630,6 +4771,9 @@ public final class ViewRootImpl implements ViewParent, case MSG_DRAW_FINISHED: { pendingDrawFinished(); } break; + case MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED: { + systemGestureExclusionChanged(); + } break; } } } @@ -5150,11 +5294,8 @@ public final class ViewRootImpl implements ViewParent, protected int onProcess(QueuedInputEvent q) { if (q.mEvent instanceof KeyEvent) { return processKeyEvent(q); - } else { - final int source = q.mEvent.getSource(); - if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) { - return processPointerEvent(q); - } + } else if (q.mEvent instanceof MotionEvent) { + return processMotionEvent(q); } return FORWARD; } @@ -5178,6 +5319,23 @@ public final class ViewRootImpl implements ViewParent, return FORWARD; } + private int processMotionEvent(QueuedInputEvent q) { + final MotionEvent event = (MotionEvent) q.mEvent; + + if (event.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) { + return processPointerEvent(q); + } + + // If the motion event is from an absolute position device, exit touch mode + final int action = event.getActionMasked(); + if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_SCROLL) { + if (event.isFromSource(InputDevice.SOURCE_CLASS_POSITION)) { + ensureTouchMode(false); + } + } + return FORWARD; + } + private int processPointerEvent(QueuedInputEvent q) { final MotionEvent event = (MotionEvent)q.mEvent; @@ -5510,6 +5668,12 @@ public final class ViewRootImpl implements ViewParent, private int processGenericMotionEvent(QueuedInputEvent q) { final MotionEvent event = (MotionEvent)q.mEvent; + if (event.isFromSource(InputDevice.SOURCE_TOUCHPAD)) { + if (hasPointerCapture() && mView.dispatchCapturedPointerEvent(event)) { + return FINISH_HANDLED; + } + } + // Deliver the event to the view. if (mView.dispatchGenericMotionEvent(event)) { return FINISH_HANDLED; @@ -6983,7 +7147,7 @@ public final class ViewRootImpl implements ViewParent, @Override public boolean performHapticFeedback(int effectId, boolean always) { try { - return mWindowSession.performHapticFeedback(mWindow, effectId, always); + return mWindowSession.performHapticFeedback(effectId, always); } catch (RemoteException e) { return false; } @@ -7080,7 +7244,7 @@ public final class ViewRootImpl implements ViewParent, RenderNode renderNode = view.mRenderNode; info[0]++; if (renderNode != null) { - info[1] += renderNode.computeApproximateMemoryUsage(); + info[1] += (int) renderNode.computeApproximateMemoryUsage(); } if (view instanceof ViewGroup) { diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java index 763ce4f45b01..2896bd049e7c 100644 --- a/core/java/android/view/ViewTreeObserver.java +++ b/core/java/android/view/ViewTreeObserver.java @@ -26,7 +26,9 @@ import android.os.Build; import android.util.Log; import java.util.ArrayList; +import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.Consumer; /** * A view tree observer is used to register listeners that can be notified of global @@ -57,6 +59,7 @@ public final class ViewTreeObserver { private CopyOnWriteArray<OnScrollChangedListener> mOnScrollChangedListeners; private CopyOnWriteArray<OnPreDrawListener> mOnPreDrawListeners; private CopyOnWriteArray<OnWindowShownListener> mOnWindowShownListeners; + private CopyOnWriteArray<Consumer<List<Rect>>> mGestureExclusionListeners; // These listeners cannot be mutated during dispatch private boolean mInDispatchOnDraw; @@ -450,6 +453,14 @@ public final class ViewTreeObserver { } } + if (observer.mGestureExclusionListeners != null) { + if (mGestureExclusionListeners != null) { + mGestureExclusionListeners.addAll(observer.mGestureExclusionListeners); + } else { + mGestureExclusionListeners = observer.mGestureExclusionListeners; + } + } + observer.kill(); } @@ -913,6 +924,35 @@ public final class ViewTreeObserver { mOnEnterAnimationCompleteListeners.remove(listener); } + /** + * Add a listener to be notified when the tree's <em>transformed</em> gesture exclusion rects + * change. This could be the result of an animation or other layout change, or a view calling + * {@link View#setSystemGestureExclusionRects(List)}. + * + * @param listener listener to add + * @see View#setSystemGestureExclusionRects(List) + */ + public void addOnSystemGestureExclusionRectsChangedListener(Consumer<List<Rect>> listener) { + checkIsAlive(); + if (mGestureExclusionListeners == null) { + mGestureExclusionListeners = new CopyOnWriteArray<>(); + } + mGestureExclusionListeners.add(listener); + } + + /** + * Unsubscribe the given listener from gesture exclusion rect changes. + * @see #addOnSystemGestureExclusionRectsChangedListener(Consumer) + * @see View#setSystemGestureExclusionRects(List) + */ + public void removeOnSystemGestureExclusionRectsChangedListener(Consumer<List<Rect>> listener) { + checkIsAlive(); + if (mGestureExclusionListeners == null) { + return; + } + mGestureExclusionListeners.remove(listener); + } + private void checkIsAlive() { if (!mAlive) { throw new IllegalStateException("This ViewTreeObserver is not alive, call " @@ -1178,6 +1218,21 @@ public final class ViewTreeObserver { } } + void dispatchOnSystemGestureExclusionRectsChanged(@NonNull List<Rect> rects) { + final CopyOnWriteArray<Consumer<List<Rect>>> listeners = mGestureExclusionListeners; + if (listeners != null && listeners.size() > 0) { + CopyOnWriteArray.Access<Consumer<List<Rect>>> access = listeners.start(); + try { + final int count = access.size(); + for (int i = 0; i < count; i++) { + access.get(i).accept(rects); + } + } finally { + listeners.end(); + } + } + } + /** * Copy on write array. This array is not thread safe, and only one loop can * iterate over this array at any given time. This class avoids allocations diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java index c1536ae2b4ae..135a8912fc60 100644 --- a/core/java/android/view/WindowInsets.java +++ b/core/java/android/view/WindowInsets.java @@ -28,6 +28,7 @@ import static android.view.WindowInsets.Type.compatSystemInsets; import static android.view.WindowInsets.Type.indexOf; import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; @@ -700,7 +701,8 @@ public final class WindowInsets { * @return the inset insets */ @NonNull - public WindowInsets inset(int left, int top, int right, int bottom) { + public WindowInsets inset(@IntRange(from = 0) int left, @IntRange(from = 0) int top, + @IntRange(from = 0) int right, @IntRange(from = 0) int bottom) { Preconditions.checkArgumentNonnegative(left); Preconditions.checkArgumentNonnegative(top); Preconditions.checkArgumentNonnegative(right); @@ -794,7 +796,7 @@ public final class WindowInsets { /** * Builder for WindowInsets. */ - public static class Builder { + public static final class Builder { private final Insets[] mTypeInsetsMap; private final Insets[] mTypeMaxInsetsMap; @@ -821,7 +823,7 @@ public final class WindowInsets { * * @param insets the instance to initialize from. */ - public Builder(WindowInsets insets) { + public Builder(@NonNull WindowInsets insets) { mTypeInsetsMap = insets.mTypeInsetsMap.clone(); mTypeMaxInsetsMap = insets.mTypeMaxInsetsMap.clone(); mTypeVisibilityMap = insets.mTypeVisibilityMap.clone(); diff --git a/core/java/android/view/accessibility/AccessibilityCache.java b/core/java/android/view/accessibility/AccessibilityCache.java index 5d59e4205579..87e18b71069c 100644 --- a/core/java/android/view/accessibility/AccessibilityCache.java +++ b/core/java/android/view/accessibility/AccessibilityCache.java @@ -424,28 +424,20 @@ public class AccessibilityCache { * * @param nodes The nodes in the hosting window. * @param rootNodeId The id of the root to evict. - * - * @return {@code true} if the cache was cleared */ - private boolean clearSubTreeRecursiveLocked(LongSparseArray<AccessibilityNodeInfo> nodes, + private void clearSubTreeRecursiveLocked(LongSparseArray<AccessibilityNodeInfo> nodes, long rootNodeId) { AccessibilityNodeInfo current = nodes.get(rootNodeId); if (current == null) { - // The node isn't in the cache, but its descendents might be. - clear(); - return true; + return; } nodes.remove(rootNodeId); final int childCount = current.getChildCount(); for (int i = 0; i < childCount; i++) { final long childNodeId = current.getChildId(i); - if (clearSubTreeRecursiveLocked(nodes, childNodeId)) { - current.recycle(); - return true; - } + clearSubTreeRecursiveLocked(nodes, childNodeId); } current.recycle(); - return false; } /** diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java index e0950948afb8..3b60aee8f604 100644 --- a/core/java/android/view/animation/Animation.java +++ b/core/java/android/view/animation/Animation.java @@ -207,6 +207,7 @@ public abstract class Animation implements Cloneable { private float mScaleFactor = 1f; private boolean mShowWallpaper; + private boolean mHasRoundedCorners; private boolean mMore = true; private boolean mOneMoreTime = true; @@ -263,6 +264,8 @@ public abstract class Animation implements Cloneable { a.getBoolean(com.android.internal.R.styleable.Animation_detachWallpaper, false)); setShowWallpaper( a.getBoolean(com.android.internal.R.styleable.Animation_showWallpaper, false)); + setHasRoundedCorners( + a.getBoolean(com.android.internal.R.styleable.Animation_hasRoundedCorners, false)); final int resID = a.getResourceId(com.android.internal.R.styleable.Animation_interpolator, 0); @@ -678,6 +681,19 @@ public abstract class Animation implements Cloneable { } /** + * If this is a window animation, the window will have rounded corners matching the display + * corner radius. + * + * @param hasRoundedCorners Whether the window should have rounded corners or not. + * @attr ref android.R.styleable#Animation_hasRoundedCorners + * @see com.android.internal.policy.ScreenDecorationsUtils#getWindowCornerRadius(Resources) + * @hide + */ + public void setHasRoundedCorners(boolean hasRoundedCorners) { + mHasRoundedCorners = hasRoundedCorners; + } + + /** * Gets the acceleration curve type for this animation. * * @return the {@link Interpolator} associated to this animation @@ -804,6 +820,16 @@ public abstract class Animation implements Cloneable { } /** + * @return if a window animation should have rounded corners or not. + * + * @attr ref android.R.styleable#Animation_hasRoundedCorners + * @hide + */ + public boolean hasRoundedCorners() { + return mHasRoundedCorners; + } + + /** * <p>Indicates whether or not this animation will affect the transformation * matrix. For instance, a fade animation will not affect the matrix whereas * a scale animation will.</p> diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index e9b16836157f..70fe230ce226 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -16,6 +16,7 @@ package android.view.autofill; +import static android.service.autofill.FillRequest.FLAG_AUGMENTED_AUTOFILL_REQUEST; import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST; import static android.view.autofill.Helper.sDebug; import static android.view.autofill.Helper.sVerbose; @@ -28,6 +29,7 @@ import android.annotation.RequiresFeature; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; +import android.content.AutofillOptions; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -369,6 +371,24 @@ public final class AutofillManager { public static final String DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES = "smart_suggestion_supported_modes"; + /** + * Sets how long (in ms) the augmented autofill service is bound while idle. + * + * <p>Use {@code 0} to keep it permanently bound. + * + * @hide + */ + public static final String DEVICE_CONFIG_AUGMENTED_SERVICE_IDLE_UNBIND_TIMEOUT = + "augmented_service_idle_unbind_timeout"; + + /** + * Sets how long (in ms) the augmented autofill service request is killed if not replied. + * + * @hide + */ + public static final String DEVICE_CONFIG_AUGMENTED_SERVICE_REQUEST_TIMEOUT = + "augmented_service_request_timeout"; + /** @hide */ public static final int RESULT_OK = 0; /** @hide */ @@ -466,6 +486,13 @@ public final class AutofillManager { @GuardedBy("mLock") @Nullable private ArraySet<AutofillId> mEnteredIds; + /** + * Views that were otherwised not important for autofill but triggered a session because the + * context is whitelisted for augmented autofill. + */ + @GuardedBy("mLock") + @Nullable private Set<AutofillId> mEnteredForAugmentedAutofillIds; + /** If set, session is commited when the field is clicked. */ @GuardedBy("mLock") @Nullable private AutofillId mSaveTriggerId; @@ -482,6 +509,9 @@ public final class AutofillManager { @GuardedBy("mLock") private CompatibilityBridge mCompatibilityBridge; + @Nullable + private final AutofillOptions mOptions; + /** @hide */ public interface AutofillClient { /** @@ -618,6 +648,12 @@ public final class AutofillManager { public AutofillManager(Context context, IAutoFillManager service) { mContext = Preconditions.checkNotNull(context, "context cannot be null"); mService = service; + mOptions = context.getAutofillOptions(); + + if (mOptions != null) { + sDebug = (mOptions.loggingLevel & FLAG_ADD_CLIENT_DEBUG) != 0; + sVerbose = (mOptions.loggingLevel & FLAG_ADD_CLIENT_VERBOSE) != 0; + } } /** @@ -1616,6 +1652,11 @@ public final class AutofillManager { @GuardedBy("mLock") private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds, @NonNull AutofillValue value, int flags) { + if (mEnteredForAugmentedAutofillIds != null + && mEnteredForAugmentedAutofillIds.contains(id)) { + if (sVerbose) Log.v(TAG, "Starting session for augmented autofill on " + id); + flags |= FLAG_AUGMENTED_AUTOFILL_REQUEST; + } if (sVerbose) { Log.v(TAG, "startSessionLocked(): id=" + id + ", bounds=" + bounds + ", value=" + value + ", flags=" + flags + ", state=" + getStateAsStringLocked() @@ -1851,6 +1892,25 @@ public final class AutofillManager { return set == null ? null : new ArrayList<T>(set); } + /** + * Notifies that a non-autofillable view was entered because the activity is whitelisted for + * augmented autofill. + * + * <p>This method is necessary to set the right flag on start, so the server-side session + * doesn't trigger the standard autofill workflow, but the augmented's instead. + * + * @hide + */ + public void notifyViewEnteredForAugmentedAutofill(@NonNull View view) { + final AutofillId id = view.getAutofillId(); + synchronized (mLock) { + if (mEnteredForAugmentedAutofillIds == null) { + mEnteredForAugmentedAutofillIds = new ArraySet<>(1); + } + mEnteredForAugmentedAutofillIds.add(id); + } + } + private void requestShowFillUi(int sessionId, AutofillId id, int width, int height, Rect anchorBounds, IAutofillWindowPresenter presenter) { final View anchor = findView(id); @@ -2350,8 +2410,15 @@ public final class AutofillManager { } pw.print(pfx); pw.print("fillable ids: "); pw.println(mFillableIds); pw.print(pfx); pw.print("entered ids: "); pw.println(mEnteredIds); + if (mEnteredForAugmentedAutofillIds != null) { + pw.print(pfx); pw.print("entered ids for augmented autofill: "); + pw.println(mEnteredForAugmentedAutofillIds); + } pw.print(pfx); pw.print("save trigger id: "); pw.println(mSaveTriggerId); pw.print(pfx); pw.print("save on finish(): "); pw.println(mSaveOnFinish); + if (mOptions != null) { + pw.print(pfx); pw.print("options: "); mOptions.dumpShort(pw); pw.println(); + } pw.print(pfx); pw.print("compat mode enabled: "); synchronized (mLock) { if (mCompatibilityBridge != null) { diff --git a/core/java/android/view/autofill/AutofillManagerInternal.java b/core/java/android/view/autofill/AutofillManagerInternal.java index 155fe721311c..d5862bd2f942 100644 --- a/core/java/android/view/autofill/AutofillManagerInternal.java +++ b/core/java/android/view/autofill/AutofillManagerInternal.java @@ -16,7 +16,9 @@ package android.view.autofill; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.content.AutofillOptions; /** * Autofill Manager local system service interface. @@ -31,12 +33,13 @@ public abstract class AutofillManagerInternal { public abstract void onBackKeyPressed(); /** - * Gets whether compatibility mode is enabled for a package + * Gets autofill options for a package * * @param packageName The package for which to query. * @param versionCode The package version code. * @param userId The user id for which to query. */ - public abstract boolean isCompatibilityModeRequested(@NonNull String packageName, + @Nullable + public abstract AutofillOptions getAutofillOptions(@NonNull String packageName, long versionCode, @UserIdInt int userId); } diff --git a/core/java/android/view/contentcapture/ChildContentCaptureSession.java b/core/java/android/view/contentcapture/ChildContentCaptureSession.java index 13e8a6584218..b3b0b72c8799 100644 --- a/core/java/android/view/contentcapture/ChildContentCaptureSession.java +++ b/core/java/android/view/contentcapture/ChildContentCaptureSession.java @@ -84,8 +84,8 @@ final class ChildContentCaptureSession extends ContentCaptureSession { } @Override - public void internalNotifyViewHierarchyEvent(boolean started) { - getMainCaptureSession().notifyInitialViewHierarchyEvent(mId, started); + public void internalNotifyViewTreeEvent(boolean started) { + getMainCaptureSession().notifyViewTreeEvent(mId, started); } @Override diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java index 9cdbefac3d1d..2585b746583d 100644 --- a/core/java/android/view/contentcapture/ContentCaptureEvent.java +++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java @@ -72,24 +72,24 @@ public final class ContentCaptureEvent implements Parcelable { public static final int TYPE_VIEW_TEXT_CHANGED = 3; /** - * Called before events (such as {@link #TYPE_VIEW_APPEARED}) representing the initial view - * hierarchy are sent. + * Called before events (such as {@link #TYPE_VIEW_APPEARED} and/or + * {@link #TYPE_VIEW_DISAPPEARED}) representing a view hierarchy are sent. * * <p><b>NOTE</b>: there is no guarantee this event will be sent. For example, it's not sent * if the initial view hierarchy doesn't initially have any view that's important for content * capture. */ - public static final int TYPE_INITIAL_VIEW_TREE_APPEARING = 4; + public static final int TYPE_VIEW_TREE_APPEARING = 4; /** - * Called after events (such as {@link #TYPE_VIEW_APPEARED}) representing the initial view - * hierarchy are sent. + * Called after events (such as {@link #TYPE_VIEW_APPEARED} and/or + * {@link #TYPE_VIEW_DISAPPEARED}) representing a view hierarchy were sent. * * <p><b>NOTE</b>: there is no guarantee this event will be sent. For example, it's not sent * if the initial view hierarchy doesn't initially have any view that's important for content * capture. */ - public static final int TYPE_INITIAL_VIEW_TREE_APPEARED = 5; + public static final int TYPE_VIEW_TREE_APPEARED = 5; /** * Called after a call to @@ -99,14 +99,29 @@ public final class ContentCaptureEvent implements Parcelable { */ public static final int TYPE_CONTEXT_UPDATED = 6; + /** + * Called after the session is ready, typically after the activity resumed and the + * initial views appeared + */ + public static final int TYPE_SESSION_RESUMED = 7; + + /** + * Called after the session is paused, typically after the activity paused and the + * views disappeared. + */ + public static final int TYPE_SESSION_PAUSED = 8; + + /** @hide */ @IntDef(prefix = { "TYPE_" }, value = { TYPE_VIEW_APPEARED, TYPE_VIEW_DISAPPEARED, TYPE_VIEW_TEXT_CHANGED, - TYPE_INITIAL_VIEW_TREE_APPEARING, - TYPE_INITIAL_VIEW_TREE_APPEARED, - TYPE_CONTEXT_UPDATED + TYPE_VIEW_TREE_APPEARING, + TYPE_VIEW_TREE_APPEARED, + TYPE_CONTEXT_UPDATED, + TYPE_SESSION_PAUSED, + TYPE_SESSION_RESUMED }) @Retention(RetentionPolicy.SOURCE) public @interface EventType{} @@ -230,8 +245,9 @@ public final class ContentCaptureEvent implements Parcelable { * Gets the type of the event. * * @return one of {@link #TYPE_VIEW_APPEARED}, {@link #TYPE_VIEW_DISAPPEARED}, - * {@link #TYPE_VIEW_TEXT_CHANGED}, {@link #TYPE_INITIAL_VIEW_TREE_APPEARING}, - * {@link #TYPE_INITIAL_VIEW_TREE_APPEARED}, or {@link #TYPE_CONTEXT_UPDATED}. + * {@link #TYPE_VIEW_TEXT_CHANGED}, {@link #TYPE_VIEW_TREE_APPEARING}, + * {@link #TYPE_VIEW_TREE_APPEARED}, {@link #TYPE_CONTEXT_UPDATED}, + * {@link #TYPE_SESSION_RESUMED}, or {@link #TYPE_SESSION_PAUSED}. */ public @EventType int getType() { return mType; @@ -411,16 +427,20 @@ public final class ContentCaptureEvent implements Parcelable { return "SESSION_STARTED"; case TYPE_SESSION_FINISHED: return "SESSION_FINISHED"; + case TYPE_SESSION_RESUMED: + return "SESSION_RESUMED"; + case TYPE_SESSION_PAUSED: + return "SESSION_PAUSED"; case TYPE_VIEW_APPEARED: return "VIEW_APPEARED"; case TYPE_VIEW_DISAPPEARED: return "VIEW_DISAPPEARED"; case TYPE_VIEW_TEXT_CHANGED: return "VIEW_TEXT_CHANGED"; - case TYPE_INITIAL_VIEW_TREE_APPEARING: - return "INITIAL_VIEW_HIERARCHY_STARTED"; - case TYPE_INITIAL_VIEW_TREE_APPEARED: - return "INITIAL_VIEW_HIERARCHY_FINISHED"; + case TYPE_VIEW_TREE_APPEARING: + return "VIEW_TREE_APPEARING"; + case TYPE_VIEW_TREE_APPEARED: + return "VIEW_TREE_APPEARED"; case TYPE_CONTEXT_UPDATED: return "CONTEXT_UPDATED"; default: diff --git a/core/java/android/view/contentcapture/ContentCaptureHelper.java b/core/java/android/view/contentcapture/ContentCaptureHelper.java index 1cf27fc56a8c..6e84ff03d0f9 100644 --- a/core/java/android/view/contentcapture/ContentCaptureHelper.java +++ b/core/java/android/view/contentcapture/ContentCaptureHelper.java @@ -63,12 +63,27 @@ public final class ContentCaptureHelper { } /** + * Gets the default logging level for the device. + */ + @LoggingLevel + public static int getDefaultLoggingLevel() { + return Build.IS_DEBUGGABLE ? LOGGING_LEVEL_DEBUG : LOGGING_LEVEL_OFF; + } + + /** * Sets the value of the static logging level constants based on device config. */ public static void setLoggingLevel() { - final int defaultLevel = Build.IS_DEBUGGABLE ? LOGGING_LEVEL_DEBUG : LOGGING_LEVEL_OFF; + final int defaultLevel = getDefaultLoggingLevel(); final int level = getIntDeviceConfigProperty(DEVICE_CONFIG_PROPERTY_LOGGING_LEVEL, defaultLevel); + setLoggingLevel(level); + } + + /** + * Sets the value of the static logging level constants based the given level. + */ + public static void setLoggingLevel(@LoggingLevel int level) { Log.i(TAG, "Setting logging level to " + getLoggingLevelAsString(level)); sVerbose = sDebug = false; switch (level) { diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index 87e358c1165f..885bd2a39580 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -26,6 +26,7 @@ import android.annotation.SystemService; import android.annotation.TestApi; import android.annotation.UiThread; import android.content.ComponentName; +import android.content.ContentCaptureOptions; import android.content.Context; import android.os.Handler; import android.os.IBinder; @@ -128,6 +129,14 @@ public final class ContentCaptureManager { @TestApi public static final String DEVICE_CONFIG_PROPERTY_LOGGING_LEVEL = "logging_level"; + /** + * Sets how long (in ms) the service is bound while idle. + * + * <p>Use {@code 0} to keep it permanently bound. + * + * @hide + */ + public static final String DEVICE_CONFIG_PROPERTY_IDLE_UNBIND_TIMEOUT = "idle_unbind_timeout"; /** @hide */ @TestApi @@ -150,6 +159,16 @@ public final class ContentCaptureManager { @Retention(RetentionPolicy.SOURCE) public @interface LoggingLevel {} + + /** @hide */ + public static final int DEFAULT_MAX_BUFFER_SIZE = 100; + /** @hide */ + public static final int DEFAULT_IDLE_FLUSHING_FREQUENCY_MS = 5_000; + /** @hide */ + public static final int DEFAULT_TEXT_CHANGE_FLUSHING_FREQUENCY_MS = 1_000; + /** @hide */ + public static final int DEFAULT_LOG_HISTORY_SIZE = 10; + private final Object mLock = new Object(); @NonNull @@ -158,6 +177,9 @@ public final class ContentCaptureManager { @NonNull private final IContentCaptureManager mService; + @NonNull + final ContentCaptureOptions mOptions; + // Flags used for starting session. @GuardedBy("mLock") private int mFlags; @@ -172,14 +194,12 @@ public final class ContentCaptureManager { /** @hide */ public ContentCaptureManager(@NonNull Context context, - @NonNull IContentCaptureManager service) { + @NonNull IContentCaptureManager service, @NonNull ContentCaptureOptions options) { mContext = Preconditions.checkNotNull(context, "context cannot be null"); mService = Preconditions.checkNotNull(service, "service cannot be null"); + mOptions = Preconditions.checkNotNull(options, "options cannot be null"); - // TODO(b/123096662): right now we're reading the device config values here, but ideally - // it should be read on ContentCaptureManagerService and passed back when the activity - // started. - ContentCaptureHelper.setLoggingLevel(); + ContentCaptureHelper.setLoggingLevel(mOptions.loggingLevel); if (sVerbose) Log.v(TAG, "Constructor for " + context.getPackageName()); @@ -212,7 +232,7 @@ public final class ContentCaptureManager { /** @hide */ @UiThread - public void onActivityStarted(@NonNull IBinder applicationToken, + public void onActivityCreated(@NonNull IBinder applicationToken, @NonNull ComponentName activityComponent, int flags) { synchronized (mLock) { mFlags |= flags; @@ -222,7 +242,19 @@ public final class ContentCaptureManager { /** @hide */ @UiThread - public void onActivityStopped() { + public void onActivityResumed() { + getMainContentCaptureSession().notifySessionLifecycle(/* started= */ true); + } + + /** @hide */ + @UiThread + public void onActivityPaused() { + getMainContentCaptureSession().notifySessionLifecycle(/* started= */ false); + } + + /** @hide */ + @UiThread + public void onActivityDestroyed() { getMainContentCaptureSession().destroy(); } @@ -355,12 +387,13 @@ public final class ContentCaptureManager { synchronized (mLock) { pw.print(prefix2); pw.print("isContentCaptureEnabled(): "); pw.println(isContentCaptureEnabled()); - pw.print(prefix); pw.print("Debug: "); pw.print(sDebug); + pw.print(prefix2); pw.print("Debug: "); pw.print(sDebug); pw.print(" Verbose: "); pw.println(sVerbose); - pw.print(prefix); pw.print("Context: "); pw.println(mContext); - pw.print(prefix); pw.print("User: "); pw.println(mContext.getUserId()); - pw.print(prefix); pw.print("Service: "); pw.println(mService); - pw.print(prefix); pw.print("Flags: "); pw.println(mFlags); + pw.print(prefix2); pw.print("Context: "); pw.println(mContext); + pw.print(prefix2); pw.print("User: "); pw.println(mContext.getUserId()); + pw.print(prefix2); pw.print("Service: "); pw.println(mService); + pw.print(prefix2); pw.print("Flags: "); pw.println(mFlags); + pw.print(prefix2); pw.print("Options: "); mOptions.dumpShort(pw); pw.println(); if (mMainSession != null) { final String prefix3 = prefix2 + " "; pw.print(prefix2); pw.println("Main session:"); diff --git a/core/java/android/view/contentcapture/ContentCaptureSession.java b/core/java/android/view/contentcapture/ContentCaptureSession.java index 1e051a43a42f..ab8f346bdd2e 100644 --- a/core/java/android/view/contentcapture/ContentCaptureSession.java +++ b/core/java/android/view/contentcapture/ContentCaptureSession.java @@ -132,27 +132,24 @@ public abstract class ContentCaptureSession implements AutoCloseable { /** @hide */ public static final int FLUSH_REASON_FULL = 1; /** @hide */ - public static final int FLUSH_REASON_ACTIVITY_PAUSED = 2; + public static final int FLUSH_REASON_VIEW_ROOT_ENTERED = 2; /** @hide */ - public static final int FLUSH_REASON_ACTIVITY_RESUMED = 3; + public static final int FLUSH_REASON_SESSION_STARTED = 3; /** @hide */ - public static final int FLUSH_REASON_SESSION_STARTED = 4; + public static final int FLUSH_REASON_SESSION_FINISHED = 4; /** @hide */ - public static final int FLUSH_REASON_SESSION_FINISHED = 5; - /** @hide */ - public static final int FLUSH_REASON_IDLE_TIMEOUT = 6; + public static final int FLUSH_REASON_IDLE_TIMEOUT = 5; /** @hide */ @IntDef(prefix = { "FLUSH_REASON_" }, value = { FLUSH_REASON_FULL, - FLUSH_REASON_ACTIVITY_PAUSED, - FLUSH_REASON_ACTIVITY_RESUMED, + FLUSH_REASON_VIEW_ROOT_ENTERED, FLUSH_REASON_SESSION_STARTED, FLUSH_REASON_SESSION_FINISHED, FLUSH_REASON_IDLE_TIMEOUT }) @Retention(RetentionPolicy.SOURCE) - @interface FlushReason{} + public @interface FlushReason{} private final Object mLock = new Object(); @@ -414,7 +411,7 @@ public abstract class ContentCaptureSession implements AutoCloseable { @Nullable CharSequence text); /** @hide */ - public abstract void internalNotifyViewHierarchyEvent(boolean started); + public abstract void internalNotifyViewTreeEvent(boolean started); /** * Creates a {@link ViewStructure} for a "standard" view. @@ -500,14 +497,12 @@ public abstract class ContentCaptureSession implements AutoCloseable { /** @hide */ @NonNull - static String getflushReasonAsString(@FlushReason int reason) { + public static String getFlushReasonAsString(@FlushReason int reason) { switch (reason) { case FLUSH_REASON_FULL: return "FULL"; - case FLUSH_REASON_ACTIVITY_PAUSED: - return "PAUSED"; - case FLUSH_REASON_ACTIVITY_RESUMED: - return "RESUMED"; + case FLUSH_REASON_VIEW_ROOT_ENTERED: + return "VIEW_ROOT"; case FLUSH_REASON_SESSION_STARTED: return "STARTED"; case FLUSH_REASON_SESSION_FINISHED: diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java index f4021b11f317..dce8ebe66111 100644 --- a/core/java/android/view/contentcapture/MainContentCaptureSession.java +++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java @@ -16,20 +16,18 @@ package android.view.contentcapture; import static android.view.contentcapture.ContentCaptureEvent.TYPE_CONTEXT_UPDATED; -import static android.view.contentcapture.ContentCaptureEvent.TYPE_INITIAL_VIEW_TREE_APPEARED; -import static android.view.contentcapture.ContentCaptureEvent.TYPE_INITIAL_VIEW_TREE_APPEARING; import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_FINISHED; +import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_PAUSED; +import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_RESUMED; import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_STARTED; import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_APPEARED; import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_DISAPPEARED; import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TEXT_CHANGED; -import static android.view.contentcapture.ContentCaptureHelper.getIntDeviceConfigProperty; +import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TREE_APPEARED; +import static android.view.contentcapture.ContentCaptureEvent.TYPE_VIEW_TREE_APPEARING; import static android.view.contentcapture.ContentCaptureHelper.getSanitizedString; import static android.view.contentcapture.ContentCaptureHelper.sDebug; import static android.view.contentcapture.ContentCaptureHelper.sVerbose; -import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY; -import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE; -import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE; import android.annotation.NonNull; import android.annotation.Nullable; @@ -76,10 +74,6 @@ public final class MainContentCaptureSession extends ContentCaptureSession { */ private static final int MSG_FLUSH = 1; - private static final int DEFAULT_MAX_BUFFER_SIZE = 100; - private static final int DEFAULT_FLUSHING_FREQUENCY_MS = 5_000; - private static final int DEFAULT_LOG_HISTORY_SIZE = 10; - /** * Name of the {@link IResultReceiver} extra used to pass the binder interface to the service. * @hide @@ -128,23 +122,12 @@ public final class MainContentCaptureSession extends ContentCaptureSession { @Nullable private ArrayList<ContentCaptureEvent> mEvents; - /** - * Maximum number of events that are buffered before sent to the app. - */ - private final int mMaxBufferSize; - - /** - * Frequency the buffer is flushed if idle. - */ - private final int mIdleFlushingFrequencyMs; - // Used just for debugging purposes (on dump) private long mNextFlush; @Nullable private final LocalLog mFlushHistory; - /** @hide */ protected MainContentCaptureSession(@NonNull Context context, @NonNull ContentCaptureManager manager, @NonNull Handler handler, @NonNull IContentCaptureManager systemServerInterface) { @@ -153,16 +136,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession { mHandler = handler; mSystemServerInterface = systemServerInterface; - // TODO(b/123096662): right now we're reading the device config values here, but ideally - // it should be read on ContentCaptureManagerService and passed back when the activity - // started. - mMaxBufferSize = getIntDeviceConfigProperty(DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE, - DEFAULT_MAX_BUFFER_SIZE); - mIdleFlushingFrequencyMs = getIntDeviceConfigProperty( - DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY, DEFAULT_FLUSHING_FREQUENCY_MS); - final int logHistorySize = getIntDeviceConfigProperty( - DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE, DEFAULT_LOG_HISTORY_SIZE); - + final int logHistorySize = mManager.mOptions.logHistorySize; mFlushHistory = logHistorySize > 0 ? new LocalLog(logHistorySize) : null; } @@ -180,8 +154,6 @@ public final class MainContentCaptureSession extends ContentCaptureSession { /** * Starts this session. - * - * @hide */ @UiThread void start(@NonNull IBinder token, @NonNull ComponentName component, @@ -302,11 +274,12 @@ public final class MainContentCaptureSession extends ContentCaptureSession { if (sVerbose) Log.v(TAG, "handleSendEvent(): ignoring when disabled"); return; } + final int maxBufferSize = mManager.mOptions.maxBufferSize; if (mEvents == null) { if (sVerbose) { - Log.v(TAG, "handleSendEvent(): creating buffer for " + mMaxBufferSize + " events"); + Log.v(TAG, "handleSendEvent(): creating buffer for " + maxBufferSize + " events"); } - mEvents = new ArrayList<>(mMaxBufferSize); + mEvents = new ArrayList<>(maxBufferSize); } // Some type of events can be merged together @@ -347,14 +320,14 @@ public final class MainContentCaptureSession extends ContentCaptureSession { final int numberEvents = mEvents.size(); - final boolean bufferEvent = numberEvents < mMaxBufferSize; + final boolean bufferEvent = numberEvents < maxBufferSize; if (bufferEvent && !forceFlush) { scheduleFlush(FLUSH_REASON_IDLE_TIMEOUT, /* checkExisting= */ true); return; } - if (mState != STATE_ACTIVE && numberEvents >= mMaxBufferSize) { + if (mState != STATE_ACTIVE && numberEvents >= maxBufferSize) { // Callback from startSession hasn't been called yet - typically happens on system // apps that are started before the system service // TODO(b/122959591): try to ignore session while system is not ready / boot @@ -435,13 +408,14 @@ public final class MainContentCaptureSession extends ContentCaptureSession { // "Renew" the flush message by removing the previous one mHandler.removeMessages(MSG_FLUSH); } - mNextFlush = System.currentTimeMillis() + mIdleFlushingFrequencyMs; + final int idleFlushingFrequencyMs = mManager.mOptions.idleFlushingFrequencyMs; + mNextFlush = System.currentTimeMillis() + idleFlushingFrequencyMs; if (sVerbose) { Log.v(TAG, "handleScheduleFlush(): scheduled to flush in " - + mIdleFlushingFrequencyMs + "ms: " + TimeUtils.logTimeOfDay(mNextFlush)); + + idleFlushingFrequencyMs + "ms: " + TimeUtils.logTimeOfDay(mNextFlush)); } // Post using a Runnable directly to trim a few μs from PooledLambda.obtainMessage() - mHandler.postDelayed(() -> flushIfNeeded(reason), MSG_FLUSH, mIdleFlushingFrequencyMs); + mHandler.postDelayed(() -> flushIfNeeded(reason), MSG_FLUSH, idleFlushingFrequencyMs); } @UiThread @@ -476,14 +450,15 @@ public final class MainContentCaptureSession extends ContentCaptureSession { } final int numberEvents = mEvents.size(); - final String reasonString = getflushReasonAsString(reason); + final String reasonString = getFlushReasonAsString(reason); if (sDebug) { Log.d(TAG, "Flushing " + numberEvents + " event(s) for " + getDebugState(reason)); } if (mFlushHistory != null) { // Logs reason, size, max size, idle timeout final String logRecord = "r=" + reasonString + " s=" + numberEvents - + " m=" + mMaxBufferSize + " i=" + mIdleFlushingFrequencyMs; + + " m=" + mManager.mOptions.maxBufferSize + + " i=" + mManager.mOptions.idleFlushingFrequencyMs; mFlushHistory.log(logRecord); } try { @@ -570,8 +545,8 @@ public final class MainContentCaptureSession extends ContentCaptureSession { } @Override - public void internalNotifyViewHierarchyEvent(boolean started) { - notifyInitialViewHierarchyEvent(mId, started); + public void internalNotifyViewTreeEvent(boolean started) { + notifyViewTreeEvent(mId, started); } @Override @@ -605,21 +580,9 @@ public final class MainContentCaptureSession extends ContentCaptureSession { .setViewNode(node.mNode)); } - void notifyViewDisappeared(@NonNull String sessionId, @NonNull AutofillId id) { - sendEvent( - new ContentCaptureEvent(sessionId, TYPE_VIEW_DISAPPEARED).setAutofillId(id)); - } - - /** @hide */ - public void notifyViewsDisappeared(@NonNull String sessionId, - @NonNull ArrayList<AutofillId> ids) { - final ContentCaptureEvent event = new ContentCaptureEvent(sessionId, TYPE_VIEW_DISAPPEARED); - if (ids.size() == 1) { - event.setAutofillId(ids.get(0)); - } else { - event.setAutofillIds(ids); - } - sendEvent(event); + /** Public because is also used by ViewRootImpl */ + public void notifyViewDisappeared(@NonNull String sessionId, @NonNull AutofillId id) { + sendEvent(new ContentCaptureEvent(sessionId, TYPE_VIEW_DISAPPEARED).setAutofillId(id)); } void notifyViewTextChanged(@NonNull String sessionId, @NonNull AutofillId id, @@ -628,13 +591,16 @@ public final class MainContentCaptureSession extends ContentCaptureSession { .setText(text)); } - void notifyInitialViewHierarchyEvent(@NonNull String sessionId, boolean started) { - if (started) { - sendEvent(new ContentCaptureEvent(sessionId, TYPE_INITIAL_VIEW_TREE_APPEARING)); - } else { - sendEvent(new ContentCaptureEvent(sessionId, TYPE_INITIAL_VIEW_TREE_APPEARED), - FORCE_FLUSH); - } + /** Public because is also used by ViewRootImpl */ + public void notifyViewTreeEvent(@NonNull String sessionId, boolean started) { + final int type = started ? TYPE_VIEW_TREE_APPEARING : TYPE_VIEW_TREE_APPEARED; + sendEvent(new ContentCaptureEvent(sessionId, type), FORCE_FLUSH); + } + + /** Public because is also used by ViewRootImpl */ + public void notifySessionLifecycle(boolean started) { + final int type = started ? TYPE_SESSION_RESUMED : TYPE_SESSION_PAUSED; + sendEvent(new ContentCaptureEvent(mId, type), FORCE_FLUSH); } void notifyContextUpdated(@NonNull String sessionId, @@ -649,7 +615,6 @@ public final class MainContentCaptureSession extends ContentCaptureSession { pw.print(prefix); pw.print("mContext: "); pw.println(mContext); pw.print(prefix); pw.print("user: "); pw.println(mContext.getUserId()); - pw.print(prefix); pw.print("mSystemServerInterface: "); if (mDirectServiceInterface != null) { pw.print(prefix); pw.print("mDirectServiceInterface: "); pw.println(mDirectServiceInterface); @@ -667,7 +632,7 @@ public final class MainContentCaptureSession extends ContentCaptureSession { if (mEvents != null && !mEvents.isEmpty()) { final int numberEvents = mEvents.size(); pw.print(prefix); pw.print("buffered events: "); pw.print(numberEvents); - pw.print('/'); pw.println(mMaxBufferSize); + pw.print('/'); pw.println(mManager.mOptions.maxBufferSize); if (sVerbose && numberEvents > 0) { final String prefix3 = prefix + " "; for (int i = 0; i < numberEvents; i++) { @@ -676,7 +641,8 @@ public final class MainContentCaptureSession extends ContentCaptureSession { pw.println(); } } - pw.print(prefix); pw.print("flush frequency: "); pw.println(mIdleFlushingFrequencyMs); + pw.print(prefix); pw.print("flush frequency: "); + pw.println(mManager.mOptions.idleFlushingFrequencyMs); pw.print(prefix); pw.print("next flush: "); TimeUtils.formatDuration(mNextFlush - System.currentTimeMillis(), pw); pw.print(" ("); pw.print(TimeUtils.logTimeOfDay(mNextFlush)); pw.println(")"); @@ -708,6 +674,6 @@ public final class MainContentCaptureSession extends ContentCaptureSession { @NonNull private String getDebugState(@FlushReason int reason) { - return getDebugState() + ", reason=" + getflushReasonAsString(reason); + return getDebugState() + ", reason=" + getFlushReasonAsString(reason); } } diff --git a/core/java/android/view/inspector/GeneratedInspectionCompanionProvider.java b/core/java/android/view/inspector/GeneratedInspectionCompanionProvider.java index 8faae1f1da98..d4b7e858bebd 100644 --- a/core/java/android/view/inspector/GeneratedInspectionCompanionProvider.java +++ b/core/java/android/view/inspector/GeneratedInspectionCompanionProvider.java @@ -40,8 +40,15 @@ public class GeneratedInspectionCompanionProvider implements InspectionCompanion final Class<InspectionCompanion<T>> companionClass = (Class<InspectionCompanion<T>>) cls.getClassLoader().loadClass(companionName); return companionClass.newInstance(); - } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { + } catch (ClassNotFoundException e) { return null; + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InstantiationException e) { + Throwable cause = e.getCause(); + if (cause instanceof RuntimeException) throw (RuntimeException) cause; + if (cause instanceof Error) throw (Error) cause; + throw new RuntimeException(cause); } } } diff --git a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java index 4d917a1b1968..efdc968909ee 100644 --- a/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java +++ b/core/java/android/view/textclassifier/ActionsSuggestionsHelper.java @@ -16,6 +16,7 @@ package android.view.textclassifier; +import android.annotation.Nullable; import android.app.Person; import android.content.Context; import android.text.TextUtils; @@ -110,6 +111,19 @@ public final class ActionsSuggestionsHelper { SelectionSessionLogger.CLASSIFIER_ID, modelName, hash); } + /** + * Returns a {@link android.view.textclassifier.LabeledIntent.TitleChooser} for + * conversation actions use case. + */ + @Nullable + public static LabeledIntent.TitleChooser createTitleChooser(String actionType) { + if (ConversationAction.TYPE_OPEN_URL.equals(actionType)) { + return (labeledIntent, resolveInfo) -> resolveInfo.handleAllWebDataURI + ? labeledIntent.titleWithEntity : labeledIntent.titleWithoutEntity; + } + return null; + } + private static final class PersonEncoder { private final Map<Person, Integer> mMapping = new ArrayMap<>(); private int mNextUserId = FIRST_NON_LOCAL_USER; diff --git a/core/java/android/view/textclassifier/ExtrasUtils.java b/core/java/android/view/textclassifier/ExtrasUtils.java index b0e7ad5d7264..2ad17a84a64e 100644 --- a/core/java/android/view/textclassifier/ExtrasUtils.java +++ b/core/java/android/view/textclassifier/ExtrasUtils.java @@ -94,7 +94,8 @@ public final class ExtrasUtils { if (actionIntents != null) { final int size = actionIntents.size(); for (int i = 0; i < size; i++) { - if (intentAction.equals(actionIntents.get(i).getAction())) { + final Intent intent = actionIntents.get(i); + if (intent != null && intentAction.equals(intent.getAction())) { return classification.getActions().get(i); } } diff --git a/core/java/android/view/textclassifier/IntentFactory.java b/core/java/android/view/textclassifier/IntentFactory.java index 9f3b97f8339f..722c812856d4 100644 --- a/core/java/android/view/textclassifier/IntentFactory.java +++ b/core/java/android/view/textclassifier/IntentFactory.java @@ -32,7 +32,7 @@ public interface IntentFactory { /** * Return a list of LabeledIntent from the classification result. */ - List<TextClassifierImpl.LabeledIntent> create( + List<LabeledIntent> create( Context context, String text, boolean foreignText, @@ -43,9 +43,10 @@ public interface IntentFactory { * Inserts translate action to the list if it is a foreign text. */ static void insertTranslateAction( - List<TextClassifierImpl.LabeledIntent> actions, Context context, String text) { - actions.add(new TextClassifierImpl.LabeledIntent( + List<LabeledIntent> actions, Context context, String text) { + actions.add(new LabeledIntent( context.getString(com.android.internal.R.string.translate), + /* titleWithEntity */ null, context.getString(com.android.internal.R.string.translate_desc), new Intent(Intent.ACTION_TRANSLATE) // TODO: Probably better to introduce a "translate" scheme instead of diff --git a/core/java/android/view/textclassifier/LabeledIntent.java b/core/java/android/view/textclassifier/LabeledIntent.java new file mode 100644 index 000000000000..7544dc1b58b1 --- /dev/null +++ b/core/java/android/view/textclassifier/LabeledIntent.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.view.textclassifier; + +import android.annotation.Nullable; +import android.app.PendingIntent; +import android.app.RemoteAction; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.graphics.drawable.Icon; +import android.text.TextUtils; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.Preconditions; + +/** + * Helper class to store the information from which RemoteActions are built. + * + * @hide + */ +@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) +public final class LabeledIntent { + private static final String TAG = "LabeledIntent"; + public static final int DEFAULT_REQUEST_CODE = 0; + private static final TitleChooser DEFAULT_TITLE_CHOOSER = + (labeledIntent, resolveInfo) -> { + if (!TextUtils.isEmpty(labeledIntent.titleWithEntity)) { + return labeledIntent.titleWithEntity; + } + return labeledIntent.titleWithoutEntity; + }; + + @Nullable + public final String titleWithoutEntity; + @Nullable + public final String titleWithEntity; + public final String description; + // Do not update this intent. + public final Intent intent; + public final int requestCode; + + /** + * Initializes a LabeledIntent. + * + * <p>NOTE: {@code requestCode} is required to not be {@link #DEFAULT_REQUEST_CODE} + * if distinguishing info (e.g. the classified text) is represented in intent extras only. + * In such circumstances, the request code should represent the distinguishing info + * (e.g. by generating a hashcode) so that the generated PendingIntent is (somewhat) + * unique. To be correct, the PendingIntent should be definitely unique but we try a + * best effort approach that avoids spamming the system with PendingIntents. + */ + // TODO: Fix the issue mentioned above so the behaviour is correct. + public LabeledIntent( + @Nullable String titleWithoutEntity, + @Nullable String titleWithEntity, + String description, + Intent intent, + int requestCode) { + if (TextUtils.isEmpty(titleWithEntity) && TextUtils.isEmpty(titleWithoutEntity)) { + throw new IllegalArgumentException( + "titleWithEntity and titleWithoutEntity should not be both null"); + } + this.titleWithoutEntity = titleWithoutEntity; + this.titleWithEntity = titleWithEntity; + this.description = Preconditions.checkNotNull(description); + this.intent = Preconditions.checkNotNull(intent); + this.requestCode = requestCode; + } + + /** + * Return the resolved result. + */ + @Nullable + public Result resolve( + Context context, @Nullable TitleChooser titleChooser) { + final PackageManager pm = context.getPackageManager(); + final ResolveInfo resolveInfo = pm.resolveActivity(intent, 0); + final String packageName = resolveInfo != null && resolveInfo.activityInfo != null + ? resolveInfo.activityInfo.packageName : null; + Icon icon = null; + Intent resolvedIntent = new Intent(intent); + boolean shouldShowIcon = false; + if (packageName != null && !"android".equals(packageName)) { + // There is a default activity handling the intent. + resolvedIntent.setComponent( + new ComponentName(packageName, resolveInfo.activityInfo.name)); + if (resolveInfo.activityInfo.getIconResource() != 0) { + icon = Icon.createWithResource( + packageName, resolveInfo.activityInfo.getIconResource()); + shouldShowIcon = true; + } + } + if (icon == null) { + // RemoteAction requires that there be an icon. + icon = Icon.createWithResource("android", + com.android.internal.R.drawable.ic_more_items); + } + final PendingIntent pendingIntent = + TextClassification.createPendingIntent(context, resolvedIntent, requestCode); + if (pendingIntent == null) { + return null; + } + if (titleChooser == null) { + titleChooser = DEFAULT_TITLE_CHOOSER; + } + CharSequence title = titleChooser.chooseTitle(this, resolveInfo); + if (TextUtils.isEmpty(title)) { + Log.w(TAG, "Custom titleChooser return null, fallback to the default titleChooser"); + title = DEFAULT_TITLE_CHOOSER.chooseTitle(this, resolveInfo); + } + final RemoteAction action = + new RemoteAction(icon, title, description, pendingIntent); + action.setShouldShowIcon(shouldShowIcon); + return new Result(resolvedIntent, action); + } + + /** + * Data class that holds the result. + */ + public static final class Result { + public final Intent resolvedIntent; + public final RemoteAction remoteAction; + + public Result(Intent resolvedIntent, RemoteAction remoteAction) { + this.resolvedIntent = Preconditions.checkNotNull(resolvedIntent); + this.remoteAction = Preconditions.checkNotNull(remoteAction); + } + } + + /** + * An object to choose a title from resolved info. If {@code null} is returned, + * {@link #titleWithEntity} will be used if it exists, {@link #titleWithoutEntity} otherwise. + */ + public interface TitleChooser { + /** + * Picks a title from a {@link LabeledIntent} by looking into resolved info. + */ + @Nullable + CharSequence chooseTitle(LabeledIntent labeledIntent, ResolveInfo resolveInfo); + } +} diff --git a/core/java/android/view/textclassifier/LegacyIntentFactory.java b/core/java/android/view/textclassifier/LegacyIntentFactory.java index 2d0d032cfef3..ea9229d28814 100644 --- a/core/java/android/view/textclassifier/LegacyIntentFactory.java +++ b/core/java/android/view/textclassifier/LegacyIntentFactory.java @@ -29,7 +29,6 @@ import android.os.UserManager; import android.provider.Browser; import android.provider.CalendarContract; import android.provider.ContactsContract; -import android.view.textclassifier.TextClassifierImpl.LabeledIntent; import com.google.android.textclassifier.AnnotatorModel; @@ -100,8 +99,7 @@ public final class LegacyIntentFactory implements IntentFactory { IntentFactory.insertTranslateAction(actions, context, text); } actions.forEach( - action -> action.getIntent() - .putExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER, true)); + action -> action.intent.putExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER, true)); return actions; } @@ -110,12 +108,14 @@ public final class LegacyIntentFactory implements IntentFactory { final List<LabeledIntent> actions = new ArrayList<>(); actions.add(new LabeledIntent( context.getString(com.android.internal.R.string.email), + /* titleWithEntity */ null, context.getString(com.android.internal.R.string.email_desc), new Intent(Intent.ACTION_SENDTO) .setData(Uri.parse(String.format("mailto:%s", text))), LabeledIntent.DEFAULT_REQUEST_CODE)); actions.add(new LabeledIntent( context.getString(com.android.internal.R.string.add_contact), + /* titleWithEntity */ null, context.getString(com.android.internal.R.string.add_contact_desc), new Intent(Intent.ACTION_INSERT_OR_EDIT) .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE) @@ -133,6 +133,7 @@ public final class LegacyIntentFactory implements IntentFactory { if (!userRestrictions.getBoolean(UserManager.DISALLOW_OUTGOING_CALLS, false)) { actions.add(new LabeledIntent( context.getString(com.android.internal.R.string.dial), + /* titleWithEntity */ null, context.getString(com.android.internal.R.string.dial_desc), new Intent(Intent.ACTION_DIAL).setData( Uri.parse(String.format("tel:%s", text))), @@ -140,6 +141,7 @@ public final class LegacyIntentFactory implements IntentFactory { } actions.add(new LabeledIntent( context.getString(com.android.internal.R.string.add_contact), + /* titleWithEntity */ null, context.getString(com.android.internal.R.string.add_contact_desc), new Intent(Intent.ACTION_INSERT_OR_EDIT) .setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE) @@ -148,6 +150,7 @@ public final class LegacyIntentFactory implements IntentFactory { if (!userRestrictions.getBoolean(UserManager.DISALLOW_SMS, false)) { actions.add(new LabeledIntent( context.getString(com.android.internal.R.string.sms), + /* titleWithEntity */ null, context.getString(com.android.internal.R.string.sms_desc), new Intent(Intent.ACTION_SENDTO) .setData(Uri.parse(String.format("smsto:%s", text))), @@ -163,6 +166,7 @@ public final class LegacyIntentFactory implements IntentFactory { final String encText = URLEncoder.encode(text, "UTF-8"); actions.add(new LabeledIntent( context.getString(com.android.internal.R.string.map), + /* titleWithEntity */ null, context.getString(com.android.internal.R.string.map_desc), new Intent(Intent.ACTION_VIEW) .setData(Uri.parse(String.format("geo:0,0?q=%s", encText))), @@ -181,6 +185,7 @@ public final class LegacyIntentFactory implements IntentFactory { final List<LabeledIntent> actions = new ArrayList<>(); actions.add(new LabeledIntent( context.getString(com.android.internal.R.string.browse), + /* titleWithEntity */ null, context.getString(com.android.internal.R.string.browse_desc), new Intent(Intent.ACTION_VIEW) .setDataAndNormalize(Uri.parse(text)) @@ -211,6 +216,7 @@ public final class LegacyIntentFactory implements IntentFactory { final List<LabeledIntent> actions = new ArrayList<>(); actions.add(new LabeledIntent( context.getString(com.android.internal.R.string.view_flight), + /* titleWithEntity */ null, context.getString(com.android.internal.R.string.view_flight_desc), new Intent(Intent.ACTION_WEB_SEARCH) .putExtra(SearchManager.QUERY, text), @@ -225,6 +231,7 @@ public final class LegacyIntentFactory implements IntentFactory { ContentUris.appendId(builder, parsedTime.toEpochMilli()); return new LabeledIntent( context.getString(com.android.internal.R.string.view_calendar), + /* titleWithEntity */ null, context.getString(com.android.internal.R.string.view_calendar_desc), new Intent(Intent.ACTION_VIEW).setData(builder.build()), LabeledIntent.DEFAULT_REQUEST_CODE); @@ -236,6 +243,7 @@ public final class LegacyIntentFactory implements IntentFactory { final boolean isAllDay = TextClassifier.TYPE_DATE.equals(type); return new LabeledIntent( context.getString(com.android.internal.R.string.add_calendar_event), + /* titleWithEntity */ null, context.getString(com.android.internal.R.string.add_calendar_event_desc), new Intent(Intent.ACTION_INSERT) .setData(CalendarContract.Events.CONTENT_URI) @@ -252,6 +260,7 @@ public final class LegacyIntentFactory implements IntentFactory { final List<LabeledIntent> actions = new ArrayList<>(); actions.add(new LabeledIntent( context.getString(com.android.internal.R.string.define), + /* titleWithEntity */ null, context.getString(com.android.internal.R.string.define_desc), new Intent(Intent.ACTION_DEFINE) .putExtra(Intent.EXTRA_TEXT, text), diff --git a/core/java/android/view/textclassifier/TemplateClassificationIntentFactory.java b/core/java/android/view/textclassifier/TemplateClassificationIntentFactory.java index 2467802ec5a8..ed0259f4cd1a 100644 --- a/core/java/android/view/textclassifier/TemplateClassificationIntentFactory.java +++ b/core/java/android/view/textclassifier/TemplateClassificationIntentFactory.java @@ -48,12 +48,12 @@ public final class TemplateClassificationIntentFactory implements IntentFactory } /** - * Returns a list of {@link android.view.textclassifier.TextClassifierImpl.LabeledIntent} + * Returns a list of {@link android.view.textclassifier.LabeledIntent} * that are constructed from the classification result. */ @NonNull @Override - public List<TextClassifierImpl.LabeledIntent> create( + public List<LabeledIntent> create( Context context, String text, boolean foreignText, @@ -68,7 +68,7 @@ public final class TemplateClassificationIntentFactory implements IntentFactory Log.w(TAG, "RemoteActionTemplate is missing, fallback to LegacyIntentFactory."); return mFallback.create(context, text, foreignText, referenceTime, classification); } - final List<TextClassifierImpl.LabeledIntent> labeledIntents = + final List<LabeledIntent> labeledIntents = mTemplateIntentFactory.create(remoteActionTemplates); if (foreignText) { IntentFactory.insertTranslateAction(labeledIntents, context, text.trim()); diff --git a/core/java/android/view/textclassifier/TemplateIntentFactory.java b/core/java/android/view/textclassifier/TemplateIntentFactory.java index 95f88c7de146..0696d98f1929 100644 --- a/core/java/android/view/textclassifier/TemplateIntentFactory.java +++ b/core/java/android/view/textclassifier/TemplateIntentFactory.java @@ -42,29 +42,29 @@ public final class TemplateIntentFactory { private static final String TAG = TextClassifier.DEFAULT_LOG_TAG; @NonNull - public List<TextClassifierImpl.LabeledIntent> create( + public List<LabeledIntent> create( @Nullable RemoteActionTemplate[] remoteActionTemplates) { if (ArrayUtils.isEmpty(remoteActionTemplates)) { return Collections.emptyList(); } - final List<TextClassifierImpl.LabeledIntent> labeledIntents = new ArrayList<>(); + final List<LabeledIntent> labeledIntents = new ArrayList<>(); for (RemoteActionTemplate remoteActionTemplate : remoteActionTemplates) { if (!isValidTemplate(remoteActionTemplate)) { Log.w(TAG, "Invalid RemoteActionTemplate skipped."); continue; } labeledIntents.add( - new TextClassifierImpl.LabeledIntent( - remoteActionTemplate.title, + new LabeledIntent( + remoteActionTemplate.titleWithoutEntity, + remoteActionTemplate.titleWithEntity, remoteActionTemplate.description, createIntent(remoteActionTemplate), remoteActionTemplate.requestCode == null - ? TextClassifierImpl.LabeledIntent.DEFAULT_REQUEST_CODE + ? LabeledIntent.DEFAULT_REQUEST_CODE : remoteActionTemplate.requestCode)); } labeledIntents.forEach( - action -> action.getIntent() - .putExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER, true)); + action -> action.intent.putExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER, true)); return labeledIntents; } @@ -73,7 +73,8 @@ public final class TemplateIntentFactory { Log.w(TAG, "Invalid RemoteActionTemplate: is null"); return false; } - if (TextUtils.isEmpty(remoteActionTemplate.title)) { + if (TextUtils.isEmpty(remoteActionTemplate.titleWithEntity) + && TextUtils.isEmpty(remoteActionTemplate.titleWithoutEntity)) { Log.w(TAG, "Invalid RemoteActionTemplate: title is null"); return false; } diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java index a05920960dcf..052ee953e971 100644 --- a/core/java/android/view/textclassifier/TextClassification.java +++ b/core/java/android/view/textclassifier/TextClassification.java @@ -54,6 +54,7 @@ import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Objects; /** * Information for generating a widget to handle classified text. @@ -276,8 +277,8 @@ public final class TextClassification implements Parcelable { @Override public String toString() { return String.format(Locale.US, - "TextClassification {text=%s, entities=%s, actions=%s, id=%s}", - mText, mEntityConfidence, mActions, mId); + "TextClassification {text=%s, entities=%s, actions=%s, id=%s, extras=%s}", + mText, mEntityConfidence, mActions, mId, mExtras); } /** @@ -532,7 +533,7 @@ public final class TextClassification implements Parcelable { private Bundle buildExtras() { final Bundle extras = mExtras == null ? new Bundle() : mExtras.deepCopy(); - if (!mActionIntents.isEmpty()) { + if (mActionIntents.stream().anyMatch(Objects::nonNull)) { ExtrasUtils.putActionsIntents(extras, mActionIntents); } if (mForeignLanguageExtra != null) { diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java index 295c8b72b943..632328be973e 100644 --- a/core/java/android/view/textclassifier/TextClassifierImpl.java +++ b/core/java/android/view/textclassifier/TextClassifierImpl.java @@ -19,21 +19,14 @@ package android.view.textclassifier; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.WorkerThread; -import android.app.PendingIntent; import android.app.RemoteAction; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.graphics.drawable.Icon; import android.icu.util.ULocale; import android.os.Bundle; import android.os.LocaleList; import android.os.ParcelFileDescriptor; import com.android.internal.annotations.GuardedBy; -import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; @@ -240,9 +233,7 @@ public final class TextClassifierImpl implements TextClassifier { refTime.getZone().getId(), localesString), mContext, - // TODO: Pass the locale list once it is supported in - // native side. - LocaleList.getDefault().get(0).toLanguageTag() + getResourceLocaleString() ); if (results.length > 0) { return createClassificationResult( @@ -403,8 +394,7 @@ public final class TextClassifierImpl implements TextClassifier { nativeConversation, null, mContext, - // TODO: Pass the locale list once it is supported in native side. - LocaleList.getDefault().get(0).toLanguageTag()); + getResourceLocaleString()); return createConversationActionResult(request, nativeSuggestions); } catch (Throwable t) { // Avoid throwing from this method. Log the error. @@ -433,7 +423,12 @@ public final class TextClassifierImpl implements TextClassifier { // Given that we only support implicit intent here, we should expect there is just one // intent for each action type. if (!labeledIntents.isEmpty()) { - remoteAction = labeledIntents.get(0).asRemoteAction(mContext); + LabeledIntent.TitleChooser titleChooser = + ActionsSuggestionsHelper.createTitleChooser(actionType); + LabeledIntent.Result result = labeledIntents.get(0).resolve(mContext, titleChooser); + if (result != null) { + remoteAction = result.remoteAction; + } } conversationActions.add( new ConversationAction.Builder(actionType) @@ -456,10 +451,9 @@ public final class TextClassifierImpl implements TextClassifier { TextLanguage textLanguage = detectLanguage(request); int localeHypothesisCount = textLanguage.getLocaleHypothesisCount(); List<String> languageTags = new ArrayList<>(); - // TODO: Reconsider this and probably make the score threshold configurable. for (int i = 0; i < localeHypothesisCount; i++) { ULocale locale = textLanguage.getLocale(i); - if (textLanguage.getConfidenceScore(locale) < 0.5) { + if (textLanguage.getConfidenceScore(locale) < getForeignLanguageThreshold()) { break; } languageTags.add(locale.toLanguageTag()); @@ -587,38 +581,36 @@ public final class TextClassifierImpl implements TextClassifier { } } - final float foreignTextThreshold = mSettings.getLangIdThresholdOverride() >= 0 - ? mSettings.getLangIdThresholdOverride() - : 0.5f /* TODO: Load this from the langId model. */; - final Bundle foreignLanguageBundle = - detectForeignLanguage(classifiedText, foreignTextThreshold); + final Bundle foreignLanguageBundle = detectForeignLanguage(classifiedText); builder.setForeignLanguageExtra(foreignLanguageBundle); boolean isPrimaryAction = true; - final ArrayList<Intent> sourceIntents = new ArrayList<>(); List<LabeledIntent> labeledIntents = mIntentFactory.create( mContext, classifiedText, foreignLanguageBundle != null, referenceTime, highestScoringResult); + LabeledIntent.TitleChooser titleChooser = + (labeledIntent, resolveInfo) -> labeledIntent.titleWithoutEntity; for (LabeledIntent labeledIntent : labeledIntents) { - final RemoteAction action = labeledIntent.asRemoteAction(mContext); - if (action == null) { + LabeledIntent.Result result = labeledIntent.resolve(mContext, titleChooser); + if (result == null) { continue; } + final RemoteAction action = result.remoteAction; if (isPrimaryAction) { // For O backwards compatibility, the first RemoteAction is also written to the // legacy API fields. builder.setIcon(action.getIcon().loadDrawable(mContext)); builder.setLabel(action.getTitle().toString()); - builder.setIntent(labeledIntent.getIntent()); + builder.setIntent(result.resolvedIntent); builder.setOnClickListener(TextClassification.createIntentOnClickListener( TextClassification.createPendingIntent(mContext, - labeledIntent.getIntent(), labeledIntent.getRequestCode()))); + result.resolvedIntent, labeledIntent.requestCode))); isPrimaryAction = false; } - builder.addAction(action, labeledIntent.getIntent()); + builder.addAction(action, result.resolvedIntent); } return builder.setId(createId(text, start, end)).build(); @@ -626,16 +618,20 @@ public final class TextClassifierImpl implements TextClassifier { /** * Returns a bundle with the language and confidence score if it finds the text to be - * in a foreign language. Otherwise returns null. + * in a foreign language. Otherwise returns null. This algorithm defines what the system thinks + * is a foreign language. */ + // TODO: Revisit this algorithm. + // TODO: Consider making this public API. @Nullable - private Bundle detectForeignLanguage(String text, float threshold) { - if (threshold > 1) { - return null; - } - - // TODO: Revisit this algorithm. + private Bundle detectForeignLanguage(String text) { try { + final float threshold = getForeignLanguageThreshold(); + if (threshold > 1) { + Log.v(LOG_TAG, "Foreign language detection disabled."); + return null; + } + final LangIdModel langId = getLangIdImpl(); final LangIdModel.LanguageResult[] langResults = langId.detectLanguages(text); if (langResults.length <= 0) { @@ -651,8 +647,8 @@ public final class TextClassifierImpl implements TextClassifier { if (highestScoringResult.getScore() < threshold) { return null; } - // TODO: Remove - Log.d(LOG_TAG, String.format("Language detected: <%s:%s>", + + Log.v(LOG_TAG, String.format("Language detected: <%s:%s>", highestScoringResult.getLanguage(), highestScoringResult.getScore())); final Locale detected = new Locale(highestScoringResult.getLanguage()); @@ -671,6 +667,18 @@ public final class TextClassifierImpl implements TextClassifier { return null; } + private float getForeignLanguageThreshold() { + try { + return mSettings.getLangIdThresholdOverride() >= 0 + ? mSettings.getLangIdThresholdOverride() + : getLangIdImpl().getTranslateThreshold(); + } catch (FileNotFoundException e) { + final float defaultThreshold = 0.5f; + Log.v(LOG_TAG, "Using default foreign language threshold: " + defaultThreshold); + return defaultThreshold; + } + } + @Override public void dump(@NonNull IndentingPrintWriter printWriter) { synchronized (mLock) { @@ -719,86 +727,15 @@ public final class TextClassifierImpl implements TextClassifier { } /** - * Helper class to store the information from which RemoteActions are built. + * Returns the locale string for the current resources configuration. */ - @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) - public static final class LabeledIntent { - - static final int DEFAULT_REQUEST_CODE = 0; - - private final String mTitle; - private final String mDescription; - private final Intent mIntent; - private final int mRequestCode; - - /** - * Initializes a LabeledIntent. - * - * <p>NOTE: {@code reqestCode} is required to not be {@link #DEFAULT_REQUEST_CODE} - * if distinguishing info (e.g. the classified text) is represented in intent extras only. - * In such circumstances, the request code should represent the distinguishing info - * (e.g. by generating a hashcode) so that the generated PendingIntent is (somewhat) - * unique. To be correct, the PendingIntent should be definitely unique but we try a - * best effort approach that avoids spamming the system with PendingIntents. - */ - // TODO: Fix the issue mentioned above so the behaviour is correct. - LabeledIntent(String title, String description, Intent intent, int requestCode) { - mTitle = title; - mDescription = description; - mIntent = intent; - mRequestCode = requestCode; - } - - @VisibleForTesting - public String getTitle() { - return mTitle; - } - - @VisibleForTesting - public String getDescription() { - return mDescription; - } - - @VisibleForTesting - public Intent getIntent() { - return mIntent; - } - - @VisibleForTesting - public int getRequestCode() { - return mRequestCode; - } - - @Nullable - RemoteAction asRemoteAction(Context context) { - final PackageManager pm = context.getPackageManager(); - final ResolveInfo resolveInfo = pm.resolveActivity(mIntent, 0); - final String packageName = resolveInfo != null && resolveInfo.activityInfo != null - ? resolveInfo.activityInfo.packageName : null; - Icon icon = null; - boolean shouldShowIcon = false; - if (packageName != null && !"android".equals(packageName)) { - // There is a default activity handling the intent. - mIntent.setComponent(new ComponentName(packageName, resolveInfo.activityInfo.name)); - if (resolveInfo.activityInfo.getIconResource() != 0) { - icon = Icon.createWithResource( - packageName, resolveInfo.activityInfo.getIconResource()); - shouldShowIcon = true; - } - } - if (icon == null) { - // RemoteAction requires that there be an icon. - icon = Icon.createWithResource("android", - com.android.internal.R.drawable.ic_more_items); - } - final PendingIntent pendingIntent = - TextClassification.createPendingIntent(context, mIntent, mRequestCode); - if (pendingIntent == null) { - return null; - } - final RemoteAction action = new RemoteAction(icon, mTitle, mDescription, pendingIntent); - action.setShouldShowIcon(shouldShowIcon); - return action; + private String getResourceLocaleString() { + // TODO: Pass the locale list once it is supported in native side. + try { + return mContext.getResources().getConfiguration().getLocales().get(0).toLanguageTag(); + } catch (NullPointerException e) { + // NPE is unexpected. Erring on the side of caution. + return LocaleList.getDefault().get(0).toLanguageTag(); } } } diff --git a/core/java/android/webkit/IWebViewUpdateService.aidl b/core/java/android/webkit/IWebViewUpdateService.aidl index dbca7ff72c7b..10cfea166abd 100644 --- a/core/java/android/webkit/IWebViewUpdateService.aidl +++ b/core/java/android/webkit/IWebViewUpdateService.aidl @@ -51,6 +51,7 @@ interface IWebViewUpdateService { * DevelopmentSettings uses this to get the current available WebView * providers (to display as choices to the user). */ + @UnsupportedAppUsage WebViewProviderInfo[] getValidWebViewPackages(); /** @@ -61,6 +62,7 @@ interface IWebViewUpdateService { /** * Used by DevelopmentSetting to get the name of the WebView provider currently in use. */ + @UnsupportedAppUsage String getCurrentWebViewPackageName(); /** @@ -72,6 +74,7 @@ interface IWebViewUpdateService { * Used by Settings to determine whether a certain package can be enabled/disabled by the user - * the package should not be modifiable in this way if it is a fallback package. */ + @UnsupportedAppUsage boolean isFallbackPackage(String packageName); /** diff --git a/core/java/android/webkit/WebResourceResponse.java b/core/java/android/webkit/WebResourceResponse.java index e66596b5c0e9..7c8f33e181d6 100644 --- a/core/java/android/webkit/WebResourceResponse.java +++ b/core/java/android/webkit/WebResourceResponse.java @@ -42,9 +42,9 @@ public class WebResourceResponse { /** * Constructs a resource response with the given MIME type, character encoding, - * and input stream. Callers must implement - * {@link InputStream#read(byte[]) InputStream.read(byte[])} for the input - * stream. + * and input stream. Callers must implement {@link InputStream#read(byte[])} for + * the input stream. {@link InputStream#close()} will be called after the WebView + * has finished with the response. * * <p class="note"><b>Note:</b> The MIME type and character encoding must * be specified as separate parameters (for example {@code "text/html"} and @@ -67,9 +67,10 @@ public class WebResourceResponse { } /** - * Constructs a resource response with the given parameters. Callers must - * implement {@link InputStream#read(byte[]) InputStream.read(byte[])} for - * the input stream. + * Constructs a resource response with the given parameters. Callers must implement + * {@link InputStream#read(byte[])} for the input stream. {@link InputStream#close()} will be + * called after the WebView has finished with the response. + * * * <p class="note"><b>Note:</b> See {@link #WebResourceResponse(String,String,InputStream)} * for details on what should be specified for {@code mimeType} and {@code encoding}. @@ -201,7 +202,8 @@ public class WebResourceResponse { /** * Sets the input stream that provides the resource response's data. Callers - * must implement {@link InputStream#read(byte[]) InputStream.read(byte[])}. + * must implement {@link InputStream#read(byte[])}. {@link InputStream#close()} + * will be called after the WebView has finished with the response. * * @param data the input stream that provides the resource response's data. Must not be a * StringBufferInputStream. diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index ded3be4e4ef5..b6ec5f936fbd 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -1947,7 +1947,7 @@ public class Editor { // Rebuild display list if it is invalid if (blockDisplayListIsInvalid) { - final RecordingCanvas recordingCanvas = blockDisplayList.start( + final RecordingCanvas recordingCanvas = blockDisplayList.beginRecording( right - left, bottom - top); try { // drawText is always relative to TextView's origin, this translation @@ -1958,7 +1958,7 @@ public class Editor { // No need to untranslate, previous context is popped after // drawDisplayList } finally { - blockDisplayList.end(recordingCanvas); + blockDisplayList.endRecording(); // Same as drawDisplayList below, handled by our TextView's parent blockDisplayList.setClipToBounds(false); } diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java index a6129b04d14f..f44c33178242 100644 --- a/core/java/android/widget/GridView.java +++ b/core/java/android/widget/GridView.java @@ -312,7 +312,7 @@ public class GridView extends AbsListView { * @return The view that is currently selected, if it happens to be in the * range that we draw. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) private View fillDown(int pos, int nextTop) { View selectedView = null; @@ -412,7 +412,7 @@ public class GridView extends AbsListView { * * @return The view that is currently selected */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) private View fillUp(int pos, int nextBottom) { View selectedView = null; diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index 2aa019b5d0f4..2f44d6ee88b1 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -782,7 +782,7 @@ public class ListView extends AbsListView { * @return The view that is currently selected, if it happens to be in the * range that we draw. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) private View fillDown(int pos, int nextTop) { View selectedView = null; @@ -817,7 +817,7 @@ public class ListView extends AbsListView { * * @return The view that is currently selected */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) private View fillUp(int pos, int nextBottom) { View selectedView = null; @@ -1490,7 +1490,7 @@ public class ListView extends AbsListView { * @return The selected view, or null if the selected view is outside the * visible area. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) private View fillSpecific(int position, int top) { boolean tempIsSelected = position == mSelectedPosition; View temp = makeAndAddView(position, top, true, mListPadding.left, tempIsSelected); diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java index 249f49956256..b7cdad2d5e51 100644 --- a/core/java/android/widget/Magnifier.java +++ b/core/java/android/widget/Magnifier.java @@ -859,7 +859,7 @@ public final class Magnifier { ); setupOverlay(); - final RecordingCanvas canvas = mRenderer.getRootNode().start(width, height); + final RecordingCanvas canvas = mRenderer.getRootNode().beginRecording(width, height); try { canvas.insertReorderBarrier(); canvas.drawRenderNode(mBitmapRenderNode); @@ -867,7 +867,7 @@ public final class Magnifier { canvas.drawRenderNode(mOverlayRenderNode); canvas.insertInorderBarrier(); } finally { - mRenderer.getRootNode().end(canvas); + mRenderer.getRootNode().endRecording(); } if (mCallback != null) { mCurrentContent = @@ -898,11 +898,12 @@ public final class Magnifier { bitmapRenderNode.setClipToOutline(true); // Create a dummy draw, which will be replaced later with real drawing. - final RecordingCanvas canvas = bitmapRenderNode.start(mContentWidth, mContentHeight); + final RecordingCanvas canvas = bitmapRenderNode.beginRecording( + mContentWidth, mContentHeight); try { canvas.drawColor(0xFF00FF00); } finally { - bitmapRenderNode.end(canvas); + bitmapRenderNode.endRecording(); } return bitmapRenderNode; @@ -954,7 +955,7 @@ public final class Magnifier { // Draw the drawable to the render node. This happens once during // initialization and whenever the overlay drawable is invalidated. final RecordingCanvas canvas = - mOverlayRenderNode.startRecording(mContentWidth, mContentHeight); + mOverlayRenderNode.beginRecording(mContentWidth, mContentHeight); try { mOverlay.setBounds(0, 0, mContentWidth, mContentHeight); mOverlay.draw(canvas); @@ -1035,7 +1036,7 @@ public final class Magnifier { } final RecordingCanvas canvas = - mBitmapRenderNode.start(mContentWidth, mContentHeight); + mBitmapRenderNode.beginRecording(mContentWidth, mContentHeight); try { final Rect srcRect = new Rect(0, 0, mBitmap.getWidth(), mBitmap.getHeight()); final Rect dstRect = new Rect(0, 0, mContentWidth, mContentHeight); @@ -1043,7 +1044,7 @@ public final class Magnifier { paint.setFilterBitmap(true); canvas.drawBitmap(mBitmap, srcRect, dstRect, paint); } finally { - mBitmapRenderNode.end(canvas); + mBitmapRenderNode.endRecording(); } if (mPendingWindowPositionUpdate || mFirstDraw) { @@ -1061,7 +1062,6 @@ public final class Magnifier { return; } synchronized (mLock) { - mRenderer.setLightCenter(mDisplay, pendingX, pendingY); // Show or move the window at the content draw frame. SurfaceControl.openTransaction(); mSurfaceControl.deferTransactionUntil(mSurface, frame); @@ -1076,6 +1076,7 @@ public final class Magnifier { } } }; + mRenderer.setLightCenter(mDisplay, pendingX, pendingY); } else { callback = null; } diff --git a/core/java/android/widget/ZoomControls.java b/core/java/android/widget/ZoomControls.java index dd6a27b8f1e4..7a5b7e8058f3 100644 --- a/core/java/android/widget/ZoomControls.java +++ b/core/java/android/widget/ZoomControls.java @@ -30,7 +30,11 @@ import com.android.internal.R; /** * The {@code ZoomControls} class displays a simple set of controls used for zooming and - * provides callbacks to register for events. */ + * provides callbacks to register for events. + * @deprecated This functionality and UI is better handled with custom views and layouts + * rather than a dedicated zoom-control widget + */ +@Deprecated @Widget public class ZoomControls extends LinearLayout { diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 0fbd4dca700b..89e3d6b6460f 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -381,6 +381,8 @@ public class ChooserActivity extends ResolverActivity { final long systemCost = mChooserShownTime - intentReceivedTime; getMetricsLogger().write(new LogMaker(MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN) + .setSubtype(isWorkProfile() ? MetricsEvent.MANAGED_PROFILE : + MetricsEvent.PARENT_PROFILE) .addTaggedData(MetricsEvent.FIELD_SHARESHEET_MIMETYPE, target.getType()) .addTaggedData(MetricsEvent.FIELD_TIME_TO_APP_TARGETS, systemCost)); @@ -418,6 +420,16 @@ public class ChooserActivity extends ResolverActivity { } /** + * Check if the profile currently used is a work profile. + * @return true if it is work profile, false if it is parent profile (or no work profile is + * set up) + */ + protected boolean isWorkProfile() { + return ((UserManager) getSystemService(Context.USER_SERVICE)) + .getUserInfo(UserHandle.myUserId()).isManagedProfile(); + } + + /** * Override method to add content preview area, specific to the chooser activity. */ @Override diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl index 8a90cade35a2..c096961c57b8 100644 --- a/core/java/com/android/internal/app/IAppOpsService.aidl +++ b/core/java/com/android/internal/app/IAppOpsService.aidl @@ -32,6 +32,7 @@ interface IAppOpsService { int noteOperation(int code, int uid, String packageName); int startOperation(IBinder token, int code, int uid, String packageName, boolean startIfModeDefault); + @UnsupportedAppUsage void finishOperation(IBinder token, int code, int uid, String packageName); void startWatchingMode(int op, String packageName, IAppOpsCallback callback); void stopWatchingMode(IAppOpsCallback callback); @@ -42,7 +43,9 @@ interface IAppOpsService { // Remaining methods are only used in Java. int checkPackage(int uid, String packageName); + @UnsupportedAppUsage List<AppOpsManager.PackageOps> getPackagesForOps(in int[] ops); + @UnsupportedAppUsage List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, in int[] ops); void getHistoricalOps(int uid, String packageName, in List<String> ops, long beginTimeMillis, long endTimeMillis, in RemoteCallback callback); @@ -55,7 +58,9 @@ interface IAppOpsService { void clearHistory(); List<AppOpsManager.PackageOps> getUidOps(int uid, in int[] ops); void setUidMode(int code, int uid, int mode); + @UnsupportedAppUsage void setMode(int code, int uid, String packageName, int mode); + @UnsupportedAppUsage void resetAllModes(int reqUserId, String reqPackageName); int checkAudioOperation(int code, int usage, int uid, String packageName); void setAudioRestriction(int code, int usage, int uid, int mode, in String[] exceptionPackages); diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index d7514d1fe26c..114d31f207bd 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -49,11 +49,13 @@ interface IBatteryStats { void noteResetFlashlight(); // Remaining methods are only used in Java. + @UnsupportedAppUsage byte[] getStatistics(); ParcelFileDescriptor getStatisticsStream(); // Return true if we see the battery as currently charging. + @UnsupportedAppUsage boolean isCharging(); // Return the computed amount of time remaining on battery, in milliseconds. @@ -62,6 +64,7 @@ interface IBatteryStats { // Return the computed amount of time remaining to fully charge, in milliseconds. // Returns -1 if nothing could be computed. + @UnsupportedAppUsage long computeChargeTimeRemaining(); void noteEvent(int code, String name, int uid); @@ -131,6 +134,7 @@ interface IBatteryStats { void noteDeviceIdleMode(int mode, String activeReason, int activeUid); void setBatteryState(int status, int health, int plugType, int level, int temp, int volt, int chargeUAh, int chargeFullUAh); + @UnsupportedAppUsage long getAwakeTimeBattery(); long getAwakeTimePlugged(); diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl index 9ce7ed1562ce..420749e558e0 100644 --- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl +++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl @@ -51,6 +51,7 @@ interface IVoiceInteractionManagerService { * @param keyphraseId The unique identifier for the keyphrase. * @param bcp47Locale The BCP47 language tag for the keyphrase's locale. */ + @UnsupportedAppUsage SoundTrigger.KeyphraseSoundModel getKeyphraseSoundModel(int keyphraseId, in String bcp47Locale); /** * Add/Update the given keyphrase sound model. diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java index d13bcf2aa186..64f00103eb4b 100644 --- a/core/java/com/android/internal/app/IntentForwarderActivity.java +++ b/core/java/com/android/internal/app/IntentForwarderActivity.java @@ -31,6 +31,7 @@ import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; +import android.metrics.LogMaker; import android.os.Bundle; import android.os.RemoteException; import android.os.UserHandle; @@ -39,6 +40,8 @@ import android.util.Slog; import android.widget.Toast; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import java.util.Arrays; import java.util.HashSet; @@ -66,6 +69,8 @@ public class IntentForwarderActivity extends Activity { private Injector mInjector; + private MetricsLogger mMetricsLogger; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -78,9 +83,17 @@ public class IntentForwarderActivity extends Activity { if (className.equals(FORWARD_INTENT_TO_PARENT)) { userMessageId = com.android.internal.R.string.forward_intent_to_owner; targetUserId = getProfileParent(); + + getMetricsLogger().write( + new LogMaker(MetricsEvent.ACTION_SWITCH_SHARE_PROFILE) + .setSubtype(MetricsEvent.PARENT_PROFILE)); } else if (className.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) { userMessageId = com.android.internal.R.string.forward_intent_to_work; targetUserId = getManagedProfile(); + + getMetricsLogger().write( + new LogMaker(MetricsEvent.ACTION_SWITCH_SHARE_PROFILE) + .setSubtype(MetricsEvent.MANAGED_PROFILE)); } else { Slog.wtf(TAG, IntentForwarderActivity.class.getName() + " cannot be called directly"); userMessageId = -1; @@ -257,6 +270,13 @@ public class IntentForwarderActivity extends Activity { intent.setComponent(null); } + protected MetricsLogger getMetricsLogger() { + if (mMetricsLogger == null) { + mMetricsLogger = new MetricsLogger(); + } + return mMetricsLogger; + } + @VisibleForTesting protected Injector createInjector() { return new InjectorImpl(); diff --git a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl index f9bf3736422b..6d1d1abd1ec4 100644 --- a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl +++ b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl @@ -42,6 +42,7 @@ interface IAppWidgetService { void deleteAppWidgetId(String callingPackage, int appWidgetId); void deleteHost(String packageName, int hostId); void deleteAllHosts(); + @UnsupportedAppUsage RemoteViews getAppWidgetViews(String callingPackage, int appWidgetId); int[] getAppWidgetIdsForHost(String callingPackage, int hostId); IntentSender createAppWidgetConfigIntentSender(String callingPackage, int appWidgetId, @@ -63,11 +64,14 @@ interface IAppWidgetService { AppWidgetProviderInfo getAppWidgetInfo(String callingPackage, int appWidgetId); boolean hasBindAppWidgetPermission(in String packageName, int userId); void setBindAppWidgetPermission(in String packageName, int userId, in boolean permission); + @UnsupportedAppUsage boolean bindAppWidgetId(in String callingPackage, int appWidgetId, int providerProfileId, in ComponentName providerComponent, in Bundle options); + @UnsupportedAppUsage boolean bindRemoteViewsService(String callingPackage, int appWidgetId, in Intent intent, IApplicationThread caller, IBinder token, IServiceConnection connection, int flags); + @UnsupportedAppUsage int[] getAppWidgetIds(in ComponentName providerComponent); boolean isBoundWidgetPackage(String packageName, int userId); boolean requestPinAppWidget(String packageName, in ComponentName providerComponent, diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java new file mode 100644 index 000000000000..74c6651003e5 --- /dev/null +++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java @@ -0,0 +1,85 @@ +/** + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.config.sysui; + +/** + * Keeps the flags related to the SystemUI namespace in {@link DeviceConfig}. + * + * @hide + */ +public final class SystemUiDeviceConfigFlags { + + // Flags related to NotificationAssistant + + /** + * Whether the Notification Assistant should generate replies for notifications. + */ + public static final String NAS_GENERATE_REPLIES = "nas_generate_replies"; + + /** + * Whether the Notification Assistant should generate contextual actions for notifications. + */ + public static final String NAS_GENERATE_ACTIONS = "nas_generate_actions"; + + /** + * The maximum number of messages the Notification Assistant should extract from a + * conversation when constructing responses for that conversation. + */ + public static final String NAS_MAX_MESSAGES_TO_EXTRACT = "nas_max_messages_to_extract"; + + /** + * The maximum number of suggestions the Notification Assistant should provide for a + * messaging conversation. + */ + public static final String NAS_MAX_SUGGESTIONS = "nas_max_suggestions"; + + // Flags related to Smart Suggestions - these are read in SmartReplyConstants. + + /** (boolean) Whether to enable smart suggestions in notifications. */ + public static final String SSIN_ENABLED = "ssin_enabled"; + + /** + * (boolean) Whether apps need to target at least P to provide their own smart replies (this + * doesn't apply to actions!). + */ + public static final String SSIN_REQUIRES_TARGETING_P = "ssin_requires_targeting_p"; + + /** + * (int) The number of times we'll try to find a better line-break for double-line smart + * suggestion buttons. + */ + public static final String SSIN_MAX_SQUEEZE_REMEASURE_ATTEMPTS = + "ssin_max_squeeze_remeasure_attempts"; + + /** (boolean) Whether to let the user edit smart replies before sending. */ + public static final String SSIN_EDIT_CHOICES_BEFORE_SENDING = + "ssin_edit_choices_before_sending"; + + /** (boolean) Whether smart suggestions should be enabled in heads-up notifications. */ + public static final String SSIN_SHOW_IN_HEADS_UP = "ssin_show_in_heads_up"; + + /** (int) Minimum number of system generated replies to show in a notification. */ + public static final String SSIN_MIN_NUM_SYSTEM_GENERATED_REPLIES = + "ssin_min_num_system_generated_replies"; + + /** + * (int) Maximum number of actions to show in a notification, -1 if there shouldn't be a limit + */ + public static final String SSIN_MAX_NUM_ACTIONS = "ssin_max_num_actions"; + + private SystemUiDeviceConfigFlags() { } +} diff --git a/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java b/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java index 26cf1809313e..293ffd34da8d 100644 --- a/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java +++ b/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java @@ -39,7 +39,7 @@ public abstract class AbstractMultiplePendingRequestsRemoteService<S private final int mInitialCapacity; - protected ArrayList<PendingRequest<S, I>> mPendingRequests; + protected ArrayList<BasePendingRequest<S, I>> mPendingRequests; public AbstractMultiplePendingRequestsRemoteService(@NonNull Context context, @NonNull String serviceInterface, @NonNull ComponentName componentName, int userId, @@ -85,7 +85,7 @@ public abstract class AbstractMultiplePendingRequestsRemoteService<S } @Override // from AbstractRemoteService - void handlePendingRequestWhileUnBound(@NonNull PendingRequest<S, I> pendingRequest) { + void handlePendingRequestWhileUnBound(@NonNull BasePendingRequest<S, I> pendingRequest) { if (mPendingRequests == null) { mPendingRequests = new ArrayList<>(mInitialCapacity); } diff --git a/core/java/com/android/internal/infra/AbstractRemoteService.java b/core/java/com/android/internal/infra/AbstractRemoteService.java index a937aa7cc32e..f4c904dc750e 100644 --- a/core/java/com/android/internal/infra/AbstractRemoteService.java +++ b/core/java/com/android/internal/infra/AbstractRemoteService.java @@ -67,7 +67,7 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S, I private static final int MSG_BIND = 1; private static final int MSG_UNBIND = 2; - protected static final long PERMANENT_BOUND_TIMEOUT_MS = 0; + public static final long PERMANENT_BOUND_TIMEOUT_MS = 0; protected static final int LAST_PRIVATE_MSG = MSG_UNBIND; @@ -95,7 +95,7 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S, I private long mNextUnbind; /** Requests that have been scheduled, but that are not finished yet */ - private final ArrayList<PendingRequest<S, I>> mUnfinishedRequests = new ArrayList<>(); + private final ArrayList<BasePendingRequest<S, I>> mUnfinishedRequests = new ArrayList<>(); /** * Callback called when the service dies. @@ -183,8 +183,15 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S, I /** * Defines how long after we make a remote request to a fill service we timeout. + * + * <p>Just need to be overridden by subclasses that uses sync {@link PendingRequest}s. + * + * @throws UnsupportedOperationException if called when not overridden. + * */ - protected abstract long getRemoteRequestMillis(); + protected long getRemoteRequestMillis() { + throw new UnsupportedOperationException("not implemented by " + getClass()); + } /** * Gets the currently registered service interface or {@code null} if the service is not @@ -243,7 +250,7 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S, I pw.append(prefix).append(tab).append("destroyed=") .append(String.valueOf(mDestroyed)).println(); pw.append(prefix).append(tab).append("numUnfinishedRequests=") - .append(String.valueOf(mUnfinishedRequests.size())); + .append(String.valueOf(mUnfinishedRequests.size())).println(); final boolean bound = handleIsBound(); pw.append(prefix).append(tab).append("bound=") .append(String.valueOf(bound)); @@ -260,9 +267,13 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S, I pw.println(); pw.append(prefix).append("mBindInstantServiceAllowed=").println(mBindInstantServiceAllowed); pw.append(prefix).append("idleTimeout=") - .append(Long.toString(idleTimeout / 1000)).append("s").println(); - pw.append(prefix).append("requestTimeout=") - .append(Long.toString(getRemoteRequestMillis() / 1000)).append("s").println(); + .append(Long.toString(idleTimeout / 1000)).append("s\n"); + pw.append(prefix).append("requestTimeout="); + try { + pw.append(Long.toString(getRemoteRequestMillis() / 1000)).append("s\n"); + } catch (UnsupportedOperationException e) { + pw.append("not supported\n"); + } pw.println(); } @@ -273,7 +284,7 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S, I * othewise it will trigger a {@link PendingRequest#onTimeout(AbstractRemoteService)} if the * service doesn't respond. */ - protected void scheduleRequest(@NonNull PendingRequest<S, I> pendingRequest) { + protected void scheduleRequest(@NonNull BasePendingRequest<S, I> pendingRequest) { mHandler.sendMessage(obtainMessage( AbstractRemoteService::handlePendingRequest, this, pendingRequest)); } @@ -283,12 +294,12 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S, I * * @param finshedRequest The request that is finished */ - void finishRequest(@NonNull PendingRequest<S, I> finshedRequest) { + void finishRequest(@NonNull BasePendingRequest<S, I> finshedRequest) { mHandler.sendMessage( obtainMessage(AbstractRemoteService::handleFinishRequest, this, finshedRequest)); } - private void handleFinishRequest(@NonNull PendingRequest<S, I> finshedRequest) { + private void handleFinishRequest(@NonNull BasePendingRequest<S, I> finshedRequest) { mUnfinishedRequests.remove(finshedRequest); if (mUnfinishedRequests.isEmpty()) { @@ -361,7 +372,7 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S, I * Handles a request, either processing it right now when bound, or saving it to be handled when * bound. */ - protected final void handlePendingRequest(@NonNull PendingRequest<S, I> pendingRequest) { + protected final void handlePendingRequest(@NonNull BasePendingRequest<S, I> pendingRequest) { if (checkIfDestroyed() || mCompleted) return; if (!handleIsBound()) { @@ -384,7 +395,8 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S, I /** * Defines what to do with a request that arrives while not bound to the service. */ - abstract void handlePendingRequestWhileUnBound(@NonNull PendingRequest<S, I> pendingRequest); + abstract void handlePendingRequestWhileUnBound( + @NonNull BasePendingRequest<S, I> pendingRequest); private boolean handleIsBound() { return mService != null; @@ -471,50 +483,28 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S, I /** * Base class for the requests serviced by the remote service. * - * <p><b>NOTE: </b> this class is typically used when the service needs to use a callback to - * communicate back with the system server. For cases where that's not needed, you should use - * {@link AbstractRemoteService#scheduleAsyncRequest(AsyncRequest)} instead. + * <p><b>NOTE: </b> this class is not used directly, you should either override + * {@link com.android.internal.infra.AbstractRemoteService.PendingRequest} for sync requests, or + * use {@link AbstractRemoteService#scheduleAsyncRequest(AsyncRequest)} for async requests. * * @param <S> the remote service class * @param <I> the interface of the binder service */ - public abstract static class PendingRequest<S extends AbstractRemoteService<S, I>, + public abstract static class BasePendingRequest<S extends AbstractRemoteService<S, I>, I extends IInterface> implements Runnable { protected final String mTag = getClass().getSimpleName(); protected final Object mLock = new Object(); - private final WeakReference<S> mWeakService; - private final Runnable mTimeoutTrigger; - private final Handler mServiceHandler; + final WeakReference<S> mWeakService; @GuardedBy("mLock") - private boolean mCancelled; + boolean mCancelled; @GuardedBy("mLock") - private boolean mCompleted; + boolean mCompleted; - protected PendingRequest(@NonNull S service) { + BasePendingRequest(@NonNull S service) { mWeakService = new WeakReference<>(service); - mServiceHandler = service.mHandler; - mTimeoutTrigger = () -> { - synchronized (mLock) { - if (mCancelled) { - return; - } - mCompleted = true; - } - - final S remoteService = mWeakService.get(); - if (remoteService != null) { - // TODO(b/117779333): we should probably ignore it if service is destroyed. - Slog.w(mTag, "timed out after " + service.getRemoteRequestMillis() + " ms"); - onTimeout(remoteService); - } else { - Slog.w(mTag, "timed out (no service)"); - } - }; - mServiceHandler.postAtTime(mTimeoutTrigger, - SystemClock.uptimeMillis() + service.getRemoteRequestMillis()); } /** @@ -543,10 +533,13 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S, I service.finishRequest(this); } - mServiceHandler.removeCallbacks(mTimeoutTrigger); + onFinished(); + return true; } + void onFinished() { } + /** * Checks whether this request was cancelled. */ @@ -568,15 +561,11 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S, I mCancelled = true; } - mServiceHandler.removeCallbacks(mTimeoutTrigger); + onCancel(); return true; } - /** - * Called by the self-destruct timeout when the remote service didn't reply to the - * request on time. - */ - protected abstract void onTimeout(S remoteService); + void onCancel() {} /** * Checks whether this request leads to a final state where no other requests can be made. @@ -587,6 +576,67 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S, I } /** + * Base class for the requests serviced by the remote service. + * + * <p><b>NOTE: </b> this class is typically used when the service needs to use a callback to + * communicate back with the system server. For cases where that's not needed, you should use + * {@link AbstractRemoteService#scheduleAsyncRequest(AsyncRequest)} instead. + * + * <p><b>NOTE: </b> you must override {@link AbstractRemoteService#getRemoteRequestMillis()}, + * otherwise the constructor will throw an {@link UnsupportedOperationException}. + * + * @param <S> the remote service class + * @param <I> the interface of the binder service + */ + public abstract static class PendingRequest<S extends AbstractRemoteService<S, I>, + I extends IInterface> extends BasePendingRequest<S, I> { + + private final Runnable mTimeoutTrigger; + private final Handler mServiceHandler; + + protected PendingRequest(S service) { + super(service); + mServiceHandler = service.mHandler; + + mTimeoutTrigger = () -> { + synchronized (mLock) { + if (mCancelled) { + return; + } + mCompleted = true; + } + + final S remoteService = mWeakService.get(); + if (remoteService != null) { + // TODO(b/117779333): we should probably ignore it if service is destroyed. + Slog.w(mTag, "timed out after " + service.getRemoteRequestMillis() + " ms"); + onTimeout(remoteService); + } else { + Slog.w(mTag, "timed out (no service)"); + } + }; + mServiceHandler.postAtTime(mTimeoutTrigger, + SystemClock.uptimeMillis() + service.getRemoteRequestMillis()); + } + + @Override + final void onFinished() { + mServiceHandler.removeCallbacks(mTimeoutTrigger); + } + + @Override + final void onCancel() { + mServiceHandler.removeCallbacks(mTimeoutTrigger); + } + + /** + * Called by the self-destruct timeout when the remote service didn't reply to the + * request on time. + */ + protected abstract void onTimeout(S remoteService); + } + + /** * Represents a request that does not expect a callback from the remote service. * * @param <I> the interface of the binder service @@ -600,7 +650,7 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S, I } private static final class MyAsyncPendingRequest<S extends AbstractRemoteService<S, I>, - I extends IInterface> extends PendingRequest<S, I> { + I extends IInterface> extends BasePendingRequest<S, I> { private static final String TAG = MyAsyncPendingRequest.class.getSimpleName(); private final AsyncRequest<I> mRequest; @@ -623,12 +673,5 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S, I finish(); } } - - @Override - protected void onTimeout(S remoteService) { - // TODO(b/117779333): should not happen because we called finish() on run(), although - // currently it might be called if the service is destroyed while showing it. - Slog.w(TAG, "AsyncPending requested timed out"); - } } } diff --git a/core/java/com/android/internal/infra/AbstractSinglePendingRequestRemoteService.java b/core/java/com/android/internal/infra/AbstractSinglePendingRequestRemoteService.java index f0c223388137..3e92a0b196ee 100644 --- a/core/java/com/android/internal/infra/AbstractSinglePendingRequestRemoteService.java +++ b/core/java/com/android/internal/infra/AbstractSinglePendingRequestRemoteService.java @@ -38,7 +38,7 @@ public abstract class AbstractSinglePendingRequestRemoteService<S extends AbstractSinglePendingRequestRemoteService<S, I>, I extends IInterface> extends AbstractRemoteService<S, I> { - protected PendingRequest<S, I> mPendingRequest; + protected BasePendingRequest<S, I> mPendingRequest; public AbstractSinglePendingRequestRemoteService(@NonNull Context context, @NonNull String serviceInterface, @NonNull ComponentName componentName, int userId, @@ -51,7 +51,7 @@ public abstract class AbstractSinglePendingRequestRemoteService<S @Override // from AbstractRemoteService void handlePendingRequests() { if (mPendingRequest != null) { - final PendingRequest<S, I> pendingRequest = mPendingRequest; + final BasePendingRequest<S, I> pendingRequest = mPendingRequest; mPendingRequest = null; handlePendingRequest(pendingRequest); } @@ -73,7 +73,7 @@ public abstract class AbstractSinglePendingRequestRemoteService<S } @Override // from AbstractRemoteService - void handlePendingRequestWhileUnBound(@NonNull PendingRequest<S, I> pendingRequest) { + void handlePendingRequestWhileUnBound(@NonNull BasePendingRequest<S, I> pendingRequest) { if (mPendingRequest != null) { if (mVerbose) { Slog.v(mTag, "handlePendingRequestWhileUnBound(): cancelling " + mPendingRequest diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java index 7f4d8a2d0587..bab478748467 100644 --- a/core/java/com/android/internal/os/BinderCallsStats.java +++ b/core/java/com/android/internal/os/BinderCallsStats.java @@ -51,7 +51,7 @@ import java.util.function.ToDoubleFunction; public class BinderCallsStats implements BinderInternal.Observer { public static final boolean ENABLED_DEFAULT = true; public static final boolean DETAILED_TRACKING_DEFAULT = true; - public static final int PERIODIC_SAMPLING_INTERVAL_DEFAULT = 100; + public static final int PERIODIC_SAMPLING_INTERVAL_DEFAULT = 1000; public static final boolean DEFAULT_TRACK_SCREEN_INTERACTIVE = false; public static final boolean DEFAULT_TRACK_DIRECT_CALLING_UID = true; public static final int MAX_BINDER_CALL_STATS_COUNT_DEFAULT = 5000; @@ -568,7 +568,7 @@ public class BinderCallsStats implements BinderInternal.Observer { } /** - * Aggregated data by uid/class/method to be sent through WestWorld. + * Aggregated data by uid/class/method to be sent through statsd. */ public static class ExportedCallStat { public int callingUid; diff --git a/core/java/com/android/internal/os/IDropBoxManagerService.aidl b/core/java/com/android/internal/os/IDropBoxManagerService.aidl index 70844ee4ef11..5e60394e5675 100644 --- a/core/java/com/android/internal/os/IDropBoxManagerService.aidl +++ b/core/java/com/android/internal/os/IDropBoxManagerService.aidl @@ -37,5 +37,6 @@ interface IDropBoxManagerService { boolean isTagEnabled(String tag); /** @see DropBoxManager#getNextEntry */ + @UnsupportedAppUsage DropBoxManager.Entry getNextEntry(String tag, long millis, String packageName); } diff --git a/core/java/com/android/internal/os/RoSystemProperties.java b/core/java/com/android/internal/os/RoSystemProperties.java index f4902d46992b..524a5cc353f3 100644 --- a/core/java/com/android/internal/os/RoSystemProperties.java +++ b/core/java/com/android/internal/os/RoSystemProperties.java @@ -61,17 +61,19 @@ public class RoSystemProperties { SystemProperties.getBoolean("ro.fw.system_user_split", false); // ------ ro.crypto.* -------- // - public static final String CRYPTO_STATE = SystemProperties.get("ro.crypto.state"); - public static final String CRYPTO_TYPE = CryptoProperties.type().orElse(""); + public static final CryptoProperties.state_values CRYPTO_STATE = + CryptoProperties.state().orElse(CryptoProperties.state_values.UNSUPPORTED); + public static final CryptoProperties.type_values CRYPTO_TYPE = + CryptoProperties.type().orElse(CryptoProperties.type_values.NONE); // These are pseudo-properties public static final boolean CRYPTO_ENCRYPTABLE = - !CRYPTO_STATE.isEmpty() && !"unsupported".equals(CRYPTO_STATE); + CRYPTO_STATE != CryptoProperties.state_values.UNSUPPORTED; public static final boolean CRYPTO_ENCRYPTED = - "encrypted".equalsIgnoreCase(CRYPTO_STATE); + CRYPTO_STATE == CryptoProperties.state_values.ENCRYPTED; public static final boolean CRYPTO_FILE_ENCRYPTED = - "file".equalsIgnoreCase(CRYPTO_TYPE); + CRYPTO_TYPE == CryptoProperties.type_values.FILE; public static final boolean CRYPTO_BLOCK_ENCRYPTED = - "block".equalsIgnoreCase(CRYPTO_TYPE); + CRYPTO_TYPE == CryptoProperties.type_values.BLOCK; public static final boolean CONTROL_PRIVAPP_PERMISSIONS_LOG = "log".equalsIgnoreCase(CONTROL_PRIVAPP_PERMISSIONS); diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index 0604ab2f7237..58b48d88fa38 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -162,9 +162,9 @@ public final class Zygote { /** * The duration to wait before re-checking Zygote related system properties. * - * Five minutes in milliseconds. + * One minute in milliseconds. */ - public static final long PROPERTY_CHECK_INTERVAL = 300000; + public static final long PROPERTY_CHECK_INTERVAL = 60000; /** * @hide for internal use only @@ -236,14 +236,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, - String packageName, String[] packagesForUID, String[] visibleVolIDs, String sandboxId) { + String packageName, String[] packagesForUID, String sandboxId) { ZygoteHooks.preFork(); // Resets nice priority for zygote process. resetNicePriority(); int pid = nativeForkAndSpecialize( uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose, fdsToIgnore, startChildZygote, instructionSet, appDataDir, packageName, - packagesForUID, visibleVolIDs, sandboxId); + packagesForUID, sandboxId); // Enable tracing as soon as possible for the child process. if (pid == 0) { Trace.setTracingEnabled(true, runtimeFlags); @@ -258,7 +258,7 @@ public final class Zygote { private static native 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, String packageName, String[] packagesForUID, String[] visibleVolIDs, + String appDataDir, String packageName, String[] packagesForUID, String sandboxId); /** @@ -285,11 +285,11 @@ public final class Zygote { public static void specializeBlastula(int uid, int gid, int[] gids, int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName, boolean startChildZygote, String instructionSet, String appDataDir, String packageName, - String[] packagesForUID, String[] visibleVolIDs, String sandboxId) { + String[] packagesForUID, String sandboxId) { nativeSpecializeBlastula(uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, startChildZygote, instructionSet, appDataDir, - packageName, packagesForUID, visibleVolIDs, sandboxId); + packageName, packagesForUID, sandboxId); // Enable tracing as soon as possible for the child process. Trace.setTracingEnabled(true, runtimeFlags); @@ -309,7 +309,7 @@ public final class Zygote { private static native void nativeSpecializeBlastula(int uid, int gid, int[] gids, int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName, boolean startChildZygote, String instructionSet, String appDataDir, String packageName, - String[] packagesForUID, String[] visibleVolIDs, String sandboxId); + String[] packagesForUID, String sandboxId); /** * Called to do any initialization before starting an application. @@ -427,6 +427,12 @@ public final class Zygote { defaultValue); } + protected static void emptyBlastulaPool() { + nativeEmptyBlastulaPool(); + } + + private static native void nativeEmptyBlastulaPool(); + /** * Returns the value of a system property converted to a boolean using specific logic. * @@ -520,7 +526,7 @@ public final class Zygote { LocalSocket sessionSocket = null; DataOutputStream blastulaOutputStream = null; Credentials peerCredentials = null; - String[] argStrings = null; + ZygoteArguments args = null; while (true) { try { @@ -533,25 +539,24 @@ public final class Zygote { peerCredentials = sessionSocket.getPeerCredentials(); - argStrings = readArgumentList(blastulaReader); + String[] argStrings = readArgumentList(blastulaReader); if (argStrings != null) { + args = new ZygoteArguments(argStrings); + + // TODO (chriswailes): Should this only be run for debug builds? + validateBlastulaCommand(args); break; } else { Log.e("Blastula", "Truncated command received."); IoUtils.closeQuietly(sessionSocket); } - } catch (IOException ioEx) { - Log.e("Blastula", "Failed to read command: " + ioEx.getMessage()); + } catch (Exception ex) { + Log.e("Blastula", ex.getMessage()); IoUtils.closeQuietly(sessionSocket); } } - ZygoteArguments args = new ZygoteArguments(argStrings); - - // TODO (chriswailes): Should this only be run for debug builds? - validateBlastulaCommand(args); - applyUidSecurityPolicy(args, peerCredentials); applyDebuggerSystemProperty(args); @@ -600,7 +605,7 @@ public final class Zygote { args.mRuntimeFlags, rlimits, args.mMountExternal, args.mSeInfo, args.mNiceName, args.mStartChildZygote, args.mInstructionSet, args.mAppDataDir, args.mPackageName, - args.mPackagesForUid, args.mVisibleVolIds, args.mSandboxId); + args.mPackagesForUid, args.mSandboxId); if (args.mNiceName != null) { Process.setArgV0(args.mNiceName); @@ -740,8 +745,8 @@ public final class Zygote { if (args.mInvokeWith != null && peerUid != 0 && (args.mRuntimeFlags & Zygote.DEBUG_ENABLE_JDWP) == 0) { - throw new ZygoteSecurityException("Peer is permitted to specify an" - + "explicit invoke-with wrapper command only for debuggable" + throw new ZygoteSecurityException("Peer is permitted to specify an " + + "explicit invoke-with wrapper command only for debuggable " + "applications."); } } diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java index c24a9e084293..9cb5820a32ce 100644 --- a/core/java/com/android/internal/os/ZygoteArguments.java +++ b/core/java/com/android/internal/os/ZygoteArguments.java @@ -101,6 +101,12 @@ class ZygoteArguments { String mSeInfo; /** + * + */ + boolean mBlastulaPoolEnabled; + boolean mBlastulaPoolStatusSpecified = false; + + /** * from all --rlimit=r,c,m */ ArrayList<int[]> mRLimits; @@ -116,9 +122,6 @@ class ZygoteArguments { /** from --packages-for-uid */ String[] mPackagesForUid; - /** from --visible-vols */ - String[] mVisibleVolIds; - /** from --sandbox-id */ String mSandboxId; @@ -395,13 +398,15 @@ class ZygoteArguments { mPackageName = arg.substring(arg.indexOf('=') + 1); } else if (arg.startsWith("--packages-for-uid=")) { mPackagesForUid = arg.substring(arg.indexOf('=') + 1).split(","); - } else if (arg.startsWith("--visible-vols=")) { - mVisibleVolIds = arg.substring(arg.indexOf('=') + 1).split(","); } else if (arg.startsWith("--sandbox-id=")) { if (mSandboxId != null) { throw new IllegalArgumentException("Duplicate arg specified"); } mSandboxId = arg.substring(arg.indexOf('=') + 1); + } else if (arg.startsWith("--blastula-pool-enabled=")) { + mBlastulaPoolStatusSpecified = true; + mBlastulaPoolEnabled = Boolean.parseBoolean(arg.substring(arg.indexOf('=') + 1)); + expectRuntimeArgs = false; } else { break; } diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java index 8a878e22a7e3..c7ba22df5700 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -158,6 +158,10 @@ class ZygoteConnection { return null; } + if (parsedArgs.mBlastulaPoolStatusSpecified) { + return handleBlastulaPoolStatusChange(zygoteServer, parsedArgs.mBlastulaPoolEnabled); + } + if (parsedArgs.mPreloadDefault) { handlePreload(); return null; @@ -185,13 +189,12 @@ class ZygoteConnection { } if (parsedArgs.mApiBlacklistExemptions != null) { - handleApiBlacklistExemptions(parsedArgs.mApiBlacklistExemptions); - return null; + return handleApiBlacklistExemptions(zygoteServer, parsedArgs.mApiBlacklistExemptions); } if (parsedArgs.mHiddenApiAccessLogSampleRate != -1) { - handleHiddenApiAccessLogSampleRate(parsedArgs.mHiddenApiAccessLogSampleRate); - return null; + return handleHiddenApiAccessLogSampleRate(zygoteServer, + parsedArgs.mHiddenApiAccessLogSampleRate); } if (parsedArgs.mPermittedCapabilities != 0 || parsedArgs.mEffectiveCapabilities != 0) { @@ -258,7 +261,7 @@ class ZygoteConnection { parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo, parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote, parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mPackageName, - parsedArgs.mPackagesForUid, parsedArgs.mVisibleVolIds, parsedArgs.mSandboxId); + parsedArgs.mPackagesForUid, parsedArgs.mSandboxId); try { if (pid == 0) { @@ -325,10 +328,64 @@ class ZygoteConnection { } } - private void handleApiBlacklistExemptions(String[] exemptions) { + private Runnable stateChangeWithBlastulaPoolReset(ZygoteServer zygoteServer, + Runnable stateChangeCode) { try { - ZygoteInit.setApiBlacklistExemptions(exemptions); + if (zygoteServer.isBlastulaPoolEnabled()) { + Zygote.emptyBlastulaPool(); + } + + stateChangeCode.run(); + + if (zygoteServer.isBlastulaPoolEnabled()) { + Runnable fpResult = + zygoteServer.fillBlastulaPool( + new int[]{mSocket.getFileDescriptor().getInt$()}); + + if (fpResult != null) { + zygoteServer.setForkChild(); + return fpResult; + } + } + mSocketOutStream.writeInt(0); + + return null; + } catch (IOException ioe) { + throw new IllegalStateException("Error writing to command socket", ioe); + } + } + + /** + * Makes the necessary changes to implement a new API blacklist exemption policy, and then + * responds to the system server, letting it know that the task has been completed. + * + * This necessitates a change to the internal state of the Zygote. As such, if the blastula + * pool is enabled all existing blastulas have an incorrect API blacklist exemption list. To + * properly handle this request the pool must be emptied and refilled. This process can return + * a Runnable object that must be returned to ZygoteServer.runSelectLoop to be invoked. + * + * @param zygoteServer The server object that received the request + * @param exemptions The new exemption list. + * @return A Runnable object representing a new app in any blastulas spawned from here; the + * zygote process will always receive a null value from this function. + */ + private Runnable handleApiBlacklistExemptions(ZygoteServer zygoteServer, String[] exemptions) { + return stateChangeWithBlastulaPoolReset(zygoteServer, + () -> ZygoteInit.setApiBlacklistExemptions(exemptions)); + } + + private Runnable handleBlastulaPoolStatusChange(ZygoteServer zygoteServer, boolean newStatus) { + try { + Runnable fpResult = zygoteServer.setBlastulaPoolStatus(newStatus, mSocket); + + if (fpResult == null) { + mSocketOutStream.writeInt(0); + } else { + zygoteServer.setForkChild(); + } + + return fpResult; } catch (IOException ioe) { throw new IllegalStateException("Error writing to command socket", ioe); } @@ -384,15 +441,26 @@ class ZygoteConnection { } } - private void handleHiddenApiAccessLogSampleRate(int samplingRate) { - try { + /** + * Changes the API access log sample rate for the Zygote and processes spawned from it. + * + * This necessitates a change to the internal state of the Zygote. As such, if the blastula + * pool is enabled all existing blastulas have an incorrect API access log sample rate. To + * properly handle this request the pool must be emptied and refilled. This process can return + * a Runnable object that must be returned to ZygoteServer.runSelectLoop to be invoked. + * + * @param zygoteServer The server object that received the request + * @param samplingRate The new sample rate + * @return A Runnable object representing a new app in any blastulas spawned from here; the + * zygote process will always receive a null value from this function. + */ + private Runnable handleHiddenApiAccessLogSampleRate(ZygoteServer zygoteServer, + int samplingRate) { + return stateChangeWithBlastulaPoolReset(zygoteServer, () -> { ZygoteInit.setHiddenApiAccessLogSampleRate(samplingRate); HiddenApiUsageLogger.setHiddenApiAccessLogSampleRate(samplingRate); ZygoteInit.setHiddenApiUsageLogger(HiddenApiUsageLogger.getInstance()); - mSocketOutStream.writeInt(0); - } catch (IOException ioe) { - throw new IllegalStateException("Error writing to command socket", ioe); - } + }); } protected void preload() { diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java index 2c17540eb6c6..c4c98baf0e4c 100644 --- a/core/java/com/android/internal/os/ZygoteServer.java +++ b/core/java/com/android/internal/os/ZygoteServer.java @@ -68,6 +68,15 @@ class ZygoteServer { private static final String BLASTULA_POOL_SIZE_MIN_DEFAULT = "1"; /** + * Indicates if this Zygote server can support a blastula pool. Currently this should only be + * true for the primary and secondary Zygotes, and not the App Zygotes or the WebView Zygote. + * + * TODO (chriswailes): Make this an explicit argument to the constructor + */ + + private final boolean mBlastulaPoolSupported; + + /** * If the blastula pool should be created and used to start applications. * * Setting this value to false will disable the creation, maintenance, and use of the blastula @@ -127,6 +136,8 @@ class ZygoteServer { mBlastulaPoolEventFD = null; mZygoteSocket = null; mBlastulaPoolSocket = null; + + mBlastulaPoolSupported = false; } /** @@ -151,12 +162,18 @@ class ZygoteServer { } fetchBlastulaPoolPolicyProps(); + + mBlastulaPoolSupported = true; } void setForkChild() { mIsForkChild = true; } + public boolean isBlastulaPoolEnabled() { + return mBlastulaPoolEnabled; + } + /** * Registers a server socket for zygote command connections. This opens the server socket * at the specified name in the abstract socket namespace. @@ -224,42 +241,43 @@ class ZygoteServer { } private void fetchBlastulaPoolPolicyProps() { - final String blastulaPoolSizeMaxPropString = - Zygote.getSystemProperty( - DeviceConfig.RuntimeNative.BLASTULA_POOL_SIZE_MAX, - BLASTULA_POOL_SIZE_MAX_DEFAULT); - - if (!blastulaPoolSizeMaxPropString.isEmpty()) { - mBlastulaPoolSizeMax = - Integer.min( - Integer.parseInt(blastulaPoolSizeMaxPropString), - BLASTULA_POOL_SIZE_MAX_LIMIT); - } + if (mBlastulaPoolSupported) { + final String blastulaPoolSizeMaxPropString = + Zygote.getSystemProperty( + DeviceConfig.RuntimeNative.BLASTULA_POOL_SIZE_MAX, + BLASTULA_POOL_SIZE_MAX_DEFAULT); + + if (!blastulaPoolSizeMaxPropString.isEmpty()) { + mBlastulaPoolSizeMax = + Integer.min( + Integer.parseInt(blastulaPoolSizeMaxPropString), + BLASTULA_POOL_SIZE_MAX_LIMIT); + } - final String blastulaPoolSizeMinPropString = - Zygote.getSystemProperty( - DeviceConfig.RuntimeNative.BLASTULA_POOL_SIZE_MIN, - BLASTULA_POOL_SIZE_MIN_DEFAULT); + final String blastulaPoolSizeMinPropString = + Zygote.getSystemProperty( + DeviceConfig.RuntimeNative.BLASTULA_POOL_SIZE_MIN, + BLASTULA_POOL_SIZE_MIN_DEFAULT); - if (!blastulaPoolSizeMinPropString.isEmpty()) { - mBlastulaPoolSizeMin = - Integer.max( - Integer.parseInt(blastulaPoolSizeMinPropString), - BLASTULA_POOL_SIZE_MIN_LIMIT); - } + if (!blastulaPoolSizeMinPropString.isEmpty()) { + mBlastulaPoolSizeMin = + Integer.max( + Integer.parseInt(blastulaPoolSizeMinPropString), + BLASTULA_POOL_SIZE_MIN_LIMIT); + } - final String blastulaPoolRefillThresholdPropString = - Zygote.getSystemProperty( - DeviceConfig.RuntimeNative.BLASTULA_POOL_REFILL_THRESHOLD, - Integer.toString(mBlastulaPoolSizeMax / 2)); + final String blastulaPoolRefillThresholdPropString = + Zygote.getSystemProperty( + DeviceConfig.RuntimeNative.BLASTULA_POOL_REFILL_THRESHOLD, + Integer.toString(mBlastulaPoolSizeMax / 2)); - if (!blastulaPoolRefillThresholdPropString.isEmpty()) { - mBlastulaPoolRefillThreshold = - Integer.min( - Integer.parseInt(blastulaPoolRefillThresholdPropString), - mBlastulaPoolSizeMax); + if (!blastulaPoolRefillThresholdPropString.isEmpty()) { + mBlastulaPoolRefillThreshold = + Integer.min( + Integer.parseInt(blastulaPoolRefillThresholdPropString), + mBlastulaPoolSizeMax); + } } - } private long mLastPropCheckTimestamp = 0; @@ -282,44 +300,65 @@ class ZygoteServer { * this function will return a Runnable object representing the new application that is * passed up from blastulaMain. */ - private Runnable fillBlastulaPool(int[] sessionSocketRawFDs) { - if (mBlastulaPoolEnabled) { - Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Zygote:FillBlastulaPool"); - int blastulaPoolCount = Zygote.getBlastulaPoolCount(); - int numBlastulasToSpawn = mBlastulaPoolSizeMax - blastulaPoolCount; + Runnable fillBlastulaPool(int[] sessionSocketRawFDs) { + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Zygote:FillBlastulaPool"); - if (blastulaPoolCount < mBlastulaPoolSizeMin - || numBlastulasToSpawn >= mBlastulaPoolRefillThreshold) { + int blastulaPoolCount = Zygote.getBlastulaPoolCount(); + int numBlastulasToSpawn = mBlastulaPoolSizeMax - blastulaPoolCount; - // Disable some VM functionality and reset some system values - // before forking. - ZygoteHooks.preFork(); - Zygote.resetNicePriority(); + if (blastulaPoolCount < mBlastulaPoolSizeMin + || numBlastulasToSpawn >= mBlastulaPoolRefillThreshold) { - while (blastulaPoolCount++ < mBlastulaPoolSizeMax) { - Runnable caller = Zygote.forkBlastula(mBlastulaPoolSocket, sessionSocketRawFDs); + // Disable some VM functionality and reset some system values + // before forking. + ZygoteHooks.preFork(); + Zygote.resetNicePriority(); - if (caller != null) { - return caller; - } - } - - // Re-enable runtime services for the Zygote. Blastula services - // are re-enabled in specializeBlastula. - ZygoteHooks.postForkCommon(); + while (blastulaPoolCount++ < mBlastulaPoolSizeMax) { + Runnable caller = Zygote.forkBlastula(mBlastulaPoolSocket, sessionSocketRawFDs); - Log.i("zygote", - "Filled the blastula pool. New blastulas: " + numBlastulasToSpawn); + if (caller != null) { + return caller; + } } - Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + // Re-enable runtime services for the Zygote. Blastula services + // are re-enabled in specializeBlastula. + ZygoteHooks.postForkCommon(); + + Log.i("zygote", + "Filled the blastula pool. New blastulas: " + numBlastulasToSpawn); } + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + return null; } /** + * Empty or fill the blastula pool as dictated by the current and new blastula pool statuses. + */ + Runnable setBlastulaPoolStatus(boolean newStatus, LocalSocket sessionSocket) { + if (!mBlastulaPoolSupported) { + Log.w(TAG, + "Attempting to enable a blastula pool for a Zygote that doesn't support it."); + return null; + } else if (mBlastulaPoolEnabled == newStatus) { + return null; + } + + mBlastulaPoolEnabled = newStatus; + + if (newStatus) { + return fillBlastulaPool(new int[]{ sessionSocket.getFileDescriptor().getInt$() }); + } else { + Zygote.emptyBlastulaPool(); + return null; + } + } + + /** * Runs the zygote process's select loop. Accepts new connections as * they happen, and reads commands from connections one spawn-request's * worth at a time. @@ -334,12 +373,26 @@ class ZygoteServer { while (true) { fetchBlastulaPoolPolicyPropsWithMinInterval(); - int[] blastulaPipeFDs = Zygote.getBlastulaPipeFDs(); + int[] blastulaPipeFDs = null; + StructPollfd[] pollFDs = null; + + // Allocate enough space for the poll structs, taking into account + // the state of the blastula pool for this Zygote (could be a + // regular Zygote, a WebView Zygote, or an AppZygote). + if (mBlastulaPoolEnabled) { + blastulaPipeFDs = Zygote.getBlastulaPipeFDs(); + pollFDs = new StructPollfd[socketFDs.size() + 1 + blastulaPipeFDs.length]; + } else { + pollFDs = new StructPollfd[socketFDs.size()]; + } - // Space for all of the socket FDs, the Blastula Pool Event FD, and - // all of the open blastula read pipe FDs. - StructPollfd[] pollFDs = - new StructPollfd[socketFDs.size() + 1 + blastulaPipeFDs.length]; + /* + * For reasons of correctness the blastula pool pipe and event FDs + * must be processed before the session and server sockets. This + * is to ensure that the blastula pool accounting information is + * accurate when handling other requests like API blacklist + * exemptions. + */ int pollIndex = 0; for (FileDescriptor socketFD : socketFDs) { @@ -350,19 +403,22 @@ class ZygoteServer { } final int blastulaPoolEventFDIndex = pollIndex; - pollFDs[pollIndex] = new StructPollfd(); - pollFDs[pollIndex].fd = mBlastulaPoolEventFD; - pollFDs[pollIndex].events = (short) POLLIN; - ++pollIndex; - - for (int blastulaPipeFD : blastulaPipeFDs) { - FileDescriptor managedFd = new FileDescriptor(); - managedFd.setInt$(blastulaPipeFD); + if (mBlastulaPoolEnabled) { pollFDs[pollIndex] = new StructPollfd(); - pollFDs[pollIndex].fd = managedFd; + pollFDs[pollIndex].fd = mBlastulaPoolEventFD; pollFDs[pollIndex].events = (short) POLLIN; ++pollIndex; + + for (int blastulaPipeFD : blastulaPipeFDs) { + FileDescriptor managedFd = new FileDescriptor(); + managedFd.setInt$(blastulaPipeFD); + + pollFDs[pollIndex] = new StructPollfd(); + pollFDs[pollIndex].fd = managedFd; + pollFDs[pollIndex].events = (short) POLLIN; + ++pollIndex; + } } try { @@ -371,6 +427,8 @@ class ZygoteServer { throw new RuntimeException("poll failed", ex); } + boolean blastulaPoolFDRead = false; + while (--pollIndex >= 0) { if ((pollFDs[pollIndex].revents & POLLIN) == 0) { continue; @@ -390,6 +448,7 @@ class ZygoteServer { ZygoteConnection connection = peers.get(pollIndex); final Runnable command = connection.processOneCommand(this); + // TODO (chriswailes): Is this extra check necessary? if (mIsForkChild) { // We're in the child. We should always have a command to run at this // stage if processOneCommand hasn't called "exec". @@ -480,17 +539,22 @@ class ZygoteServer { Zygote.removeBlastulaTableEntry((int) messagePayload); } - int[] sessionSocketRawFDs = - socketFDs.subList(1, socketFDs.size()) + blastulaPoolFDRead = true; + } + } + + // Check to see if the blastula pool needs to be refilled. + if (blastulaPoolFDRead) { + int[] sessionSocketRawFDs = + socketFDs.subList(1, socketFDs.size()) .stream() .mapToInt(fd -> fd.getInt$()) .toArray(); - final Runnable command = fillBlastulaPool(sessionSocketRawFDs); + final Runnable command = fillBlastulaPool(sessionSocketRawFDs); - if (command != null) { - return command; - } + if (command != null) { + return command; } } } diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java index cc958f4ed735..fa737582f7f6 100644 --- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java +++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java @@ -339,7 +339,7 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame mFrameAndBackdropNode.setLeftTopRightBottom(left, top, left + width, top + height); // Draw the caption and content backdrops in to our render node. - RecordingCanvas canvas = mFrameAndBackdropNode.start(width, height); + RecordingCanvas canvas = mFrameAndBackdropNode.beginRecording(width, height); final Drawable drawable = mUserCaptionBackgroundDrawable != null ? mUserCaptionBackgroundDrawable : mCaptionBackgroundDrawable; @@ -353,7 +353,7 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame mResizingBackgroundDrawable.setBounds(0, mLastCaptionHeight, left + width, top + height); mResizingBackgroundDrawable.draw(canvas); } - mFrameAndBackdropNode.end(canvas); + mFrameAndBackdropNode.endRecording(); drawColorViews(left, top, width, height, fullscreen, systemInsets, stableInsets); @@ -368,7 +368,7 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame if (mSystemBarBackgroundNode == null) { return; } - RecordingCanvas canvas = mSystemBarBackgroundNode.start(width, height); + RecordingCanvas canvas = mSystemBarBackgroundNode.beginRecording(width, height); mSystemBarBackgroundNode.setLeftTopRightBottom(left, top, left + width, top + height); final int topInset = DecorView.getColorViewTopInset(mStableInsets.top, mSystemInsets.top); if (mStatusBarColor != null) { @@ -384,7 +384,7 @@ public class BackdropFrameRenderer extends Thread implements Choreographer.Frame mNavigationBarColor.setBounds(mTmpRect); mNavigationBarColor.draw(canvas); } - mSystemBarBackgroundNode.end(canvas); + mSystemBarBackgroundNode.endRecording(); mRenderer.drawRenderNode(mSystemBarBackgroundNode); } diff --git a/core/java/com/android/internal/policy/DecorContext.java b/core/java/com/android/internal/policy/DecorContext.java index 429c6187dfe0..67cdd5d8a479 100644 --- a/core/java/com/android/internal/policy/DecorContext.java +++ b/core/java/com/android/internal/policy/DecorContext.java @@ -16,6 +16,7 @@ package com.android.internal.policy; +import android.content.ContentCaptureOptions; import android.content.Context; import android.content.res.AssetManager; import android.content.res.Resources; @@ -93,7 +94,11 @@ class DecorContext extends ContextThemeWrapper { } @Override - public boolean isContentCaptureSupported() { - return true; + public ContentCaptureOptions getContentCaptureOptions() { + Context activityContext = mActivityContext.get(); + if (activityContext != null) { + return activityContext.getContentCaptureOptions(); + } + return null; } } diff --git a/core/java/com/android/internal/policy/IKeyguardService.aidl b/core/java/com/android/internal/policy/IKeyguardService.aidl index e5d5685ab3e8..54f31f9199cb 100644 --- a/core/java/com/android/internal/policy/IKeyguardService.aidl +++ b/core/java/com/android/internal/policy/IKeyguardService.aidl @@ -88,8 +88,10 @@ oneway interface IKeyguardService { */ void onScreenTurnedOff(); + @UnsupportedAppUsage void setKeyguardEnabled(boolean enabled); void onSystemReady(); + @UnsupportedAppUsage void doKeyguardTimeout(in Bundle options); void setSwitchingUser(boolean switching); void setCurrentUser(int userId); diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index bfb50848df26..343761430762 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -31,16 +31,21 @@ import com.android.internal.statusbar.NotificationVisibility; /** @hide */ interface IStatusBarService { + @UnsupportedAppUsage void expandNotificationsPanel(); + @UnsupportedAppUsage void collapsePanels(); void togglePanel(); + @UnsupportedAppUsage void disable(int what, IBinder token, String pkg); void disableForUser(int what, IBinder token, String pkg, int userId); void disable2(int what, IBinder token, String pkg); void disable2ForUser(int what, IBinder token, String pkg, int userId); int[] getDisableFlags(IBinder token, int userId); void setIcon(String slot, String iconPackage, int iconId, int iconLevel, String contentDescription); + @UnsupportedAppUsage void setIconVisibility(String slot, boolean visible); + @UnsupportedAppUsage void removeIcon(String slot); // TODO(b/117478341): support back button change when IME is showing on a external display. void setImeWindowStatus(in IBinder token, int vis, int backDisposition, @@ -87,6 +92,7 @@ interface IStatusBarService void addTile(in ComponentName tile); void remTile(in ComponentName tile); void clickTile(in ComponentName tile); + @UnsupportedAppUsage void handleSystemKey(in int key); /** diff --git a/core/java/com/android/internal/util/MimeIconUtils.java b/core/java/com/android/internal/util/MimeIconUtils.java index 841ec7ca994c..0b5fa6d4538a 100644 --- a/core/java/com/android/internal/util/MimeIconUtils.java +++ b/core/java/com/android/internal/util/MimeIconUtils.java @@ -16,215 +16,253 @@ package com.android.internal.util; -import android.content.Context; -import android.graphics.drawable.Drawable; -import android.provider.DocumentsContract; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.ContentResolver.TypeInfo; +import android.content.res.Resources; +import android.graphics.drawable.Icon; +import android.text.TextUtils; +import android.util.ArrayMap; import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; -import java.util.HashMap; +import libcore.net.MimeUtils; + +import java.util.Locale; +import java.util.Objects; public class MimeIconUtils { + @GuardedBy("sCache") + private static final ArrayMap<String, TypeInfo> sCache = new ArrayMap<>(); - private static HashMap<String, Integer> sMimeIcons = new HashMap<>(); + private static TypeInfo buildTypeInfo(String mimeType, int iconId, + int labelId, int extLabelId) { + final Resources res = Resources.getSystem(); - private static void add(String mimeType, int resId) { - if (sMimeIcons.put(mimeType, resId) != null) { - throw new RuntimeException(mimeType + " already registered!"); + // If this MIME type has an extension, customize the label + final CharSequence label; + final String ext = MimeUtils.guessExtensionFromMimeType(mimeType); + if (!TextUtils.isEmpty(ext) && extLabelId != -1) { + label = res.getString(extLabelId, ext.toUpperCase(Locale.US)); + } else { + label = res.getString(labelId); } - } - static { - int icon; - - // Package - icon = R.drawable.ic_doc_apk; - add("application/vnd.android.package-archive", icon); - - // Audio - icon = R.drawable.ic_doc_audio; - add("application/ogg", icon); - add("application/x-flac", icon); - - // Certificate - icon = R.drawable.ic_doc_certificate; - add("application/pgp-keys", icon); - add("application/pgp-signature", icon); - add("application/x-pkcs12", icon); - add("application/x-pkcs7-certreqresp", icon); - add("application/x-pkcs7-crl", icon); - add("application/x-x509-ca-cert", icon); - add("application/x-x509-user-cert", icon); - add("application/x-pkcs7-certificates", icon); - add("application/x-pkcs7-mime", icon); - add("application/x-pkcs7-signature", icon); - - // Source code - icon = R.drawable.ic_doc_codes; - add("application/rdf+xml", icon); - add("application/rss+xml", icon); - add("application/x-object", icon); - add("application/xhtml+xml", icon); - add("text/css", icon); - add("text/html", icon); - add("text/xml", icon); - add("text/x-c++hdr", icon); - add("text/x-c++src", icon); - add("text/x-chdr", icon); - add("text/x-csrc", icon); - add("text/x-dsrc", icon); - add("text/x-csh", icon); - add("text/x-haskell", icon); - add("text/x-java", icon); - add("text/x-literate-haskell", icon); - add("text/x-pascal", icon); - add("text/x-tcl", icon); - add("text/x-tex", icon); - add("application/x-latex", icon); - add("application/x-texinfo", icon); - add("application/atom+xml", icon); - add("application/ecmascript", icon); - add("application/json", icon); - add("application/javascript", icon); - add("application/xml", icon); - add("text/javascript", icon); - add("application/x-javascript", icon); - - // Compressed - icon = R.drawable.ic_doc_compressed; - add("application/mac-binhex40", icon); - add("application/rar", icon); - add("application/zip", icon); - add("application/x-apple-diskimage", icon); - add("application/x-debian-package", icon); - add("application/x-gtar", icon); - add("application/x-iso9660-image", icon); - add("application/x-lha", icon); - add("application/x-lzh", icon); - add("application/x-lzx", icon); - add("application/x-stuffit", icon); - add("application/x-tar", icon); - add("application/x-webarchive", icon); - add("application/x-webarchive-xml", icon); - add("application/gzip", icon); - add("application/x-7z-compressed", icon); - add("application/x-deb", icon); - add("application/x-rar-compressed", icon); - - // Contact - icon = R.drawable.ic_doc_contact; - add("text/x-vcard", icon); - add("text/vcard", icon); - - // Event - icon = R.drawable.ic_doc_event; - add("text/calendar", icon); - add("text/x-vcalendar", icon); - - // Font - icon = R.drawable.ic_doc_font; - add("application/x-font", icon); - add("application/font-woff", icon); - add("application/x-font-woff", icon); - add("application/x-font-ttf", icon); - - // Image - icon = R.drawable.ic_doc_image; - add("application/vnd.oasis.opendocument.graphics", icon); - add("application/vnd.oasis.opendocument.graphics-template", icon); - add("application/vnd.oasis.opendocument.image", icon); - add("application/vnd.stardivision.draw", icon); - add("application/vnd.sun.xml.draw", icon); - add("application/vnd.sun.xml.draw.template", icon); - - // PDF - icon = R.drawable.ic_doc_pdf; - add("application/pdf", icon); - - // Presentation - icon = R.drawable.ic_doc_presentation; - add("application/vnd.stardivision.impress", icon); - add("application/vnd.sun.xml.impress", icon); - add("application/vnd.sun.xml.impress.template", icon); - add("application/x-kpresenter", icon); - add("application/vnd.oasis.opendocument.presentation", icon); - - // Spreadsheet - icon = R.drawable.ic_doc_spreadsheet; - add("application/vnd.oasis.opendocument.spreadsheet", icon); - add("application/vnd.oasis.opendocument.spreadsheet-template", icon); - add("application/vnd.stardivision.calc", icon); - add("application/vnd.sun.xml.calc", icon); - add("application/vnd.sun.xml.calc.template", icon); - add("application/x-kspread", icon); - - // Document - icon = R.drawable.ic_doc_document; - add("application/vnd.oasis.opendocument.text", icon); - add("application/vnd.oasis.opendocument.text-master", icon); - add("application/vnd.oasis.opendocument.text-template", icon); - add("application/vnd.oasis.opendocument.text-web", icon); - add("application/vnd.stardivision.writer", icon); - add("application/vnd.stardivision.writer-global", icon); - add("application/vnd.sun.xml.writer", icon); - add("application/vnd.sun.xml.writer.global", icon); - add("application/vnd.sun.xml.writer.template", icon); - add("application/x-abiword", icon); - add("application/x-kword", icon); - - // Video - icon = R.drawable.ic_doc_video; - add("application/x-quicktimeplayer", icon); - add("application/x-shockwave-flash", icon); - - // Word - icon = R.drawable.ic_doc_word; - add("application/msword", icon); - add("application/vnd.openxmlformats-officedocument.wordprocessingml.document", icon); - add("application/vnd.openxmlformats-officedocument.wordprocessingml.template", icon); - - // Excel - icon = R.drawable.ic_doc_excel; - add("application/vnd.ms-excel", icon); - add("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", icon); - add("application/vnd.openxmlformats-officedocument.spreadsheetml.template", icon); - - // Powerpoint - icon = R.drawable.ic_doc_powerpoint; - add("application/vnd.ms-powerpoint", icon); - add("application/vnd.openxmlformats-officedocument.presentationml.presentation", icon); - add("application/vnd.openxmlformats-officedocument.presentationml.template", icon); - add("application/vnd.openxmlformats-officedocument.presentationml.slideshow", icon); + return new TypeInfo(Icon.createWithResource(res, iconId), label, label); } - public static Drawable loadMimeIcon(Context context, String mimeType) { - if (DocumentsContract.Document.MIME_TYPE_DIR.equals(mimeType)) { - return context.getDrawable(R.drawable.ic_doc_folder); + private static @Nullable TypeInfo buildTypeInfo(@NonNull String mimeType) { + switch (mimeType) { + case "inode/directory": + case "vnd.android.document/directory": + return buildTypeInfo(mimeType, R.drawable.ic_doc_folder, + R.string.mime_type_folder, -1); + + case "application/vnd.android.package-archive": + return buildTypeInfo(mimeType, R.drawable.ic_doc_apk, + R.string.mime_type_apk, -1); + + case "application/pgp-keys": + case "application/pgp-signature": + case "application/x-pkcs12": + case "application/x-pkcs7-certreqresp": + case "application/x-pkcs7-crl": + case "application/x-x509-ca-cert": + case "application/x-x509-user-cert": + case "application/x-pkcs7-certificates": + case "application/x-pkcs7-mime": + case "application/x-pkcs7-signature": + return buildTypeInfo(mimeType, R.drawable.ic_doc_certificate, + R.string.mime_type_generic, R.string.mime_type_generic_ext); + + case "application/rdf+xml": + case "application/rss+xml": + case "application/x-object": + case "application/xhtml+xml": + case "text/css": + case "text/html": + case "text/xml": + case "text/x-c++hdr": + case "text/x-c++src": + case "text/x-chdr": + case "text/x-csrc": + case "text/x-dsrc": + case "text/x-csh": + case "text/x-haskell": + case "text/x-java": + case "text/x-literate-haskell": + case "text/x-pascal": + case "text/x-tcl": + case "text/x-tex": + case "application/x-latex": + case "application/x-texinfo": + case "application/atom+xml": + case "application/ecmascript": + case "application/json": + case "application/javascript": + case "application/xml": + case "text/javascript": + case "application/x-javascript": + return buildTypeInfo(mimeType, R.drawable.ic_doc_codes, + R.string.mime_type_document, R.string.mime_type_document_ext); + + case "application/mac-binhex40": + case "application/rar": + case "application/zip": + case "application/x-apple-diskimage": + case "application/x-debian-package": + case "application/x-gtar": + case "application/x-iso9660-image": + case "application/x-lha": + case "application/x-lzh": + case "application/x-lzx": + case "application/x-stuffit": + case "application/x-tar": + case "application/x-webarchive": + case "application/x-webarchive-xml": + case "application/gzip": + case "application/x-7z-compressed": + case "application/x-deb": + case "application/x-rar-compressed": + return buildTypeInfo(mimeType, R.drawable.ic_doc_compressed, + R.string.mime_type_compressed, R.string.mime_type_compressed_ext); + + case "text/x-vcard": + case "text/vcard": + return buildTypeInfo(mimeType, R.drawable.ic_doc_contact, + R.string.mime_type_generic, R.string.mime_type_generic_ext); + + case "text/calendar": + case "text/x-vcalendar": + return buildTypeInfo(mimeType, R.drawable.ic_doc_event, + R.string.mime_type_generic, R.string.mime_type_generic_ext); + + case "application/x-font": + case "application/font-woff": + case "application/x-font-woff": + case "application/x-font-ttf": + return buildTypeInfo(mimeType, R.drawable.ic_doc_font, + R.string.mime_type_generic, R.string.mime_type_generic_ext); + + case "application/vnd.oasis.opendocument.graphics": + case "application/vnd.oasis.opendocument.graphics-template": + case "application/vnd.oasis.opendocument.image": + case "application/vnd.stardivision.draw": + case "application/vnd.sun.xml.draw": + case "application/vnd.sun.xml.draw.template": + case "application/vnd.google-apps.drawing": + return buildTypeInfo(mimeType, R.drawable.ic_doc_image, + R.string.mime_type_image, R.string.mime_type_image_ext); + + case "application/pdf": + return buildTypeInfo(mimeType, R.drawable.ic_doc_pdf, + R.string.mime_type_document, R.string.mime_type_document_ext); + + case "application/vnd.stardivision.impress": + case "application/vnd.sun.xml.impress": + case "application/vnd.sun.xml.impress.template": + case "application/x-kpresenter": + case "application/vnd.oasis.opendocument.presentation": + case "application/vnd.google-apps.presentation": + return buildTypeInfo(mimeType, R.drawable.ic_doc_presentation, + R.string.mime_type_presentation, R.string.mime_type_presentation_ext); + + case "application/vnd.oasis.opendocument.spreadsheet": + case "application/vnd.oasis.opendocument.spreadsheet-template": + case "application/vnd.stardivision.calc": + case "application/vnd.sun.xml.calc": + case "application/vnd.sun.xml.calc.template": + case "application/x-kspread": + case "application/vnd.google-apps.spreadsheet": + return buildTypeInfo(mimeType, R.drawable.ic_doc_spreadsheet, + R.string.mime_type_spreadsheet, R.string.mime_type_spreadsheet_ext); + + case "application/vnd.oasis.opendocument.text": + case "application/vnd.oasis.opendocument.text-master": + case "application/vnd.oasis.opendocument.text-template": + case "application/vnd.oasis.opendocument.text-web": + case "application/vnd.stardivision.writer": + case "application/vnd.stardivision.writer-global": + case "application/vnd.sun.xml.writer": + case "application/vnd.sun.xml.writer.global": + case "application/vnd.sun.xml.writer.template": + case "application/x-abiword": + case "application/x-kword": + case "application/vnd.google-apps.document": + return buildTypeInfo(mimeType, R.drawable.ic_doc_document, + R.string.mime_type_document, R.string.mime_type_document_ext); + + case "application/x-quicktimeplayer": + case "application/x-shockwave-flash": + return buildTypeInfo(mimeType, R.drawable.ic_doc_video, + R.string.mime_type_video, R.string.mime_type_video_ext); + + case "application/msword": + case "application/vnd.openxmlformats-officedocument.wordprocessingml.document": + case "application/vnd.openxmlformats-officedocument.wordprocessingml.template": + return buildTypeInfo(mimeType, R.drawable.ic_doc_word, + R.string.mime_type_document, R.string.mime_type_document_ext); + + case "application/vnd.ms-excel": + case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": + case "application/vnd.openxmlformats-officedocument.spreadsheetml.template": + return buildTypeInfo(mimeType, R.drawable.ic_doc_excel, + R.string.mime_type_spreadsheet, R.string.mime_type_spreadsheet_ext); + + case "application/vnd.ms-powerpoint": + case "application/vnd.openxmlformats-officedocument.presentationml.presentation": + case "application/vnd.openxmlformats-officedocument.presentationml.template": + case "application/vnd.openxmlformats-officedocument.presentationml.slideshow": + return buildTypeInfo(mimeType, R.drawable.ic_doc_powerpoint, + R.string.mime_type_presentation, R.string.mime_type_presentation_ext); + + default: + return buildGenericTypeInfo(mimeType); } + } - // Look for exact match first - Integer resId = sMimeIcons.get(mimeType); - if (resId != null) { - return context.getDrawable(resId); + private static @Nullable TypeInfo buildGenericTypeInfo(@NonNull String mimeType) { + // Look for partial matches + if (mimeType.startsWith("audio/")) { + return buildTypeInfo(mimeType, R.drawable.ic_doc_audio, + R.string.mime_type_audio, R.string.mime_type_audio_ext); + } else if (mimeType.startsWith("video/")) { + return buildTypeInfo(mimeType, R.drawable.ic_doc_video, + R.string.mime_type_video, R.string.mime_type_video_ext); + } else if (mimeType.startsWith("image/")) { + return buildTypeInfo(mimeType, R.drawable.ic_doc_image, + R.string.mime_type_image, R.string.mime_type_image_ext); + } else if (mimeType.startsWith("text/")) { + return buildTypeInfo(mimeType, R.drawable.ic_doc_text, + R.string.mime_type_document, R.string.mime_type_document_ext); } - if (mimeType == null) { - // TODO: generic icon? - return null; + // As one last-ditch effort, try "bouncing" the MIME type through its + // default extension. This handles cases like "application/x-flac" to + // ".flac" to "audio/flac". + final String bouncedMimeType = MimeUtils + .guessMimeTypeFromExtension(MimeUtils.guessExtensionFromMimeType(mimeType)); + if (bouncedMimeType != null && !Objects.equals(mimeType, bouncedMimeType)) { + return buildTypeInfo(bouncedMimeType); } - // Otherwise look for partial match - final String typeOnly = mimeType.split("/")[0]; - if ("audio".equals(typeOnly)) { - return context.getDrawable(R.drawable.ic_doc_audio); - } else if ("image".equals(typeOnly)) { - return context.getDrawable(R.drawable.ic_doc_image); - } else if ("text".equals(typeOnly)) { - return context.getDrawable(R.drawable.ic_doc_text); - } else if ("video".equals(typeOnly)) { - return context.getDrawable(R.drawable.ic_doc_video); - } else { - return context.getDrawable(R.drawable.ic_doc_generic); + // Worst case, return a generic file + return buildTypeInfo(mimeType, R.drawable.ic_doc_generic, + R.string.mime_type_generic, R.string.mime_type_generic_ext); + } + + public static @NonNull TypeInfo getTypeInfo(@NonNull String mimeType) { + // Normalize MIME type + mimeType = mimeType.toLowerCase(Locale.US); + + synchronized (sCache) { + TypeInfo res = sCache.get(mimeType); + if (res == null) { + res = buildTypeInfo(mimeType); + sCache.put(mimeType, res); + } + return res; } } } diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl index 9a77802aa541..3be7c3e35895 100644 --- a/core/java/com/android/internal/widget/ILockSettings.aidl +++ b/core/java/com/android/internal/widget/ILockSettings.aidl @@ -30,23 +30,31 @@ import java.util.Map; /** {@hide} */ interface ILockSettings { + @UnsupportedAppUsage void setBoolean(in String key, in boolean value, in int userId); + @UnsupportedAppUsage void setLong(in String key, in long value, in int userId); + @UnsupportedAppUsage void setString(in String key, in String value, in int userId); + @UnsupportedAppUsage boolean getBoolean(in String key, in boolean defaultValue, in int userId); + @UnsupportedAppUsage long getLong(in String key, in long defaultValue, in int userId); + @UnsupportedAppUsage String getString(in String key, in String defaultValue, in int userId); - void setLockCredential(in String credential, int type, in String savedCredential, int requestedQuality, int userId); + void setLockCredential(in byte[] credential, int type, in byte[] savedCredential, int requestedQuality, int userId); void resetKeyStore(int userId); - VerifyCredentialResponse checkCredential(in String credential, int type, int userId, + VerifyCredentialResponse checkCredential(in byte[] credential, int type, int userId, in ICheckCredentialProgressCallback progressCallback); - VerifyCredentialResponse verifyCredential(in String credential, int type, long challenge, int userId); - VerifyCredentialResponse verifyTiedProfileChallenge(String credential, int type, long challenge, int userId); + VerifyCredentialResponse verifyCredential(in byte[] credential, int type, long challenge, int userId); + VerifyCredentialResponse verifyTiedProfileChallenge(in byte[] credential, int type, long challenge, int userId); boolean checkVoldPassword(int userId); + @UnsupportedAppUsage boolean havePattern(int userId); + @UnsupportedAppUsage boolean havePassword(int userId); - byte[] getHashFactor(String currentCredential, int userId); - void setSeparateProfileChallengeEnabled(int userId, boolean enabled, String managedUserPassword); + byte[] getHashFactor(in byte[] currentCredential, int userId); + void setSeparateProfileChallengeEnabled(int userId, boolean enabled, in byte[] managedUserPassword); boolean getSeparateProfileChallengeEnabled(int userId); void registerStrongAuthTracker(in IStrongAuthTracker tracker); void unregisterStrongAuthTracker(in IStrongAuthTracker tracker); diff --git a/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl b/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl index 7317ecf68fe9..d6efca5d36eb 100644 --- a/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl +++ b/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl @@ -21,15 +21,23 @@ import android.widget.RemoteViews; /** {@hide} */ interface IRemoteViewsFactory { + @UnsupportedAppUsage void onDataSetChanged(); oneway void onDataSetChangedAsync(); oneway void onDestroy(in Intent intent); + @UnsupportedAppUsage int getCount(); + @UnsupportedAppUsage RemoteViews getViewAt(int position); + @UnsupportedAppUsage RemoteViews getLoadingView(); + @UnsupportedAppUsage int getViewTypeCount(); + @UnsupportedAppUsage long getItemId(int position); + @UnsupportedAppUsage boolean hasStableIds(); + @UnsupportedAppUsage boolean isCreated(); } diff --git a/core/java/com/android/internal/widget/LockPatternChecker.java b/core/java/com/android/internal/widget/LockPatternChecker.java index 586ece0a274a..bda3b5728fdc 100644 --- a/core/java/com/android/internal/widget/LockPatternChecker.java +++ b/core/java/com/android/internal/widget/LockPatternChecker.java @@ -150,12 +150,33 @@ public final class LockPatternChecker { * @param challenge The challenge to verify against the pattern. * @param userId The user to check against the pattern. * @param callback The callback to be invoked with the verification result. + * + * @deprecated Pass the password as a byte array. */ + @Deprecated public static AsyncTask<?, ?, ?> verifyPassword(final LockPatternUtils utils, final String password, final long challenge, final int userId, final OnVerifyCallback callback) { + byte[] passwordBytes = password != null ? password.getBytes() : null; + return verifyPassword(utils, passwordBytes, challenge, userId, callback); + } + + /** + * Verify a password asynchronously. + * + * @param utils The LockPatternUtils instance to use. + * @param password The password to check. + * @param challenge The challenge to verify against the pattern. + * @param userId The user to check against the pattern. + * @param callback The callback to be invoked with the verification result. + */ + public static AsyncTask<?, ?, ?> verifyPassword(final LockPatternUtils utils, + final byte[] password, + final long challenge, + final int userId, + final OnVerifyCallback callback) { AsyncTask<Void, Void, byte[]> task = new AsyncTask<Void, Void, byte[]>() { private int mThrottleTimeout; @@ -188,7 +209,7 @@ public final class LockPatternChecker { * @param callback The callback to be invoked with the verification result. */ public static AsyncTask<?, ?, ?> verifyTiedProfileChallenge(final LockPatternUtils utils, - final String password, + final byte[] password, final boolean isPattern, final long challenge, final int userId, @@ -222,18 +243,36 @@ public final class LockPatternChecker { * @param password The password to check. * @param userId The user to check against the pattern. * @param callback The callback to be invoked with the check result. + * @deprecated Pass passwords as byte[] */ + @Deprecated public static AsyncTask<?, ?, ?> checkPassword(final LockPatternUtils utils, final String password, final int userId, final OnCheckCallback callback) { + byte[] passwordBytes = password != null ? password.getBytes() : null; + return checkPassword(utils, passwordBytes, userId, callback); + } + + /** + * Checks a password asynchronously. + * + * @param utils The LockPatternUtils instance to use. + * @param passwordBytes The password to check. + * @param userId The user to check against the pattern. + * @param callback The callback to be invoked with the check result. + */ + public static AsyncTask<?, ?, ?> checkPassword(final LockPatternUtils utils, + final byte[] passwordBytes, + final int userId, + final OnCheckCallback callback) { AsyncTask<Void, Void, Boolean> task = new AsyncTask<Void, Void, Boolean>() { private int mThrottleTimeout; @Override protected Boolean doInBackground(Void... args) { try { - return utils.checkPassword(password, userId, callback::onEarlyMatched); + return utils.checkPassword(passwordBytes, userId, callback::onEarlyMatched); } catch (RequestThrottledException ex) { mThrottleTimeout = ex.getTimeoutMs(); return false; diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 8d3c482104f7..17ed2a0a048c 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -67,6 +67,7 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.StringJoiner; @@ -356,7 +357,7 @@ public class LockPatternUtils { null /* componentName */, userId); } - private byte[] verifyCredential(String credential, int type, long challenge, int userId) + private byte[] verifyCredential(byte[] credential, int type, long challenge, int userId) throws RequestThrottledException { try { VerifyCredentialResponse response = getLockSettings().verifyCredential(credential, @@ -373,7 +374,7 @@ public class LockPatternUtils { } } - private boolean checkCredential(String credential, int type, int userId, + private boolean checkCredential(byte[] credential, int type, int userId, @Nullable CheckCredentialProgressCallback progressCallback) throws RequestThrottledException { try { @@ -404,7 +405,7 @@ public class LockPatternUtils { public byte[] verifyPattern(List<LockPatternView.Cell> pattern, long challenge, int userId) throws RequestThrottledException { throwIfCalledOnMainThread(); - return verifyCredential(patternToString(pattern), CREDENTIAL_TYPE_PATTERN, challenge, + return verifyCredential(patternToByteArray(pattern), CREDENTIAL_TYPE_PATTERN, challenge, userId); } @@ -429,7 +430,7 @@ public class LockPatternUtils { @Nullable CheckCredentialProgressCallback progressCallback) throws RequestThrottledException { throwIfCalledOnMainThread(); - return checkCredential(patternToString(pattern), CREDENTIAL_TYPE_PATTERN, userId, + return checkCredential(patternToByteArray(pattern), CREDENTIAL_TYPE_PATTERN, userId, progressCallback); } @@ -442,7 +443,7 @@ public class LockPatternUtils { * @param challenge The challenge to verify against the password * @return the attestation that the challenge was verified, or null. */ - public byte[] verifyPassword(String password, long challenge, int userId) + public byte[] verifyPassword(byte[] password, long challenge, int userId) throws RequestThrottledException { throwIfCalledOnMainThread(); return verifyCredential(password, CREDENTIAL_TYPE_PASSWORD, challenge, userId); @@ -458,7 +459,7 @@ public class LockPatternUtils { * @param challenge The challenge to verify against the password * @return the attestation that the challenge was verified, or null. */ - public byte[] verifyTiedProfileChallenge(String password, boolean isPattern, long challenge, + public byte[] verifyTiedProfileChallenge(byte[] password, boolean isPattern, long challenge, int userId) throws RequestThrottledException { throwIfCalledOnMainThread(); try { @@ -480,16 +481,31 @@ public class LockPatternUtils { } /** + * * Check to see if a password matches the saved password. If no password exists, * always returns true. * @param password The password to check. * @return Whether the password matches the stored one. */ public boolean checkPassword(String password, int userId) throws RequestThrottledException { - return checkPassword(password, userId, null /* progressCallback */); + byte[] passwordBytes = password != null ? password.getBytes() : null; + return checkPassword(passwordBytes, userId, null /* progressCallback */); } + /** + * + * Check to see if a password matches the saved password. If no password exists, + * always returns true. + * @param password The password to check. + * @return Whether the password matches the stored one. + */ + public boolean checkPassword(byte[] password, int userId) throws RequestThrottledException { + return checkPassword(password, userId, null /* progressCallback */); + } + + // TODO(b/120484642): This method is necessary for vendor/qcom code and is a hidden api + /* * * Check to see if a password matches the saved password. If no password exists, * always returns true. * @param password The password to check. @@ -498,6 +514,22 @@ public class LockPatternUtils { public boolean checkPassword(String password, int userId, @Nullable CheckCredentialProgressCallback progressCallback) throws RequestThrottledException { + byte[] passwordBytes = password != null ? password.getBytes() : null; + throwIfCalledOnMainThread(); + return checkCredential(passwordBytes, CREDENTIAL_TYPE_PASSWORD, userId, progressCallback); + + } + + /** + * Check to see if a password matches the saved password. If no password exists, + * always returns true. + * @param password The password to check. + * @return Whether the password matches the stored one. + */ + + public boolean checkPassword(byte[] password, int userId, + @Nullable CheckCredentialProgressCallback progressCallback) + throws RequestThrottledException { throwIfCalledOnMainThread(); return checkCredential(password, CREDENTIAL_TYPE_PASSWORD, userId, progressCallback); } @@ -519,7 +551,7 @@ public class LockPatternUtils { * Returns the password history hash factor, needed to check new password against password * history with {@link #checkPasswordHistory(String, byte[], int)} */ - public byte[] getPasswordHistoryHashFactor(String currentPassword, int userId) { + public byte[] getPasswordHistoryHashFactor(byte[] currentPassword, int userId) { try { return getLockSettings().getHashFactor(currentPassword, userId); } catch (RemoteException e) { @@ -537,8 +569,8 @@ public class LockPatternUtils { * {@link ILockSettings#getHashFactor} * @return Whether the password matches any in the history. */ - public boolean checkPasswordHistory(String passwordToCheck, byte[] hashFactor, int userId) { - if (TextUtils.isEmpty(passwordToCheck)) { + public boolean checkPasswordHistory(byte[] passwordToCheck, byte[] hashFactor, int userId) { + if (passwordToCheck == null || passwordToCheck.length == 0) { Log.e(TAG, "checkPasswordHistory: empty password"); return false; } @@ -639,13 +671,13 @@ public class LockPatternUtils { /** * Clear any lock pattern or password. */ - public void clearLock(String savedCredential, int userHandle) { + public void clearLock(byte[] savedCredential, int userHandle) { final int currentQuality = getKeyguardStoredPasswordQuality(userHandle); setKeyguardStoredPasswordQuality(PASSWORD_QUALITY_UNSPECIFIED, userHandle); try{ - getLockSettings().setLockCredential(null, CREDENTIAL_TYPE_NONE, savedCredential, - PASSWORD_QUALITY_UNSPECIFIED, userHandle); + getLockSettings().setLockCredential(null, CREDENTIAL_TYPE_NONE, + savedCredential, PASSWORD_QUALITY_UNSPECIFIED, userHandle); } catch (Exception e) { Log.e(TAG, "Failed to clear lock", e); setKeyguardStoredPasswordQuality(currentQuality, userHandle); @@ -704,10 +736,11 @@ public class LockPatternUtils { /** * Save a lock pattern. * @param pattern The new pattern to save. - * @param savedPattern The previously saved pattern, converted to String format + * @param savedPattern The previously saved pattern, converted to byte[] format * @param userId the user whose pattern is to be saved. */ - public void saveLockPattern(List<LockPatternView.Cell> pattern, String savedPattern, int userId) { + public void saveLockPattern(List<LockPatternView.Cell> pattern, byte[] savedPattern, + int userId) { if (!hasSecureLockScreen()) { throw new UnsupportedOperationException( "This operation requires the lock screen feature."); @@ -717,12 +750,12 @@ public class LockPatternUtils { + MIN_LOCK_PATTERN_SIZE + " dots long."); } - final String stringPattern = patternToString(pattern); + final byte[] bytePattern = patternToByteArray(pattern); final int currentQuality = getKeyguardStoredPasswordQuality(userId); setKeyguardStoredPasswordQuality(PASSWORD_QUALITY_SOMETHING, userId); try { - getLockSettings().setLockCredential(stringPattern, CREDENTIAL_TYPE_PATTERN, - savedPattern, PASSWORD_QUALITY_SOMETHING, userId); + getLockSettings().setLockCredential(bytePattern, CREDENTIAL_TYPE_PATTERN, savedPattern, + PASSWORD_QUALITY_SOMETHING, userId); } catch (Exception e) { Log.e(TAG, "Couldn't save lock pattern", e); setKeyguardStoredPasswordQuality(currentQuality, userId); @@ -734,7 +767,7 @@ public class LockPatternUtils { if (!shouldEncryptWithCredentials(true)) { clearEncryptionPassword(); } else { - updateEncryptionPassword(StorageManager.CRYPT_TYPE_PATTERN, stringPattern); + updateEncryptionPassword(StorageManager.CRYPT_TYPE_PATTERN, bytePattern); } } @@ -806,7 +839,7 @@ public class LockPatternUtils { } /** Update the encryption password if it is enabled **/ - private void updateEncryptionPassword(final int type, final String password) { + private void updateEncryptionPassword(final int type, final byte[] password) { if (!hasSecureLockScreen()) { throw new UnsupportedOperationException( "This operation requires the lock screen feature."); @@ -825,7 +858,9 @@ public class LockPatternUtils { protected Void doInBackground(Void... dummy) { IStorageManager storageManager = IStorageManager.Stub.asInterface(service); try { - storageManager.changeEncryptionPassword(type, password); + // TODO(b/120484642): This is a location where we still use a String for vold + String passwordString = password != null ? new String(password) : null; + storageManager.changeEncryptionPassword(type, passwordString); } catch (RemoteException e) { Log.e(TAG, "Error changing encryption password", e); } @@ -842,14 +877,34 @@ public class LockPatternUtils { * @param savedPassword The previously saved lock password, or null if none * @param requestedQuality {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)} * @param userHandle The userId of the user to change the password for + * + * @deprecated Pass password as a byte array */ + @Deprecated public void saveLockPassword(String password, String savedPassword, int requestedQuality, int userHandle) { + byte[] passwordBytes = password != null ? password.getBytes() : null; + byte[] savedPasswordBytes = savedPassword != null ? savedPassword.getBytes() : null; + saveLockPassword(passwordBytes, savedPasswordBytes, requestedQuality, userHandle); + } + + /** + * Save a lock password. Does not ensure that the password is as good + * as the requested mode, but will adjust the mode to be as good as the + * password. + * @param password The password to save + * @param savedPassword The previously saved lock password, or null if none + * @param requestedQuality {@see DevicePolicyManager#getPasswordQuality( + * android.content.ComponentName)} + * @param userHandle The userId of the user to change the password for + */ + public void saveLockPassword(byte[] password, byte[] savedPassword, int requestedQuality, + int userHandle) { if (!hasSecureLockScreen()) { throw new UnsupportedOperationException( "This operation requires the lock screen feature."); } - if (password == null || password.length() < MIN_LOCK_PASSWORD_SIZE) { + if (password == null || password.length < MIN_LOCK_PASSWORD_SIZE) { throw new IllegalArgumentException("password must not be null and at least " + "of length " + MIN_LOCK_PASSWORD_SIZE); } @@ -864,8 +919,8 @@ public class LockPatternUtils { computePasswordQuality(CREDENTIAL_TYPE_PASSWORD, password, requestedQuality), userHandle); try { - getLockSettings().setLockCredential(password, CREDENTIAL_TYPE_PASSWORD, - savedPassword, requestedQuality, userHandle); + getLockSettings().setLockCredential(password, CREDENTIAL_TYPE_PASSWORD, savedPassword, + requestedQuality, userHandle); } catch (Exception e) { Log.e(TAG, "Unable to save lock password", e); setKeyguardStoredPasswordQuality(currentQuality, userHandle); @@ -882,7 +937,7 @@ public class LockPatternUtils { * Update device encryption password if calling user is USER_SYSTEM and device supports * encryption. */ - private void updateEncryptionPasswordIfNeeded(String password, int quality, int userHandle) { + private void updateEncryptionPasswordIfNeeded(byte[] password, int quality, int userHandle) { // Update the device encryption password. if (userHandle == UserHandle.USER_SYSTEM && LockPatternUtils.isDeviceEncryptionEnabled()) { @@ -902,8 +957,8 @@ public class LockPatternUtils { * Store the hash of the *current* password in the password history list, if device policy * enforces password history requirement. */ - private void updatePasswordHistory(String password, int userHandle) { - if (TextUtils.isEmpty(password)) { + private void updatePasswordHistory(byte[] password, int userHandle) { + if (password == null || password.length == 0) { Log.e(TAG, "checkPasswordHistory: empty password"); return; } @@ -982,7 +1037,7 @@ public class LockPatternUtils { * if DevicePolicyManager has a stronger quality requirement. This value will be written * to PASSWORD_TYPE_KEY. */ - private int computePasswordQuality(int type, String credential, int requestedQuality) { + private int computePasswordQuality(int type, byte[] credential, int requestedQuality) { final int quality; if (type == CREDENTIAL_TYPE_PASSWORD) { int computedQuality = PasswordMetrics.computeForPassword(credential).quality; @@ -1005,7 +1060,7 @@ public class LockPatternUtils { * true */ public void setSeparateProfileChallengeEnabled(int userHandle, boolean enabled, - String managedUserPassword) { + byte[] managedUserPassword) { if (!isManagedProfile(userHandle)) { return; } @@ -1069,15 +1124,28 @@ public class LockPatternUtils { * Deserialize a pattern. * @param string The pattern serialized with {@link #patternToString} * @return The pattern. + * @deprecated Pass patterns as byte[] and use byteArrayToPattern */ + @Deprecated public static List<LockPatternView.Cell> stringToPattern(String string) { if (string == null) { return null; } + return byteArrayToPattern(string.getBytes()); + } + + /** + * Deserialize a pattern. + * @param bytes The pattern serialized with {@link #patternToByteArray} + * @return The pattern. + */ + public static List<LockPatternView.Cell> byteArrayToPattern(byte[] bytes) { + if (bytes == null) { + return null; + } List<LockPatternView.Cell> result = Lists.newArrayList(); - final byte[] bytes = string.getBytes(); for (int i = 0; i < bytes.length; i++) { byte b = (byte) (bytes[i] - '1'); result.add(LockPatternView.Cell.of(b / 3, b % 3)); @@ -1089,10 +1157,22 @@ public class LockPatternUtils { * Serialize a pattern. * @param pattern The pattern. * @return The pattern in string form. + * @deprecated Use patternToByteArray instead. */ + @Deprecated public static String patternToString(List<LockPatternView.Cell> pattern) { + return new String(patternToByteArray(pattern)); + } + + + /** + * Serialize a pattern. + * @param pattern The pattern. + * @return The pattern in byte array form. + */ + public static byte[] patternToByteArray(List<LockPatternView.Cell> pattern) { if (pattern == null) { - return ""; + return new byte[0]; } final int patternSize = pattern.size(); @@ -1101,21 +1181,24 @@ public class LockPatternUtils { LockPatternView.Cell cell = pattern.get(i); res[i] = (byte) (cell.getRow() * 3 + cell.getColumn() + '1'); } - return new String(res); + return res; } - public static String patternStringToBaseZero(String pattern) { - if (pattern == null) { - return ""; + /** + * Transform a pattern byte array to base zero form. + * @param bytes pattern byte array. + * @return The pattern in base zero form. + */ + public static byte[] patternByteArrayToBaseZero(byte[] bytes) { + if (bytes == null) { + return new byte[0]; } - final int patternSize = pattern.length(); - + final int patternSize = bytes.length; byte[] res = new byte[patternSize]; - final byte[] bytes = pattern.getBytes(); for (int i = 0; i < patternSize; i++) { res[i] = (byte) (bytes[i] - '1'); } - return new String(res); + return res; } /* @@ -1169,13 +1252,18 @@ public class LockPatternUtils { * * @return the hash of the pattern in a byte array. */ - public String legacyPasswordToHash(String password, int userId) { - if (password == null) { + public String legacyPasswordToHash(byte[] password, int userId) { + if (password == null || password.length == 0) { return null; } try { - byte[] saltedPassword = (password + getSalt(userId)).getBytes(); + // Previously the password was passed as a String with the following code: + // byte[] saltedPassword = (password + getSalt(userId)).getBytes(); + // The code below creates the identical digest preimage using byte arrays: + byte[] salt = getSalt(userId).getBytes(); + byte[] saltedPassword = Arrays.copyOf(password, password.length + salt.length); + System.arraycopy(salt, 0, saltedPassword, password.length, salt.length); byte[] sha1 = MessageDigest.getInstance("SHA-1").digest(saltedPassword); byte[] md5 = MessageDigest.getInstance("MD5").digest(saltedPassword); @@ -1184,6 +1272,7 @@ public class LockPatternUtils { System.arraycopy(md5, 0, combined, sha1.length, md5.length); final char[] hexEncoded = HexEncoding.encode(combined); + Arrays.fill(saltedPassword, (byte) 0); return new String(hexEncoded); } catch (NoSuchAlgorithmException e) { throw new AssertionError("Missing digest algorithm: ", e); @@ -1193,14 +1282,19 @@ public class LockPatternUtils { /** * Hash the password for password history check purpose. */ - private String passwordToHistoryHash(String passwordToHash, byte[] hashFactor, int userId) { - if (TextUtils.isEmpty(passwordToHash) || hashFactor == null) { + private String passwordToHistoryHash(byte[] passwordToHash, byte[] hashFactor, int userId) { + if (passwordToHash == null || passwordToHash.length == 0 || hashFactor == null) { return null; } try { MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); sha256.update(hashFactor); - sha256.update((passwordToHash + getSalt(userId)).getBytes()); + byte[] salt = getSalt(userId).getBytes(); + byte[] saltedPassword = Arrays.copyOf(passwordToHash, passwordToHash.length + + salt.length); + System.arraycopy(salt, 0, saltedPassword, passwordToHash.length, salt.length); + sha256.update(saltedPassword); + Arrays.fill(saltedPassword, (byte) 0); return new String(HexEncoding.encode(sha256.digest())); } catch (NoSuchAlgorithmException e) { throw new AssertionError("Missing digest algorithm: ", e); @@ -1633,7 +1727,7 @@ public class LockPatternUtils { * @param userId The user who's lock credential to be changed * @return {@code true} if the operation is successful. */ - public boolean setLockCredentialWithToken(String credential, int type, int requestedQuality, + public boolean setLockCredentialWithToken(byte[] credential, int type, int requestedQuality, long tokenHandle, byte[] token, int userId) { if (!hasSecureLockScreen()) { throw new UnsupportedOperationException( @@ -1641,13 +1735,13 @@ public class LockPatternUtils { } LockSettingsInternal localService = getLockSettingsInternal(); if (type != CREDENTIAL_TYPE_NONE) { - if (TextUtils.isEmpty(credential) || credential.length() < MIN_LOCK_PASSWORD_SIZE) { + if (credential == null || credential.length < MIN_LOCK_PASSWORD_SIZE) { throw new IllegalArgumentException("password must not be null and at least " + "of length " + MIN_LOCK_PASSWORD_SIZE); } final int quality = computePasswordQuality(type, credential, requestedQuality); - if (!localService.setLockCredentialWithToken(credential, type, tokenHandle, - token, quality, userId)) { + if (!localService.setLockCredentialWithToken(credential, type, tokenHandle, token, + quality, userId)) { return false; } setKeyguardStoredPasswordQuality(quality, userId); @@ -1656,11 +1750,11 @@ public class LockPatternUtils { updatePasswordHistory(credential, userId); onAfterChangingPassword(userId); } else { - if (!TextUtils.isEmpty(credential)) { + if (!(credential == null || credential.length == 0)) { throw new IllegalArgumentException("password must be emtpy for NONE type"); } - if (!localService.setLockCredentialWithToken(null, CREDENTIAL_TYPE_NONE, - tokenHandle, token, PASSWORD_QUALITY_UNSPECIFIED, userId)) { + if (!localService.setLockCredentialWithToken(null, CREDENTIAL_TYPE_NONE, tokenHandle, + token, PASSWORD_QUALITY_UNSPECIFIED, userId)) { return false; } setKeyguardStoredPasswordQuality(PASSWORD_QUALITY_UNSPECIFIED, userId); @@ -1891,4 +1985,22 @@ public class LockPatternUtils { return FRP_CREDENTIAL_ENABLED && context.getResources().getBoolean( com.android.internal.R.bool.config_enableCredentialFactoryResetProtection); } + + /** + * Converts a CharSequence to a byte array without requiring a toString(), which creates an + * additional copy. + * + * @param chars The CharSequence to convert + * @return A byte array representing the input + */ + public static byte[] charSequenceToByteArray(CharSequence chars) { + if (chars == null) { + return null; + } + byte[] bytes = new byte[chars.length()]; + for (int i = 0; i < chars.length(); i++) { + bytes[i] = (byte) chars.charAt(i); + } + return bytes; + } } diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java index 25a5a0734bf5..4b269901ccf6 100644 --- a/core/java/com/android/internal/widget/LockPatternView.java +++ b/core/java/com/android/internal/widget/LockPatternView.java @@ -1273,8 +1273,10 @@ public class LockPatternView extends View { @Override protected Parcelable onSaveInstanceState() { Parcelable superState = super.onSaveInstanceState(); + byte[] patternBytes = LockPatternUtils.patternToByteArray(mPattern); + String patternString = patternBytes != null ? new String(patternBytes) : null; return new SavedState(superState, - LockPatternUtils.patternToString(mPattern), + patternString, mPatternDisplayMode.ordinal(), mInputEnabled, mInStealthMode, mEnableHapticFeedback); } diff --git a/core/java/com/android/internal/widget/LockSettingsInternal.java b/core/java/com/android/internal/widget/LockSettingsInternal.java index 9de9ef7f2aea..90397dffe5f9 100644 --- a/core/java/com/android/internal/widget/LockSettingsInternal.java +++ b/core/java/com/android/internal/widget/LockSettingsInternal.java @@ -49,7 +49,11 @@ public abstract class LockSettingsInternal { */ public abstract boolean isEscrowTokenActive(long handle, int userId); - public abstract boolean setLockCredentialWithToken(String credential, int type, + /** + * Set the lock credential. + * @return true if password is set. + */ + public abstract boolean setLockCredentialWithToken(byte[] credential, int type, long tokenHandle, byte[] token, int requestedQuality, int userId); public abstract boolean unlockUserWithToken(long tokenHandle, byte[] token, int userId); diff --git a/core/jni/Android.bp b/core/jni/Android.bp index c309f276d3ed..cd34d2e1b441 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -165,6 +165,8 @@ cc_library_shared { "android_media_AudioTrack.cpp", "android_media_AudioAttributes.cpp", "android_media_AudioProductStrategies.cpp", + "android_media_AudioVolumeGroups.cpp", + "android_media_AudioVolumeGroupCallback.cpp", "android_media_DeviceCallback.cpp", "android_media_JetPlayer.cpp", "android_media_MediaMetricsJNI.cpp", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 0938e569ad7e..da27852a3a35 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -110,6 +110,8 @@ extern int register_android_media_AudioSystem(JNIEnv *env); extern int register_android_media_AudioTrack(JNIEnv *env); extern int register_android_media_AudioAttributes(JNIEnv *env); extern int register_android_media_AudioProductStrategies(JNIEnv *env); +extern int register_android_media_AudioVolumeGroups(JNIEnv *env); +extern int register_android_media_AudioVolumeGroupChangeHandler(JNIEnv *env); extern int register_android_media_MicrophoneInfo(JNIEnv *env); extern int register_android_media_JetPlayer(JNIEnv *env); extern int register_android_media_ToneGenerator(JNIEnv *env); @@ -244,6 +246,11 @@ static const char* kGenerationalCCRuntimeOption = "-Xgc:generational_cc"; // Copying (CC) garbage collector. static const char* kNoGenerationalCCRuntimeOption = "-Xgc:nogenerational_cc"; +// Feature flag name for running the JIT in Zygote experiment, b/119800099. +static const char* ENABLE_APEX_IMAGE = "enable_apex_image"; +// Flag to pass to the runtime when using the apex image. +static const char* kApexImageOption = "-Ximage:/system/framework/apex.art"; + static AndroidRuntime* gCurRuntime = NULL; /* @@ -678,8 +685,17 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote) char jdwpProviderBuf[sizeof("-XjdwpProvider:") - 1 + PROPERTY_VALUE_MAX]; char bootImageBuf[sizeof("-Ximage:") - 1 + PROPERTY_VALUE_MAX]; - if (parseRuntimeOption("dalvik.vm.boot-image", bootImageBuf, "-Ximage:")) { - ALOGI("Boot image: '%s'\n", bootImageBuf); + std::string use_apex_image = + server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE, + ENABLE_APEX_IMAGE, + /*default_value=*/ ""); + if (use_apex_image == "true") { + addOption(kApexImageOption); + ALOGI("Using Apex boot image: '%s'\n", kApexImageOption); + } else if (parseRuntimeOption("dalvik.vm.boot-image", bootImageBuf, "-Ximage:")) { + ALOGI("Using dalvik.vm.boot-image: '%s'\n", bootImageBuf); + } else { + ALOGI("Using default boot image"); } bool checkJni = false; @@ -1510,6 +1526,8 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_media_AudioTrack), REG_JNI(register_android_media_AudioAttributes), REG_JNI(register_android_media_AudioProductStrategies), + REG_JNI(register_android_media_AudioVolumeGroups), + REG_JNI(register_android_media_AudioVolumeGroupChangeHandler), REG_JNI(register_android_media_JetPlayer), REG_JNI(register_android_media_MicrophoneInfo), REG_JNI(register_android_media_RemoteDisplay), diff --git a/core/jni/android_hardware_SoundTrigger.cpp b/core/jni/android_hardware_SoundTrigger.cpp index 8a280343e731..c7805ea12f02 100644 --- a/core/jni/android_hardware_SoundTrigger.cpp +++ b/core/jni/android_hardware_SoundTrigger.cpp @@ -221,12 +221,23 @@ void JNISoundTriggerCallback::onRecognitionEvent(struct sound_trigger_recognitio jobject jAudioFormat = NULL; if (event->trigger_in_data || event->capture_available) { + jint channelMask = (jint)audio_channel_mask_get_bits(event->audio_config.channel_mask); + jint channelIndexMask = (jint)AUDIO_CHANNEL_NONE; + + switch (audio_channel_mask_get_representation(event->audio_config.channel_mask)) { + case AUDIO_CHANNEL_REPRESENTATION_INDEX: + channelIndexMask = channelMask; + channelMask = (jint)AUDIO_CHANNEL_NONE; + break; + default: + break; + } jAudioFormat = env->NewObject(gAudioFormatClass, gAudioFormatCstor, audioFormatFromNative(event->audio_config.format), event->audio_config.sample_rate, - inChannelMaskFromNative(event->audio_config.channel_mask), - (jint)0 /* channelIndexMask */); + channelMask, + channelIndexMask); } if (event->type == SOUND_MODEL_TYPE_KEYPHRASE) { diff --git a/core/jni/android_media_AudioProductStrategies.cpp b/core/jni/android_media_AudioProductStrategies.cpp index a18e80a4e6e1..822b74a6b990 100644 --- a/core/jni/android_media_AudioProductStrategies.cpp +++ b/core/jni/android_media_AudioProductStrategies.cpp @@ -57,7 +57,7 @@ static struct { static jclass gAudioAttributesGroupClass; static jmethodID gAudioAttributesGroupCstor; static struct { - jfieldID mGroupId; + jfieldID mVolumeGroupId; jfieldID mLegacyStreamType; jfieldID mAudioAttributes; } gAudioAttributesGroupsFields; @@ -194,12 +194,34 @@ exit: return jStatus; } +static jint +android_media_AudioSystem_getProductStrategyFromAudioAttributes(JNIEnv *env, jobject clazz, + jobject jAudioAttributes) +{ + JNIAudioAttributeHelper::UniqueAaPtr attributes = JNIAudioAttributeHelper::makeUnique(); + jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, + jAudioAttributes, + attributes.get()); + if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { + return jStatus; + } + product_strategy_t psId; + status_t status = AudioSystem::getProductStrategyFromAudioAttributes( + AudioAttributes(*attributes.get()), psId); + if (status != NO_ERROR) { + return nativeToJavaStatus(status); + } + return psId; +} + /* * JNI registration. */ static const JNINativeMethod gMethods[] = { {"native_list_audio_product_strategies", "(Ljava/util/ArrayList;)I", (void *)android_media_AudioSystem_listAudioProductStrategies}, + {"native_get_product_strategies_from_audio_attributes", "(Landroid/media/AudioAttributes;)I", + (void *)android_media_AudioSystem_getProductStrategyFromAudioAttributes}, }; int register_android_media_AudioProductStrategies(JNIEnv *env) @@ -227,8 +249,8 @@ int register_android_media_AudioProductStrategies(JNIEnv *env) gAudioAttributesGroupClass = MakeGlobalRefOrDie(env, audioAttributesGroupClass); gAudioAttributesGroupCstor = GetMethodIDOrDie(env, audioAttributesGroupClass, "<init>", "(II[Landroid/media/AudioAttributes;)V"); - gAudioAttributesGroupsFields.mGroupId = GetFieldIDOrDie( - env, audioAttributesGroupClass, "mGroupId", "I"); + gAudioAttributesGroupsFields.mVolumeGroupId = GetFieldIDOrDie( + env, audioAttributesGroupClass, "mVolumeGroupId", "I"); gAudioAttributesGroupsFields.mLegacyStreamType = GetFieldIDOrDie( env, audioAttributesGroupClass, "mLegacyStreamType", "I"); gAudioAttributesGroupsFields.mAudioAttributes = GetFieldIDOrDie( diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index bd998999ce63..1a90ebfee999 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -573,6 +573,81 @@ android_media_AudioSystem_getStreamVolumeIndex(JNIEnv *env, } static jint +android_media_AudioSystem_setVolumeIndexForAttributes(JNIEnv *env, + jobject thiz, + jobject jaa, + jint index, + jint device) +{ + // read the AudioAttributes values + JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique(); + jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get()); + if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { + return jStatus; + } + return (jint) check_AudioSystem_Command( + AudioSystem::setVolumeIndexForAttributes(*(paa.get()), index, (audio_devices_t)device)); +} + +static jint +android_media_AudioSystem_getVolumeIndexForAttributes(JNIEnv *env, + jobject thiz, + jobject jaa, + jint device) +{ + // read the AudioAttributes values + JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique(); + jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get()); + if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { + return jStatus; + } + int index; + if (AudioSystem::getVolumeIndexForAttributes(*(paa.get()), index, (audio_devices_t)device) + != NO_ERROR) { + index = -1; + } + return (jint) index; +} + +static jint +android_media_AudioSystem_getMinVolumeIndexForAttributes(JNIEnv *env, + jobject thiz, + jobject jaa) +{ + // read the AudioAttributes values + JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique(); + jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get()); + if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { + return jStatus; + } + int index; + if (AudioSystem::getMinVolumeIndexForAttributes(*(paa.get()), index) + != NO_ERROR) { + index = -1; + } + return (jint) index; +} + +static jint +android_media_AudioSystem_getMaxVolumeIndexForAttributes(JNIEnv *env, + jobject thiz, + jobject jaa) +{ + // read the AudioAttributes values + JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique(); + jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get()); + if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { + return jStatus; + } + int index; + if (AudioSystem::getMaxVolumeIndexForAttributes(*(paa.get()), index) + != NO_ERROR) { + index = -1; + } + return (jint) index; +} + +static jint android_media_AudioSystem_setMasterVolume(JNIEnv *env, jobject thiz, jfloat value) { return (jint) check_AudioSystem_Command(AudioSystem::setMasterVolume(value)); @@ -2172,6 +2247,10 @@ static const JNINativeMethod gMethods[] = { {"initStreamVolume", "(III)I", (void *)android_media_AudioSystem_initStreamVolume}, {"setStreamVolumeIndex","(III)I", (void *)android_media_AudioSystem_setStreamVolumeIndex}, {"getStreamVolumeIndex","(II)I", (void *)android_media_AudioSystem_getStreamVolumeIndex}, + {"setVolumeIndexForAttributes","(Landroid/media/AudioAttributes;II)I", (void *)android_media_AudioSystem_setVolumeIndexForAttributes}, + {"getVolumeIndexForAttributes","(Landroid/media/AudioAttributes;I)I", (void *)android_media_AudioSystem_getVolumeIndexForAttributes}, + {"getMinVolumeIndexForAttributes","(Landroid/media/AudioAttributes;)I", (void *)android_media_AudioSystem_getMinVolumeIndexForAttributes}, + {"getMaxVolumeIndexForAttributes","(Landroid/media/AudioAttributes;)I", (void *)android_media_AudioSystem_getMaxVolumeIndexForAttributes}, {"setMasterVolume", "(F)I", (void *)android_media_AudioSystem_setMasterVolume}, {"getMasterVolume", "()F", (void *)android_media_AudioSystem_getMasterVolume}, {"setMasterMute", "(Z)I", (void *)android_media_AudioSystem_setMasterMute}, diff --git a/core/jni/android_media_AudioVolumeGroupCallback.cpp b/core/jni/android_media_AudioVolumeGroupCallback.cpp new file mode 100644 index 000000000000..cb4ddbd119d5 --- /dev/null +++ b/core/jni/android_media_AudioVolumeGroupCallback.cpp @@ -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. + */ + +//#define LOG_NDEBUG 0 + +#define LOG_TAG "AudioVolumeGroupCallback-JNI" + +#include <utils/Log.h> +#include <nativehelper/JNIHelp.h> +#include "core_jni_helpers.h" + +#include "android_media_AudioVolumeGroupCallback.h" + + +// ---------------------------------------------------------------------------- +using namespace android; + +static const char* const kAudioVolumeGroupChangeHandlerClassPathName = + "android/media/audiopolicy/AudioVolumeGroupChangeHandler"; + +static struct { + jfieldID mJniCallback; +} gAudioVolumeGroupChangeHandlerFields; + +static struct { + jmethodID postEventFromNative; +} gAudioVolumeGroupChangeHandlerMethods; + +static Mutex gLock; + +JNIAudioVolumeGroupCallback::JNIAudioVolumeGroupCallback(JNIEnv* env, + jobject thiz, + jobject weak_thiz) +{ + jclass clazz = env->GetObjectClass(thiz); + if (clazz == NULL) { + ALOGE("Can't find class %s", kAudioVolumeGroupChangeHandlerClassPathName); + return; + } + mClass = (jclass)env->NewGlobalRef(clazz); + + // We use a weak reference so the AudioVolumeGroupChangeHandler object can be garbage collected. + // The reference is only used as a proxy for callbacks. + mObject = env->NewGlobalRef(weak_thiz); +} + +JNIAudioVolumeGroupCallback::~JNIAudioVolumeGroupCallback() +{ + // remove global references + JNIEnv *env = AndroidRuntime::getJNIEnv(); + if (env == NULL) { + return; + } + env->DeleteGlobalRef(mObject); + env->DeleteGlobalRef(mClass); +} + +void JNIAudioVolumeGroupCallback::onAudioVolumeGroupChanged(volume_group_t group, int flags) +{ + JNIEnv *env = AndroidRuntime::getJNIEnv(); + if (env == NULL) { + return; + } + ALOGV("%s volume group id %d", __FUNCTION__, group); + env->CallStaticVoidMethod(mClass, + gAudioVolumeGroupChangeHandlerMethods.postEventFromNative, + mObject, + AUDIOVOLUMEGROUP_EVENT_VOLUME_CHANGED, group, flags, NULL); + if (env->ExceptionCheck()) { + ALOGW("An exception occurred while notifying an event."); + env->ExceptionClear(); + } +} + +void JNIAudioVolumeGroupCallback::onServiceDied() +{ + JNIEnv *env = AndroidRuntime::getJNIEnv(); + if (env == NULL) { + return; + } + env->CallStaticVoidMethod(mClass, + gAudioVolumeGroupChangeHandlerMethods.postEventFromNative, + mObject, + AUDIOVOLUMEGROUP_EVENT_SERVICE_DIED, 0, 0, NULL); + if (env->ExceptionCheck()) { + ALOGW("An exception occurred while notifying an event."); + env->ExceptionClear(); + } +} + +static +sp<JNIAudioVolumeGroupCallback> setJniCallback(JNIEnv* env, + jobject thiz, + const sp<JNIAudioVolumeGroupCallback>& callback) +{ + Mutex::Autolock l(gLock); + sp<JNIAudioVolumeGroupCallback> old = (JNIAudioVolumeGroupCallback*)env->GetLongField( + thiz, gAudioVolumeGroupChangeHandlerFields.mJniCallback); + if (callback.get()) { + callback->incStrong((void*)setJniCallback); + } + if (old != 0) { + old->decStrong((void*)setJniCallback); + } + env->SetLongField(thiz, gAudioVolumeGroupChangeHandlerFields.mJniCallback, + (jlong)callback.get()); + return old; +} + +static void +android_media_AudioVolumeGroupChangeHandler_eventHandlerSetup(JNIEnv *env, + jobject thiz, + jobject weak_this) +{ + ALOGV("%s", __FUNCTION__); + sp<JNIAudioVolumeGroupCallback> callback = + new JNIAudioVolumeGroupCallback(env, thiz, weak_this); + + if (AudioSystem::addAudioVolumeGroupCallback(callback) == NO_ERROR) { + setJniCallback(env, thiz, callback); + } +} + +static void +android_media_AudioVolumeGroupChangeHandler_eventHandlerFinalize(JNIEnv *env, jobject thiz) +{ + ALOGV("%s", __FUNCTION__); + sp<JNIAudioVolumeGroupCallback> callback = setJniCallback(env, thiz, 0); + if (callback != 0) { + AudioSystem::removeAudioVolumeGroupCallback(callback); + } +} + +/* + * JNI registration. + */ +static const JNINativeMethod gMethods[] = { + {"native_setup", "(Ljava/lang/Object;)V", + (void *)android_media_AudioVolumeGroupChangeHandler_eventHandlerSetup}, + {"native_finalize", "()V", + (void *)android_media_AudioVolumeGroupChangeHandler_eventHandlerFinalize}, +}; + +int register_android_media_AudioVolumeGroupChangeHandler(JNIEnv *env) +{ + jclass audioVolumeGroupChangeHandlerClass = + FindClassOrDie(env, kAudioVolumeGroupChangeHandlerClassPathName); + gAudioVolumeGroupChangeHandlerMethods.postEventFromNative = + GetStaticMethodIDOrDie(env, audioVolumeGroupChangeHandlerClass, "postEventFromNative", + "(Ljava/lang/Object;IIILjava/lang/Object;)V"); + + gAudioVolumeGroupChangeHandlerFields.mJniCallback = + GetFieldIDOrDie(env, audioVolumeGroupChangeHandlerClass, "mJniCallback", "J"); + + env->DeleteLocalRef(audioVolumeGroupChangeHandlerClass); + + return RegisterMethodsOrDie(env, + kAudioVolumeGroupChangeHandlerClassPathName, + gMethods, + NELEM(gMethods)); +} + diff --git a/core/jni/android_media_AudioVolumeGroupCallback.h b/core/jni/android_media_AudioVolumeGroupCallback.h new file mode 100644 index 000000000000..de06549621b9 --- /dev/null +++ b/core/jni/android_media_AudioVolumeGroupCallback.h @@ -0,0 +1,44 @@ +/* + * 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. + */ + +#pragma once + +#include <system/audio.h> +#include <media/AudioSystem.h> + +namespace android { + +// keep in sync with AudioManager.AudioVolumeGroupChangeHandler.java +#define AUDIOVOLUMEGROUP_EVENT_VOLUME_CHANGED 1000 +#define AUDIOVOLUMEGROUP_EVENT_SERVICE_DIED 1001 + +class JNIAudioVolumeGroupCallback: public AudioSystem::AudioVolumeGroupCallback +{ +public: + JNIAudioVolumeGroupCallback(JNIEnv* env, jobject thiz, jobject weak_thiz); + ~JNIAudioVolumeGroupCallback(); + + void onAudioVolumeGroupChanged(volume_group_t group, int flags) override; + void onServiceDied() override; + +private: + void sendEvent(int event); + + jclass mClass; /**< Reference to AudioVolumeGroupChangeHandler class. */ + jobject mObject; /**< Weak ref to AudioVolumeGroupChangeHandler object to call on. */ +}; + +} // namespace android diff --git a/core/jni/android_media_AudioVolumeGroups.cpp b/core/jni/android_media_AudioVolumeGroups.cpp new file mode 100644 index 000000000000..64f0c1e33e1c --- /dev/null +++ b/core/jni/android_media_AudioVolumeGroups.cpp @@ -0,0 +1,183 @@ +/* + * 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_NDEBUG 0 + +#define LOG_TAG "AudioVolumeGroups-JNI" + +#include <inttypes.h> +#include <jni.h> +#include <nativehelper/JNIHelp.h> +#include "core_jni_helpers.h" + +#include <utils/Log.h> +#include <vector> + +#include <media/AudioSystem.h> +#include <media/AudioPolicy.h> + +#include <nativehelper/ScopedUtfChars.h> + +#include "android_media_AudioAttributes.h" +#include "android_media_AudioErrors.h" + +// ---------------------------------------------------------------------------- + +using namespace android; + +// ---------------------------------------------------------------------------- +static const char* const kClassPathName = "android/media/audiopolicy/AudioVolumeGroups"; +static const char* const kAudioVolumeGroupClassPathName = + "android/media/audiopolicy/AudioVolumeGroup"; + +static jclass gAudioVolumeGroupClass; +static jmethodID gAudioVolumeGroupCstor; +static struct { + jfieldID mName; + jfieldID mId; +} gAudioVolumeGroupFields; + +static jclass gArrayListClass; +static jmethodID gArrayListCstor; +static struct { + jmethodID add; + jmethodID toArray; +} gArrayListMethods; + + +static jint convertAudioVolumeGroupsFromNative( + JNIEnv *env, jobject *jGroup, const AudioVolumeGroup &group) +{ + jint jStatus = (jint)AUDIO_JAVA_SUCCESS; + jstring jName = NULL; + jint Id = NULL; + + jintArray jLegacyStreamTypes = NULL; + jobjectArray jAudioAttributes = NULL; + jint numAttributes; + jobject jAudioAttribute = NULL; + + jName = env->NewStringUTF(group.getName().c_str()); + Id = static_cast<jint>(group.getId()); + + // Legacy stream types array + jLegacyStreamTypes = env->NewIntArray(group.getStreamTypes().size()); + if (jLegacyStreamTypes == NULL) { + jStatus = (jint)AUDIO_JAVA_ERROR; + goto exit; + } + for (size_t streamIndex = 0; streamIndex < group.getStreamTypes().size(); streamIndex++) { + jint jStream = group.getStreamTypes()[streamIndex]; + env->SetIntArrayRegion(jLegacyStreamTypes, streamIndex, 1, &jStream); + } + + // Audio Attributes array + numAttributes = group.getAudioAttributes().size(); + jStatus = JNIAudioAttributeHelper::getJavaArray(env, &jAudioAttributes, numAttributes); + if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { + goto exit; + } + + for (size_t j = 0; j < static_cast<size_t>(numAttributes); j++) { + auto attributes = group.getAudioAttributes()[j]; + + jStatus = JNIAudioAttributeHelper::nativeToJava(env, &jAudioAttribute, attributes); + if (jStatus != AUDIO_JAVA_SUCCESS) { + goto exit; + } + env->SetObjectArrayElement(jAudioAttributes, j, jAudioAttribute); + } + + *jGroup = env->NewObject(gAudioVolumeGroupClass, gAudioVolumeGroupCstor, + jName, Id, jAudioAttributes, jLegacyStreamTypes); +exit: + if (jName != NULL) { + env->DeleteLocalRef(jName); + } + return jStatus; +} + +static jint +android_media_AudioSystem_listAudioVolumeGroups(JNIEnv *env, jobject clazz, jobject jVolumeGroups) +{ + if (env == NULL) { + return AUDIO_JAVA_DEAD_OBJECT; + } + if (jVolumeGroups == NULL) { + ALOGE("listAudioVolumeGroups NULL AudioVolumeGroups"); + return (jint)AUDIO_JAVA_BAD_VALUE; + } + if (!env->IsInstanceOf(jVolumeGroups, gArrayListClass)) { + ALOGE("listAudioVolumeGroups not an arraylist"); + return (jint)AUDIO_JAVA_BAD_VALUE; + } + + status_t status; + AudioVolumeGroupVector groups; + jint jStatus; + jobject jGroup = NULL; + + status = AudioSystem::listAudioVolumeGroups(groups); + if (status != NO_ERROR) { + ALOGE("AudioSystem::listAudioVolumeGroups error %d", status); + return nativeToJavaStatus(status); + } + for (const auto &group : groups) { + jStatus = convertAudioVolumeGroupsFromNative(env, &jGroup, group); + if (jStatus != AUDIO_JAVA_SUCCESS) { + goto exit; + } + env->CallBooleanMethod(jVolumeGroups, gArrayListMethods.add, jGroup); + } +exit: + if (jGroup != NULL) { + env->DeleteLocalRef(jGroup); + } + return jStatus; +} + +/* + * JNI registration. + */ +static const JNINativeMethod gMethods[] = { + {"native_list_audio_volume_groups", "(Ljava/util/ArrayList;)I", + (void *)android_media_AudioSystem_listAudioVolumeGroups}, +}; + +int register_android_media_AudioVolumeGroups(JNIEnv *env) +{ + jclass arrayListClass = FindClassOrDie(env, "java/util/ArrayList"); + gArrayListClass = MakeGlobalRefOrDie(env, arrayListClass); + gArrayListCstor = GetMethodIDOrDie(env, arrayListClass, "<init>", "()V"); + gArrayListMethods.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z"); + gArrayListMethods.toArray = GetMethodIDOrDie(env, arrayListClass, + "toArray", "()[Ljava/lang/Object;"); + + jclass audioVolumeGroupClass = FindClassOrDie(env, kAudioVolumeGroupClassPathName); + gAudioVolumeGroupClass = MakeGlobalRefOrDie(env, audioVolumeGroupClass); + gAudioVolumeGroupCstor = GetMethodIDOrDie( + env, audioVolumeGroupClass, "<init>", + "(Ljava/lang/String;I[Landroid/media/AudioAttributes;[I)V"); + + gAudioVolumeGroupFields.mName = GetFieldIDOrDie( + env, audioVolumeGroupClass, "mName", "Ljava/lang/String;"); + gAudioVolumeGroupFields.mId = GetFieldIDOrDie( + env, audioVolumeGroupClass, "mId", "I"); + + env->DeleteLocalRef(audioVolumeGroupClass); + + return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); +} diff --git a/core/jni/android_net_LocalSocketImpl.cpp b/core/jni/android_net_LocalSocketImpl.cpp index a1f2377041e8..1163b860977d 100644 --- a/core/jni/android_net_LocalSocketImpl.cpp +++ b/core/jni/android_net_LocalSocketImpl.cpp @@ -33,14 +33,16 @@ #include <unistd.h> #include <sys/ioctl.h> +#include <android-base/cmsg.h> +#include <android-base/macros.h> #include <cutils/sockets.h> #include <netinet/tcp.h> #include <nativehelper/ScopedUtfChars.h> -namespace android { +using android::base::ReceiveFileDescriptorVector; +using android::base::SendFileDescriptorVector; -template <typename T> -void UNUSED(T t) {} +namespace android { static jfieldID field_inboundFileDescriptors; static jfieldID field_outboundFileDescriptors; @@ -118,67 +120,6 @@ socket_bind_local (JNIEnv *env, jobject object, jobject fileDescriptor, } /** - * Processes ancillary data, handling only - * SCM_RIGHTS. Creates appropriate objects and sets appropriate - * fields in the LocalSocketImpl object. Returns 0 on success - * or -1 if an exception was thrown. - */ -static int socket_process_cmsg(JNIEnv *env, jobject thisJ, struct msghdr * pMsg) -{ - struct cmsghdr *cmsgptr; - - for (cmsgptr = CMSG_FIRSTHDR(pMsg); - cmsgptr != NULL; cmsgptr = CMSG_NXTHDR(pMsg, cmsgptr)) { - - if (cmsgptr->cmsg_level != SOL_SOCKET) { - continue; - } - - if (cmsgptr->cmsg_type == SCM_RIGHTS) { - int *pDescriptors = (int *)CMSG_DATA(cmsgptr); - jobjectArray fdArray; - int count - = ((cmsgptr->cmsg_len - CMSG_LEN(0)) / sizeof(int)); - - if (count < 0) { - jniThrowException(env, "java/io/IOException", - "invalid cmsg length"); - return -1; - } - - fdArray = env->NewObjectArray(count, class_FileDescriptor, NULL); - - if (fdArray == NULL) { - return -1; - } - - for (int i = 0; i < count; i++) { - jobject fdObject - = jniCreateFileDescriptor(env, pDescriptors[i]); - - if (env->ExceptionCheck()) { - return -1; - } - - env->SetObjectArrayElement(fdArray, i, fdObject); - - if (env->ExceptionCheck()) { - return -1; - } - } - - env->SetObjectField(thisJ, field_inboundFileDescriptors, fdArray); - - if (env->ExceptionCheck()) { - return -1; - } - } - } - - return 0; -} - -/** * Reads data from a socket into buf, processing any ancillary data * and adding it to thisJ. * @@ -189,47 +130,48 @@ static ssize_t socket_read_all(JNIEnv *env, jobject thisJ, int fd, void *buffer, size_t len) { ssize_t ret; - struct msghdr msg; - struct iovec iv; - unsigned char *buf = (unsigned char *)buffer; - // Enough buffer for a pile of fd's. We throw an exception if - // this buffer is too small. - struct cmsghdr cmsgbuf[2*sizeof(cmsghdr) + 0x100]; - - memset(&msg, 0, sizeof(msg)); - memset(&iv, 0, sizeof(iv)); - - iv.iov_base = buf; - iv.iov_len = len; + std::vector<android::base::unique_fd> received_fds; - msg.msg_iov = &iv; - msg.msg_iovlen = 1; - msg.msg_control = cmsgbuf; - msg.msg_controllen = sizeof(cmsgbuf); - - ret = TEMP_FAILURE_RETRY(recvmsg(fd, &msg, MSG_NOSIGNAL | MSG_CMSG_CLOEXEC)); - - if (ret < 0 && errno == EPIPE) { - // Treat this as an end of stream - return 0; - } + ret = ReceiveFileDescriptorVector(fd, buffer, len, 64, &received_fds); if (ret < 0) { + if (errno == EPIPE) { + // Treat this as an end of stream + return 0; + } + jniThrowIOException(env, errno); return -1; } - if ((msg.msg_flags & (MSG_CTRUNC | MSG_OOB | MSG_ERRQUEUE)) != 0) { - // To us, any of the above flags are a fatal error + if (received_fds.size() > 0) { + jobjectArray fdArray = env->NewObjectArray(received_fds.size(), class_FileDescriptor, NULL); + + if (fdArray == NULL) { + // NewObjectArray has thrown. + return -1; + } - jniThrowException(env, "java/io/IOException", - "Unexpected error or truncation during recvmsg()"); + for (size_t i = 0; i < received_fds.size(); i++) { + jobject fdObject = jniCreateFileDescriptor(env, received_fds[i].get()); - return -1; - } + if (env->ExceptionCheck()) { + return -1; + } + + env->SetObjectArrayElement(fdArray, i, fdObject); + + if (env->ExceptionCheck()) { + return -1; + } + } - if (ret >= 0) { - socket_process_cmsg(env, thisJ, &msg); + for (auto &fd : received_fds) { + // The fds are stored in java.io.FileDescriptors now. + static_cast<void>(fd.release()); + } + + env->SetObjectField(thisJ, field_inboundFileDescriptors, fdArray); } return ret; @@ -243,7 +185,6 @@ static ssize_t socket_read_all(JNIEnv *env, jobject thisJ, int fd, static int socket_write_all(JNIEnv *env, jobject object, int fd, void *buf, size_t len) { - ssize_t ret; struct msghdr msg; unsigned char *buffer = (unsigned char *)buf; memset(&msg, 0, sizeof(msg)); @@ -256,14 +197,11 @@ static int socket_write_all(JNIEnv *env, jobject object, int fd, return -1; } - struct cmsghdr *cmsg; int countFds = outboundFds == NULL ? 0 : env->GetArrayLength(outboundFds); - int fds[countFds]; - char msgbuf[CMSG_SPACE(countFds)]; + std::vector<int> fds; // Add any pending outbound file descriptors to the message if (outboundFds != NULL) { - if (env->ExceptionCheck()) { return -1; } @@ -274,47 +212,25 @@ static int socket_write_all(JNIEnv *env, jobject object, int fd, return -1; } - fds[i] = jniGetFDFromFileDescriptor(env, fdObject); + fds.push_back(jniGetFDFromFileDescriptor(env, fdObject)); if (env->ExceptionCheck()) { return -1; } } - - // See "man cmsg" really - msg.msg_control = msgbuf; - msg.msg_controllen = sizeof msgbuf; - cmsg = CMSG_FIRSTHDR(&msg); - cmsg->cmsg_level = SOL_SOCKET; - cmsg->cmsg_type = SCM_RIGHTS; - cmsg->cmsg_len = CMSG_LEN(sizeof fds); - memcpy(CMSG_DATA(cmsg), fds, sizeof fds); } - // We only write our msg_control during the first write - while (len > 0) { - struct iovec iv; - memset(&iv, 0, sizeof(iv)); - - iv.iov_base = buffer; - iv.iov_len = len; - - msg.msg_iov = &iv; - msg.msg_iovlen = 1; - - do { - ret = sendmsg(fd, &msg, MSG_NOSIGNAL); - } while (ret < 0 && errno == EINTR); + ssize_t rc = SendFileDescriptorVector(fd, buffer, len, fds); - if (ret < 0) { + while (rc != len) { + if (rc == -1) { jniThrowIOException(env, errno); return -1; } - buffer += ret; - len -= ret; + buffer += rc; + len -= rc; - // Wipes out any msg_control too - memset(&msg, 0, sizeof(msg)); + rc = send(fd, buffer, len, MSG_NOSIGNAL); } return 0; diff --git a/core/jni/android_opengl_EGL14.cpp b/core/jni/android_opengl_EGL14.cpp index 15d1944205b6..de5e3a52a0c1 100644 --- a/core/jni/android_opengl_EGL14.cpp +++ b/core/jni/android_opengl_EGL14.cpp @@ -35,8 +35,6 @@ #include <ui/ANativeObjectBase.h> -static int initialized = 0; - static jclass egldisplayClass; static jclass eglcontextClass; static jclass eglsurfaceClass; @@ -239,7 +237,7 @@ exit: } if (_exception) { jniThrowException(_env, _exceptionType, _exceptionMessage); - return false; + return JNI_FALSE; } return (jboolean)_returnValue; } @@ -337,7 +335,7 @@ exit: } if (_exception) { jniThrowException(_env, _exceptionType, _exceptionMessage); - return false; + return JNI_FALSE; } return (jboolean)_returnValue; } @@ -457,7 +455,7 @@ exit: } if (_exception) { jniThrowException(_env, _exceptionType, _exceptionMessage); - return false; + return JNI_FALSE; } return (jboolean)_returnValue; } @@ -513,7 +511,7 @@ exit: } if (_exception) { jniThrowException(_env, _exceptionType, _exceptionMessage); - return false; + return JNI_FALSE; } return (jboolean)_returnValue; } @@ -808,7 +806,7 @@ exit: } if (_exception) { jniThrowException(_env, _exceptionType, _exceptionMessage); - return false; + return JNI_FALSE; } return (jboolean)_returnValue; } @@ -1163,7 +1161,7 @@ exit: } if (_exception) { jniThrowException(_env, _exceptionType, _exceptionMessage); - return false; + return JNI_FALSE; } return (jboolean)_returnValue; } @@ -1209,7 +1207,7 @@ android_eglCopyBuffers (JNIEnv *_env, jobject _this, jobject dpy, jobject surface, jint target) { jniThrowException(_env, "java/lang/UnsupportedOperationException", "eglCopyBuffers"); - return (EGLBoolean) 0; + return JNI_FALSE; } static const char *classPathName = "android/opengl/EGL14"; diff --git a/core/jni/android_opengl_EGL15.cpp b/core/jni/android_opengl_EGL15.cpp index 2abd95020f1c..99bdce274f3e 100644 --- a/core/jni/android_opengl_EGL15.cpp +++ b/core/jni/android_opengl_EGL15.cpp @@ -14,6 +14,8 @@ ** limitations under the License. */ +// This source file is automatically generated + #pragma GCC diagnostic ignored "-Wunused-variable" #pragma GCC diagnostic ignored "-Wunused-function" @@ -27,8 +29,6 @@ #include <ui/ANativeObjectBase.h> -static int initialized = 0; - // classes from EGL 1.4 static jclass egldisplayClass; static jclass eglsurfaceClass; @@ -92,7 +92,6 @@ nativeClassInit(JNIEnv *_env, jclass glImplClass) egldisplayGetHandleID = _env->GetMethodID(egldisplayClass, "getNativeHandle", "()J"); eglsurfaceGetHandleID = _env->GetMethodID(eglsurfaceClass, "getNativeHandle", "()J"); - eglconfigConstructor = _env->GetMethodID(eglconfigClass, "<init>", "(J)V"); eglcontextConstructor = _env->GetMethodID(eglcontextClass, "<init>", "(J)V"); egldisplayConstructor = _env->GetMethodID(egldisplayClass, "<init>", "(J)V"); @@ -105,7 +104,6 @@ nativeClassInit(JNIEnv *_env, jclass glImplClass) jobject localeglNoSurfaceObject = _env->NewObject(eglsurfaceClass, eglsurfaceConstructor, reinterpret_cast<jlong>(EGL_NO_SURFACE)); eglNoSurfaceObject = _env->NewGlobalRef(localeglNoSurfaceObject); - jclass eglClass = _env->FindClass("android/opengl/EGL15"); jfieldID noContextFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_CONTEXT", "Landroid/opengl/EGLContext;"); _env->SetStaticObjectField(eglClass, noContextFieldID, eglNoContextObject); @@ -171,8 +169,6 @@ getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining, jint *o *array = NULL; return reinterpret_cast<void*>(pointer); } - eglimageGetHandleID = _env->GetMethodID(eglimageClass, "getNativeHandle", "()J"); - eglsyncGetHandleID = _env->GetMethodID(eglsyncClass, "getNativeHandle", "()J"); *array = (jarray) _env->CallStaticObjectMethod(nioAccessClass, getBaseArrayID, buffer); @@ -191,7 +187,7 @@ releasePointer(JNIEnv *_env, jarray array, void *data, jboolean commit) static void * fromEGLHandle(JNIEnv *_env, jmethodID mid, jobject obj) { - if (obj == NULL){ + if (obj == NULL) { jniThrowException(_env, "java/lang/IllegalArgumentException", "Object is set to null."); return nullptr; @@ -202,10 +198,9 @@ fromEGLHandle(JNIEnv *_env, jmethodID mid, jobject obj) { } static jobject -toEGLHandle(JNIEnv *_env, jclass cls, jmethodID con, void * handle) { - if (cls == eglimageClass && - (EGLImage)handle == EGL_NO_IMAGE) { - return eglNoImageObject; +toEGLHandle(JNIEnv *_env, jclass cls, jmethodID con, void *handle) { + if (cls == eglimageClass && (EGLImage)handle == EGL_NO_IMAGE) { + return eglNoImageObject; } return _env->NewObject(cls, con, reinterpret_cast<jlong>(handle)); @@ -337,7 +332,7 @@ exit: } if (_exception) { jniThrowException(_env, _exceptionType, _exceptionMessage); - return false; + return JNI_FALSE; } return (jboolean)_returnValue; } @@ -461,12 +456,9 @@ exit: static jobject android_eglCreatePlatformPixmapSurface (JNIEnv *_env, jobject _this, jobject dpy, jobject config, jobject native_pixmap_buf, jlongArray attrib_list_ref, jint offset) { - if ((true)) { - jniThrowException(_env, "java/lang/UnsupportedOperationException", - "eglCreatePlatformPixmapSurface"); + jniThrowException(_env, "java/lang/UnsupportedOperationException", + "eglCreatePlatformPixmapSurface"); return nullptr; - } - return toEGLHandle(_env, eglsurfaceClass, eglsurfaceConstructor, (EGLSurface) 0); } /* EGLBoolean eglWaitSync ( EGLDisplay dpy, EGLSync sync, EGLint flags ) */ diff --git a/core/jni/android_opengl_EGLExt.cpp b/core/jni/android_opengl_EGLExt.cpp index 75a25fe95e09..fef8116dc93a 100644 --- a/core/jni/android_opengl_EGLExt.cpp +++ b/core/jni/android_opengl_EGLExt.cpp @@ -36,8 +36,6 @@ #include <ui/ANativeObjectBase.h> -static int initialized = 0; - static jclass egldisplayClass; static jclass eglcontextClass; static jclass eglsurfaceClass; @@ -104,6 +102,7 @@ fromEGLHandle(JNIEnv *_env, jmethodID mid, jobject obj) { if (obj == NULL){ jniThrowException(_env, "java/lang/IllegalArgumentException", "Object is set to null."); + return nullptr; } return reinterpret_cast<void*>(_env->CallLongMethod(obj, mid)); diff --git a/core/jni/android_opengl_GLES10.cpp b/core/jni/android_opengl_GLES10.cpp index ee5b5941202d..a4ab5db8c8d7 100644 --- a/core/jni/android_opengl_GLES10.cpp +++ b/core/jni/android_opengl_GLES10.cpp @@ -29,8 +29,6 @@ #include <utils/misc.h> #include <assert.h> -static int initialized = 0; - static jclass nioAccessClass; static jclass bufferClass; static jmethodID getBasePointerID; diff --git a/core/jni/android_opengl_GLES10Ext.cpp b/core/jni/android_opengl_GLES10Ext.cpp index da7d0f01928f..a5dcbf765b60 100644 --- a/core/jni/android_opengl_GLES10Ext.cpp +++ b/core/jni/android_opengl_GLES10Ext.cpp @@ -29,8 +29,6 @@ #include <utils/misc.h> #include <assert.h> -static int initialized = 0; - static jclass nioAccessClass; static jclass bufferClass; static jmethodID getBasePointerID; @@ -531,6 +529,7 @@ exit: } if (_exception) { jniThrowException(_env, _exceptionType, _exceptionMessage); + return (jint)0; } return (jint)_returnValue; } @@ -600,6 +599,7 @@ exit: } if (_exception) { jniThrowException(_env, _exceptionType, _exceptionMessage); + return (jint)0; } return (jint)_returnValue; } diff --git a/core/jni/android_opengl_GLES11.cpp b/core/jni/android_opengl_GLES11.cpp index 391ae5366a3b..be86a037bfbd 100644 --- a/core/jni/android_opengl_GLES11.cpp +++ b/core/jni/android_opengl_GLES11.cpp @@ -29,8 +29,6 @@ #include <utils/misc.h> #include <assert.h> -static int initialized = 0; - static jclass nioAccessClass; static jclass bufferClass; static jmethodID getBasePointerID; diff --git a/core/jni/android_opengl_GLES11Ext.cpp b/core/jni/android_opengl_GLES11Ext.cpp index 09dce32b9e56..d28d9a387b5e 100644 --- a/core/jni/android_opengl_GLES11Ext.cpp +++ b/core/jni/android_opengl_GLES11Ext.cpp @@ -29,8 +29,6 @@ #include <utils/misc.h> #include <assert.h> -static int initialized = 0; - static jclass nioAccessClass; static jclass bufferClass; static jmethodID getBasePointerID; diff --git a/core/jni/android_opengl_GLES20.cpp b/core/jni/android_opengl_GLES20.cpp index 99922cfe401d..0e20d474e275 100644 --- a/core/jni/android_opengl_GLES20.cpp +++ b/core/jni/android_opengl_GLES20.cpp @@ -29,8 +29,6 @@ #include <utils/misc.h> #include <assert.h> -static int initialized = 0; - static jclass nioAccessClass; static jclass bufferClass; static jmethodID getBasePointerID; @@ -2684,6 +2682,7 @@ exit: if (_exception) { jniThrowException(_env, _exceptionType, _exceptionMessage); + return (jint)0; } return (jint)_returnValue; } @@ -3909,6 +3908,7 @@ exit: if (_exception) { jniThrowException(_env, _exceptionType, _exceptionMessage); + return (jint)0; } return (jint)_returnValue; } diff --git a/core/jni/android_opengl_GLES30.cpp b/core/jni/android_opengl_GLES30.cpp index adc635ed4eea..992239865db7 100644 --- a/core/jni/android_opengl_GLES30.cpp +++ b/core/jni/android_opengl_GLES30.cpp @@ -29,8 +29,6 @@ #include <utils/misc.h> #include <assert.h> -static int initialized = 0; - static jclass nioAccessClass; static jclass bufferClass; static jmethodID getBasePointerID; @@ -3012,6 +3010,7 @@ exit: if (_exception) { jniThrowException(_env, _exceptionType, _exceptionMessage); + return (jint)0; } return (jint)_returnValue; } @@ -3981,6 +3980,7 @@ exit: if (_exception) { jniThrowException(_env, _exceptionType, _exceptionMessage); + return (jint)0; } return (jint)_returnValue; } diff --git a/core/jni/android_opengl_GLES31.cpp b/core/jni/android_opengl_GLES31.cpp index 512f562062e6..27dbd399d77c 100644 --- a/core/jni/android_opengl_GLES31.cpp +++ b/core/jni/android_opengl_GLES31.cpp @@ -27,8 +27,6 @@ #include <utils/misc.h> #include <assert.h> -static int initialized = 0; - static jclass nioAccessClass; static jclass bufferClass; static jmethodID getBasePointerID; @@ -708,6 +706,7 @@ exit: if (_exception) { jniThrowException(_env, _exceptionType, _exceptionMessage); + return (jint)0; } return (jint)_returnValue; } @@ -919,6 +918,7 @@ exit: if (_exception) { jniThrowException(_env, _exceptionType, _exceptionMessage); + return (jint)0; } return (jint)_returnValue; } diff --git a/core/jni/android_opengl_GLES31Ext.cpp b/core/jni/android_opengl_GLES31Ext.cpp index 5543fcadaed0..5b671c8e55fb 100644 --- a/core/jni/android_opengl_GLES31Ext.cpp +++ b/core/jni/android_opengl_GLES31Ext.cpp @@ -28,8 +28,6 @@ #include <utils/misc.h> #include <assert.h> -static int initialized = 0; - static jclass nioAccessClass; static jclass bufferClass; static jmethodID getBasePointerID; diff --git a/core/jni/android_opengl_GLES32.cpp b/core/jni/android_opengl_GLES32.cpp index 2f1e31e7fab9..d59d25c2c483 100644 --- a/core/jni/android_opengl_GLES32.cpp +++ b/core/jni/android_opengl_GLES32.cpp @@ -27,8 +27,6 @@ #include <utils/misc.h> #include <assert.h> -static int initialized = 0; - static jclass nioAccessClass; static jclass bufferClass; static jmethodID getBasePointerID; diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp index 0ccc327f6d97..72e3d3495e37 100644 --- a/core/jni/android_os_GraphicsEnvironment.cpp +++ b/core/jni/android_os_GraphicsEnvironment.cpp @@ -27,22 +27,23 @@ int getCanLoadSystemLibraries_native() { return android::GraphicsEnv::getInstance().getCanLoadSystemLibraries(); } -void setDriverPath(JNIEnv* env, jobject clazz, jstring path) { +void setDriverPathAndSphalLibraries_native(JNIEnv* env, jobject clazz, jstring path, + jstring sphalLibraries) { ScopedUtfChars pathChars(env, path); - android::GraphicsEnv::getInstance().setDriverPath(pathChars.c_str()); + ScopedUtfChars sphalLibrariesChars(env, sphalLibraries); + android::GraphicsEnv::getInstance().setDriverPathAndSphalLibraries(pathChars.c_str(), + sphalLibrariesChars.c_str()); } void setGpuStats_native(JNIEnv* env, jobject clazz, jstring driverPackageName, jstring driverVersionName, jlong driverVersionCode, - jstring driverBuildDate, jstring appPackageName) { + jlong driverBuildTime, jstring appPackageName) { ScopedUtfChars driverPackageNameChars(env, driverPackageName); ScopedUtfChars driverVersionNameChars(env, driverVersionName); - ScopedUtfChars driverBuildDateChars(env, driverBuildDate); ScopedUtfChars appPackageNameChars(env, appPackageName); android::GraphicsEnv::getInstance().setGpuStats(driverPackageNameChars.c_str(), driverVersionNameChars.c_str(), - driverVersionCode, - driverBuildDateChars.c_str(), + driverVersionCode, driverBuildTime, appPackageNameChars.c_str()); } @@ -86,8 +87,8 @@ void setDebugLayersGLES_native(JNIEnv* env, jobject clazz, jstring layers) { const JNINativeMethod g_methods[] = { { "getCanLoadSystemLibraries", "()I", reinterpret_cast<void*>(getCanLoadSystemLibraries_native) }, - { "setDriverPath", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDriverPath) }, - { "setGpuStats", "(Ljava/lang/String;Ljava/lang/String;JLjava/lang/String;Ljava/lang/String;)V", reinterpret_cast<void*>(setGpuStats_native) }, + { "setDriverPathAndSphalLibraries", "(Ljava/lang/String;Ljava/lang/String;)V", reinterpret_cast<void*>(setDriverPathAndSphalLibraries_native) }, + { "setGpuStats", "(Ljava/lang/String;Ljava/lang/String;JJLjava/lang/String;)V", reinterpret_cast<void*>(setGpuStats_native) }, { "setAngleInfo", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/io/FileDescriptor;JJ)V", reinterpret_cast<void*>(setAngleInfo_native) }, { "getShouldUseAngle", "(Ljava/lang/String;)Z", reinterpret_cast<void*>(shouldUseAngle_native) }, { "setLayerPaths", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V", reinterpret_cast<void*>(setLayerPaths_native) }, diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index a212f47c0104..af2d41399016 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -343,7 +343,9 @@ static jobject NativeGetOverlayableMap(JNIEnv* env, jclass /*clazz*/, jlong ptr, assetmanager->ForEachPackage([&](const std::string& this_package_name, uint8_t package_id) { if (this_package_name == std_package_name) { map = assetmanager->GetOverlayableMapForPackage(package_id); + return false; } + return true; }); if (map == nullptr) { @@ -521,15 +523,16 @@ static jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/ return nullptr; } - assetmanager->ForEachPackage([&](const std::string& package_name, uint8_t package_id) { + assetmanager->ForEachPackage([&](const std::string& package_name, uint8_t package_id) -> bool { jstring jpackage_name = env->NewStringUTF(package_name.c_str()); if (jpackage_name == nullptr) { // An exception is pending. - return; + return false; } env->CallVoidMethod(sparse_array, gSparseArrayOffsets.put, static_cast<jint>(package_id), jpackage_name); + return true; }); return sparse_array; } diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp index 191472d086f6..8d702d11d8fe 100644 --- a/core/jni/android_view_DisplayEventReceiver.cpp +++ b/core/jni/android_view_DisplayEventReceiver.cpp @@ -41,6 +41,7 @@ static struct { jmethodID dispatchVsync; jmethodID dispatchHotplug; + jmethodID dispatchConfigChanged; } gDisplayEventReceiverClassInfo; @@ -61,6 +62,8 @@ private: void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) override; void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override; + void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId, + int32_t configId) override; }; @@ -114,6 +117,23 @@ void NativeDisplayEventReceiver::dispatchHotplug(nsecs_t timestamp, PhysicalDisp mMessageQueue->raiseAndClearException(env, "dispatchHotplug"); } +void NativeDisplayEventReceiver::dispatchConfigChanged(nsecs_t timestamp, + PhysicalDisplayId displayId, + int32_t configId) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + + ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal)); + if (receiverObj.get()) { + ALOGV("receiver %p ~ Invoking config changed handler.", this); + env->CallVoidMethod(receiverObj.get(), + gDisplayEventReceiverClassInfo.dispatchConfigChanged, + timestamp, displayId, configId); + ALOGV("receiver %p ~ Returned from config changed handler.", this); + } + + mMessageQueue->raiseAndClearException(env, "dispatchConfigChanged"); +} + static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject messageQueueObj, jint vsyncSource) { @@ -180,6 +200,8 @@ int register_android_view_DisplayEventReceiver(JNIEnv* env) { gDisplayEventReceiverClassInfo.clazz, "dispatchVsync", "(JJI)V"); gDisplayEventReceiverClassInfo.dispatchHotplug = GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", "(JJZ)V"); + gDisplayEventReceiverClassInfo.dispatchConfigChanged = GetMethodIDOrDie(env, + gDisplayEventReceiverClassInfo.clazz, "dispatchConfigChanged", "(JJI)V"); return res; } diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp index 464f24901eb1..a9002945ff91 100644 --- a/core/jni/android_view_Surface.cpp +++ b/core/jni/android_view_Surface.cpp @@ -58,8 +58,6 @@ namespace android { -using ui::Dataspace; - static const char* const OutOfResourcesException = "android/view/Surface$OutOfResourcesException"; @@ -129,135 +127,17 @@ jobject android_view_Surface_createFromIGraphicBufferProducer(JNIEnv* env, } int android_view_Surface_mapPublicFormatToHalFormat(PublicFormat f) { - - switch(f) { - case PublicFormat::JPEG: - case PublicFormat::DEPTH_POINT_CLOUD: - case PublicFormat::DEPTH_JPEG: - case PublicFormat::HEIC: - return HAL_PIXEL_FORMAT_BLOB; - case PublicFormat::DEPTH16: - return HAL_PIXEL_FORMAT_Y16; - case PublicFormat::RAW_SENSOR: - case PublicFormat::RAW_DEPTH: - return HAL_PIXEL_FORMAT_RAW16; - default: - // Most formats map 1:1 - return static_cast<int>(f); - } + return mapPublicFormatToHalFormat(f); } android_dataspace android_view_Surface_mapPublicFormatToHalDataspace( PublicFormat f) { - Dataspace dataspace; - switch(f) { - case PublicFormat::JPEG: - dataspace = Dataspace::V0_JFIF; - break; - case PublicFormat::DEPTH_POINT_CLOUD: - case PublicFormat::DEPTH16: - case PublicFormat::RAW_DEPTH: - dataspace = Dataspace::DEPTH; - break; - case PublicFormat::RAW_SENSOR: - case PublicFormat::RAW_PRIVATE: - case PublicFormat::RAW10: - case PublicFormat::RAW12: - dataspace = Dataspace::ARBITRARY; - break; - case PublicFormat::YUV_420_888: - case PublicFormat::NV21: - case PublicFormat::YV12: - dataspace = Dataspace::V0_JFIF; - break; - case PublicFormat::DEPTH_JPEG: - dataspace = Dataspace::DYNAMIC_DEPTH; - break; - case PublicFormat::HEIC: - dataspace = Dataspace::HEIF; - break; - default: - // Most formats map to UNKNOWN - dataspace = Dataspace::UNKNOWN; - break; - } - return static_cast<android_dataspace>(dataspace); + return mapPublicFormatToHalDataspace(f); } PublicFormat android_view_Surface_mapHalFormatDataspaceToPublicFormat( int format, android_dataspace dataSpace) { - Dataspace ds = static_cast<Dataspace>(dataSpace); - switch(format) { - case HAL_PIXEL_FORMAT_RGBA_8888: - case HAL_PIXEL_FORMAT_RGBX_8888: - case HAL_PIXEL_FORMAT_RGBA_FP16: - case HAL_PIXEL_FORMAT_RGBA_1010102: - case HAL_PIXEL_FORMAT_RGB_888: - case HAL_PIXEL_FORMAT_RGB_565: - case HAL_PIXEL_FORMAT_Y8: - case HAL_PIXEL_FORMAT_RAW10: - case HAL_PIXEL_FORMAT_RAW12: - case HAL_PIXEL_FORMAT_YCbCr_420_888: - case HAL_PIXEL_FORMAT_YV12: - // Enums overlap in both name and value - return static_cast<PublicFormat>(format); - case HAL_PIXEL_FORMAT_RAW16: - switch (ds) { - case Dataspace::DEPTH: - return PublicFormat::RAW_DEPTH; - default: - return PublicFormat::RAW_SENSOR; - } - case HAL_PIXEL_FORMAT_RAW_OPAQUE: - // Name differs, though value is the same - return PublicFormat::RAW_PRIVATE; - case HAL_PIXEL_FORMAT_YCbCr_422_SP: - // Name differs, though the value is the same - return PublicFormat::NV16; - case HAL_PIXEL_FORMAT_YCrCb_420_SP: - // Name differs, though the value is the same - return PublicFormat::NV21; - case HAL_PIXEL_FORMAT_YCbCr_422_I: - // Name differs, though the value is the same - return PublicFormat::YUY2; - case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED: - // Name differs, though the value is the same - return PublicFormat::PRIVATE; - case HAL_PIXEL_FORMAT_Y16: - // Dataspace-dependent - switch (ds) { - case Dataspace::DEPTH: - return PublicFormat::DEPTH16; - default: - // Assume non-depth Y16 is just Y16. - return PublicFormat::Y16; - } - break; - case HAL_PIXEL_FORMAT_BLOB: - // Dataspace-dependent - switch (ds) { - case Dataspace::DEPTH: - return PublicFormat::DEPTH_POINT_CLOUD; - case Dataspace::V0_JFIF: - return PublicFormat::JPEG; - case Dataspace::HEIF: - return PublicFormat::HEIC; - default: - if (dataSpace == static_cast<android_dataspace>(HAL_DATASPACE_DYNAMIC_DEPTH)) { - return PublicFormat::DEPTH_JPEG; - } else { - // Assume otherwise-marked blobs are also JPEG - return PublicFormat::JPEG; - } - } - break; - case HAL_PIXEL_FORMAT_BGRA_8888: - // Not defined in public API - return PublicFormat::UNKNOWN; - - default: - return PublicFormat::UNKNOWN; - } + return mapHalFormatDataspaceToPublicFormat(format, dataSpace); } // ---------------------------------------------------------------------------- diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 6b8d8b1bb91f..af2bf2d40146 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -205,7 +205,7 @@ static Rect rectFromObj(JNIEnv* env, jobject rectObj) { static jobject nativeScreenshot(JNIEnv* env, jclass clazz, jobject displayTokenObj, jobject sourceCropObj, jint width, jint height, - bool useIdentityTransform, int rotation) { + bool useIdentityTransform, int rotation, bool captureSecureLayers) { sp<IBinder> displayToken = ibinderForJavaObject(env, displayTokenObj); if (displayToken == NULL) { return NULL; @@ -213,9 +213,9 @@ static jobject nativeScreenshot(JNIEnv* env, jclass clazz, Rect sourceCrop = rectFromObj(env, sourceCropObj); sp<GraphicBuffer> buffer; status_t res = ScreenshotClient::capture(displayToken, ui::Dataspace::V0_SRGB, - ui::PixelFormat::RGBA_8888, - sourceCrop, width, height, - useIdentityTransform, rotation, &buffer); + ui::PixelFormat::RGBA_8888, + sourceCrop, width, height, + useIdentityTransform, rotation, captureSecureLayers, &buffer); if (res != NO_ERROR) { return NULL; } @@ -406,6 +406,11 @@ static void nativeTransferTouchFocus(JNIEnv* env, jclass clazz, jlong transactio transaction->transferTouchFocus(fromToken, toToken); } +static void nativeSyncInputWindows(JNIEnv* env, jclass clazz, jlong transactionObj) { + auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj); + transaction->syncInputWindows(); +} + static void nativeSetMetadata(JNIEnv* env, jclass clazz, jlong transactionObj, jlong nativeObject, jint id, jobject parcelObj) { Parcel* parcel = parcelForJavaObject(env, parcelObj); @@ -693,6 +698,48 @@ static jobjectArray nativeGetDisplayConfigs(JNIEnv* env, jclass clazz, return configArray; } +static jboolean nativeSetAllowedDisplayConfigs(JNIEnv* env, jclass clazz, + jobject tokenObj, jintArray configArray) { + sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); + if (token == nullptr) return JNI_FALSE; + + std::vector<int32_t> allowedConfigs; + jsize configArraySize = env->GetArrayLength(configArray); + allowedConfigs.reserve(configArraySize); + + jint* configArrayElements = env->GetIntArrayElements(configArray, 0); + for (int i = 0; i < configArraySize; i++) { + allowedConfigs.push_back(configArrayElements[i]); + } + env->ReleaseIntArrayElements(configArray, configArrayElements, 0); + + size_t result = SurfaceComposerClient::setAllowedDisplayConfigs(token, allowedConfigs); + return result == NO_ERROR ? JNI_TRUE : JNI_FALSE; +} + +static jintArray nativeGetAllowedDisplayConfigs(JNIEnv* env, jclass clazz, jobject tokenObj) { + sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); + if (token == nullptr) return JNI_FALSE; + + std::vector<int32_t> allowedConfigs; + size_t result = SurfaceComposerClient::getAllowedDisplayConfigs(token, &allowedConfigs); + if (result != NO_ERROR) { + return nullptr; + } + + jintArray allowedConfigsArray = env->NewIntArray(allowedConfigs.size()); + if (allowedConfigsArray == nullptr) { + jniThrowException(env, "java/lang/OutOfMemoryError", NULL); + return nullptr; + } + jint* allowedConfigsArrayValues = env->GetIntArrayElements(allowedConfigsArray, 0); + for (size_t i = 0; i < allowedConfigs.size(); i++) { + allowedConfigsArrayValues[i] = static_cast<jint>(allowedConfigs[i]); + } + env->ReleaseIntArrayElements(allowedConfigsArray, allowedConfigsArrayValues, 0); + return allowedConfigsArray; +} + static jint nativeGetActiveConfig(JNIEnv* env, jclass clazz, jobject tokenObj) { sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); if (token == NULL) return -1; @@ -1189,6 +1236,10 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeGetActiveConfig }, {"nativeSetActiveConfig", "(Landroid/os/IBinder;I)Z", (void*)nativeSetActiveConfig }, + {"nativeSetAllowedDisplayConfigs", "(Landroid/os/IBinder;[I)Z", + (void*)nativeSetAllowedDisplayConfigs }, + {"nativeGetAllowedDisplayConfigs", "(Landroid/os/IBinder;)[I", + (void*)nativeGetAllowedDisplayConfigs }, {"nativeGetDisplayColorModes", "(Landroid/os/IBinder;)[I", (void*)nativeGetDisplayColorModes}, {"nativeGetDisplayNativePrimaries", "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DisplayPrimaries;", @@ -1227,7 +1278,7 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeSetOverrideScalingMode }, {"nativeGetHandle", "(J)Landroid/os/IBinder;", (void*)nativeGetHandle }, - {"nativeScreenshot", "(Landroid/os/IBinder;Landroid/graphics/Rect;IIZI)Landroid/graphics/GraphicBuffer;", + {"nativeScreenshot", "(Landroid/os/IBinder;Landroid/graphics/Rect;IIZIZ)Landroid/graphics/GraphicBuffer;", (void*)nativeScreenshot }, {"nativeCaptureLayers", "(Landroid/os/IBinder;Landroid/graphics/Rect;F)Landroid/graphics/GraphicBuffer;", (void*)nativeCaptureLayers }, @@ -1246,7 +1297,9 @@ static const JNINativeMethod sSurfaceControlMethods[] = { "(Landroid/os/IBinder;JJ)Landroid/hardware/display/DisplayedContentSample;", (void*)nativeGetDisplayedContentSample }, {"nativeSetGeometry", "(JJLandroid/graphics/Rect;Landroid/graphics/Rect;J)V", - (void*)nativeSetGeometry } + (void*)nativeSetGeometry }, + {"nativeSyncInputWindows", "(J)V", + (void*)nativeSyncInputWindows } }; int register_android_view_SurfaceControl(JNIEnv* env) diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 15ceca96313e..2ccb01adfd3e 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -155,10 +155,10 @@ static std::atomic_uint32_t gBlastulaPoolCount = 0; static int gBlastulaPoolEventFD = -1; /** - * The maximum value that the gBlastulaPoolMax variable may take. This value - * is a mirror of Zygote.BLASTULA_POOL_MAX_LIMIT + * The maximum value that the gBlastulaPoolSizeMax variable may take. This value + * is a mirror of ZygoteServer.BLASTULA_POOL_SIZE_MAX_LIMIT */ -static constexpr int BLASTULA_POOL_MAX_LIMIT = 10; +static constexpr int BLASTULA_POOL_SIZE_MAX_LIMIT = 100; /** * A helper class containing accounting information for Blastulas. @@ -216,6 +216,19 @@ class BlastulaTableEntry { } } + void Clear() { + EntryStorage storage = mStorage.load(); + + if (storage != INVALID_ENTRY_VALUE) { + close(storage.read_pipe_fd); + mStorage.store(INVALID_ENTRY_VALUE); + } + } + + void Invalidate() { + mStorage.store(INVALID_ENTRY_VALUE); + } + /** * @return A copy of the data stored in this entry. */ @@ -257,7 +270,7 @@ class BlastulaTableEntry { * the BlastulaTableEntry class prevent data races during these concurrent * operations. */ -static std::array<BlastulaTableEntry, BLASTULA_POOL_MAX_LIMIT> gBlastulaTable; +static std::array<BlastulaTableEntry, BLASTULA_POOL_SIZE_MAX_LIMIT> gBlastulaTable; /** * The list of open zygote file descriptors. @@ -572,214 +585,255 @@ static void SetSchedulerPolicy(fail_fn_t fail_fn) { } static int UnmountTree(const char* path) { - size_t path_len = strlen(path); + size_t path_len = strlen(path); - FILE* fp = setmntent("/proc/mounts", "r"); - if (fp == nullptr) { - ALOGE("Error opening /proc/mounts: %s", strerror(errno)); - return -errno; - } + FILE* fp = setmntent("/proc/mounts", "r"); + if (fp == nullptr) { + ALOGE("Error opening /proc/mounts: %s", strerror(errno)); + return -errno; + } - // Some volumes can be stacked on each other, so force unmount in - // reverse order to give us the best chance of success. - std::list<std::string> toUnmount; - mntent* mentry; - while ((mentry = getmntent(fp)) != nullptr) { - if (strncmp(mentry->mnt_dir, path, path_len) == 0) { - toUnmount.push_front(std::string(mentry->mnt_dir)); - } + // Some volumes can be stacked on each other, so force unmount in + // reverse order to give us the best chance of success. + std::list<std::string> to_unmount; + mntent* mentry; + while ((mentry = getmntent(fp)) != nullptr) { + if (strncmp(mentry->mnt_dir, path, path_len) == 0) { + to_unmount.push_front(std::string(mentry->mnt_dir)); } - endmntent(fp); + } + endmntent(fp); - for (const auto& path : toUnmount) { - if (umount2(path.c_str(), MNT_DETACH)) { - ALOGW("Failed to unmount %s: %s", path.c_str(), strerror(errno)); - } + for (const auto& path : to_unmount) { + if (umount2(path.c_str(), MNT_DETACH)) { + ALOGW("Failed to unmount %s: %s", path.c_str(), strerror(errno)); } - return 0; + } + return 0; } static void CreateDir(const std::string& dir, mode_t mode, uid_t uid, gid_t gid, fail_fn_t fail_fn) { - if (TEMP_FAILURE_RETRY(access(dir.c_str(), F_OK)) == 0) { - return; - } else if (errno != ENOENT) { - fail_fn(CREATE_ERROR("Failed to stat %s: %s", dir.c_str(), strerror(errno))); - } - if (fs_prepare_dir(dir.c_str(), mode, uid, gid) != 0) { - fail_fn(CREATE_ERROR("fs_prepare_dir failed on %s: %s", - dir.c_str(), strerror(errno))); - } + if (TEMP_FAILURE_RETRY(access(dir.c_str(), F_OK)) == 0) { + return; + } else if (errno != ENOENT) { + fail_fn(CREATE_ERROR("Failed to stat %s: %s", dir.c_str(), strerror(errno))); + } + if (fs_prepare_dir(dir.c_str(), mode, uid, gid) != 0) { + fail_fn(CREATE_ERROR("fs_prepare_dir failed on %s: %s", + dir.c_str(), strerror(errno))); + } } -static void CreatePkgSandboxTarget(uid_t uid, const std::string& package_name, fail_fn_t fail_fn) { - // Create /mnt/user/0/package/<package-name> - userid_t user_id = multiuser_get_user_id(uid); - std::string pkg_sandbox_dir = StringPrintf("/mnt/user/%d", user_id); - CreateDir(pkg_sandbox_dir, 0751, AID_ROOT, AID_ROOT, fail_fn); - - StringAppendF(&pkg_sandbox_dir, "/package"); - CreateDir(pkg_sandbox_dir, 0700, AID_ROOT, AID_ROOT, fail_fn); +static void CreatePkgSandboxTarget(userid_t user_id, fail_fn_t fail_fn) { + // Create /mnt/user/0/package + std::string pkg_sandbox_dir = StringPrintf("/mnt/user/%d", user_id); + CreateDir(pkg_sandbox_dir, 0751, AID_ROOT, AID_ROOT, fail_fn); - StringAppendF(&pkg_sandbox_dir, "/%s", package_name.c_str()); - CreateDir(pkg_sandbox_dir, 0755, uid, uid, fail_fn); + StringAppendF(&pkg_sandbox_dir, "/package"); + CreateDir(pkg_sandbox_dir, 0755, AID_ROOT, AID_ROOT, fail_fn); } -static void BindMount(const std::string& sourceDir, const std::string& targetDir, +static void BindMount(const std::string& source_dir, const std::string& target_dir, fail_fn_t fail_fn) { - if (TEMP_FAILURE_RETRY(mount(sourceDir.c_str(), targetDir.c_str(), nullptr, - MS_BIND, nullptr)) == -1) { - fail_fn(CREATE_ERROR("Failed to mount %s to %s: %s", - sourceDir.c_str(), targetDir.c_str(), strerror(errno))); - } + if (TEMP_FAILURE_RETRY(mount(source_dir.c_str(), target_dir.c_str(), nullptr, + MS_BIND, nullptr)) == -1) { + fail_fn(CREATE_ERROR("Failed to mount %s to %s: %s", + source_dir.c_str(), target_dir.c_str(), strerror(errno))); + } } -static void MountPkgSpecificDir(const std::string& mntSourceRoot, - const std::string& mntTargetRoot, - const std::string& packageName, +static void MountPkgSpecificDir(const std::string& mnt_source_root, + const std::string& mnt_target_root, + const std::string& package_name, uid_t uid, - const char* dirName, + const char* dir_name, fail_fn_t fail_fn) { - std::string mntSourceDir = StringPrintf("%s/Android/%s/%s", - mntSourceRoot.c_str(), dirName, packageName.c_str()); + std::string mnt_source_dir = StringPrintf("%s/Android/%s/%s", + mnt_source_root.c_str(), dir_name, package_name.c_str()); - std::string mntTargetDir = StringPrintf("%s/Android/%s/%s", - mntTargetRoot.c_str(), dirName, packageName.c_str()); + std::string mnt_target_dir = StringPrintf("%s/Android/%s/%s", + mnt_target_root.c_str(), dir_name, package_name.c_str()); - BindMount(mntSourceDir, mntTargetDir, fail_fn); + BindMount(mnt_source_dir, mnt_target_dir, fail_fn); } -static void CreateSubDirs(int dirfd, const std::string& parentDirPath, - const std::vector<std::string>& subDirs, +static void CreateSubDirs(int parent_fd, const std::string& parent_path, + const std::vector<std::string>& sub_dirs, fail_fn_t fail_fn) { - for (auto& dirName : subDirs) { - struct stat sb; - if (TEMP_FAILURE_RETRY(fstatat(dirfd, dirName.c_str(), &sb, 0)) == 0) { - if (S_ISDIR(sb.st_mode)) { - continue; - } else if (TEMP_FAILURE_RETRY(unlinkat(dirfd, dirName.c_str(), 0)) == -1) { - fail_fn(CREATE_ERROR("Failed to unlinkat on %s/%s: %s", - parentDirPath.c_str(), dirName.c_str(), strerror(errno))); - } - } else if (errno != ENOENT) { - fail_fn(CREATE_ERROR("Failed to fstatat on %s/%s: %s", - parentDirPath.c_str(), dirName.c_str(), strerror(errno))); - } - if (TEMP_FAILURE_RETRY(mkdirat(dirfd, dirName.c_str(), 0700)) == -1 && errno != EEXIST) { - fail_fn(CREATE_ERROR("Failed to mkdirat on %s/%s: %s", - parentDirPath.c_str(), dirName.c_str(), strerror(errno))); - } + for (auto& dir_name : sub_dirs) { + struct stat sb; + if (TEMP_FAILURE_RETRY(fstatat(parent_fd, dir_name.c_str(), &sb, 0)) == 0) { + if (S_ISDIR(sb.st_mode)) { + continue; + } else if (TEMP_FAILURE_RETRY(unlinkat(parent_fd, dir_name.c_str(), 0)) == -1) { + fail_fn(CREATE_ERROR("Failed to unlinkat on %s/%s: %s", + parent_path.c_str(), dir_name.c_str(), strerror(errno))); + } + } else if (errno != ENOENT) { + fail_fn(CREATE_ERROR("Failed to fstatat on %s/%s: %s", + parent_path.c_str(), dir_name.c_str(), strerror(errno))); + } + if (TEMP_FAILURE_RETRY(mkdirat(parent_fd, dir_name.c_str(), 0700)) == -1 && errno != EEXIST) { + fail_fn(CREATE_ERROR("Failed to mkdirat on %s/%s: %s", + parent_path.c_str(), dir_name.c_str(), strerror(errno))); } + } } static void EnsurePkgSpecificDirs(const std::string& path, - const std::vector<std::string>& packageNames, - bool createSandboxDir, + const std::vector<std::string>& package_names, + bool create_sandbox_dir, fail_fn_t fail_fn) { - std::string androidDir = StringPrintf("%s/Android", path.c_str()); - android::base::unique_fd androidFd( - open(androidDir.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC)); - if (androidFd.get() < 0) { - if (errno == ENOENT || errno == ENOTDIR) { - if (errno == ENOTDIR && TEMP_FAILURE_RETRY(unlink(androidDir.c_str())) == -1) { - fail_fn(CREATE_ERROR("Failed to unlink %s: %s", - androidDir.c_str(), strerror(errno))); - } - if (TEMP_FAILURE_RETRY(mkdir(androidDir.c_str(), 0700)) == -1 - && errno != EEXIST) { - fail_fn(CREATE_ERROR("Failed to mkdir %s: %s", - androidDir.c_str(), strerror(errno))); - } - androidFd.reset(open(androidDir.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC)); - } - - if (androidFd.get() < 0) { - fail_fn(CREATE_ERROR("Failed to open %s: %s", androidDir.c_str(), strerror(errno))); - } + std::string android_dir = StringPrintf("%s/Android", path.c_str()); + android::base::unique_fd android_fd(open(android_dir.c_str(), + O_RDONLY | O_DIRECTORY | O_CLOEXEC)); + if (android_fd.get() < 0) { + if (errno == ENOENT || errno == ENOTDIR) { + if (errno == ENOTDIR && TEMP_FAILURE_RETRY(unlink(android_dir.c_str())) == -1) { + fail_fn(CREATE_ERROR("Failed to unlink %s: %s", + android_dir.c_str(), strerror(errno))); + } + if (TEMP_FAILURE_RETRY(mkdir(android_dir.c_str(), 0700)) == -1 + && errno != EEXIST) { + fail_fn(CREATE_ERROR("Failed to mkdir %s: %s", + android_dir.c_str(), strerror(errno))); + } + android_fd.reset(open(android_dir.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC)); } - std::vector<std::string> dataMediaObbDirs = {"data", "media", "obb"}; - if (createSandboxDir) { - dataMediaObbDirs.push_back("sandbox"); + if (android_fd.get() < 0) { + fail_fn(CREATE_ERROR("Failed to open %s: %s", android_dir.c_str(), strerror(errno))); } - CreateSubDirs(androidFd.get(), androidDir, dataMediaObbDirs, fail_fn); - if (createSandboxDir) { - dataMediaObbDirs.pop_back(); - } - for (auto& dirName : dataMediaObbDirs) { - std::string dataDir = StringPrintf("%s/%s", androidDir.c_str(), dirName.c_str()); - android::base::unique_fd dataFd( - openat(androidFd, dirName.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC)); - if (dataFd.get() < 0) { - fail_fn(CREATE_ERROR("Failed to openat %s/%s: %s", - androidDir.c_str(), dirName.c_str(), strerror(errno))); - } - CreateSubDirs(dataFd.get(), dataDir, packageNames, fail_fn); + } + + std::vector<std::string> data_media_obb_dirs = {"data", "media", "obb"}; + if (create_sandbox_dir) { + data_media_obb_dirs.push_back("sandbox"); + } + CreateSubDirs(android_fd.get(), android_dir, data_media_obb_dirs, fail_fn); + if (create_sandbox_dir) { + data_media_obb_dirs.pop_back(); + } + for (auto& dir_name : data_media_obb_dirs) { + std::string data_dir = StringPrintf("%s/%s", android_dir.c_str(), dir_name.c_str()); + android::base::unique_fd data_fd(openat(android_fd, dir_name.c_str(), + O_RDONLY | O_DIRECTORY | O_CLOEXEC)); + if (data_fd.get() < 0) { + fail_fn(CREATE_ERROR("Failed to openat %s/%s: %s", + android_dir.c_str(), dir_name.c_str(), strerror(errno))); } + CreateSubDirs(data_fd.get(), data_dir, package_names, fail_fn); + } } -static void CreatePkgSandboxSource(const std::string& sandboxSource, fail_fn_t fail_fn) { +static void CreatePkgSandboxSource(const std::string& sandbox_source, fail_fn_t fail_fn) { - struct stat sb; - if (TEMP_FAILURE_RETRY(stat(sandboxSource.c_str(), &sb)) == 0) { - if (S_ISDIR(sb.st_mode)) { - return; - } else if (TEMP_FAILURE_RETRY(unlink(sandboxSource.c_str())) == -1) { - fail_fn(CREATE_ERROR("Failed to unlink %s: %s", - sandboxSource.c_str(), strerror(errno))); - } - } else if (errno != ENOENT) { - fail_fn(CREATE_ERROR("Failed to stat %s: %s", - sandboxSource.c_str(), strerror(errno))); - } - if (TEMP_FAILURE_RETRY(mkdir(sandboxSource.c_str(), 0700)) == -1 && errno != EEXIST) { - fail_fn(CREATE_ERROR("Failed to mkdir %s: %s", - sandboxSource.c_str(), strerror(errno))); + struct stat sb; + if (TEMP_FAILURE_RETRY(stat(sandbox_source.c_str(), &sb)) == 0) { + if (S_ISDIR(sb.st_mode)) { + return; + } else if (TEMP_FAILURE_RETRY(unlink(sandbox_source.c_str())) == -1) { + fail_fn(CREATE_ERROR("Failed to unlink %s: %s", + sandbox_source.c_str(), strerror(errno))); } + } else if (errno != ENOENT) { + fail_fn(CREATE_ERROR("Failed to stat %s: %s", + sandbox_source.c_str(), strerror(errno))); + } + if (TEMP_FAILURE_RETRY(mkdir(sandbox_source.c_str(), 0700)) == -1 && errno != EEXIST) { + fail_fn(CREATE_ERROR("Failed to mkdir %s: %s", + sandbox_source.c_str(), strerror(errno))); + } } -static void PreparePkgSpecificDirs(const std::vector<std::string>& packageNames, - const std::vector<std::string>& volumeLabels, - bool mountAllObbs, const std::string& sandboxId, - userid_t userId, uid_t uid, fail_fn_t fail_fn) { - for (auto& label : volumeLabels) { - std::string mntSource = StringPrintf("/mnt/runtime/write/%s", label.c_str()); - std::string mntTarget = StringPrintf("/storage/%s", label.c_str()); - if (label == "emulated") { - StringAppendF(&mntSource, "/%d", userId); - StringAppendF(&mntTarget, "/%d", userId); - } +static void PreparePkgSpecificDirs(const std::vector<std::string>& package_names, + bool mount_all_obbs, const std::string& sandbox_id, + userid_t user_id, uid_t uid, fail_fn_t fail_fn) { + std::unique_ptr<DIR, decltype(&closedir)> dirp(opendir("/storage"), closedir); + if (!dirp) { + fail_fn(CREATE_ERROR("Failed to opendir /storage: %s", strerror(errno))); + } + struct dirent* ent; + while ((ent = readdir(dirp.get()))) { + if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..") || !strcmp(ent->d_name, "self")) { + continue; + } + std::string label(ent->d_name); - if (TEMP_FAILURE_RETRY(access(mntSource.c_str(), F_OK)) == -1) { - ALOGE("Can't access %s: %s", mntSource.c_str(), strerror(errno)); - continue; - } + std::string mnt_source = StringPrintf("/mnt/runtime/write/%s", label.c_str()); + std::string mnt_target = StringPrintf("/storage/%s", label.c_str()); + if (label == "emulated") { + StringAppendF(&mnt_source, "/%d", user_id); + StringAppendF(&mnt_target, "/%d", user_id); + } - // Ensure /mnt/runtime/write/emulated/0/Android/{data,media,obb} - EnsurePkgSpecificDirs(mntSource, packageNames, true, fail_fn); - - std::string sandboxSource = StringPrintf("%s/Android/sandbox/%s", - mntSource.c_str(), sandboxId.c_str()); - CreatePkgSandboxSource(sandboxSource, fail_fn); - BindMount(sandboxSource, mntTarget, fail_fn); - - // Ensure /storage/emulated/0/Android/{data,media,obb} - EnsurePkgSpecificDirs(mntTarget, packageNames, false, fail_fn); - for (auto& package : packageNames) { - MountPkgSpecificDir(mntSource, mntTarget, package, uid, "data", fail_fn); - MountPkgSpecificDir(mntSource, mntTarget, package, uid, "media", fail_fn); - if (!mountAllObbs) { - MountPkgSpecificDir(mntSource, mntTarget, package, uid, "obb", fail_fn); - } - } + if (TEMP_FAILURE_RETRY(access(mnt_source.c_str(), F_OK)) == -1) { + ALOGE("Can't access %s: %s", mnt_source.c_str(), strerror(errno)); + continue; + } else if (TEMP_FAILURE_RETRY(access(mnt_target.c_str(), F_OK)) == -1) { + ALOGE("Can't access %s: %s", mnt_target.c_str(), strerror(errno)); + continue; + } - if (mountAllObbs) { - StringAppendF(&mntSource, "/Android/obb"); - StringAppendF(&mntTarget, "/Android/obb"); - BindMount(mntSource, mntTarget, fail_fn); - } + // Ensure /mnt/runtime/write/emulated/0/Android/{data,media,obb} + EnsurePkgSpecificDirs(mnt_source, package_names, true, fail_fn); + + std::string sandbox_source = StringPrintf("%s/Android/sandbox/%s", + mnt_source.c_str(), sandbox_id.c_str()); + CreatePkgSandboxSource(sandbox_source, fail_fn); + BindMount(sandbox_source, mnt_target, fail_fn); + + // Ensure /storage/emulated/0/Android/{data,media,obb} + EnsurePkgSpecificDirs(mnt_target, package_names, false, fail_fn); + for (auto& package : package_names) { + MountPkgSpecificDir(mnt_source, mnt_target, package, uid, "data", fail_fn); + MountPkgSpecificDir(mnt_source, mnt_target, package, uid, "media", fail_fn); + if (!mount_all_obbs) { + MountPkgSpecificDir(mnt_source, mnt_target, package, uid, "obb", fail_fn); + } } + + if (mount_all_obbs) { + StringAppendF(&mnt_source, "/Android/obb"); + StringAppendF(&mnt_target, "/Android/obb"); + BindMount(mnt_source, mnt_target, fail_fn); + } + } +} + +static void HandleMountModeInstaller(int mount_mode, + userid_t user_id, + const std::string& sandbox_id, + fail_fn_t fail_fn) { + std::string obb_mount_dir = StringPrintf("/mnt/user/%d/obb_mount", user_id); + std::string obb_mount_file = StringPrintf("%s/%s", obb_mount_dir.c_str(), sandbox_id.c_str()); + if (mount_mode == MOUNT_EXTERNAL_INSTALLER) { + if (TEMP_FAILURE_RETRY(access(obb_mount_file.c_str(), F_OK)) != -1) { + return; + } else if (errno != ENOENT) { + fail_fn(CREATE_ERROR("Failed to access %s: %s", obb_mount_file.c_str(), strerror(errno))); + } + if (fs_prepare_dir(obb_mount_dir.c_str(), 0700, AID_ROOT, AID_ROOT) != 0) { + fail_fn(CREATE_ERROR("Failed to fs_prepare_dir %s: %s", + obb_mount_dir.c_str(), strerror(errno))); + } + const android::base::unique_fd fd(TEMP_FAILURE_RETRY( + open(obb_mount_file.c_str(), O_RDWR | O_CREAT, 0600))); + if (fd.get() < 0) { + fail_fn(CREATE_ERROR("Failed to create %s: %s", obb_mount_file.c_str(), strerror(errno))); + } + } else { + if (TEMP_FAILURE_RETRY(access(obb_mount_file.c_str(), F_OK)) != -1) { + if (TEMP_FAILURE_RETRY(unlink(obb_mount_file.c_str())) == -1) { + fail_fn(CREATE_ERROR("Failed to unlink %s: %s", + obb_mount_dir.c_str(), strerror(errno))); + } + } else if (errno != ENOENT) { + fail_fn(CREATE_ERROR("Failed to access %s: %s", obb_mount_file.c_str(), strerror(errno))); + } + } } // Create a private mount namespace and bind mount appropriate emulated @@ -787,128 +841,99 @@ static void PreparePkgSpecificDirs(const std::vector<std::string>& packageNames, static void MountEmulatedStorage(uid_t uid, jint mount_mode, bool force_mount_namespace, const std::string& package_name, const std::vector<std::string>& packages_for_uid, - const std::vector<std::string>& visible_vol_ids, const std::string& sandbox_id, + const std::string& sandbox_id, fail_fn_t fail_fn) { - // See storage config details at http://source.android.com/tech/storage/ + // See storage config details at http://source.android.com/tech/storage/ + + String8 storage_source; + if (mount_mode == MOUNT_EXTERNAL_DEFAULT) { + storage_source = "/mnt/runtime/default"; + } else if (mount_mode == MOUNT_EXTERNAL_READ) { + storage_source = "/mnt/runtime/read"; + } else if (mount_mode == MOUNT_EXTERNAL_WRITE) { + storage_source = "/mnt/runtime/write"; + } else if (mount_mode == MOUNT_EXTERNAL_NONE && !force_mount_namespace) { + // Sane default of no storage visible + return; + } - String8 storageSource; - if (mount_mode == MOUNT_EXTERNAL_DEFAULT) { - storageSource = "/mnt/runtime/default"; - } else if (mount_mode == MOUNT_EXTERNAL_READ) { - storageSource = "/mnt/runtime/read"; - } else if (mount_mode == MOUNT_EXTERNAL_WRITE) { - storageSource = "/mnt/runtime/write"; - } else if (mount_mode == MOUNT_EXTERNAL_NONE && !force_mount_namespace) { - // Sane default of no storage visible - return; - } + // Create a second private mount namespace for our process + if (unshare(CLONE_NEWNS) == -1) { + fail_fn(CREATE_ERROR("Failed to unshare(): %s", strerror(errno))); + } - // Create a second private mount namespace for our process - if (unshare(CLONE_NEWNS) == -1) { - fail_fn(CREATE_ERROR("Failed to unshare(): %s", strerror(errno))); - } + // Handle force_mount_namespace with MOUNT_EXTERNAL_NONE. + if (mount_mode == MOUNT_EXTERNAL_NONE) { + return; + } - // Handle force_mount_namespace with MOUNT_EXTERNAL_NONE. - if (mount_mode == MOUNT_EXTERNAL_NONE) { - return; - } + if (GetBoolProperty(kIsolatedStorageSnapshot, GetBoolProperty(kIsolatedStorage, true))) { + if (mount_mode == MOUNT_EXTERNAL_FULL || mount_mode == MOUNT_EXTERNAL_LEGACY) { + storage_source = (mount_mode == MOUNT_EXTERNAL_FULL) + ? "/mnt/runtime/full" : "/mnt/runtime/write"; + if (TEMP_FAILURE_RETRY(mount(storage_source.string(), "/storage", + NULL, MS_BIND | MS_REC | MS_SLAVE, NULL)) == -1) { + fail_fn(CREATE_ERROR("Failed to mount %s to /storage: %s", + storage_source.string(), + strerror(errno))); + } - if (GetBoolProperty(kIsolatedStorageSnapshot, GetBoolProperty(kIsolatedStorage, true))) { - if (mount_mode == MOUNT_EXTERNAL_FULL || mount_mode == MOUNT_EXTERNAL_LEGACY) { - storageSource = (mount_mode == MOUNT_EXTERNAL_FULL) - ? "/mnt/runtime/full" : "/mnt/runtime/write"; - if (TEMP_FAILURE_RETRY(mount(storageSource.string(), "/storage", - NULL, MS_BIND | MS_REC | MS_SLAVE, NULL)) == -1) { - fail_fn(CREATE_ERROR("Failed to mount %s to /storage: %s", - storageSource.string(), - strerror(errno))); - } - - // 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) { - fail_fn(CREATE_ERROR("fs_prepare_dir failed on %s (%s)", - userSource.string(), strerror(errno))); - } - - if (TEMP_FAILURE_RETRY(mount(userSource.string(), "/storage/self", nullptr, MS_BIND, - nullptr)) == -1) { - fail_fn(CREATE_ERROR("Failed to mount %s to /storage/self: %s", - userSource.string(), - strerror(errno))); - } - } else { - if (package_name.empty() || sandbox_id.empty()) { - return; - } - - userid_t user_id = multiuser_get_user_id(uid); - std::string pkgSandboxDir = - StringPrintf("/mnt/user/%d/package/%s", user_id, package_name.c_str()); - bool sandboxAlreadyCreated = true; - if (TEMP_FAILURE_RETRY(access(pkgSandboxDir.c_str(), F_OK)) == -1) { - if (errno == ENOENT) { - ALOGD("Sandbox not yet created for %s", pkgSandboxDir.c_str()); - sandboxAlreadyCreated = false; - CreatePkgSandboxTarget(uid, package_name, fail_fn); - } else { - fail_fn(CREATE_ERROR("Failed to access %s: %s", - pkgSandboxDir.c_str(), strerror(errno))); - } - } - - if (TEMP_FAILURE_RETRY(mount(pkgSandboxDir.c_str(), "/storage", - nullptr, MS_BIND | MS_REC | MS_SLAVE, nullptr)) == -1) { - fail_fn(CREATE_ERROR("Failed to mount %s to /storage: %s", - pkgSandboxDir.c_str(), strerror(errno))); - } - - if (TEMP_FAILURE_RETRY(access("/storage/obb_mount", F_OK)) == 0) { - if (mount_mode != MOUNT_EXTERNAL_INSTALLER) { - remove("/storage/obb_mount"); - } - } else { - if (mount_mode == MOUNT_EXTERNAL_INSTALLER) { - int fd = - TEMP_FAILURE_RETRY(open("/storage/obb_mount", O_RDWR | O_CREAT, 0660)); - if (fd == -1) { - fail_fn(CREATE_ERROR("Couldn't create /storage/obb_mount: %s", - strerror(errno))); - } - close(fd); - } - } - // If the sandbox was already created by vold, only then set up the bind mounts for - // pkg specific directories. Otherwise, leave as is and bind mounts will be taken - // care of by vold later. - if (sandboxAlreadyCreated) { - PreparePkgSpecificDirs(packages_for_uid, visible_vol_ids, - mount_mode == MOUNT_EXTERNAL_INSTALLER, sandbox_id, user_id, uid, fail_fn); - } - } + // Mount user-specific symlink helper into place + userid_t user_id = multiuser_get_user_id(uid); + const String8 user_source(String8::format("/mnt/user/%d", user_id)); + if (fs_prepare_dir(user_source.string(), 0751, 0, 0) == -1) { + fail_fn(CREATE_ERROR("fs_prepare_dir failed on %s (%s)", + user_source.string(), strerror(errno))); + } + + if (TEMP_FAILURE_RETRY(mount(user_source.string(), "/storage/self", nullptr, MS_BIND, + nullptr)) == -1) { + fail_fn(CREATE_ERROR("Failed to mount %s to /storage/self: %s", + user_source.string(), + strerror(errno))); + } } else { - if (TEMP_FAILURE_RETRY(mount(storageSource.string(), "/storage", nullptr, - MS_BIND | MS_REC | MS_SLAVE, nullptr)) == -1) { - fail_fn(CREATE_ERROR("Failed to mount %s to /storage: %s", - storageSource.string(), - strerror(errno))); - } + if (package_name.empty() || sandbox_id.empty()) { + return; + } - // 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) { - fail_fn(CREATE_ERROR("fs_prepare_dir failed on %s", - userSource.string())); - } + userid_t user_id = multiuser_get_user_id(uid); + CreatePkgSandboxTarget(user_id, fail_fn); - if (TEMP_FAILURE_RETRY(mount(userSource.string(), "/storage/self", - nullptr, MS_BIND, nullptr)) == -1) { - fail_fn(CREATE_ERROR("Failed to mount %s to /storage/self: %s", - userSource.string(), strerror(errno))); - } + std::string pkg_sandbox_dir = StringPrintf("/mnt/user/%d/package", user_id); + if (TEMP_FAILURE_RETRY(mount(pkg_sandbox_dir.c_str(), "/storage", + nullptr, MS_BIND | MS_REC | MS_SLAVE, nullptr)) == -1) { + fail_fn(CREATE_ERROR("Failed to mount %s to /storage: %s", + pkg_sandbox_dir.c_str(), strerror(errno))); + } + + HandleMountModeInstaller(mount_mode, user_id, sandbox_id, fail_fn); + + PreparePkgSpecificDirs(packages_for_uid, + mount_mode == MOUNT_EXTERNAL_INSTALLER, sandbox_id, user_id, uid, fail_fn); + } + } else { + if (TEMP_FAILURE_RETRY(mount(storage_source.string(), "/storage", nullptr, + MS_BIND | MS_REC | MS_SLAVE, nullptr)) == -1) { + fail_fn(CREATE_ERROR("Failed to mount %s to /storage: %s", + storage_source.string(), + strerror(errno))); + } + + // Mount user-specific symlink helper into place + userid_t user_id = multiuser_get_user_id(uid); + const String8 user_source(String8::format("/mnt/user/%d", user_id)); + if (fs_prepare_dir(user_source.string(), 0751, 0, 0) == -1) { + fail_fn(CREATE_ERROR("fs_prepare_dir failed on %s", + user_source.string())); } + + if (TEMP_FAILURE_RETRY(mount(user_source.string(), "/storage/self", + nullptr, MS_BIND, nullptr)) == -1) { + fail_fn(CREATE_ERROR("Failed to mount %s to /storage/self: %s", + user_source.string(), strerror(errno))); + } + } } static bool NeedsNoRandomizeWorkaround() { @@ -1156,6 +1181,14 @@ static void UnblockSignal(int signum, fail_fn_t fail_fn) { } } +static void ClearBlastulaTable() { + for (BlastulaTableEntry& entry : gBlastulaTable) { + entry.Clear(); + } + + gBlastulaPoolCount = 0; +} + // Utility routine to fork a process from the zygote. static pid_t ForkCommon(JNIEnv* env, bool is_system_server, const std::vector<int>& fds_to_close, @@ -1198,12 +1231,17 @@ static pid_t ForkCommon(JNIEnv* env, bool is_system_server, // Clean up any descriptors which must be closed immediately DetachDescriptors(env, fds_to_close, fail_fn); + // Invalidate the entries in the blastula table. + ClearBlastulaTable(); + // Re-open all remaining open file descriptors so that they aren't shared // with the zygote across a fork. gOpenFdTable->ReopenOrDetach(fail_fn); // Turn fdsan back on. android_fdsan_set_error_level(fdsan_error_level); + } else { + ALOGD("Forked child process %d", pid); } // We blocked SIGCHLD prior to a fork, we unblock it here. @@ -1221,7 +1259,7 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, bool is_child_zygote, jstring managed_instruction_set, jstring managed_app_data_dir, jstring managed_package_name, jobjectArray managed_pacakges_for_uid, - jobjectArray managed_visible_vol_ids, jstring managed_sandbox_id) { + jstring managed_sandbox_id) { const char* process_name = is_system_server ? "system_server" : "zygote"; auto fail_fn = std::bind(ZygoteFailure, env, process_name, managed_nice_name, _1); auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1); @@ -1269,12 +1307,8 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, ExtractJStringArray(env, process_name, managed_nice_name, managed_pacakges_for_uid). value_or(std::vector<std::string>()); - std::vector<std::string> visible_vol_ids = - ExtractJStringArray(env, process_name, managed_nice_name, managed_visible_vol_ids). - value_or(std::vector<std::string>()); - MountEmulatedStorage(uid, mount_external, use_native_bridge, package_name.value(), - packages_for_uid, visible_vol_ids, sandbox_id.value_or(""), fail_fn); + packages_for_uid, sandbox_id.value_or(""), fail_fn); // If this zygote isn't root, it won't be able to create a process group, // since the directory is owned by root. @@ -1577,7 +1611,7 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize( jint mount_external, jstring se_info, jstring nice_name, jintArray managed_fds_to_close, jintArray managed_fds_to_ignore, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, jstring package_name, - jobjectArray packages_for_uid, jobjectArray visible_vol_ids, jstring sandbox_id) { + jobjectArray packages_for_uid, jstring sandbox_id) { jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote); if (UNLIKELY(managed_fds_to_close == nullptr)) { @@ -1609,7 +1643,7 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize( capabilities, capabilities, mount_external, se_info, nice_name, false, is_child_zygote == JNI_TRUE, instruction_set, app_data_dir, - package_name, packages_for_uid, visible_vol_ids, sandbox_id); + package_name, packages_for_uid, sandbox_id); } return pid; } @@ -1635,7 +1669,7 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer( SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits, permitted_capabilities, effective_capabilities, MOUNT_EXTERNAL_DEFAULT, nullptr, nullptr, true, - false, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); + false, nullptr, nullptr, nullptr, nullptr, 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); @@ -1789,7 +1823,7 @@ static void com_android_internal_os_Zygote_nativeSpecializeBlastula( jint runtime_flags, jobjectArray rlimits, jint mount_external, jstring se_info, jstring nice_name, jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir, - jstring package_name, jobjectArray packages_for_uid, jobjectArray visible_vol_ids, + jstring package_name, jobjectArray packages_for_uid, jstring sandbox_id) { jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote); @@ -1797,7 +1831,7 @@ static void com_android_internal_os_Zygote_nativeSpecializeBlastula( capabilities, capabilities, mount_external, se_info, nice_name, false, is_child_zygote == JNI_TRUE, instruction_set, app_data_dir, - package_name, packages_for_uid, visible_vol_ids, sandbox_id); + package_name, packages_for_uid, sandbox_id); } /** @@ -1884,11 +1918,32 @@ static jint com_android_internal_os_Zygote_nativeGetBlastulaPoolCount(JNIEnv* en return gBlastulaPoolCount; } +/** + * Kills all processes currently in the blastula pool. + * + * @param env Managed runtime environment + * @return The number of blastulas currently in the blastula pool + */ +static void com_android_internal_os_Zygote_nativeEmptyBlastulaPool(JNIEnv* env, jclass) { + for (auto& entry : gBlastulaTable) { + auto entry_storage = entry.GetValues(); + + if (entry_storage.has_value()) { + kill(entry_storage.value().pid, SIGKILL); + close(entry_storage.value().read_pipe_fd); + + // Avoid a second atomic load by invalidating instead of clearing. + entry.Invalidate(); + --gBlastulaPoolCount; + } + } +} + 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;Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)I", + "(II[II[[IILjava/lang/String;Ljava/lang/String;[I[IZLjava/lang/String;Ljava/lang/String;Ljava/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 }, @@ -1903,7 +1958,7 @@ static const JNINativeMethod gMethods[] = { { "nativeForkBlastula", "(II[I)I", (void *) com_android_internal_os_Zygote_nativeForkBlastula }, { "nativeSpecializeBlastula", - "(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)V", + "(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)V", (void *) com_android_internal_os_Zygote_nativeSpecializeBlastula }, { "nativeGetSocketFDs", "(Z)V", (void *) com_android_internal_os_Zygote_nativeGetSocketFDs }, @@ -1914,7 +1969,9 @@ static const JNINativeMethod gMethods[] = { { "nativeGetBlastulaPoolEventFD", "()I", (void *) com_android_internal_os_Zygote_nativeGetBlastulaPoolEventFD }, { "nativeGetBlastulaPoolCount", "()I", - (void *) com_android_internal_os_Zygote_nativeGetBlastulaPoolCount } + (void *) com_android_internal_os_Zygote_nativeGetBlastulaPoolCount }, + { "nativeEmptyBlastulaPool", "()V", + (void *) com_android_internal_os_Zygote_nativeEmptyBlastulaPool } }; int register_com_android_internal_os_Zygote(JNIEnv* env) { diff --git a/core/jni/com_google_android_gles_jni_GLImpl.cpp b/core/jni/com_google_android_gles_jni_GLImpl.cpp index 40ff7e4c5517..c806162c2534 100644 --- a/core/jni/com_google_android_gles_jni_GLImpl.cpp +++ b/core/jni/com_google_android_gles_jni_GLImpl.cpp @@ -65,8 +65,6 @@ GL_API void GL_APIENTRY glWeightPointerOESBounds(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer, GLsizei count); } -static int initialized = 0; - static jclass nioAccessClass; static jclass bufferClass; static jclass G11ImplClass; @@ -4239,6 +4237,7 @@ exit: } if (_exception) { jniThrowException(_env, _exceptionType, _exceptionMessage); + return (jint)0; } return (jint)_returnValue; } @@ -4308,6 +4307,7 @@ exit: } if (_exception) { jniThrowException(_env, _exceptionType, _exceptionMessage); + return (jint)0; } return (jint)_returnValue; } @@ -4904,6 +4904,7 @@ android_glGetBufferParameteriv__II_3II (JNIEnv *_env, jobject _this, jint target, jint pname, jintArray params_ref, jint offset) { jniThrowException(_env, "java/lang/UnsupportedOperationException", "glGetBufferParameteriv"); + return; } /* void glGetBufferParameteriv ( GLenum target, GLenum pname, GLint *params ) */ @@ -4912,6 +4913,7 @@ android_glGetBufferParameteriv__IILjava_nio_IntBuffer_2 (JNIEnv *_env, jobject _this, jint target, jint pname, jobject params_buf) { jniThrowException(_env, "java/lang/UnsupportedOperationException", "glGetBufferParameteriv"); + return; } /* void glGetClipPlanef ( GLenum pname, GLfloat *eqn ) */ diff --git a/core/jni/include/android_runtime/android_view_Surface.h b/core/jni/include/android_runtime/android_view_Surface.h index 3f7c00c9ff01..04718cd981ff 100644 --- a/core/jni/include/android_runtime/android_view_Surface.h +++ b/core/jni/include/android_runtime/android_view_Surface.h @@ -19,6 +19,7 @@ #include <android/native_window.h> #include <system/graphics.h> +#include <ui/PublicFormat.h> #include "jni.h" @@ -27,41 +28,6 @@ namespace android { class Surface; class IGraphicBufferProducer; -/** - * Enum mirroring the public API definitions for image and pixel formats. - * Some of these are hidden in the public API - * - * Keep up to date with android.graphics.ImageFormat and - * android.graphics.PixelFormat - */ -enum class PublicFormat { - UNKNOWN = 0x0, - RGBA_8888 = 0x1, - RGBX_8888 = 0x2, - RGB_888 = 0x3, - RGB_565 = 0x4, - NV16 = 0x10, - NV21 = 0x11, - YUY2 = 0x14, - RGBA_FP16 = 0x16, - RAW_SENSOR = 0x20, - PRIVATE = 0x22, - YUV_420_888 = 0x23, - RAW_PRIVATE = 0x24, - RAW10 = 0x25, - RAW12 = 0x26, - RGBA_1010102 = 0x2b, - JPEG = 0x100, - DEPTH_POINT_CLOUD = 0x101, - RAW_DEPTH = 0x1002, // @hide - YV12 = 0x32315659, - Y8 = 0x20203859, - Y16 = 0x20363159, // @hide - DEPTH16 = 0x44363159, - DEPTH_JPEG = 0x69656963, - HEIC = 0x48454946, -}; - /* Gets the underlying ANativeWindow for a Surface. */ extern sp<ANativeWindow> android_view_Surface_getNativeWindow( JNIEnv* env, jobject surfaceObj); diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto index 5497b8665cf0..efbe8bacb58f 100644 --- a/core/proto/android/app/settings_enums.proto +++ b/core/proto/android/app/settings_enums.proto @@ -615,6 +615,40 @@ enum Action { // CATEGORY: SETTINGS // OS: Q ACTION_PANEL_INTERACTION = 1658; + + // ACTION: Show Contextual homepage, log latency in loading cards + ACTION_CONTEXTUAL_HOME_SHOW = 1662; + + // ACTION: Contextual card displays + ACTION_CONTEXTUAL_CARD_SHOW = 1663; + + // ACTION: Contextual cards are eligible to be shown, but don't rank high + ACTION_CONTEXTUAL_CARD_NOT_SHOW = 1664; + + // ACTION: Settings > long press a card, and click dismiss + // Contextual card is dismissed + ACTION_CONTEXTUAL_CARD_DISMISS = 1665; + + // ACTION: Settings > click a card + // Contextual card is clicked + ACTION_CONTEXTUAL_CARD_CLICK = 1666; + + // Mapping: go/at-mapping + ACTION_ATSG = 1674; + + ACTION_ATPG = 1675; + + ACTION_ATCLPB = 1676; + + ACTION_ATCGIB = 1677; + + ACTION_ATCPAB = 1678; + + ACTION_ATCSAUC = 1679; + + ACTION_ATCSCUC = 1680; + + ACTION_ATCHNUC = 1681; } /** @@ -1622,6 +1656,10 @@ enum PageId { // OPEN: Settings > Apps > Default Apps > Default sms DEFAULT_SMS_PICKER = 789; + // OPEN: Settings > Apps > Notification > Notification Assistant + DEFAULT_NOTIFICATION_ASSISTANT = 790; + + // OPEN: Settings > Apps > Default Apps > Warning dialog to confirm selection DEFAULT_APP_PICKER_CONFIRMATION_DIALOG = 791; @@ -2231,4 +2269,19 @@ enum PageId { // Panel for Media Output PANEL_MEDIA_OUTPUT = 1657; + + // Mapping: go/at-mapping + PAGE_ATSSI = 1667; + + PAGE_ATSII = 1668; + + PAGE_ATUS = 1669; + + PAGE_ATSSP = 1670; + + PAGE_ATSAP = 1671; + + PAGE_ATSCP = 1672; + + PAGE_ATHNP = 1673; } diff --git a/core/proto/android/hardware/sensor/assist/enums.proto b/core/proto/android/hardware/sensor/assist/enums.proto index 8c5841a32d54..012dcb2e937e 100644 --- a/core/proto/android/hardware/sensor/assist/enums.proto +++ b/core/proto/android/hardware/sensor/assist/enums.proto @@ -21,14 +21,14 @@ option java_outer_classname = "AssistGestureProtoEnums"; option java_multiple_files = true; enum AssistGestureStageEnum { - ASSIST_GESTURE_STAGE_UNKNOWN = 0; - ASSIST_GESTURE_STAGE_PROGRESS = 1; - ASSIST_GESTURE_STAGE_PRIMED = 2; - ASSIST_GESTURE_STAGE_DETECTED = 3; + ASSIST_GESTURE_STAGE_UNKNOWN = 0; + ASSIST_GESTURE_STAGE_PROGRESS = 1; + ASSIST_GESTURE_STAGE_PRIMED = 2; + ASSIST_GESTURE_STAGE_DETECTED = 3; } enum AssistGestureFeedbackEnum { - ASSIST_GESTURE_FEEDBACK_UNKNOWN = 0; - ASSIST_GESTURE_FEEDBACK_NOT_USED = 1; - ASSIST_GESTURE_FEEDBACK_USED = 2; + ASSIST_GESTURE_FEEDBACK_UNKNOWN = 0; + ASSIST_GESTURE_FEEDBACK_NOT_USED = 1; + ASSIST_GESTURE_FEEDBACK_USED = 2; }
\ No newline at end of file diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto index cccb40d51722..c1cbd525b4d1 100644 --- a/core/proto/android/providers/settings/global.proto +++ b/core/proto/android/providers/settings/global.proto @@ -920,8 +920,7 @@ message GlobalSettingsProto { // Temperature at which the high temperature warning notification should // be shown. optional SettingProto warning_temperature_level = 2 [ (android.privacy).dest = DEST_AUTOMATIC ]; - // USB temperature at which the high temperature alarm notification should be shown. - optional SettingProto usb_alarm_temperature_level = 3 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto show_usb_temperature_alarm = 3 [ (android.privacy).dest = DEST_AUTOMATIC ]; } optional TemperatureWarning temperature_warning = 119; diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto index 6f9a5649d4ac..79a5dd78ffb3 100644 --- a/core/proto/android/server/activitymanagerservice.proto +++ b/core/proto/android/server/activitymanagerservice.proto @@ -132,6 +132,7 @@ message KeyguardControllerProto { optional bool keyguard_showing = 1; repeated KeyguardOccludedProto keyguard_occluded_states= 2; + optional bool aod_showing = 3; } message KeyguardOccludedProto { diff --git a/core/proto/android/stats/style/Android.bp b/core/proto/android/stats/style/Android.bp new file mode 100644 index 000000000000..f085a52f8cdb --- /dev/null +++ b/core/proto/android/stats/style/Android.bp @@ -0,0 +1,27 @@ +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +java_library { + name: "styleprotosnano", + proto: { + type: "nano", + output_params: ["store_unknown_fields=true"], + include_dirs: ["external/protobuf/src"], + }, + + sdk_version: "current", + srcs: [ + "*.proto", + ], +} diff --git a/core/proto/android/stats/style/style_enums.proto b/core/proto/android/stats/style/style_enums.proto new file mode 100644 index 000000000000..b0e4391efd00 --- /dev/null +++ b/core/proto/android/stats/style/style_enums.proto @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; +package android.stats.style; +option java_multiple_files = true; + +enum Action { + DEFAULT_ACTION = 0; + ONRESUME = 1; + ONSTOP = 2; + PICKER_SELECT = 3; + PICKER_APPLIED = 4; + WALLPAPER_OPEN_CATEGORY = 5; + WALLPAPER_SELECT = 6; + WALLPAPER_APPLIED = 7; + WALLPAPER_EXPLORE = 8; + WALLPAPER_DOWNLOAD = 9; + WALLPAPER_REMOVE = 10; +} + diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 5b74d90608f7..0149365b4fb5 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1167,8 +1167,7 @@ <!-- ====================================================================== --> <eat-comment /> - <!-- Used for permissions that are associated with activity recognition. - TODO(zezeozue). STOPSHIP: Add icon --> + <!-- Used for permissions that are associated with activity recognition. --> <permission-group android:name="android.permission-group.ACTIVITY_RECOGNITION" android:icon="@drawable/perm_group_activity_recognition" android:label="@string/permgrouplab_activityRecognition" @@ -3419,6 +3418,11 @@ <permission android:name="android.permission.GET_RUNTIME_PERMISSIONS" android:protectionLevel="signature" /> + <!-- @SystemApi Allows an application to change policy_fixed permissions. + @hide --> + <permission android:name="android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY" + android:protectionLevel="signature|installer" /> + <!-- @hide Allows an application to observe permission changes. --> <permission android:name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS" android:protectionLevel="signature|privileged" /> diff --git a/core/res/res/anim-ldrtl/cross_profile_apps_thumbnail_enter.xml b/core/res/res/anim-ldrtl/cross_profile_apps_thumbnail_enter.xml index 6f3dc8c7de0e..5add19bba51b 100644 --- a/core/res/res/anim-ldrtl/cross_profile_apps_thumbnail_enter.xml +++ b/core/res/res/anim-ldrtl/cross_profile_apps_thumbnail_enter.xml @@ -18,7 +18,9 @@ --> <!-- This should be kept in sync with task_open_enter.xml --> <set xmlns:android="http://schemas.android.com/apk/res/android" - android:shareInterpolator="false" android:zAdjustment="top"> + android:hasRoundedCorners="true" + android:shareInterpolator="false" + android:zAdjustment="top"> <alpha android:fromAlpha="1" diff --git a/core/res/res/anim-ldrtl/task_close_enter.xml b/core/res/res/anim-ldrtl/task_close_enter.xml index 7abada332fb6..e00141a8c155 100644 --- a/core/res/res/anim-ldrtl/task_close_enter.xml +++ b/core/res/res/anim-ldrtl/task_close_enter.xml @@ -16,6 +16,7 @@ <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false" android:zAdjustment="top" + android:hasRoundedCorners="true" android:showWallpaper="true"> <alpha diff --git a/core/res/res/anim-ldrtl/task_close_exit.xml b/core/res/res/anim-ldrtl/task_close_exit.xml index a017820a4b3e..71a44ae7d2fc 100644 --- a/core/res/res/anim-ldrtl/task_close_exit.xml +++ b/core/res/res/anim-ldrtl/task_close_exit.xml @@ -16,6 +16,7 @@ <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false" + android:hasRoundedCorners="true" android:showWallpaper="true"> <alpha diff --git a/core/res/res/anim-ldrtl/task_open_enter.xml b/core/res/res/anim-ldrtl/task_open_enter.xml index 0433664717eb..7815f7d661d0 100644 --- a/core/res/res/anim-ldrtl/task_open_enter.xml +++ b/core/res/res/anim-ldrtl/task_open_enter.xml @@ -18,6 +18,7 @@ <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false" android:zAdjustment="top" + android:hasRoundedCorners="true" android:showWallpaper="true"> <alpha diff --git a/core/res/res/anim-ldrtl/task_open_enter_cross_profile_apps.xml b/core/res/res/anim-ldrtl/task_open_enter_cross_profile_apps.xml index 45ca80e00e22..5fccd6df14a5 100644 --- a/core/res/res/anim-ldrtl/task_open_enter_cross_profile_apps.xml +++ b/core/res/res/anim-ldrtl/task_open_enter_cross_profile_apps.xml @@ -18,6 +18,7 @@ <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false" android:zAdjustment="top" + android:hasRoundedCorners="true" android:showWallpaper="true"> <alpha diff --git a/core/res/res/anim-ldrtl/task_open_exit.xml b/core/res/res/anim-ldrtl/task_open_exit.xml index f50494d81eb5..025e1bdc05c9 100644 --- a/core/res/res/anim-ldrtl/task_open_exit.xml +++ b/core/res/res/anim-ldrtl/task_open_exit.xml @@ -16,6 +16,7 @@ <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false" + android:hasRoundedCorners="true" android:showWallpaper="true"> <alpha diff --git a/core/res/res/anim/cross_profile_apps_thumbnail_enter.xml b/core/res/res/anim/cross_profile_apps_thumbnail_enter.xml index 4c2559f1e47d..2cfeecf4685d 100644 --- a/core/res/res/anim/cross_profile_apps_thumbnail_enter.xml +++ b/core/res/res/anim/cross_profile_apps_thumbnail_enter.xml @@ -18,7 +18,9 @@ --> <!-- This should be kept in sync with task_open_enter.xml --> <set xmlns:android="http://schemas.android.com/apk/res/android" - android:shareInterpolator="false" android:zAdjustment="top"> + android:shareInterpolator="false" + android:hasRoundedCorners="true" + android:zAdjustment="top"> <alpha android:fromAlpha="1" diff --git a/core/res/res/anim/task_close_enter.xml b/core/res/res/anim/task_close_enter.xml index b059aa9cb28c..487ff5c748d3 100644 --- a/core/res/res/anim/task_close_enter.xml +++ b/core/res/res/anim/task_close_enter.xml @@ -18,6 +18,7 @@ <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false" android:zAdjustment="top" + android:hasRoundedCorners="true" android:showWallpaper="true"> <alpha diff --git a/core/res/res/anim/task_close_exit.xml b/core/res/res/anim/task_close_exit.xml index c9ade227819b..afc3256cb617 100644 --- a/core/res/res/anim/task_close_exit.xml +++ b/core/res/res/anim/task_close_exit.xml @@ -18,6 +18,7 @@ <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false" + android:hasRoundedCorners="true" android:showWallpaper="true"> <alpha diff --git a/core/res/res/anim/task_open_enter.xml b/core/res/res/anim/task_open_enter.xml index 5c618594364f..0aafc1c0b91c 100644 --- a/core/res/res/anim/task_open_enter.xml +++ b/core/res/res/anim/task_open_enter.xml @@ -20,6 +20,7 @@ <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false" android:zAdjustment="top" + android:hasRoundedCorners="true" android:showWallpaper="true"> <alpha diff --git a/core/res/res/anim/task_open_enter_cross_profile_apps.xml b/core/res/res/anim/task_open_enter_cross_profile_apps.xml index 644104721463..702f7ba162aa 100644 --- a/core/res/res/anim/task_open_enter_cross_profile_apps.xml +++ b/core/res/res/anim/task_open_enter_cross_profile_apps.xml @@ -20,6 +20,7 @@ <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false" android:zAdjustment="top" + android:hasRoundedCorners="true" android:showWallpaper="true"> <alpha diff --git a/core/res/res/anim/task_open_exit.xml b/core/res/res/anim/task_open_exit.xml index 9394c577da78..691317d2e6e0 100644 --- a/core/res/res/anim/task_open_exit.xml +++ b/core/res/res/anim/task_open_exit.xml @@ -18,6 +18,7 @@ <set xmlns:android="http://schemas.android.com/apk/res/android" android:shareInterpolator="false" + android:hasRoundedCorners="true" android:showWallpaper="true"> <alpha diff --git a/core/res/res/drawable/ic_action_open.xml b/core/res/res/drawable/ic_action_open.xml index 3d3d36ece0af..ba0a38d6c104 100644 --- a/core/res/res/drawable/ic_action_open.xml +++ b/core/res/res/drawable/ic_action_open.xml @@ -17,8 +17,10 @@ Copyright (C) 2019 The Android Open Source Project android:width="24dp" android:height="24dp" android:viewportWidth="24.0" - android:viewportHeight="24.0"> + android:viewportHeight="24.0" + android:tint="?attr/colorControlNormal"> + > <path - android:fillColor="#FF737373" + android:fillColor="@color/white" android:pathData="M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"/> </vector>
\ No newline at end of file diff --git a/core/res/res/drawable/ic_qs_battery_saver.xml b/core/res/res/drawable/ic_qs_battery_saver.xml index 89b25690a5db..93975b61948e 100644 --- a/core/res/res/drawable/ic_qs_battery_saver.xml +++ b/core/res/res/drawable/ic_qs_battery_saver.xml @@ -1,5 +1,5 @@ <!-- - Copyright (C) 2018 The Android Open Source Project + Copyright (C) 2019 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -21,8 +21,10 @@ android:viewportHeight="24.0" android:tint="?android:attr/colorControlNormal"> <path - android:pathData="M5,3 - l3.5,0 l0,-1.5 l7,0 l0,1.5 l3.5,0 l0,19.5 l-14,0z - M10.5,8.5 l0,3 l-3,0 l0,3 l3,0 l0,3 l3,0 l0,-3 l3,0 l0,-3 l-3,0 l0,-3 z" - android:fillColor="#FFFFFF"/> + android:fillColor="#FFF" + android:pathData="M16.67,4H14.5V2h-5v2H7.33C6.6,4 6,4.6 6,5.33V15v5.67C6,21.4 6.6,22 7.33,22h9.33C17.4,22 18,21.4 18,20.67V15V5.33C18,4.6 17.4,4 16.67,4zM16,15v5H8v-5V6h8V15z"/> + <path + android:fillColor="#FFF" + android:pathData="M15,12l-2,0l0,-2l-2,0l0,2l-2,0l0,2l2,0l0,2l2,0l0,-2l2,0z"/> + </vector> diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml index 733878b9882b..8f2d6c3e02f4 100644 --- a/core/res/res/values/arrays.xml +++ b/core/res/res/values/arrays.xml @@ -185,4 +185,20 @@ <item>@string/app_info</item> </string-array> + <!-- Device-specific array of SIM slot indexes which are are embedded eUICCs. + e.g. If a device has two physical slots with indexes 0, 1, and slot 1 is an + eUICC, then the value of this array should be: + <integer-array name="non_removable_euicc_slots"> + <item>1</item> + </integer-array> + If a device has three physical slots and slot 1 and 2 are eUICCs, then the value of + this array should be: + <integer-array name="non_removable_euicc_slots"> + <item>1</item> + <item>2</item> + </integer-array> + This is used to differentiate between removable eUICCs and built in eUICCs, and should + be set by OEMs for devices which use eUICCs. --> + <integer-array name="non_removable_euicc_slots"></integer-array> + </resources> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 088669da7b91..743496fdffb5 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -6488,6 +6488,9 @@ <!-- Special option for window animations: show the wallpaper behind when running this animation. --> <attr name="showWallpaper" format="boolean" /> + <!-- Special option for window animations: whether window should have rounded corners. + @see ScreenDecorationsUtils#getWindowCornerRadius(Resources) --> + <attr name="hasRoundedCorners" format="boolean" /> </declare-styleable> <declare-styleable name="AnimationSet"> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 1053184bc2fc..6b8e000777d8 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -1667,6 +1667,9 @@ This flag is turned on by default. <em>This attribute is usable only by system apps. </em> --> <attr name="allowClearUserDataOnFailedRestore"/> + <!-- If {@code true} the app's non sensitive audio can be capture by other apps. + The default value is true. --> + <attr name="allowAudioPlaybackCapture" format="boolean" /> </declare-styleable> <!-- The <code>permission</code> tag declares a security permission that can be used to control access from other packages to specific components or diff --git a/core/res/res/values/colors_device_defaults.xml b/core/res/res/values/colors_device_defaults.xml index 824b4b5977d1..7209103de981 100644 --- a/core/res/res/values/colors_device_defaults.xml +++ b/core/res/res/values/colors_device_defaults.xml @@ -26,6 +26,8 @@ <color name="primary_dark_device_default_settings">@color/primary_dark_material_settings</color> <color name="primary_dark_device_default_settings_light">@color/primary_dark_material_settings_light</color> + <color name="navigation_bar_divider_device_default_settings">#1f000000</color> + <color name="secondary_device_default_settings">@color/secondary_material_settings</color> <color name="secondary_device_default_settings_light">@color/secondary_material_settings_light</color> <color name="tertiary_device_default_settings">@color/tertiary_material_settings</color> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 6571cd264583..f1a7059140c7 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2008,11 +2008,6 @@ a transaction, so it interacts poorly with SECURE_DELETE. --> <string name="db_default_journal_mode" translatable="false">TRUNCATE</string> - <!-- Enables compatibility WAL mode. - In this mode, only database journal mode will be changed, connection pool - size will still be limited to a single connection. --> - <bool name="db_compatibility_wal_supported">true</bool> - <!-- Maximum size of the persistent journal file in bytes. If the journal file grows to be larger than this amount then SQLite will truncate it after committing the transaction. --> @@ -2272,6 +2267,7 @@ <!-- If the sensor that wakes up the lock screen is available or not. --> <bool name="config_dozeWakeLockScreenSensorAvailable">false</bool> + <integer name="config_dozeWakeLockScreenDebounce">1500</integer> <!-- Control whether the always on display mode is available. This should only be enabled on devices where the display has been tuned to be power efficient in DOZE and/or DOZE_SUSPEND @@ -2673,11 +2669,6 @@ </string-array> - <!-- Flag indicating that this device does not rotate and will always remain in its default - orientation. Activities that desire to run in a non-compatible orientation will be run - from an emulated display within the physical display. --> - <bool name="config_forceDefaultOrientation">false</bool> - <!-- Default Gravity setting for the system Toast view. Equivalent to: Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM --> <integer name="config_toastDefaultGravity">0x00000051</integer> @@ -3368,6 +3359,10 @@ <!-- True if home app should be pinned via Pinner Service --> <bool name="config_pinnerHomeApp">false</bool> + <!-- List of files pinned by the Pinner Service with the apex boot image b/119800099 --> + <string-array translatable="false" name="config_apexBootImagePinnerServiceFiles"> + </string-array> + <!-- Number of days preloaded file cache should be preserved on a device before it can be deleted --> <integer name="config_keepPreloadsMinDays">7</integer> @@ -3935,4 +3930,25 @@ The same restrictions apply to this array. --> <array name="config_displayWhiteBalanceDisplayColorTemperatures"> </array> + + <!-- All of the paths defined for the batterymeter are defined on a 12x20 canvas, and must + be parsable by android.utill.PathParser --> + <string name="config_batterymeterPerimeterPath" translatable="false"> + M3.5,2 v0 H1.33 C0.6,2 0,2.6 0,3.33 V13v5.67 C0,19.4 0.6,20 1.33,20 h9.33 C11.4,20 12,19.4 12,18.67 V13V3.33 C12,2.6 11.4,2 10.67,2 H8.5 V0 H3.5 z M2,18v-7V4h8v9v5H2L2,18z + </string> + + <string name="config_batterymeterFillMask" translatable="false"> + M2,18 v-14 h8 v14 z + </string> + <string name="config_batterymeterBoltPath" translatable="false"> + M5,17.5 V12 H3 L7,4.5 V10 h2 L5,17.5 z + </string> + <string name="config_batterymeterPowersavePath" translatable="false"> + M9,10l-2,0l0,-2l-2,0l0,2l-2,0l0,2l2,0l0,2l2,0l0,-2l2,0z + </string> + + <!-- A dual tone battery meter draws the perimeter path twice - once to define the shape + and a second time clipped to the fill level to indicate charge --> + <bool name="config_batterymeterDualTone">false</bool> + </resources> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index e1ce2f69ba70..61d530027eaa 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2941,6 +2941,7 @@ <public name="forceUriPermissions" /> <!-- @hide @SystemApi --> <public name="allowClearUserDataOnFailedRestore"/> + <public name="allowAudioPlaybackCapture"/> </public-group> <public-group type="drawable" first-id="0x010800b4"> @@ -2998,6 +2999,8 @@ <public name="config_showDefaultEmergency" /> <!-- @hide @SystemApi --> <public name="config_showDefaultHome" /> + <!-- @hide @TestApi --> + <public name="config_perDisplayFocusEnabled" /> </public-group> <public-group type="dimen" first-id="0x01050007"> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 09fb663468f5..4e47b1fb6b2a 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -779,7 +779,7 @@ <b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g></b> to make and manage phone calls?</string> <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permgrouplab_sensors">Body Sensors</string> + <string name="permgrouplab_sensors">Body sensors</string> <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> <string name="permgroupdesc_sensors">access sensor data about your vital signs</string> <!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] --> @@ -795,12 +795,12 @@ <b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g></b> to access your music?</string> <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permgrouplab_visual">Photos & Videos</string> + <string name="permgrouplab_visual">Photos & videos</string> <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. --> <string name="permgroupdesc_visual">access your photos & videos</string> <!-- Message shown to the user when the apps requests permission from this group. If ever possible this should stay below 80 characters (assuming the parameters takes 20 characters). Don't abbreviate until the message reaches 120 characters though. [CHAR LIMIT=120] --> <string name="permgrouprequest_visual">Allow - <b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g></b> to access your photos & videos?</string> + <b><xliff:g id="app_name" example="Gmail">%1$s</xliff:g></b> to access your photos and videos, including tagged locations?</string> <!-- Title for the capability of an accessibility service to retrieve window content. --> <string name="capability_title_canRetrieveWindowContent">Retrieve window content</string> @@ -5287,6 +5287,50 @@ <!-- Summary of notification letting users know why battery saver was turned on automatically [CHAR_LIMIT=NONE]--> <string name="dynamic_mode_notification_summary">Battery Saver activated to extend battery life</string> + <!-- Description of media type: folder or directory that contains additional files. [CHAR LIMIT=32] --> + <string name="mime_type_folder">Folder</string> + <!-- Description of media type: application file, such as APK. [CHAR LIMIT=32] --> + <string name="mime_type_apk">Android application</string> + + <!-- Description of media type: generic file with unknown contents. [CHAR LIMIT=32] --> + <string name="mime_type_generic">File</string> + <!-- Description of media type: generic file with unknown contents. The 'extension' variable is the file name extension. [CHAR LIMIT=32] --> + <string name="mime_type_generic_ext"><xliff:g id="extension" example="PDF">%1$s</xliff:g> file</string> + + <!-- Description of media type: audio file, such as MP3 or WAV. [CHAR LIMIT=32] --> + <string name="mime_type_audio">Audio</string> + <!-- Description of media type: audio file, such as MP3 or WAV. The 'extension' variable is the file name extension. [CHAR LIMIT=32] --> + <string name="mime_type_audio_ext"><xliff:g id="extension" example="PDF">%1$s</xliff:g> audio</string> + + <!-- Description of media type: video file, such as MP4 or MKV. [CHAR LIMIT=32] --> + <string name="mime_type_video">Video</string> + <!-- Description of media type: video file, such as MP4 or MKV. The 'extension' variable is the file name extension. [CHAR LIMIT=32] --> + <string name="mime_type_video_ext"><xliff:g id="extension" example="PDF">%1$s</xliff:g> video</string> + + <!-- Description of media type: image file, such as JPG or PNG. [CHAR LIMIT=32] --> + <string name="mime_type_image">Image</string> + <!-- Description of media type: image file, such as JPG or PNG. The 'extension' variable is the file name extension. [CHAR LIMIT=32] --> + <string name="mime_type_image_ext"><xliff:g id="extension" example="PDF">%1$s</xliff:g> image</string> + + <!-- Description of media type: archive file, such as ZIP or TAR. [CHAR LIMIT=32] --> + <string name="mime_type_compressed">Archive</string> + <!-- Description of media type: archive file, such as ZIP or TAR. The 'extension' variable is the file name extension. [CHAR LIMIT=32] --> + <string name="mime_type_compressed_ext"><xliff:g id="extension" example="PDF">%1$s</xliff:g> archive</string> + + <!-- Description of media type: document file, such as DOC or PDF. [CHAR LIMIT=32] --> + <string name="mime_type_document">Document</string> + <!-- Description of media type: document file, such as DOC or PDF. The 'extension' variable is the file name extension. [CHAR LIMIT=32] --> + <string name="mime_type_document_ext"><xliff:g id="extension" example="PDF">%1$s</xliff:g> document</string> + + <!-- Description of media type: spreadsheet file, such as XLS. [CHAR LIMIT=32] --> + <string name="mime_type_spreadsheet">Spreadsheet</string> + <!-- Description of media type: spreadsheet file, such as XLS. The 'extension' variable is the file name extension. [CHAR LIMIT=32] --> + <string name="mime_type_spreadsheet_ext"><xliff:g id="extension" example="PDF">%1$s</xliff:g> spreadsheet</string> + + <!-- Description of media type: presentation file, such as PPT. [CHAR LIMIT=32] --> + <string name="mime_type_presentation">Presentation</string> + <!-- Description of media type: presentation file, such as PPT. The 'extension' variable is the file name extension. [CHAR LIMIT=32] --> + <string name="mime_type_presentation_ext"><xliff:g id="extension" example="PDF">%1$s</xliff:g> presentation</string> <!-- Strings for car --> <!-- String displayed when loading a user in the car [CHAR LIMIT=30] --> diff --git a/core/res/res/values/styles_car.xml b/core/res/res/values/styles_car.xml index f6ff1b651788..2129734ef9ce 100644 --- a/core/res/res/values/styles_car.xml +++ b/core/res/res/values/styles_car.xml @@ -59,7 +59,7 @@ <style name="CarAction1"> <item name="textStyle">bold</item> <item name="textSize">@dimen/car_action1_size</item> - <item name="textColor">@color/car_highlight</item> + <item name="textColor">@color/control_default_material</item> </style> <style name="CarAction1.Dark"> diff --git a/core/res/res/values/styles_device_defaults.xml b/core/res/res/values/styles_device_defaults.xml index 93068ea975bd..3c7b36d8030e 100644 --- a/core/res/res/values/styles_device_defaults.xml +++ b/core/res/res/values/styles_device_defaults.xml @@ -38,6 +38,8 @@ easier. <style name="Widget.DeviceDefault.Button.Inset" parent="Widget.Material.Button.Inset"/> <style name="Widget.DeviceDefault.Button.Toggle" parent="Widget.Material.Button.Toggle"/> <style name="Widget.DeviceDefault.Button.Colored" parent="Widget.Material.Button.Colored"> + <item name="outlineAmbientShadowColor">@color/btn_colored_background_material</item> + <item name="outlineSpotShadowColor">@color/btn_colored_background_material</item> <item name="textAppearance">?attr/textAppearanceButton</item> <item name="textColor">@color/btn_colored_text_material</item> </style> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 1ebd6e931ab6..426d813885fe 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -347,7 +347,6 @@ <java-symbol type="bool" name="config_requireRadioPowerOffOnSimRefreshReset" /> <java-symbol type="bool" name="config_speed_up_audio_on_mt_calls" /> <java-symbol type="bool" name="config_useFixedVolume" /> - <java-symbol type="bool" name="config_forceDefaultOrientation" /> <java-symbol type="bool" name="config_wifi_batched_scan_supported" /> <java-symbol type="bool" name="config_wifi_softap_acs_supported" /> <java-symbol type="string" name="config_wifi_softap_acs_supported_channel_list" /> @@ -753,7 +752,6 @@ <java-symbol type="string" name="date_time" /> <java-symbol type="string" name="date_time_set" /> <java-symbol type="string" name="date_time_done" /> - <java-symbol type="bool" name="db_compatibility_wal_supported" /> <java-symbol type="string" name="db_default_journal_mode" /> <java-symbol type="string" name="db_default_sync_mode" /> <java-symbol type="string" name="db_wal_sync_mode" /> @@ -2988,6 +2986,8 @@ <java-symbol type="array" name="resolver_target_actions_pin" /> <java-symbol type="array" name="resolver_target_actions_unpin" /> + <java-symbol type="array" name="non_removable_euicc_slots" /> + <java-symbol type="string" name="install_carrier_app_notification_title" /> <java-symbol type="string" name="install_carrier_app_notification_text" /> <java-symbol type="string" name="install_carrier_app_notification_text_app_name" /> @@ -3048,6 +3048,7 @@ <java-symbol type="array" name="config_defaultPinnerServiceFiles" /> <java-symbol type="bool" name="config_pinnerCameraApp" /> <java-symbol type="bool" name="config_pinnerHomeApp" /> + <java-symbol type="array" name="config_apexBootImagePinnerServiceFiles" /> <java-symbol type="string" name="config_doubleTouchGestureEnableFile" /> @@ -3195,6 +3196,11 @@ <java-symbol type="raw" name="fallback_categories" /> <java-symbol type="string" name="config_icon_mask" /> + <java-symbol type="string" name="config_batterymeterPerimeterPath" /> + <java-symbol type="string" name="config_batterymeterFillMask" /> + <java-symbol type="string" name="config_batterymeterBoltPath" /> + <java-symbol type="string" name="config_batterymeterPowersavePath" /> + <java-symbol type="bool" name="config_batterymeterDualTone" /> <!-- Accessibility Shortcut --> <java-symbol type="string" name="accessibility_shortcut_warning_dialog_title" /> @@ -3404,6 +3410,7 @@ <java-symbol type="string" name="config_dozeLongPressSensorType" /> <java-symbol type="bool" name="config_dozeWakeLockScreenSensorAvailable" /> + <java-symbol type="integer" name="config_dozeWakeLockScreenDebounce" /> <java-symbol type="array" name="config_allowedGlobalInstantAppSettings" /> <java-symbol type="array" name="config_allowedSystemInstantAppSettings" /> @@ -3661,4 +3668,24 @@ <java-symbol type="array" name="config_displayWhiteBalanceAmbientColorTemperatures" /> <java-symbol type="array" name="config_displayWhiteBalanceDisplayColorTemperatures" /> <java-symbol type="drawable" name="ic_action_open" /> + + <!-- MIME types --> + <java-symbol type="string" name="mime_type_folder" /> + <java-symbol type="string" name="mime_type_apk" /> + <java-symbol type="string" name="mime_type_generic" /> + <java-symbol type="string" name="mime_type_generic_ext" /> + <java-symbol type="string" name="mime_type_audio" /> + <java-symbol type="string" name="mime_type_audio_ext" /> + <java-symbol type="string" name="mime_type_video" /> + <java-symbol type="string" name="mime_type_video_ext" /> + <java-symbol type="string" name="mime_type_image" /> + <java-symbol type="string" name="mime_type_image_ext" /> + <java-symbol type="string" name="mime_type_compressed" /> + <java-symbol type="string" name="mime_type_compressed_ext" /> + <java-symbol type="string" name="mime_type_document" /> + <java-symbol type="string" name="mime_type_document_ext" /> + <java-symbol type="string" name="mime_type_spreadsheet" /> + <java-symbol type="string" name="mime_type_spreadsheet_ext" /> + <java-symbol type="string" name="mime_type_presentation" /> + <java-symbol type="string" name="mime_type_presentation_ext" /> </resources> diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml index 194c86c2fb81..9a95ecca5876 100644 --- a/core/res/res/values/themes_device_defaults.xml +++ b/core/res/res/values/themes_device_defaults.xml @@ -1468,7 +1468,7 @@ easier. <item name="colorEdgeEffect">@android:color/black</item> <!-- Add white nav bar with divider that matches material --> - <item name="navigationBarDividerColor">#1f000000</item> + <item name="navigationBarDividerColor">@color/navigation_bar_divider_device_default_settings</item> <item name="navigationBarColor">@android:color/white</item> <item name="windowLightNavigationBar">true</item> diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp new file mode 100644 index 000000000000..833c7347926b --- /dev/null +++ b/core/tests/coretests/Android.bp @@ -0,0 +1,113 @@ +android_test { + name: "FrameworksCoreTests", + + srcs: [ + "src/**/*.java", + "src/**/I*.aidl", + "DisabledTestApp/src/**/*.java", + "EnabledTestApp/src/**/*.java", + "BinderProxyCountingTestApp/src/**/*.java", + "BinderProxyCountingTestService/src/**/*.java", + "aidl/**/I*.aidl", + ], + + aidl: { + local_include_dirs: ["aidl"], + }, + + dxflags: ["--core-library"], + + aaptflags: [ + "-0 .dat", + "-0 .gld", + "-c fa", + ], + static_libs: [ + "frameworks-base-testutils", + "core-tests-support", + "android-common", + "frameworks-core-util-lib", + "mockwebserver", + "guava", + "androidx.test.espresso.core", + "androidx.test.ext.junit", + "androidx.test.runner", + "androidx.test.rules", + "mockito-target-minus-junit4", + "ub-uiautomator", + "platform-test-annotations", + "truth-prebuilt", + "print-test-util-lib", + "testng", + ], + + libs: [ + "android.test.runner", + "telephony-common", + "testables", + "org.apache.http.legacy", + "android.test.base", + "android.test.mock", + "framework-atb-backward-compatibility", + ], + + platform_apis: true, + test_suites: ["device-tests"], + + certificate: "platform", + + resource_dirs: ["res"], + resource_zips: [":FrameworksCoreTests_apks_as_resources"], +} + +// Rules to copy all the test apks to the intermediate raw resource directory +java_genrule { + name: "FrameworksCoreTests_apks_as_resources", + srcs: [ + ":FrameworksCoreTests_install", + ":FrameworksCoreTests_install_bad_dex", + ":FrameworksCoreTests_install_complete_package_info", + ":FrameworksCoreTests_install_decl_perm", + ":FrameworksCoreTests_install_jni_lib_open_from_apk", + ":FrameworksCoreTests_install_loc_auto", + ":FrameworksCoreTests_install_loc_internal", + ":FrameworksCoreTests_install_loc_sdcard", + ":FrameworksCoreTests_install_loc_unspecified", + ":FrameworksCoreTests_install_multi_package", + ":FrameworksCoreTests_install_split_base", + ":FrameworksCoreTests_install_split_feature_a", + ":FrameworksCoreTests_install_use_perm_good", + ":FrameworksCoreTests_install_uses_feature", + ":FrameworksCoreTests_install_verifier_bad", + ":FrameworksCoreTests_install_verifier_good", + ":FrameworksCoreTests_keyset_permdef_sa_unone", + ":FrameworksCoreTests_keyset_permuse_sa_ua_ub", + ":FrameworksCoreTests_keyset_permuse_sb_ua_ub", + ":FrameworksCoreTests_keyset_sab_ua", + ":FrameworksCoreTests_keyset_sa_ua", + ":FrameworksCoreTests_keyset_sa_uab", + ":FrameworksCoreTests_keyset_sa_ua_ub", + ":FrameworksCoreTests_keyset_sa_ub", + ":FrameworksCoreTests_keyset_sa_unone", + ":FrameworksCoreTests_keyset_sau_ub", + ":FrameworksCoreTests_keyset_sb_ua", + ":FrameworksCoreTests_keyset_sb_ub", + ":FrameworksCoreTests_keyset_splata_api", + ":FrameworksCoreTests_keyset_splat_api", + ":FrameworksCoreTests_locales", + ":FrameworksCoreTests_version_1", + ":FrameworksCoreTests_version_1_diff", + ":FrameworksCoreTests_version_1_nosys", + ":FrameworksCoreTests_version_2", + ":FrameworksCoreTests_version_2_diff", + ":FrameworksCoreTests_version_3", + ], + out: ["FrameworkCoreTests_apks_as_resources.res.zip"], + tools: ["soong_zip"], + + cmd: "mkdir -p $(genDir)/res/raw && " + + "for i in $(in); do " + + " x=$${i##*FrameworksCoreTests_}; echo $${x}; cp $$i $(genDir)/res/raw/$${x%.apk};" + + "done && " + + "$(location soong_zip) -o $(out) -C $(genDir)/res -D $(genDir)/res", +} diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk deleted file mode 100644 index 40ebd44b9bd0..000000000000 --- a/core/tests/coretests/Android.mk +++ /dev/null @@ -1,85 +0,0 @@ -ACTUAL_LOCAL_PATH := $(call my-dir) - -# this var will hold all the test apk module names later. -FrameworkCoreTests_all_apks := - -# We have to include the subdir makefiles first -# so that FrameworkCoreTests_all_apks will be populated correctly. -include $(call all-makefiles-under,$(ACTUAL_LOCAL_PATH)) - -LOCAL_PATH := $(ACTUAL_LOCAL_PATH) - -include $(CLEAR_VARS) - -# We only want this apk build for tests. -LOCAL_MODULE_TAGS := tests - -# Include all test java files. -LOCAL_SRC_FILES := \ - $(call all-java-files-under, src) \ - $(call all-Iaidl-files-under, src) \ - $(call all-java-files-under, DisabledTestApp/src) \ - $(call all-java-files-under, EnabledTestApp/src) \ - $(call all-java-files-under, BinderProxyCountingTestApp/src) \ - $(call all-java-files-under, BinderProxyCountingTestService/src) \ - $(call all-Iaidl-files-under, aidl) - -LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/aidl - -LOCAL_DX_FLAGS := --core-library -LOCAL_JACK_FLAGS := --multi-dex native -LOCAL_AAPT_FLAGS = -0 dat -0 gld -c fa -LOCAL_STATIC_JAVA_LIBRARIES := \ - frameworks-base-testutils \ - core-tests-support \ - android-common \ - frameworks-core-util-lib \ - mockwebserver \ - guava \ - androidx.test.espresso.core \ - androidx.test.ext.junit \ - androidx.test.runner \ - androidx.test.rules \ - mockito-target-minus-junit4 \ - ub-uiautomator \ - platform-test-annotations \ - truth-prebuilt \ - print-test-util-lib \ - testng # TODO: remove once Android migrates to JUnit 4.12, which provide assertThrows - -LOCAL_JAVA_LIBRARIES := \ - android.test.runner \ - telephony-common \ - testables \ - org.apache.http.legacy \ - android.test.base \ - android.test.mock \ - framework-atb-backward-compatibility \ - -LOCAL_PACKAGE_NAME := FrameworksCoreTests -LOCAL_PRIVATE_PLATFORM_APIS := true -LOCAL_COMPATIBILITY_SUITE := device-tests - -LOCAL_CERTIFICATE := platform - -# intermediate dir to include all the test apks as raw resource -FrameworkCoreTests_intermediates := $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME))/test_apks/res -LOCAL_RESOURCE_DIR := $(FrameworkCoreTests_intermediates) $(LOCAL_PATH)/res - -# Disable AAPT2 because the hacks below depend on the AAPT rules implementation -LOCAL_USE_AAPT2 := false - -include $(BUILD_PACKAGE) -# Rules to copy all the test apks to the intermediate raw resource directory -FrameworkCoreTests_all_apks_res := $(addprefix $(FrameworkCoreTests_intermediates)/raw/, \ - $(foreach a, $(FrameworkCoreTests_all_apks), $(patsubst FrameworkCoreTests_%,%,$(a)))) - -$(FrameworkCoreTests_all_apks_res): $(FrameworkCoreTests_intermediates)/raw/%: $(call intermediates-dir-for,APPS,FrameworkCoreTests_%)/package.apk - $(call copy-file-to-new-target) - -# Use R_file_stamp as dependency because we want the test apks in place before the R.java is generated. -$(R_file_stamp) : $(FrameworkCoreTests_all_apks_res) - -FrameworkCoreTests_all_apks := -FrameworkCoreTests_intermediates := -FrameworkCoreTests_all_apks_res := diff --git a/core/tests/coretests/BinderProxyCountingTestApp/Android.bp b/core/tests/coretests/BinderProxyCountingTestApp/Android.bp new file mode 100644 index 000000000000..6279a4873c67 --- /dev/null +++ b/core/tests/coretests/BinderProxyCountingTestApp/Android.bp @@ -0,0 +1,25 @@ +// Copyright (C) 2017 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +android_test_helper_app { + name: "BinderProxyCountingTestApp", + + static_libs: ["coretests-aidl"], + srcs: ["**/*.java"], + + sdk_version: "current", + certificate: "platform", + + test_suites: ["device-tests"], +} diff --git a/core/tests/coretests/BinderProxyCountingTestApp/Android.mk b/core/tests/coretests/BinderProxyCountingTestApp/Android.mk deleted file mode 100644 index 4642694d7b67..000000000000 --- a/core/tests/coretests/BinderProxyCountingTestApp/Android.mk +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (C) 2017 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests - -LOCAL_STATIC_JAVA_LIBRARIES := coretests-aidl -LOCAL_SRC_FILES := $(call all-subdir-java-files) - -LOCAL_PACKAGE_NAME := BinderProxyCountingTestApp -LOCAL_SDK_VERSION := current -LOCAL_CERTIFICATE := platform - -LOCAL_COMPATIBILITY_SUITE := device-tests -include $(BUILD_PACKAGE) - diff --git a/core/tests/coretests/BinderProxyCountingTestService/Android.bp b/core/tests/coretests/BinderProxyCountingTestService/Android.bp new file mode 100644 index 000000000000..22718cb86d66 --- /dev/null +++ b/core/tests/coretests/BinderProxyCountingTestService/Android.bp @@ -0,0 +1,25 @@ +// Copyright (C) 2017 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +android_test_helper_app { + name: "BinderProxyCountingTestService", + + static_libs: ["coretests-aidl"], + srcs: ["**/*.java"], + + platform_apis: true, + certificate: "platform", + + test_suites: ["device-tests"], +} diff --git a/core/tests/coretests/BinderProxyCountingTestService/Android.mk b/core/tests/coretests/BinderProxyCountingTestService/Android.mk deleted file mode 100644 index f852c7afeacd..000000000000 --- a/core/tests/coretests/BinderProxyCountingTestService/Android.mk +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (C) 2017 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests - -LOCAL_STATIC_JAVA_LIBRARIES := coretests-aidl -LOCAL_SRC_FILES := $(call all-subdir-java-files) - -LOCAL_PACKAGE_NAME := BinderProxyCountingTestService -LOCAL_PRIVATE_PLATFORM_APIS := true -LOCAL_CERTIFICATE := platform - -LOCAL_COMPATIBILITY_SUITE := device-tests -include $(BUILD_PACKAGE) - diff --git a/core/tests/coretests/BstatsTestApp/Android.bp b/core/tests/coretests/BstatsTestApp/Android.bp new file mode 100644 index 000000000000..424c71a4d8d0 --- /dev/null +++ b/core/tests/coretests/BstatsTestApp/Android.bp @@ -0,0 +1,34 @@ +// Copyright (C) 2017 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +android_test_helper_app { + name: "BstatsTestApp", + + test_suites: [ + "device-tests", + ], + + static_libs: ["coretests-aidl"], + + srcs: ["**/*.java"], + + sdk_version: "current", + certificate: "platform", + dex_preopt: { + enabled: false, + }, + optimize: { + enabled: false, + }, +} diff --git a/core/tests/coretests/BstatsTestApp/Android.mk b/core/tests/coretests/BstatsTestApp/Android.mk deleted file mode 100644 index a5872a5e5be9..000000000000 --- a/core/tests/coretests/BstatsTestApp/Android.mk +++ /dev/null @@ -1,34 +0,0 @@ -# Copyright (C) 2017 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests - -LOCAL_COMPATIBILITY_SUITE := device-tests - -LOCAL_STATIC_JAVA_LIBRARIES := coretests-aidl - -LOCAL_SRC_FILES := $(call all-subdir-java-files) - -LOCAL_PACKAGE_NAME := BstatsTestApp -LOCAL_SDK_VERSION := current -LOCAL_CERTIFICATE := platform -LOCAL_DEX_PREOPT := false -LOCAL_PROGUARD_ENABLED := disabled - -LOCAL_COMPATIBILITY_SUITE := device-tests -include $(BUILD_PACKAGE) diff --git a/core/tests/coretests/DisabledTestApp/Android.bp b/core/tests/coretests/DisabledTestApp/Android.bp new file mode 100644 index 000000000000..419816e42eff --- /dev/null +++ b/core/tests/coretests/DisabledTestApp/Android.bp @@ -0,0 +1,8 @@ +android_test_helper_app { + name: "DisabledTestApp", + + srcs: ["**/*.java"], + + sdk_version: "current", + certificate: "platform", +} diff --git a/core/tests/coretests/DisabledTestApp/Android.mk b/core/tests/coretests/DisabledTestApp/Android.mk deleted file mode 100644 index e4304f7cef73..000000000000 --- a/core/tests/coretests/DisabledTestApp/Android.mk +++ /dev/null @@ -1,13 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests - -LOCAL_SRC_FILES := $(call all-subdir-java-files) - -LOCAL_PACKAGE_NAME := DisabledTestApp -LOCAL_SDK_VERSION := current -LOCAL_CERTIFICATE := platform - -include $(BUILD_PACKAGE) - diff --git a/core/tests/coretests/EnabledTestApp/Android.bp b/core/tests/coretests/EnabledTestApp/Android.bp new file mode 100644 index 000000000000..bc4f4bd2e4d7 --- /dev/null +++ b/core/tests/coretests/EnabledTestApp/Android.bp @@ -0,0 +1,8 @@ +android_test_helper_app { + name: "EnabledTestApp", + + srcs: ["**/*.java"], + + sdk_version: "current", + certificate: "platform", +} diff --git a/core/tests/coretests/EnabledTestApp/Android.mk b/core/tests/coretests/EnabledTestApp/Android.mk deleted file mode 100644 index cd37f0883c63..000000000000 --- a/core/tests/coretests/EnabledTestApp/Android.mk +++ /dev/null @@ -1,13 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests - -LOCAL_SRC_FILES := $(call all-subdir-java-files) - -LOCAL_PACKAGE_NAME := EnabledTestApp -LOCAL_SDK_VERSION := current -LOCAL_CERTIFICATE := platform - -include $(BUILD_PACKAGE) - diff --git a/core/tests/coretests/aidl/Android.bp b/core/tests/coretests/aidl/Android.bp new file mode 100644 index 000000000000..6e442db78500 --- /dev/null +++ b/core/tests/coretests/aidl/Android.bp @@ -0,0 +1,19 @@ +// Copyright (C) 2017 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +java_test { + name: "coretests-aidl", + sdk_version: "current", + srcs: ["**/*.aidl"], +} diff --git a/core/tests/coretests/aidl/Android.mk b/core/tests/coretests/aidl/Android.mk deleted file mode 100644 index 86e36b61a5ae..000000000000 --- a/core/tests/coretests/aidl/Android.mk +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (C) 2017 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_MODULE_TAGS := tests -LOCAL_SDK_VERSION := current -LOCAL_SRC_FILES := $(call all-subdir-Iaidl-files) -LOCAL_MODULE := coretests-aidl -include $(BUILD_STATIC_JAVA_LIBRARY)
\ No newline at end of file diff --git a/core/tests/coretests/apks/Android.bp b/core/tests/coretests/apks/Android.bp new file mode 100644 index 000000000000..20c87b2d2ce9 --- /dev/null +++ b/core/tests/coretests/apks/Android.bp @@ -0,0 +1,7 @@ +java_defaults { + name: "FrameworksCoreTests_apks_defaults", + sdk_version: "current", + + // Every package should have a native library + jni_libs: ["libframeworks_coretests_jni"], +} diff --git a/core/tests/coretests/apks/Android.mk b/core/tests/coretests/apks/Android.mk deleted file mode 100644 index 98c0c2aee38b..000000000000 --- a/core/tests/coretests/apks/Android.mk +++ /dev/null @@ -1,7 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -FrameworkCoreTests_BUILD_PACKAGE := $(LOCAL_PATH)/FrameworkCoreTests_apk.mk - -# build sub packages -include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/core/tests/coretests/apks/FrameworkCoreTests_apk.mk b/core/tests/coretests/apks/FrameworkCoreTests_apk.mk deleted file mode 100644 index 8a7d72a5200b..000000000000 --- a/core/tests/coretests/apks/FrameworkCoreTests_apk.mk +++ /dev/null @@ -1,16 +0,0 @@ - -LOCAL_MODULE_TAGS := tests - -# Disable dexpreopt. -LOCAL_DEX_PREOPT := false - -# Make sure every package name gets the FrameworkCoreTests_ prefix. -LOCAL_PACKAGE_NAME := FrameworkCoreTests_$(LOCAL_PACKAGE_NAME) -LOCAL_SDK_VERSION := current - -# Every package should have a native library -LOCAL_JNI_SHARED_LIBRARIES := libframeworks_coretests_jni - -FrameworkCoreTests_all_apks += $(LOCAL_PACKAGE_NAME) - -include $(BUILD_PACKAGE) diff --git a/core/tests/coretests/apks/install-split-base/Android.bp b/core/tests/coretests/apks/install-split-base/Android.bp new file mode 100644 index 000000000000..ddf75b224359 --- /dev/null +++ b/core/tests/coretests/apks/install-split-base/Android.bp @@ -0,0 +1,6 @@ +android_test_helper_app { + name: "FrameworksCoreTests_install_split_base", + defaults: ["FrameworksCoreTests_apks_defaults"], + + srcs: ["**/*.java"], +} diff --git a/core/tests/coretests/apks/install-split-base/Android.mk b/core/tests/coretests/apks/install-split-base/Android.mk deleted file mode 100644 index 5b60e3167fc7..000000000000 --- a/core/tests/coretests/apks/install-split-base/Android.mk +++ /dev/null @@ -1,10 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests - -LOCAL_SRC_FILES := $(call all-subdir-java-files) - -LOCAL_PACKAGE_NAME := install_split_base - -include $(FrameworkCoreTests_BUILD_PACKAGE)
\ No newline at end of file diff --git a/core/tests/coretests/apks/install-split-feature-a/Android.bp b/core/tests/coretests/apks/install-split-feature-a/Android.bp new file mode 100644 index 000000000000..9ec9893cc408 --- /dev/null +++ b/core/tests/coretests/apks/install-split-feature-a/Android.bp @@ -0,0 +1,11 @@ +android_test_helper_app { + name: "FrameworksCoreTests_install_split_feature_a", + defaults: ["FrameworksCoreTests_apks_defaults"], + + srcs: ["**/*.java"], + + aaptflags: [ + "--custom-package com.google.android.dexapis.splitapp.feature_a", + "--package-id 0x80", + ], +} diff --git a/core/tests/coretests/apks/install-split-feature-a/Android.mk b/core/tests/coretests/apks/install-split-feature-a/Android.mk deleted file mode 100644 index 0f37d16a7688..000000000000 --- a/core/tests/coretests/apks/install-split-feature-a/Android.mk +++ /dev/null @@ -1,14 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests - -LOCAL_SRC_FILES := $(call all-subdir-java-files) - -LOCAL_PACKAGE_NAME := install_split_feature_a - -LOCAL_USE_AAPT2 := true -LOCAL_AAPT_FLAGS += --custom-package com.google.android.dexapis.splitapp.feature_a -LOCAL_AAPT_FLAGS += --package-id 0x80 - -include $(FrameworkCoreTests_BUILD_PACKAGE)
\ No newline at end of file diff --git a/core/tests/coretests/apks/install/Android.bp b/core/tests/coretests/apks/install/Android.bp new file mode 100644 index 000000000000..e783fe2ec2dc --- /dev/null +++ b/core/tests/coretests/apks/install/Android.bp @@ -0,0 +1,6 @@ +android_test_helper_app { + name: "FrameworksCoreTests_install", + defaults: ["FrameworksCoreTests_apks_defaults"], + + srcs: ["**/*.java"], +} diff --git a/core/tests/coretests/apks/install/Android.mk b/core/tests/coretests/apks/install/Android.mk deleted file mode 100644 index b38dc20a181e..000000000000 --- a/core/tests/coretests/apks/install/Android.mk +++ /dev/null @@ -1,8 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := $(call all-subdir-java-files) - -LOCAL_PACKAGE_NAME := install - -include $(FrameworkCoreTests_BUILD_PACKAGE) diff --git a/core/tests/coretests/apks/install_bad_dex/Android.bp b/core/tests/coretests/apks/install_bad_dex/Android.bp new file mode 100644 index 000000000000..d156793cf22a --- /dev/null +++ b/core/tests/coretests/apks/install_bad_dex/Android.bp @@ -0,0 +1,23 @@ +android_test_helper_app { + name: "FrameworksCoreTests_install_bad_dex_", + defaults: ["FrameworksCoreTests_apks_defaults"], + + srcs: ["src/**/*.java"], +} + +// Inject bad classes.dex file. +java_genrule { + name: "FrameworksCoreTests_install_bad_dex", + tools: [ + "soong_zip", + "merge_zips", + ], + srcs: [ + ":FrameworksCoreTests_install_bad_dex_", + "classes.dex", + ], + out: ["FrameworksCoreTests_install_bad_dex.apk"], + cmd: "$(location soong_zip) -o $(genDir)/classes.dex.zip -j -f $(location classes.dex) && " + + "$(location merge_zips) -ignore-duplicates $(out) $(genDir)/classes.dex.zip " + + "$(location :FrameworksCoreTests_install_bad_dex_)", +} diff --git a/core/tests/coretests/apks/install_bad_dex/Android.mk b/core/tests/coretests/apks/install_bad_dex/Android.mk deleted file mode 100644 index 05983aa6ec7b..000000000000 --- a/core/tests/coretests/apks/install_bad_dex/Android.mk +++ /dev/null @@ -1,11 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := $(call all-subdir-java-files) - -LOCAL_PACKAGE_NAME := install_bad_dex - -include $(FrameworkCoreTests_BUILD_PACKAGE) - -# Override target specific variable PRIVATE_DEX_FILE to inject bad classes.dex file. -$(LOCAL_BUILT_MODULE): PRIVATE_DEX_FILE := $(LOCAL_PATH)/classes.dex diff --git a/core/tests/coretests/apks/install_complete_package_info/Android.bp b/core/tests/coretests/apks/install_complete_package_info/Android.bp new file mode 100644 index 000000000000..123558bda076 --- /dev/null +++ b/core/tests/coretests/apks/install_complete_package_info/Android.bp @@ -0,0 +1,7 @@ +android_test_helper_app { + name: "FrameworksCoreTests_install_complete_package_info", + defaults: ["FrameworksCoreTests_apks_defaults"], + + srcs: ["**/*.java"], +} + diff --git a/core/tests/coretests/apks/install_complete_package_info/Android.mk b/core/tests/coretests/apks/install_complete_package_info/Android.mk deleted file mode 100644 index 19bf3561a706..000000000000 --- a/core/tests/coretests/apks/install_complete_package_info/Android.mk +++ /dev/null @@ -1,13 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests - -LOCAL_SRC_FILES := $(call all-subdir-java-files) - -LOCAL_PACKAGE_NAME := install_complete_package_info -#LOCAL_MANIFEST_FILE := api_test/AndroidManifest.xml - -include $(FrameworkCoreTests_BUILD_PACKAGE) -#include $(BUILD_PACKAGE) - diff --git a/core/tests/coretests/apks/install_decl_perm/Android.bp b/core/tests/coretests/apks/install_decl_perm/Android.bp new file mode 100644 index 000000000000..868e8b51bd49 --- /dev/null +++ b/core/tests/coretests/apks/install_decl_perm/Android.bp @@ -0,0 +1,6 @@ +android_test_helper_app { + name: "FrameworksCoreTests_install_decl_perm", + defaults: ["FrameworksCoreTests_apks_defaults"], + + srcs: ["**/*.java"], +} diff --git a/core/tests/coretests/apks/install_decl_perm/Android.mk b/core/tests/coretests/apks/install_decl_perm/Android.mk deleted file mode 100644 index 86370c8017a5..000000000000 --- a/core/tests/coretests/apks/install_decl_perm/Android.mk +++ /dev/null @@ -1,8 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := $(call all-subdir-java-files) - -LOCAL_PACKAGE_NAME := install_decl_perm - -include $(FrameworkCoreTests_BUILD_PACKAGE) diff --git a/core/tests/coretests/apks/install_jni_lib/Android.bp b/core/tests/coretests/apks/install_jni_lib/Android.bp index c1a6bd0b3d21..f20f59958736 100644 --- a/core/tests/coretests/apks/install_jni_lib/Android.bp +++ b/core/tests/coretests/apks/install_jni_lib/Android.bp @@ -14,6 +14,7 @@ cc_test_library { name: "libframeworks_coretests_jni", + defaults: ["FrameworksCoreTests_apks_defaults"], srcs: ["com_android_frameworks_coretests_JNITest.cpp"], diff --git a/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.bp b/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.bp new file mode 100644 index 000000000000..602b704a0a5d --- /dev/null +++ b/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.bp @@ -0,0 +1,6 @@ +android_test_helper_app { + name: "FrameworksCoreTests_install_jni_lib_open_from_apk", + defaults: ["FrameworksCoreTests_apks_defaults"], + + srcs: ["**/*.java"], +} diff --git a/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.mk b/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.mk deleted file mode 100644 index 6b3b55ed39e9..000000000000 --- a/core/tests/coretests/apks/install_jni_lib_open_from_apk/Android.mk +++ /dev/null @@ -1,8 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := $(call all-subdir-java-files) - -LOCAL_PACKAGE_NAME := install_jni_lib_open_from_apk - -include $(FrameworkCoreTests_BUILD_PACKAGE) diff --git a/core/tests/coretests/apks/install_loc_auto/Android.bp b/core/tests/coretests/apks/install_loc_auto/Android.bp new file mode 100644 index 000000000000..6393915ba283 --- /dev/null +++ b/core/tests/coretests/apks/install_loc_auto/Android.bp @@ -0,0 +1,6 @@ +android_test_helper_app { + name: "FrameworksCoreTests_install_loc_auto", + defaults: ["FrameworksCoreTests_apks_defaults"], + + srcs: ["**/*.java"], +} diff --git a/core/tests/coretests/apks/install_loc_auto/Android.mk b/core/tests/coretests/apks/install_loc_auto/Android.mk deleted file mode 100644 index 6435f3624a15..000000000000 --- a/core/tests/coretests/apks/install_loc_auto/Android.mk +++ /dev/null @@ -1,8 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := $(call all-subdir-java-files) - -LOCAL_PACKAGE_NAME := install_loc_auto - -include $(FrameworkCoreTests_BUILD_PACKAGE) diff --git a/core/tests/coretests/apks/install_loc_internal/Android.bp b/core/tests/coretests/apks/install_loc_internal/Android.bp new file mode 100644 index 000000000000..770aaa511b9d --- /dev/null +++ b/core/tests/coretests/apks/install_loc_internal/Android.bp @@ -0,0 +1,6 @@ +android_test_helper_app { + name: "FrameworksCoreTests_install_loc_internal", + defaults: ["FrameworksCoreTests_apks_defaults"], + + srcs: ["**/*.java"], +} diff --git a/core/tests/coretests/apks/install_loc_internal/Android.mk b/core/tests/coretests/apks/install_loc_internal/Android.mk deleted file mode 100644 index 8cc8b8e0e778..000000000000 --- a/core/tests/coretests/apks/install_loc_internal/Android.mk +++ /dev/null @@ -1,8 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := $(call all-subdir-java-files) - -LOCAL_PACKAGE_NAME := install_loc_internal - -include $(FrameworkCoreTests_BUILD_PACKAGE) diff --git a/core/tests/coretests/apks/install_loc_sdcard/Android.bp b/core/tests/coretests/apks/install_loc_sdcard/Android.bp new file mode 100644 index 000000000000..177940102e83 --- /dev/null +++ b/core/tests/coretests/apks/install_loc_sdcard/Android.bp @@ -0,0 +1,6 @@ +android_test_helper_app { + name: "FrameworksCoreTests_install_loc_sdcard", + defaults: ["FrameworksCoreTests_apks_defaults"], + + srcs: ["**/*.java"], +} diff --git a/core/tests/coretests/apks/install_loc_sdcard/Android.mk b/core/tests/coretests/apks/install_loc_sdcard/Android.mk deleted file mode 100644 index e1411c2276a5..000000000000 --- a/core/tests/coretests/apks/install_loc_sdcard/Android.mk +++ /dev/null @@ -1,8 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := $(call all-subdir-java-files) - -LOCAL_PACKAGE_NAME := install_loc_sdcard - -include $(FrameworkCoreTests_BUILD_PACKAGE) diff --git a/core/tests/coretests/apks/install_loc_unspecified/Android.bp b/core/tests/coretests/apks/install_loc_unspecified/Android.bp new file mode 100644 index 000000000000..21c0f82b44ff --- /dev/null +++ b/core/tests/coretests/apks/install_loc_unspecified/Android.bp @@ -0,0 +1,6 @@ +android_test_helper_app { + name: "FrameworksCoreTests_install_loc_unspecified", + defaults: ["FrameworksCoreTests_apks_defaults"], + + srcs: ["**/*.java"], +} diff --git a/core/tests/coretests/apks/install_loc_unspecified/Android.mk b/core/tests/coretests/apks/install_loc_unspecified/Android.mk deleted file mode 100644 index 0741d04b61d5..000000000000 --- a/core/tests/coretests/apks/install_loc_unspecified/Android.mk +++ /dev/null @@ -1,8 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := $(call all-subdir-java-files) - -LOCAL_PACKAGE_NAME := install_loc_unspecified - -include $(FrameworkCoreTests_BUILD_PACKAGE) diff --git a/core/tests/coretests/apks/install_multi_package/Android.bp b/core/tests/coretests/apks/install_multi_package/Android.bp new file mode 100644 index 000000000000..249242e239e4 --- /dev/null +++ b/core/tests/coretests/apks/install_multi_package/Android.bp @@ -0,0 +1,6 @@ +android_test_helper_app { + name: "FrameworksCoreTests_install_multi_package", + defaults: ["FrameworksCoreTests_apks_defaults"], + + srcs: ["**/*.java"], +} diff --git a/core/tests/coretests/apks/install_multi_package/Android.mk b/core/tests/coretests/apks/install_multi_package/Android.mk deleted file mode 100644 index 3f163def9ce0..000000000000 --- a/core/tests/coretests/apks/install_multi_package/Android.mk +++ /dev/null @@ -1,14 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests - -LOCAL_SRC_FILES := $(call all-subdir-java-files) - -LOCAL_PACKAGE_NAME := install_multi_package - -LOCAL_USE_AAPT2 := true - -include $(FrameworkCoreTests_BUILD_PACKAGE) -#include $(BUILD_PACKAGE) - diff --git a/core/tests/coretests/apks/install_use_perm_good/Android.bp b/core/tests/coretests/apks/install_use_perm_good/Android.bp new file mode 100644 index 000000000000..bb41ebb2fdca --- /dev/null +++ b/core/tests/coretests/apks/install_use_perm_good/Android.bp @@ -0,0 +1,6 @@ +android_test_helper_app { + name: "FrameworksCoreTests_install_use_perm_good", + defaults: ["FrameworksCoreTests_apks_defaults"], + + srcs: ["**/*.java"], +} diff --git a/core/tests/coretests/apks/install_use_perm_good/Android.mk b/core/tests/coretests/apks/install_use_perm_good/Android.mk deleted file mode 100644 index e2661a13793a..000000000000 --- a/core/tests/coretests/apks/install_use_perm_good/Android.mk +++ /dev/null @@ -1,8 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := $(call all-subdir-java-files) - -LOCAL_PACKAGE_NAME := install_use_perm_good - -include $(FrameworkCoreTests_BUILD_PACKAGE) diff --git a/core/tests/coretests/apks/install_uses_feature/Android.bp b/core/tests/coretests/apks/install_uses_feature/Android.bp new file mode 100644 index 000000000000..0ec747b0f151 --- /dev/null +++ b/core/tests/coretests/apks/install_uses_feature/Android.bp @@ -0,0 +1,6 @@ +android_test_helper_app { + name: "FrameworksCoreTests_install_uses_feature", + defaults: ["FrameworksCoreTests_apks_defaults"], + + srcs: ["**/*.java"], +} diff --git a/core/tests/coretests/apks/install_uses_feature/Android.mk b/core/tests/coretests/apks/install_uses_feature/Android.mk deleted file mode 100644 index b60d734fe635..000000000000 --- a/core/tests/coretests/apks/install_uses_feature/Android.mk +++ /dev/null @@ -1,8 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := $(call all-subdir-java-files) - -LOCAL_PACKAGE_NAME := install_uses_feature - -include $(FrameworkCoreTests_BUILD_PACKAGE) diff --git a/core/tests/coretests/apks/install_verifier_bad/Android.bp b/core/tests/coretests/apks/install_verifier_bad/Android.bp new file mode 100644 index 000000000000..1265739a3107 --- /dev/null +++ b/core/tests/coretests/apks/install_verifier_bad/Android.bp @@ -0,0 +1,6 @@ +android_test_helper_app { + name: "FrameworksCoreTests_install_verifier_bad", + defaults: ["FrameworksCoreTests_apks_defaults"], + + srcs: ["**/*.java"], +} diff --git a/core/tests/coretests/apks/install_verifier_bad/Android.mk b/core/tests/coretests/apks/install_verifier_bad/Android.mk deleted file mode 100644 index 745b4d32ccc4..000000000000 --- a/core/tests/coretests/apks/install_verifier_bad/Android.mk +++ /dev/null @@ -1,10 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := $(call all-subdir-java-files) - -LOCAL_PACKAGE_NAME := install_verifier_bad - -LOCAL_USE_AAPT2 := true - -include $(FrameworkCoreTests_BUILD_PACKAGE) diff --git a/core/tests/coretests/apks/install_verifier_good/Android.bp b/core/tests/coretests/apks/install_verifier_good/Android.bp new file mode 100644 index 000000000000..4911ffbd2020 --- /dev/null +++ b/core/tests/coretests/apks/install_verifier_good/Android.bp @@ -0,0 +1,6 @@ +android_test_helper_app { + name: "FrameworksCoreTests_install_verifier_good", + defaults: ["FrameworksCoreTests_apks_defaults"], + + srcs: ["**/*.java"], +} diff --git a/core/tests/coretests/apks/install_verifier_good/Android.mk b/core/tests/coretests/apks/install_verifier_good/Android.mk deleted file mode 100644 index 150fd8dd8701..000000000000 --- a/core/tests/coretests/apks/install_verifier_good/Android.mk +++ /dev/null @@ -1,10 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := $(call all-subdir-java-files) - -LOCAL_PACKAGE_NAME := install_verifier_good - -LOCAL_USE_AAPT2 := true - -include $(FrameworkCoreTests_BUILD_PACKAGE) diff --git a/core/tests/coretests/apks/keyset/Android.bp b/core/tests/coretests/apks/keyset/Android.bp new file mode 100644 index 000000000000..e252b081341a --- /dev/null +++ b/core/tests/coretests/apks/keyset/Android.bp @@ -0,0 +1,120 @@ +//apks signed by keyset_A +android_test_helper_app { + name: "FrameworksCoreTests_keyset_sa_unone", + defaults: ["FrameworksCoreTests_apks_defaults"], + srcs: ["**/*.java"], + certificate: ":FrameworksCoreTests_keyset_A_cert", + manifest: "uNone/AndroidManifest.xml", +} + +android_test_helper_app { + name: "FrameworksCoreTests_keyset_sa_ua", + defaults: ["FrameworksCoreTests_apks_defaults"], + srcs: ["**/*.java"], + certificate: ":FrameworksCoreTests_keyset_A_cert", + manifest: "uA/AndroidManifest.xml", +} + +android_test_helper_app { + name: "FrameworksCoreTests_keyset_sa_ub", + defaults: ["FrameworksCoreTests_apks_defaults"], + srcs: ["**/*.java"], + certificate: ":FrameworksCoreTests_keyset_A_cert", + manifest: "uB/AndroidManifest.xml", +} + +android_test_helper_app { + name: "FrameworksCoreTests_keyset_sa_uab", + defaults: ["FrameworksCoreTests_apks_defaults"], + srcs: ["**/*.java"], + certificate: ":FrameworksCoreTests_keyset_A_cert", + manifest: "uAB/AndroidManifest.xml", +} + +android_test_helper_app { + name: "FrameworksCoreTests_keyset_sa_ua_ub", + defaults: ["FrameworksCoreTests_apks_defaults"], + srcs: ["**/*.java"], + certificate: ":FrameworksCoreTests_keyset_A_cert", + manifest: "uAuB/AndroidManifest.xml", +} + +android_test_helper_app { + name: "FrameworksCoreTests_keyset_permdef_sa_unone", + defaults: ["FrameworksCoreTests_apks_defaults"], + srcs: ["**/*.java"], + certificate: ":FrameworksCoreTests_keyset_A_cert", + manifest: "permDef/AndroidManifest.xml", +} + +android_test_helper_app { + name: "FrameworksCoreTests_keyset_permuse_sa_ua_ub", + defaults: ["FrameworksCoreTests_apks_defaults"], + srcs: ["**/*.java"], + certificate: ":FrameworksCoreTests_keyset_A_cert", + manifest: "permUse/AndroidManifest.xml", +} + +//apks signed by keyset_B +android_test_helper_app { + name: "FrameworksCoreTests_keyset_sb_ua", + defaults: ["FrameworksCoreTests_apks_defaults"], + srcs: ["**/*.java"], + certificate: ":FrameworksCoreTests_keyset_B_cert", + manifest: "uA/AndroidManifest.xml", +} + +android_test_helper_app { + name: "FrameworksCoreTests_keyset_sb_ub", + defaults: ["FrameworksCoreTests_apks_defaults"], + srcs: ["**/*.java"], + certificate: ":FrameworksCoreTests_keyset_B_cert", + manifest: "uB/AndroidManifest.xml", +} + +android_test_helper_app { + name: "FrameworksCoreTests_keyset_permuse_sb_ua_ub", + defaults: ["FrameworksCoreTests_apks_defaults"], + srcs: ["**/*.java"], + certificate: ":FrameworksCoreTests_keyset_B_cert", + manifest: "permUse/AndroidManifest.xml", +} + +//apks signed by keyset_A and keyset_B +android_test_helper_app { + name: "FrameworksCoreTests_keyset_sab_ua", + defaults: ["FrameworksCoreTests_apks_defaults"], + srcs: ["**/*.java"], + certificate: ":FrameworksCoreTests_keyset_A_cert", + additional_certificates: [":FrameworksCoreTests_keyset_B_cert"], + manifest: "uA/AndroidManifest.xml", +} + +//apks signed by keyset_A and unit_test +android_test_helper_app { + name: "FrameworksCoreTests_keyset_sau_ub", + defaults: ["FrameworksCoreTests_apks_defaults"], + srcs: ["**/*.java"], + certificate: ":FrameworksCoreTests_keyset_A_cert", + additional_certificates: [":FrameworksCoreTests_keyset_B_cert"], + manifest: "uB/AndroidManifest.xml", +} + +//apks signed by platform only +android_test_helper_app { + name: "FrameworksCoreTests_keyset_splat_api", + defaults: ["FrameworksCoreTests_apks_defaults"], + srcs: ["**/*.java"], + certificate: "platform", + manifest: "api_test/AndroidManifest.xml", +} + +//apks signed by platform and keyset_A +android_test_helper_app { + name: "FrameworksCoreTests_keyset_splata_api", + defaults: ["FrameworksCoreTests_apks_defaults"], + srcs: ["**/*.java"], + certificate: "platform", + additional_certificates: [":FrameworksCoreTests_keyset_A_cert"], + manifest: "api_test/AndroidManifest.xml", +} diff --git a/core/tests/coretests/apks/keyset/Android.mk b/core/tests/coretests/apks/keyset/Android.mk deleted file mode 100644 index 306dc90118f7..000000000000 --- a/core/tests/coretests/apks/keyset/Android.mk +++ /dev/null @@ -1,108 +0,0 @@ -LOCAL_PATH:= $(call my-dir) - -#apks signed by keyset_A -include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_PACKAGE_NAME := keyset_sa_unone -LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_A -LOCAL_MANIFEST_FILE := uNone/AndroidManifest.xml -include $(FrameworkCoreTests_BUILD_PACKAGE) - -include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_PACKAGE_NAME := keyset_sa_ua -LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_A -LOCAL_MANIFEST_FILE := uA/AndroidManifest.xml -include $(FrameworkCoreTests_BUILD_PACKAGE) - -include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_PACKAGE_NAME := keyset_sa_ub -LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_A -LOCAL_MANIFEST_FILE := uB/AndroidManifest.xml -include $(FrameworkCoreTests_BUILD_PACKAGE) - -include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_PACKAGE_NAME := keyset_sa_uab -LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_A -LOCAL_MANIFEST_FILE := uAB/AndroidManifest.xml -include $(FrameworkCoreTests_BUILD_PACKAGE) - -include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_PACKAGE_NAME := keyset_sa_ua_ub -LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_A -LOCAL_MANIFEST_FILE := uAuB/AndroidManifest.xml -include $(FrameworkCoreTests_BUILD_PACKAGE) - -include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_PACKAGE_NAME := keyset_permdef_sa_unone -LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_A -LOCAL_MANIFEST_FILE := permDef/AndroidManifest.xml -include $(FrameworkCoreTests_BUILD_PACKAGE) - -include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_PACKAGE_NAME := keyset_permuse_sa_ua_ub -LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_A -LOCAL_MANIFEST_FILE := permUse/AndroidManifest.xml -include $(FrameworkCoreTests_BUILD_PACKAGE) - -#apks signed by keyset_B -include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_PACKAGE_NAME := keyset_sb_ua -LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_B -LOCAL_MANIFEST_FILE := uA/AndroidManifest.xml -include $(FrameworkCoreTests_BUILD_PACKAGE) - -include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_PACKAGE_NAME := keyset_sb_ub -LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_B -LOCAL_MANIFEST_FILE := uB/AndroidManifest.xml -include $(FrameworkCoreTests_BUILD_PACKAGE) - -include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_PACKAGE_NAME := keyset_permuse_sb_ua_ub -LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_B -LOCAL_MANIFEST_FILE := permUse/AndroidManifest.xml -include $(FrameworkCoreTests_BUILD_PACKAGE) - -#apks signed by keyset_A and keyset_B -include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_PACKAGE_NAME := keyset_sab_ua -LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_A -LOCAL_ADDITIONAL_CERTIFICATES := $(LOCAL_PATH)/../../certs/keyset_B -LOCAL_MANIFEST_FILE := uA/AndroidManifest.xml -include $(FrameworkCoreTests_BUILD_PACKAGE) - -#apks signed by keyset_A and unit_test -include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_PACKAGE_NAME := keyset_sau_ub -LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_A -LOCAL_ADDITIONAL_CERTIFICATES := $(LOCAL_PATH)/../../certs/keyset_B -LOCAL_MANIFEST_FILE := uB/AndroidManifest.xml -include $(FrameworkCoreTests_BUILD_PACKAGE) - -#apks signed by platform only -include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_PACKAGE_NAME := keyset_splat_api -LOCAL_CERTIFICATE := platform -LOCAL_MANIFEST_FILE := api_test/AndroidManifest.xml -include $(FrameworkCoreTests_BUILD_PACKAGE) - -#apks signed by platform and keyset_A -include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_PACKAGE_NAME := keyset_splata_api -LOCAL_CERTIFICATE := platform -LOCAL_ADDITIONAL_CERTIFICATES := $(LOCAL_PATH)/../../certs/keyset_A -LOCAL_MANIFEST_FILE := api_test/AndroidManifest.xml -include $(FrameworkCoreTests_BUILD_PACKAGE)
\ No newline at end of file diff --git a/core/tests/coretests/apks/locales/Android.bp b/core/tests/coretests/apks/locales/Android.bp new file mode 100644 index 000000000000..4a730ef53404 --- /dev/null +++ b/core/tests/coretests/apks/locales/Android.bp @@ -0,0 +1,6 @@ +android_test_helper_app { + name: "FrameworksCoreTests_locales", + defaults: ["FrameworksCoreTests_apks_defaults"], + + srcs: ["**/*.java"], +} diff --git a/core/tests/coretests/apks/locales/Android.mk b/core/tests/coretests/apks/locales/Android.mk deleted file mode 100644 index 9cb13dd4cd0e..000000000000 --- a/core/tests/coretests/apks/locales/Android.mk +++ /dev/null @@ -1,8 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := $(call all-subdir-java-files) - -LOCAL_PACKAGE_NAME := locales - -include $(FrameworkCoreTests_BUILD_PACKAGE) diff --git a/core/tests/coretests/apks/version/Android.bp b/core/tests/coretests/apks/version/Android.bp new file mode 100644 index 000000000000..371ccfc301cb --- /dev/null +++ b/core/tests/coretests/apks/version/Android.bp @@ -0,0 +1,54 @@ +android_test_helper_app { + name: "FrameworksCoreTests_version_1", + defaults: ["FrameworksCoreTests_apks_defaults"], + srcs: ["**/*.java"], + aaptflags: [ + "--version-code 1", + "--version-name 1.0", + ], + certificate: ":FrameworksCoreTests_unit_test_cert", +} + +android_test_helper_app { + name: "FrameworksCoreTests_version_2", + defaults: ["FrameworksCoreTests_apks_defaults"], + srcs: ["**/*.java"], + aaptflags: [ + "--version-code 2", + "--version-name 2.0", + ], + certificate: ":FrameworksCoreTests_unit_test_cert", +} + +android_test_helper_app { + name: "FrameworksCoreTests_version_3", + defaults: ["FrameworksCoreTests_apks_defaults"], + srcs: ["**/*.java"], + aaptflags: [ + "--version-code 3", + "--version-name 3.0", + ], + certificate: ":FrameworksCoreTests_unit_test_cert", +} + +android_test_helper_app { + name: "FrameworksCoreTests_version_1_diff", + defaults: ["FrameworksCoreTests_apks_defaults"], + srcs: ["**/*.java"], + aaptflags: [ + "--version-code 1", + "--version-name 1.0", + ], + certificate: ":FrameworksCoreTests_unit_test_cert", +} + +android_test_helper_app { + name: "FrameworksCoreTests_version_2_diff", + defaults: ["FrameworksCoreTests_apks_defaults"], + srcs: ["**/*.java"], + aaptflags: [ + "--version-code 2", + "--version-name 2.0", + ], + certificate: ":FrameworksCoreTests_unit_test_cert", +} diff --git a/core/tests/coretests/apks/version/Android.mk b/core/tests/coretests/apks/version/Android.mk deleted file mode 100644 index 3635a581dee5..000000000000 --- a/core/tests/coretests/apks/version/Android.mk +++ /dev/null @@ -1,36 +0,0 @@ -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_PACKAGE_NAME := version_1 -LOCAL_AAPT_FLAGS := --version-code 1 --version-name 1.0 -LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/unit_test -include $(FrameworkCoreTests_BUILD_PACKAGE) - -include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_PACKAGE_NAME := version_2 -LOCAL_AAPT_FLAGS := --version-code 2 --version-name 2.0 -LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/unit_test -include $(FrameworkCoreTests_BUILD_PACKAGE) - -include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_PACKAGE_NAME := version_3 -LOCAL_AAPT_FLAGS := --version-code 3 --version-name 3.0 -LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/unit_test -include $(FrameworkCoreTests_BUILD_PACKAGE) - -include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_PACKAGE_NAME := version_1_diff -LOCAL_AAPT_FLAGS := --version-code 1 --version-name 1.0 -LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/unit_test_diff -include $(FrameworkCoreTests_BUILD_PACKAGE) - -include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_PACKAGE_NAME := version_2_diff -LOCAL_AAPT_FLAGS := --version-code 2 --version-name 2.0 -LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/unit_test_diff -include $(FrameworkCoreTests_BUILD_PACKAGE) diff --git a/core/tests/coretests/apks/version_nosys/Android.bp b/core/tests/coretests/apks/version_nosys/Android.bp new file mode 100644 index 000000000000..575667822393 --- /dev/null +++ b/core/tests/coretests/apks/version_nosys/Android.bp @@ -0,0 +1,10 @@ +android_test_helper_app { + name: "FrameworksCoreTests_version_1_nosys", + defaults: ["FrameworksCoreTests_apks_defaults"], + srcs: ["**/*.java"], + aaptflags: [ + "--version-code 1", + "--version-name 1.0", + ], + certificate: ":FrameworksCoreTests_unit_test_cert", +} diff --git a/core/tests/coretests/apks/version_nosys/Android.mk b/core/tests/coretests/apks/version_nosys/Android.mk deleted file mode 100644 index bbc8e12afe8d..000000000000 --- a/core/tests/coretests/apks/version_nosys/Android.mk +++ /dev/null @@ -1,9 +0,0 @@ -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_PACKAGE_NAME := version_1_nosys -LOCAL_AAPT_FLAGS := --version-code 1 --version-name 1.0 -LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/unit_test -include $(FrameworkCoreTests_BUILD_PACKAGE) - diff --git a/core/tests/coretests/certs/Android.bp b/core/tests/coretests/certs/Android.bp new file mode 100644 index 000000000000..bd5c8293f6f6 --- /dev/null +++ b/core/tests/coretests/certs/Android.bp @@ -0,0 +1,14 @@ +android_app_certificate { + name: "FrameworksCoreTests_keyset_A_cert", + certificate: "keyset_A", +} + +android_app_certificate { + name: "FrameworksCoreTests_keyset_B_cert", + certificate: "keyset_B", +} + +android_app_certificate { + name: "FrameworksCoreTests_unit_test_cert", + certificate: "unit_test", +} diff --git a/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java b/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java index 5731daa0b2a9..4ae9494aa362 100644 --- a/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java +++ b/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java @@ -99,7 +99,8 @@ public class PasswordMetricsTest { @Test public void testComputeForPassword_metrics() { - final PasswordMetrics metrics = PasswordMetrics.computeForPassword("6B~0z1Z3*8A"); + final PasswordMetrics metrics = + PasswordMetrics.computeForPassword("6B~0z1Z3*8A".getBytes()); assertEquals(11, metrics.length); assertEquals(4, metrics.letters); assertEquals(3, metrics.upperCase); @@ -112,32 +113,32 @@ public class PasswordMetricsTest { @Test public void testComputeForPassword_quality() { assertEquals(DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC, - PasswordMetrics.computeForPassword("a1").quality); + PasswordMetrics.computeForPassword("a1".getBytes()).quality); assertEquals(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC, - PasswordMetrics.computeForPassword("a").quality); + PasswordMetrics.computeForPassword("a".getBytes()).quality); assertEquals(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC, - PasswordMetrics.computeForPassword("*~&%$").quality); + PasswordMetrics.computeForPassword("*~&%$".getBytes()).quality); assertEquals(DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX, - PasswordMetrics.computeForPassword("1").quality); + PasswordMetrics.computeForPassword("1".getBytes()).quality); // contains a long sequence so isn't complex assertEquals(PASSWORD_QUALITY_NUMERIC, - PasswordMetrics.computeForPassword("1234").quality); + PasswordMetrics.computeForPassword("1234".getBytes()).quality); assertEquals(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, - PasswordMetrics.computeForPassword("").quality); + PasswordMetrics.computeForPassword("".getBytes()).quality); } @Test public void testMaxLengthSequence() { - assertEquals(4, PasswordMetrics.maxLengthSequence("1234")); - assertEquals(5, PasswordMetrics.maxLengthSequence("13579")); - assertEquals(4, PasswordMetrics.maxLengthSequence("1234abd")); - assertEquals(3, PasswordMetrics.maxLengthSequence("aabc")); - assertEquals(1, PasswordMetrics.maxLengthSequence("qwertyuio")); - assertEquals(3, PasswordMetrics.maxLengthSequence("@ABC")); + assertEquals(4, PasswordMetrics.maxLengthSequence("1234".getBytes())); + assertEquals(5, PasswordMetrics.maxLengthSequence("13579".getBytes())); + assertEquals(4, PasswordMetrics.maxLengthSequence("1234abd".getBytes())); + assertEquals(3, PasswordMetrics.maxLengthSequence("aabc".getBytes())); + assertEquals(1, PasswordMetrics.maxLengthSequence("qwertyuio".getBytes())); + assertEquals(3, PasswordMetrics.maxLengthSequence("@ABC".getBytes())); // anything that repeats - assertEquals(4, PasswordMetrics.maxLengthSequence(";;;;")); + assertEquals(4, PasswordMetrics.maxLengthSequence(";;;;".getBytes())); // ordered, but not composed of alphas or digits - assertEquals(1, PasswordMetrics.maxLengthSequence(":;<=>")); + assertEquals(1, PasswordMetrics.maxLengthSequence(":;<=>".getBytes())); } @Test @@ -158,8 +159,8 @@ public class PasswordMetricsTest { assertNotEquals(new PasswordMetrics(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, 4), new PasswordMetrics(PASSWORD_QUALITY_COMPLEX, 4)); - metrics0 = PasswordMetrics.computeForPassword("1234abcd,./"); - metrics1 = PasswordMetrics.computeForPassword("1234abcd,./"); + metrics0 = PasswordMetrics.computeForPassword("1234abcd,./".getBytes()); + metrics1 = PasswordMetrics.computeForPassword("1234abcd,./".getBytes()); assertEquals(metrics0, metrics1); metrics1.letters++; assertNotEquals(metrics0, metrics1); @@ -197,7 +198,7 @@ public class PasswordMetricsTest { @Test public void testDetermineComplexity_none() { assertEquals(PASSWORD_COMPLEXITY_NONE, - PasswordMetrics.computeForPassword("").determineComplexity()); + PasswordMetrics.computeForPassword("".getBytes()).determineComplexity()); } @Test @@ -209,61 +210,61 @@ public class PasswordMetricsTest { @Test public void testDetermineComplexity_lowNumeric() { assertEquals(PASSWORD_COMPLEXITY_LOW, - PasswordMetrics.computeForPassword("1234").determineComplexity()); + PasswordMetrics.computeForPassword("1234".getBytes()).determineComplexity()); } @Test public void testDetermineComplexity_lowNumericComplex() { assertEquals(PASSWORD_COMPLEXITY_LOW, - PasswordMetrics.computeForPassword("124").determineComplexity()); + PasswordMetrics.computeForPassword("124".getBytes()).determineComplexity()); } @Test public void testDetermineComplexity_lowAlphabetic() { assertEquals(PASSWORD_COMPLEXITY_LOW, - PasswordMetrics.computeForPassword("a!").determineComplexity()); + PasswordMetrics.computeForPassword("a!".getBytes()).determineComplexity()); } @Test public void testDetermineComplexity_lowAlphanumeric() { assertEquals(PASSWORD_COMPLEXITY_LOW, - PasswordMetrics.computeForPassword("a!1").determineComplexity()); + PasswordMetrics.computeForPassword("a!1".getBytes()).determineComplexity()); } @Test public void testDetermineComplexity_mediumNumericComplex() { assertEquals(PASSWORD_COMPLEXITY_MEDIUM, - PasswordMetrics.computeForPassword("1238").determineComplexity()); + PasswordMetrics.computeForPassword("1238".getBytes()).determineComplexity()); } @Test public void testDetermineComplexity_mediumAlphabetic() { assertEquals(PASSWORD_COMPLEXITY_MEDIUM, - PasswordMetrics.computeForPassword("ab!c").determineComplexity()); + PasswordMetrics.computeForPassword("ab!c".getBytes()).determineComplexity()); } @Test public void testDetermineComplexity_mediumAlphanumeric() { assertEquals(PASSWORD_COMPLEXITY_MEDIUM, - PasswordMetrics.computeForPassword("ab!1").determineComplexity()); + PasswordMetrics.computeForPassword("ab!1".getBytes()).determineComplexity()); } @Test public void testDetermineComplexity_highNumericComplex() { assertEquals(PASSWORD_COMPLEXITY_HIGH, - PasswordMetrics.computeForPassword("12389647!").determineComplexity()); + PasswordMetrics.computeForPassword("12389647!".getBytes()).determineComplexity()); } @Test public void testDetermineComplexity_highAlphabetic() { assertEquals(PASSWORD_COMPLEXITY_HIGH, - PasswordMetrics.computeForPassword("alphabetic!").determineComplexity()); + PasswordMetrics.computeForPassword("alphabetic!".getBytes()).determineComplexity()); } @Test public void testDetermineComplexity_highAlphanumeric() { - assertEquals(PASSWORD_COMPLEXITY_HIGH, - PasswordMetrics.computeForPassword("alphanumeric123!").determineComplexity()); + assertEquals(PASSWORD_COMPLEXITY_HIGH, PasswordMetrics.computeForPassword( + "alphanumeric123!".getBytes()).determineComplexity()); } @Test diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java index a5ea4411f97b..d73c174212bd 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java @@ -28,7 +28,9 @@ import android.app.IApplicationThread; import android.app.IInstrumentationWatcher; import android.app.IUiAutomationConnection; import android.app.ProfilerInfo; +import android.content.AutofillOptions; import android.content.ComponentName; +import android.content.ContentCaptureOptions; import android.content.IIntentReceiver; import android.content.Intent; import android.content.pm.ActivityInfo; @@ -406,7 +408,7 @@ public class TransactionParcelTests { IUiAutomationConnection iUiAutomationConnection, int i, boolean b, boolean b1, boolean b2, boolean b3, Configuration configuration, CompatibilityInfo compatibilityInfo, Map map, Bundle bundle1, String s1, - boolean autofillCompatEnabled) throws RemoteException { + AutofillOptions ao, ContentCaptureOptions co) throws RemoteException { } @Override diff --git a/core/tests/coretests/src/android/app/timezone/RulesStateTest.java b/core/tests/coretests/src/android/app/timezone/RulesStateTest.java index bb535b6fac7d..30cc7ffc5366 100644 --- a/core/tests/coretests/src/android/app/timezone/RulesStateTest.java +++ b/core/tests/coretests/src/android/app/timezone/RulesStateTest.java @@ -46,11 +46,11 @@ public class RulesStateTest { RulesState.DISTRO_STATUS_INSTALLED, rulesVersion("2016b", 2)); assertEqualsContract(one, two); - RulesState differentSystemRules = new RulesState( + RulesState differentBaseRules = new RulesState( "2016b", formatVersion(1, 2), false /* operationInProgress */, RulesState.STAGED_OPERATION_INSTALL, rulesVersion("2016a", 3), RulesState.DISTRO_STATUS_INSTALLED, rulesVersion("2016b", 2)); - assertFalse(one.equals(differentSystemRules)); + assertFalse(one.equals(differentBaseRules)); RulesState differentFormatVersion = new RulesState( "2016a", formatVersion(1, 1), false /* operationInProgress */, @@ -121,14 +121,14 @@ public class RulesStateTest { } @Test - public void isSystemVersionNewerThan() { + public void isBaseVersionNewerThan() { RulesState rulesState = new RulesState( "2016b", formatVersion(1, 1), false /* operationInProgress */, RulesState.STAGED_OPERATION_NONE, null /* stagedDistroRulesVersion */, RulesState.DISTRO_STATUS_INSTALLED, rulesVersion("2016b", 3)); - assertTrue(rulesState.isSystemVersionNewerThan(rulesVersion("2016a", 1))); - assertFalse(rulesState.isSystemVersionNewerThan(rulesVersion("2016b", 1))); - assertFalse(rulesState.isSystemVersionNewerThan(rulesVersion("2016c", 1))); + assertTrue(rulesState.isBaseVersionNewerThan(rulesVersion("2016a", 1))); + assertFalse(rulesState.isBaseVersionNewerThan(rulesVersion("2016b", 1))); + assertFalse(rulesState.isBaseVersionNewerThan(rulesVersion("2016c", 1))); } private static void assertEqualsContract(RulesState one, RulesState two) { diff --git a/core/tests/coretests/src/android/content/pm/AndroidTestBaseUpdaterTest.java b/core/tests/coretests/src/android/content/pm/AndroidTestBaseUpdaterTest.java index 0ed76dcf7c5d..125b9ff61f7f 100644 --- a/core/tests/coretests/src/android/content/pm/AndroidTestBaseUpdaterTest.java +++ b/core/tests/coretests/src/android/content/pm/AndroidTestBaseUpdaterTest.java @@ -37,37 +37,37 @@ public class AndroidTestBaseUpdaterTest extends PackageSharedLibraryUpdaterTest private static final String OTHER_LIBRARY = "other.library"; @Test - public void targeted_at_O() { + public void targeted_at_P() { PackageBuilder before = builder() - .targetSdkVersion(Build.VERSION_CODES.O); + .targetSdkVersion(Build.VERSION_CODES.P); // Should add org.apache.http.legacy. PackageBuilder after = builder() - .targetSdkVersion(Build.VERSION_CODES.O) + .targetSdkVersion(Build.VERSION_CODES.P) .requiredLibraries(ANDROID_TEST_BASE); checkBackwardsCompatibility(before, after); } @Test - public void targeted_at_O_not_empty_usesLibraries() { + public void targeted_at_P_not_empty_usesLibraries() { PackageBuilder before = builder() - .targetSdkVersion(Build.VERSION_CODES.O) + .targetSdkVersion(Build.VERSION_CODES.P) .requiredLibraries(OTHER_LIBRARY); // The org.apache.http.legacy jar should be added at the start of the list because it // is not on the bootclasspath and the package targets pre-P. PackageBuilder after = builder() - .targetSdkVersion(Build.VERSION_CODES.O) + .targetSdkVersion(Build.VERSION_CODES.P) .requiredLibraries(ANDROID_TEST_BASE, OTHER_LIBRARY); checkBackwardsCompatibility(before, after); } @Test - public void targeted_at_O_in_usesLibraries() { + public void targeted_at_P_in_usesLibraries() { PackageBuilder before = builder() - .targetSdkVersion(Build.VERSION_CODES.O) + .targetSdkVersion(Build.VERSION_CODES.P) .requiredLibraries(ANDROID_TEST_BASE); // No change is required because although org.apache.http.legacy has been removed from @@ -76,9 +76,9 @@ public class AndroidTestBaseUpdaterTest extends PackageSharedLibraryUpdaterTest } @Test - public void targeted_at_O_in_usesOptionalLibraries() { + public void targeted_at_P_in_usesOptionalLibraries() { PackageBuilder before = builder() - .targetSdkVersion(Build.VERSION_CODES.O) + .targetSdkVersion(Build.VERSION_CODES.P) .optionalLibraries(ANDROID_TEST_BASE); // No change is required because although org.apache.http.legacy has been removed from diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java index 5dbcb3c46b50..82bd588ea9ff 100644 --- a/core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java +++ b/core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java @@ -19,6 +19,7 @@ package android.database.sqlite; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import android.content.Context; import android.database.DatabaseUtils; @@ -54,44 +55,52 @@ public class SQLiteCompatibilityWalFlagsTest { @Test public void testParseConfig() { SQLiteCompatibilityWalFlags.init(""); - assertFalse(SQLiteCompatibilityWalFlags.areFlagsSet()); - SQLiteCompatibilityWalFlags.init(null); - assertFalse(SQLiteCompatibilityWalFlags.areFlagsSet()); - SQLiteCompatibilityWalFlags.init("compatibility_wal_supported=false,wal_syncmode=OFF"); - assertTrue(SQLiteCompatibilityWalFlags.areFlagsSet()); - assertFalse(SQLiteCompatibilityWalFlags.isCompatibilityWalSupported()); - assertEquals("OFF", SQLiteCompatibilityWalFlags.getWALSyncMode()); + // Ensure that legacy compatibility wal isn't turned on by the old flag. + SQLiteCompatibilityWalFlags.init("compatibility_wal_supported=true,wal_syncmode=OFF"); + assertFalse(SQLiteCompatibilityWalFlags.isLegacyCompatibilityWalEnabled()); + try { + SQLiteCompatibilityWalFlags.getWALSyncMode(); + fail(); + } catch (IllegalStateException expected) { + } assertEquals(-1, SQLiteCompatibilityWalFlags.getTruncateSize()); + SQLiteCompatibilityWalFlags.init("wal_syncmode=VALUE"); - assertTrue(SQLiteCompatibilityWalFlags.areFlagsSet()); - assertEquals(SQLiteGlobal.isCompatibilityWalSupported(), - SQLiteCompatibilityWalFlags.isCompatibilityWalSupported()); - assertEquals("VALUE", SQLiteCompatibilityWalFlags.getWALSyncMode()); + assertFalse(SQLiteCompatibilityWalFlags.isLegacyCompatibilityWalEnabled()); assertEquals(-1, SQLiteCompatibilityWalFlags.getTruncateSize()); + try { + SQLiteCompatibilityWalFlags.getWALSyncMode(); + fail(); + } catch (IllegalStateException expected) { + } - SQLiteCompatibilityWalFlags.init("compatibility_wal_supported=true"); - assertTrue(SQLiteCompatibilityWalFlags.areFlagsSet()); + SQLiteCompatibilityWalFlags.init("legacy_compatibility_wal_enabled=true"); + assertTrue(SQLiteCompatibilityWalFlags.isLegacyCompatibilityWalEnabled()); assertEquals(SQLiteGlobal.getWALSyncMode(), SQLiteCompatibilityWalFlags.getWALSyncMode()); - assertTrue(SQLiteCompatibilityWalFlags.isCompatibilityWalSupported()); - assertEquals(-1, SQLiteCompatibilityWalFlags.getTruncateSize()); + + SQLiteCompatibilityWalFlags.init( + "legacy_compatibility_wal_enabled=true,wal_syncmode=VALUE"); + assertTrue(SQLiteCompatibilityWalFlags.isLegacyCompatibilityWalEnabled()); + assertEquals("VALUE", SQLiteCompatibilityWalFlags.getWALSyncMode()); SQLiteCompatibilityWalFlags.init("truncate_size=1024"); assertEquals(1024, SQLiteCompatibilityWalFlags.getTruncateSize()); SQLiteCompatibilityWalFlags.reset(); SQLiteCompatibilityWalFlags.init("Invalid value"); - assertFalse(SQLiteCompatibilityWalFlags.areFlagsSet()); + assertFalse(SQLiteCompatibilityWalFlags.isLegacyCompatibilityWalEnabled()); } @Test public void testApplyFlags() { Context ctx = InstrumentationRegistry.getContext(); - SQLiteCompatibilityWalFlags.init("compatibility_wal_supported=true,wal_syncmode=NORMAL"); + SQLiteCompatibilityWalFlags.init( + "legacy_compatibility_wal_enabled=true,wal_syncmode=NORMAL"); mDatabase = SQLiteDatabase .openOrCreateDatabase(ctx.getDatabasePath("SQLiteCompatibilityWalFlagsTest"), null); String journalMode = DatabaseUtils.stringForQuery(mDatabase, "PRAGMA journal_mode", null); @@ -100,5 +109,22 @@ public class SQLiteCompatibilityWalFlagsTest { assertEquals("Normal mode (1) is expected", "1", syncMode); } + @Test + public void testApplyFlags_thenDisableWriteAheadLogging() { + Context ctx = InstrumentationRegistry.getContext(); + + SQLiteCompatibilityWalFlags.init( + "legacy_compatibility_wal_enabled=true,wal_syncmode=FULL"); + mDatabase = SQLiteDatabase + .openOrCreateDatabase(ctx.getDatabasePath("SQLiteCompatibilityWalFlagsTest"), null); + mDatabase.disableWriteAheadLogging(); + String journalMode = DatabaseUtils.stringForQuery(mDatabase, "PRAGMA journal_mode", null); + assertEquals(SQLiteGlobal.getDefaultJournalMode(), journalMode.toUpperCase()); + String syncMode = DatabaseUtils.stringForQuery(mDatabase, "PRAGMA synchronous", null); + // TODO: This is the old behaviour and seems incorrect. The specified wal_syncmode was only + // intended to be used if the database is in WAL mode, and we should revert to the global + // default sync mode if WAL is disabled. + assertEquals("Normal mode (2) is expected", "2", syncMode); + } } diff --git a/core/tests/coretests/src/android/provider/DeviceConfigTest.java b/core/tests/coretests/src/android/provider/DeviceConfigTest.java index 17e9654b651a..de1453ab5a99 100644 --- a/core/tests/coretests/src/android/provider/DeviceConfigTest.java +++ b/core/tests/coretests/src/android/provider/DeviceConfigTest.java @@ -18,15 +18,11 @@ package android.provider; import static android.provider.DeviceConfig.OnPropertyChangedListener; -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertNotNull; -import static junit.framework.Assert.assertNull; -import static junit.framework.Assert.fail; +import static com.google.common.truth.Truth.assertThat; import android.app.ActivityThread; import android.content.ContentResolver; import android.os.Bundle; -import android.os.SystemClock; import android.platform.test.annotations.Presubmit; import androidx.test.InstrumentationRegistry; @@ -37,8 +33,8 @@ import org.junit.After; import org.junit.Test; import org.junit.runner.RunWith; -import java.util.concurrent.Executor; -import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; /** Tests that ensure appropriate settings are backed up. */ @Presubmit @@ -51,8 +47,6 @@ public class DeviceConfigTest { private static final String sValue = "value1"; private static final long WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS = 2000; // 2 sec - private final Object mLock = new Object(); - @After public void cleanUp() { deleteViaContentProvider(sNamespace, sKey); @@ -61,14 +55,139 @@ public class DeviceConfigTest { @Test public void getProperty_empty() { String result = DeviceConfig.getProperty(sNamespace, sKey); - assertNull(result); + assertThat(result).isNull(); + } + + @Test + public void getString_empty() { + final String default_value = "default_value"; + final String result = DeviceConfig.getString(sNamespace, sKey, default_value); + assertThat(result).isEqualTo(default_value); + } + + @Test + public void getString_nonEmpty() { + final String value = "new_value"; + final String default_value = "default"; + DeviceConfig.setProperty(sNamespace, sKey, value, false); + + final String result = DeviceConfig.getString(sNamespace, sKey, default_value); + assertThat(result).isEqualTo(value); + } + + @Test + public void getBoolean_empty() { + final boolean default_value = true; + final boolean result = DeviceConfig.getBoolean(sNamespace, sKey, default_value); + assertThat(result).isEqualTo(default_value); + } + + @Test + public void getBoolean_valid() { + final boolean value = true; + final boolean default_value = false; + DeviceConfig.setProperty(sNamespace, sKey, String.valueOf(value), false); + + final boolean result = DeviceConfig.getBoolean(sNamespace, sKey, default_value); + assertThat(result).isEqualTo(value); + } + + @Test + public void getBoolean_invalid() { + final boolean default_value = true; + DeviceConfig.setProperty(sNamespace, sKey, "not_a_boolean", false); + + final boolean result = DeviceConfig.getBoolean(sNamespace, sKey, default_value); + // Anything non-null other than case insensitive "true" parses to false. + assertThat(result).isFalse(); + } + + @Test + public void getInt_empty() { + final int default_value = 999; + final int result = DeviceConfig.getInt(sNamespace, sKey, default_value); + assertThat(result).isEqualTo(default_value); + } + + @Test + public void getInt_valid() { + final int value = 123; + final int default_value = 999; + DeviceConfig.setProperty(sNamespace, sKey, String.valueOf(value), false); + + final int result = DeviceConfig.getInt(sNamespace, sKey, default_value); + assertThat(result).isEqualTo(value); + } + + @Test + public void getInt_invalid() { + final int default_value = 999; + DeviceConfig.setProperty(sNamespace, sKey, "not_an_int", false); + + final int result = DeviceConfig.getInt(sNamespace, sKey, default_value); + // Failure to parse results in using the default value + assertThat(result).isEqualTo(default_value); + } + + @Test + public void getLong_empty() { + final long default_value = 123456; + final long result = DeviceConfig.getLong(sNamespace, sKey, default_value); + assertThat(result).isEqualTo(default_value); + } + + @Test + public void getLong_valid() { + final long value = 456789; + final long default_value = 123456; + DeviceConfig.setProperty(sNamespace, sKey, String.valueOf(value), false); + + final long result = DeviceConfig.getLong(sNamespace, sKey, default_value); + assertThat(result).isEqualTo(value); + } + + @Test + public void getLong_invalid() { + final long default_value = 123456; + DeviceConfig.setProperty(sNamespace, sKey, "not_a_long", false); + + final long result = DeviceConfig.getLong(sNamespace, sKey, default_value); + // Failure to parse results in using the default value + assertThat(result).isEqualTo(default_value); + } + + @Test + public void getFloat_empty() { + final float default_value = 123.456f; + final float result = DeviceConfig.getFloat(sNamespace, sKey, default_value); + assertThat(result).isEqualTo(default_value); + } + + @Test + public void getFloat_valid() { + final float value = 456.789f; + final float default_value = 123.456f; + DeviceConfig.setProperty(sNamespace, sKey, String.valueOf(value), false); + + final float result = DeviceConfig.getFloat(sNamespace, sKey, default_value); + assertThat(result).isEqualTo(value); + } + + @Test + public void getFloat_invalid() { + final float default_value = 123.456f; + DeviceConfig.setProperty(sNamespace, sKey, "not_a_float", false); + + final float result = DeviceConfig.getFloat(sNamespace, sKey, default_value); + // Failure to parse results in using the default value + assertThat(result).isEqualTo(default_value); } @Test public void setAndGetProperty_sameNamespace() { DeviceConfig.setProperty(sNamespace, sKey, sValue, false); String result = DeviceConfig.getProperty(sNamespace, sKey); - assertEquals(sValue, result); + assertThat(result).isEqualTo(sValue); } @Test @@ -76,7 +195,7 @@ public class DeviceConfigTest { String newNamespace = "namespace2"; DeviceConfig.setProperty(sNamespace, sKey, sValue, false); String result = DeviceConfig.getProperty(newNamespace, sKey); - assertNull(result); + assertThat(result).isNull(); } @Test @@ -86,9 +205,9 @@ public class DeviceConfigTest { DeviceConfig.setProperty(sNamespace, sKey, sValue, false); DeviceConfig.setProperty(newNamespace, sKey, newValue, false); String result = DeviceConfig.getProperty(sNamespace, sKey); - assertEquals(sValue, result); + assertThat(result).isEqualTo(sValue); result = DeviceConfig.getProperty(newNamespace, sKey); - assertEquals(newValue, result); + assertThat(result).isEqualTo(newValue); // clean up deleteViaContentProvider(newNamespace, sKey); @@ -100,59 +219,30 @@ public class DeviceConfigTest { DeviceConfig.setProperty(sNamespace, sKey, sValue, false); DeviceConfig.setProperty(sNamespace, sKey, newValue, false); String result = DeviceConfig.getProperty(sNamespace, sKey); - assertEquals(newValue, result); + assertThat(result).isEqualTo(newValue); } @Test - public void testListener() { - setPropertyAndAssertSuccessfulChange(sNamespace, sKey, sValue); - } - - private void setPropertyAndAssertSuccessfulChange(String setNamespace, String setName, - String setValue) { - final AtomicBoolean success = new AtomicBoolean(); + public void testListener() throws InterruptedException { + CountDownLatch countDownLatch = new CountDownLatch(1); - OnPropertyChangedListener changeListener = new OnPropertyChangedListener() { - @Override - public void onPropertyChanged(String namespace, String name, String value) { - assertEquals(setNamespace, namespace); - assertEquals(setName, name); - assertEquals(setValue, value); - success.set(true); + OnPropertyChangedListener changeListener = (namespace, name, value) -> { + assertThat(namespace).isEqualTo(sNamespace); + assertThat(name).isEqualTo(sKey); + assertThat(value).isEqualTo(sValue); + countDownLatch.countDown(); + }; - synchronized (mLock) { - mLock.notifyAll(); - } - } - }; - Executor executor = ActivityThread.currentApplication().getMainExecutor(); - DeviceConfig.addOnPropertyChangedListener(setNamespace, executor, changeListener); try { - DeviceConfig.setProperty(setNamespace, setName, setValue, false); - - final long startTimeMillis = SystemClock.uptimeMillis(); - synchronized (mLock) { - while (true) { - if (success.get()) { - return; - } - final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; - if (elapsedTimeMillis >= WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS) { - fail("Could not change setting for " - + WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS + " ms"); - } - final long remainingTimeMillis = WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS - - elapsedTimeMillis; - try { - mLock.wait(remainingTimeMillis); - } catch (InterruptedException ie) { - /* ignore */ - } - } - } + DeviceConfig.addOnPropertyChangedListener(sNamespace, + ActivityThread.currentApplication().getMainExecutor(), changeListener); + DeviceConfig.setProperty(sNamespace, sKey, sValue, false); + assertThat(countDownLatch.await( + WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue(); } finally { DeviceConfig.removeOnPropertyChangedListener(changeListener); } + } private static boolean deleteViaContentProvider(String namespace, String key) { @@ -160,7 +250,7 @@ public class DeviceConfigTest { String compositeName = namespace + "/" + key; Bundle result = resolver.call( DeviceConfig.CONTENT_URI, Settings.CALL_METHOD_DELETE_CONFIG, compositeName, null); - assertNotNull(result); + assertThat(result).isNotNull(); return compositeName.equals(result.getString(Settings.NameValueTable.VALUE)); } diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 46cac7a49f13..23cd96382d1a 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -319,6 +319,7 @@ public class SettingsBackupTest { Settings.Global.LOW_POWER_MODE_STICKY, Settings.Global.LOW_POWER_MODE_SUGGESTION_PARAMS, Settings.Global.LTE_SERVICE_FORCED, + Settings.Global.LID_BEHAVIOR, Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE, Settings.Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY, Settings.Global.MDC_INITIAL_MAX_RETRY, @@ -429,6 +430,7 @@ public class SettingsBackupTest { Settings.Global.SHOW_NOTIFICATION_CHANNEL_WARNINGS, Settings.Global.SHOW_RESTART_IN_CRASH_DIALOG, Settings.Global.SHOW_TEMPERATURE_WARNING, + Settings.Global.SHOW_USB_TEMPERATURE_ALARM, Settings.Global.SIGNED_CONFIG_VERSION, Settings.Global.SMART_SELECTION_UPDATE_CONTENT_URL, Settings.Global.SMART_SELECTION_UPDATE_METADATA_URL, @@ -504,7 +506,6 @@ public class SettingsBackupTest { Settings.Global.USER_SWITCHER_ENABLED, Settings.Global.NETWORK_ACCESS_TIMEOUT_MS, Settings.Global.WARNING_TEMPERATURE, - Settings.Global.USB_ALARM_TEMPERATURE, Settings.Global.WEBVIEW_DATA_REDUCTION_PROXY_KEY, Settings.Global.WEBVIEW_FALLBACK_LOGIC_ENABLED, Settings.Global.WEBVIEW_MULTIPROCESS, diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java index 71ce02d859f5..23ab05e952a3 100644 --- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java +++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java @@ -129,6 +129,29 @@ public class InsetsAnimationControlImplTest { assertPosition(navParams.matrix, new Rect(400, 0, 500, 500), new Rect(460, 0, 560, 500)); } + @Test + public void testFinishing() { + when(mMockController.getState()).thenReturn(mInsetsState); + mController.finish(sideBars()); + mController.applyChangeInsets(mInsetsState); + assertFalse(mInsetsState.getSource(TYPE_TOP_BAR).isVisible()); + assertTrue(mInsetsState.getSource(TYPE_NAVIGATION_BAR).isVisible()); + assertEquals(Insets.of(0, 0, 100, 0), mController.getCurrentInsets()); + verify(mMockController).notifyFinished(eq(mController), eq(sideBars())); + } + + @Test + public void testCancelled() { + mController.onCancelled(); + try { + mController.changeInsets(Insets.NONE); + fail("Expected exception to be thrown"); + } catch (IllegalStateException ignored) { + } + verify(mMockListener).onCancelled(); + mController.finish(sideBars()); + } + private void assertPosition(Matrix m, Rect original, Rect transformed) { RectF rect = new RectF(original); rect.offsetTo(0, 0); diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java index 731d5644504b..d71bde837d6f 100644 --- a/core/tests/coretests/src/android/view/InsetsControllerTest.java +++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java @@ -20,6 +20,7 @@ import static android.view.InsetsState.TYPE_IME; import static android.view.InsetsState.TYPE_NAVIGATION_BAR; import static android.view.InsetsState.TYPE_TOP_BAR; +import static android.view.WindowInsets.Type.topBar; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; @@ -48,6 +49,9 @@ import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; + +import java.util.concurrent.CountDownLatch; @Presubmit @FlakyTest(detail = "Promote once confirmed non-flaky") @@ -80,6 +84,8 @@ public class InsetsControllerTest { new DisplayCutout( Insets.of(10, 10, 10, 10), rect, rect, rect, rect), rect, rect, SOFT_INPUT_ADJUST_RESIZE); + mController.onFrameChanged(new Rect(0, 0, 100, 100)); + mController.getState().setDisplayFrame(new Rect(0, 0, 100, 100)); }); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); } @@ -101,6 +107,19 @@ public class InsetsControllerTest { } @Test + public void testControlsRevoked_duringAnim() { + InsetsSourceControl control = new InsetsSourceControl(TYPE_TOP_BAR, mLeash, new Point()); + mController.onControlsChanged(new InsetsSourceControl[] { control }); + + WindowInsetsAnimationControlListener mockListener = + mock(WindowInsetsAnimationControlListener.class); + mController.controlWindowInsetsAnimation(topBar(), mockListener); + verify(mockListener).onReady(any(), anyInt()); + mController.onControlsChanged(new InsetsSourceControl[0]); + verify(mockListener).onCancelled(); + } + + @Test public void testFrameDoesntMatchDisplay() { mController.onFrameChanged(new Rect(0, 0, 100, 100)); mController.getState().setDisplayFrame(new Rect(0, 0, 200, 200)); @@ -119,24 +138,21 @@ public class InsetsControllerTest { InsetsSourceControl ime = controls[2]; InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + // since there is no focused view, forcefully make IME visible. + mController.applyImeVisibility(true /* setVisible */); mController.show(Type.all()); // quickly jump to final state by cancelling it. mController.cancelExistingAnimation(); assertTrue(mController.getSourceConsumer(navBar.getType()).isVisible()); assertTrue(mController.getSourceConsumer(topBar.getType()).isVisible()); - // no focused view, no IME. - assertFalse(mController.getSourceConsumer(ime.getType()).isVisible()); + assertTrue(mController.getSourceConsumer(ime.getType()).isVisible()); + mController.applyImeVisibility(false /* setVisible */); mController.hide(Type.all()); mController.cancelExistingAnimation(); assertFalse(mController.getSourceConsumer(navBar.getType()).isVisible()); assertFalse(mController.getSourceConsumer(topBar.getType()).isVisible()); assertFalse(mController.getSourceConsumer(ime.getType()).isVisible()); - - mController.show(Type.ime()); - mController.cancelExistingAnimation(); - // no focused view, no IME. - assertFalse(mController.getSourceConsumer(ime.getType()).isVisible()); }); InstrumentationRegistry.getInstrumentation().waitForIdleSync(); } @@ -292,6 +308,35 @@ public class InsetsControllerTest { InstrumentationRegistry.getInstrumentation().waitForIdleSync(); } + @Test + public void testAnimationEndState_controller() throws Exception { + InsetsSourceControl control = new InsetsSourceControl(TYPE_TOP_BAR, mLeash, new Point()); + mController.onControlsChanged(new InsetsSourceControl[] { control }); + + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + WindowInsetsAnimationControlListener mockListener = + mock(WindowInsetsAnimationControlListener.class); + mController.controlWindowInsetsAnimation(topBar(), mockListener); + + ArgumentCaptor<WindowInsetsAnimationController> controllerCaptor = + ArgumentCaptor.forClass(WindowInsetsAnimationController.class); + verify(mockListener).onReady(controllerCaptor.capture(), anyInt()); + controllerCaptor.getValue().finish(0 /* shownTypes */); + }); + waitUntilNextFrame(); + InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + assertFalse(mController.getSourceConsumer(TYPE_TOP_BAR).isVisible()); + }); + InstrumentationRegistry.getInstrumentation().waitForIdleSync(); + } + + private void waitUntilNextFrame() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); + Choreographer.getMainThreadInstance().postCallback(Choreographer.CALLBACK_COMMIT, + latch::countDown, null /* token */); + latch.await(); + } + private InsetsSourceControl[] prepareControls() { final InsetsSourceControl navBar = new InsetsSourceControl(TYPE_NAVIGATION_BAR, mLeash, new Point()); diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java index a88968bfd089..e3852e1b627e 100644 --- a/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityCacheTest.java @@ -301,26 +301,6 @@ public class AccessibilityCacheTest { } @Test - public void subTreeChangeEventFromUncachedNode_clearsNodeInCache() { - AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(CHILD_VIEW_ID, WINDOW_ID_1); - long id = nodeInfo.getSourceNodeId(); - mAccessibilityCache.add(nodeInfo); - nodeInfo.recycle(); - - AccessibilityEvent event = AccessibilityEvent - .obtain(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); - event.setContentChangeTypes(AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE); - event.setSource(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1)); - - mAccessibilityCache.onAccessibilityEvent(event); - AccessibilityNodeInfo shouldBeNull = mAccessibilityCache.getNode(WINDOW_ID_1, id); - if (shouldBeNull != null) { - shouldBeNull.recycle(); - } - assertNull(shouldBeNull); - } - - @Test public void scrollEvent_clearsNodeAndChild() { AccessibilityEvent event = AccessibilityEvent .obtain(AccessibilityEvent.TYPE_VIEW_SCROLLED); diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java index 312e0e0d8268..cd885e0b9bcf 100644 --- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java +++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureManagerTest.java @@ -41,7 +41,8 @@ public class ContentCaptureManagerTest { @Before public void before() { - mManager = new ContentCaptureManager(mMockContext, null); + mManager = new ContentCaptureManager(mMockContext, /* service= */ null, + /* options= */ null); } @Test diff --git a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java index b6717e16256f..013408e0bd12 100644 --- a/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java +++ b/core/tests/coretests/src/android/view/contentcapture/ContentCaptureSessionTest.java @@ -157,7 +157,7 @@ public class ContentCaptureSessionTest { } @Override - public void internalNotifyViewHierarchyEvent(boolean started) { + public void internalNotifyViewTreeEvent(boolean started) { throw new UnsupportedOperationException("should not have been called"); } diff --git a/core/tests/coretests/src/android/view/textclassifier/LabeledIntentTest.java b/core/tests/coretests/src/android/view/textclassifier/LabeledIntentTest.java new file mode 100644 index 000000000000..e4e9cde5e977 --- /dev/null +++ b/core/tests/coretests/src/android/view/textclassifier/LabeledIntentTest.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.textclassifier; + +import static com.google.common.truth.Truth.assertThat; + +import static org.testng.Assert.assertThrows; + +import android.content.Context; +import android.content.Intent; +import android.net.Uri; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public final class LabeledIntentTest { + private static final String TITLE_WITHOUT_ENTITY = "Map"; + private static final String TITLE_WITH_ENTITY = "Map NW14D1"; + private static final String DESCRIPTION = "Check the map"; + private static final Intent INTENT = + new Intent(Intent.ACTION_VIEW).setDataAndNormalize(Uri.parse("http://www.android.com")); + private static final int REQUEST_CODE = 42; + private Context mContext; + + @Before + public void setup() { + mContext = InstrumentationRegistry.getTargetContext(); + } + + @Test + public void resolve_preferTitleWithEntity() { + LabeledIntent labeledIntent = new LabeledIntent( + TITLE_WITHOUT_ENTITY, + TITLE_WITH_ENTITY, + DESCRIPTION, + INTENT, + REQUEST_CODE + ); + + LabeledIntent.Result result = + labeledIntent.resolve(mContext, /*titleChooser*/ null); + + assertThat(result).isNotNull(); + assertThat(result.remoteAction.getTitle()).isEqualTo(TITLE_WITH_ENTITY); + assertThat(result.remoteAction.getContentDescription()).isEqualTo(DESCRIPTION); + Intent intent = result.resolvedIntent; + assertThat(intent.getAction()).isEqualTo(intent.getAction()); + assertThat(intent.getComponent()).isNotNull(); + } + + @Test + public void resolve_useAvailableTitle() { + LabeledIntent labeledIntent = new LabeledIntent( + TITLE_WITHOUT_ENTITY, + null, + DESCRIPTION, + INTENT, + REQUEST_CODE + ); + + LabeledIntent.Result result = + labeledIntent.resolve(mContext, /*titleChooser*/ null); + + assertThat(result).isNotNull(); + assertThat(result.remoteAction.getTitle()).isEqualTo(TITLE_WITHOUT_ENTITY); + assertThat(result.remoteAction.getContentDescription()).isEqualTo(DESCRIPTION); + Intent intent = result.resolvedIntent; + assertThat(intent.getAction()).isEqualTo(intent.getAction()); + assertThat(intent.getComponent()).isNotNull(); + } + + @Test + public void resolve_titleChooser() { + LabeledIntent labeledIntent = new LabeledIntent( + TITLE_WITHOUT_ENTITY, + null, + DESCRIPTION, + INTENT, + REQUEST_CODE + ); + + LabeledIntent.Result result = + labeledIntent.resolve(mContext, (labeledIntent1, resolveInfo) -> "chooser"); + + assertThat(result).isNotNull(); + assertThat(result.remoteAction.getTitle()).isEqualTo("chooser"); + assertThat(result.remoteAction.getContentDescription()).isEqualTo(DESCRIPTION); + Intent intent = result.resolvedIntent; + assertThat(intent.getAction()).isEqualTo(intent.getAction()); + assertThat(intent.getComponent()).isNotNull(); + } + + @Test + public void resolve_titleChooserReturnsNull() { + LabeledIntent labeledIntent = new LabeledIntent( + TITLE_WITHOUT_ENTITY, + null, + DESCRIPTION, + INTENT, + REQUEST_CODE + ); + + LabeledIntent.Result result = + labeledIntent.resolve(mContext, (labeledIntent1, resolveInfo) -> null); + + assertThat(result).isNotNull(); + assertThat(result.remoteAction.getTitle()).isEqualTo(TITLE_WITHOUT_ENTITY); + assertThat(result.remoteAction.getContentDescription()).isEqualTo(DESCRIPTION); + Intent intent = result.resolvedIntent; + assertThat(intent.getAction()).isEqualTo(intent.getAction()); + assertThat(intent.getComponent()).isNotNull(); + } + + + @Test + public void resolve_missingTitle() { + assertThrows( + IllegalArgumentException.class, + () -> + new LabeledIntent( + null, + null, + DESCRIPTION, + INTENT, + REQUEST_CODE + )); + } +} diff --git a/core/tests/coretests/src/android/view/textclassifier/LegacyIntentFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/LegacyIntentFactoryTest.java index 73d3eec734d7..743818cd3d4e 100644 --- a/core/tests/coretests/src/android/view/textclassifier/LegacyIntentFactoryTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/LegacyIntentFactoryTest.java @@ -58,9 +58,12 @@ public class LegacyIntentFactoryTest { null, null, null, + null, + null, + null, null); - List<TextClassifierImpl.LabeledIntent> intents = mLegacyIntentFactory.create( + List<LabeledIntent> intents = mLegacyIntentFactory.create( InstrumentationRegistry.getContext(), TEXT, /* foreignText */ false, @@ -68,8 +71,8 @@ public class LegacyIntentFactoryTest { classificationResult); assertThat(intents).hasSize(1); - TextClassifierImpl.LabeledIntent labeledIntent = intents.get(0); - Intent intent = labeledIntent.getIntent(); + LabeledIntent labeledIntent = intents.get(0); + Intent intent = labeledIntent.intent; assertThat(intent.getAction()).isEqualTo(Intent.ACTION_DEFINE); assertThat(intent.getStringExtra(Intent.EXTRA_TEXT)).isEqualTo(TEXT); assertThat( @@ -89,9 +92,12 @@ public class LegacyIntentFactoryTest { null, null, null, + null, + null, + null, null); - List<TextClassifierImpl.LabeledIntent> intents = mLegacyIntentFactory.create( + List<LabeledIntent> intents = mLegacyIntentFactory.create( InstrumentationRegistry.getContext(), TEXT, /* foreignText */ true, @@ -99,7 +105,7 @@ public class LegacyIntentFactoryTest { classificationResult); assertThat(intents).hasSize(2); - assertThat(intents.get(0).getIntent().getAction()).isEqualTo(Intent.ACTION_DEFINE); - assertThat(intents.get(1).getIntent().getAction()).isEqualTo(Intent.ACTION_TRANSLATE); + assertThat(intents.get(0).intent.getAction()).isEqualTo(Intent.ACTION_DEFINE); + assertThat(intents.get(1).intent.getAction()).isEqualTo(Intent.ACTION_TRANSLATE); } } diff --git a/core/tests/coretests/src/android/view/textclassifier/TemplateClassificationIntentFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/TemplateClassificationIntentFactoryTest.java index d9dac314fc7c..9fd9e8eb2690 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TemplateClassificationIntentFactoryTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TemplateClassificationIntentFactoryTest.java @@ -40,7 +40,7 @@ import java.util.List; public class TemplateClassificationIntentFactoryTest { private static final String TEXT = "text"; - private static final String TITLE = "Map"; + private static final String TITLE_WITHOUT_ENTITY = "Map"; private static final String DESCRIPTION = "Opens in Maps"; private static final String ACTION = Intent.ACTION_VIEW; @@ -69,9 +69,12 @@ public class TemplateClassificationIntentFactoryTest { null, null, null, + null, + null, + null, createRemoteActionTemplates()); - List<TextClassifierImpl.LabeledIntent> intents = + List<LabeledIntent> intents = mTemplateClassificationIntentFactory.create( InstrumentationRegistry.getContext(), TEXT, @@ -80,14 +83,14 @@ public class TemplateClassificationIntentFactoryTest { classificationResult); assertThat(intents).hasSize(2); - TextClassifierImpl.LabeledIntent labeledIntent = intents.get(0); - assertThat(labeledIntent.getTitle()).isEqualTo(TITLE); - Intent intent = labeledIntent.getIntent(); + LabeledIntent labeledIntent = intents.get(0); + assertThat(labeledIntent.titleWithoutEntity).isEqualTo(TITLE_WITHOUT_ENTITY); + Intent intent = labeledIntent.intent; assertThat(intent.getAction()).isEqualTo(ACTION); assertThat(intent.hasExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER)).isTrue(); labeledIntent = intents.get(1); - intent = labeledIntent.getIntent(); + intent = labeledIntent.intent; assertThat(intent.getAction()).isEqualTo(Intent.ACTION_TRANSLATE); assertThat(intent.hasExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER)).isTrue(); } @@ -105,9 +108,12 @@ public class TemplateClassificationIntentFactoryTest { null, null, null, + null, + null, + null, createRemoteActionTemplates()); - List<TextClassifierImpl.LabeledIntent> intents = + List<LabeledIntent> intents = mTemplateClassificationIntentFactory.create( InstrumentationRegistry.getContext(), TEXT, @@ -116,9 +122,9 @@ public class TemplateClassificationIntentFactoryTest { classificationResult); assertThat(intents).hasSize(1); - TextClassifierImpl.LabeledIntent labeledIntent = intents.get(0); - assertThat(labeledIntent.getTitle()).isEqualTo(TITLE); - Intent intent = labeledIntent.getIntent(); + LabeledIntent labeledIntent = intents.get(0); + assertThat(labeledIntent.titleWithoutEntity).isEqualTo(TITLE_WITHOUT_ENTITY); + Intent intent = labeledIntent.intent; assertThat(intent.getAction()).isEqualTo(ACTION); assertThat(intent.hasExtra(TextClassifier.EXTRA_FROM_TEXT_CLASSIFIER)).isTrue(); } @@ -126,7 +132,8 @@ public class TemplateClassificationIntentFactoryTest { private static RemoteActionTemplate[] createRemoteActionTemplates() { return new RemoteActionTemplate[]{ new RemoteActionTemplate( - TITLE, + TITLE_WITHOUT_ENTITY, + null, DESCRIPTION, ACTION, null, diff --git a/core/tests/coretests/src/android/view/textclassifier/TemplateIntentFactoryTest.java b/core/tests/coretests/src/android/view/textclassifier/TemplateIntentFactoryTest.java index a1158a7cbb6c..186073475bf4 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TemplateIntentFactoryTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TemplateIntentFactoryTest.java @@ -38,7 +38,8 @@ import java.util.List; public class TemplateIntentFactoryTest { private static final String TEXT = "text"; - private static final String TITLE = "Map"; + private static final String TITLE_WITHOUT_ENTITY = "Map"; + private static final String TITLE_WITH_ENTITY = "Map NW14D1"; private static final String DESCRIPTION = "Check the map"; private static final String ACTION = Intent.ACTION_VIEW; private static final String DATA = Uri.parse("http://www.android.com").toString(); @@ -69,7 +70,8 @@ public class TemplateIntentFactoryTest { @Test public void create_full() { RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate( - TITLE, + TITLE_WITHOUT_ENTITY, + TITLE_WITH_ENTITY, DESCRIPTION, ACTION, DATA, @@ -81,15 +83,16 @@ public class TemplateIntentFactoryTest { REQUEST_CODE ); - List<TextClassifierImpl.LabeledIntent> intents = + List<LabeledIntent> intents = mTemplateIntentFactory.create(new RemoteActionTemplate[]{remoteActionTemplate}); assertThat(intents).hasSize(1); - TextClassifierImpl.LabeledIntent labeledIntent = intents.get(0); - assertThat(labeledIntent.getTitle()).isEqualTo(TITLE); - assertThat(labeledIntent.getDescription()).isEqualTo(DESCRIPTION); - assertThat(labeledIntent.getRequestCode()).isEqualTo(REQUEST_CODE); - Intent intent = labeledIntent.getIntent(); + LabeledIntent labeledIntent = intents.get(0); + assertThat(labeledIntent.titleWithoutEntity).isEqualTo(TITLE_WITHOUT_ENTITY); + assertThat(labeledIntent.titleWithEntity).isEqualTo(TITLE_WITH_ENTITY); + assertThat(labeledIntent.description).isEqualTo(DESCRIPTION); + assertThat(labeledIntent.requestCode).isEqualTo(REQUEST_CODE); + Intent intent = labeledIntent.intent; assertThat(intent.getAction()).isEqualTo(ACTION); assertThat(intent.getData().toString()).isEqualTo(DATA); assertThat(intent.getType()).isEqualTo(TYPE); @@ -104,7 +107,8 @@ public class TemplateIntentFactoryTest { @Test public void normalizesScheme() { RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate( - TITLE, + TITLE_WITHOUT_ENTITY, + TITLE_WITH_ENTITY, DESCRIPTION, ACTION, "HTTp://www.android.com", @@ -116,17 +120,18 @@ public class TemplateIntentFactoryTest { REQUEST_CODE ); - List<TextClassifierImpl.LabeledIntent> intents = + List<LabeledIntent> intents = mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate}); - String data = intents.get(0).getIntent().getData().toString(); + String data = intents.get(0).intent.getData().toString(); assertThat(data).isEqualTo("http://www.android.com"); } @Test public void create_minimal() { RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate( - TITLE, + TITLE_WITHOUT_ENTITY, + null, DESCRIPTION, ACTION, null, @@ -138,16 +143,17 @@ public class TemplateIntentFactoryTest { null ); - List<TextClassifierImpl.LabeledIntent> intents = + List<LabeledIntent> intents = mTemplateIntentFactory.create(new RemoteActionTemplate[]{remoteActionTemplate}); assertThat(intents).hasSize(1); - TextClassifierImpl.LabeledIntent labeledIntent = intents.get(0); - assertThat(labeledIntent.getTitle()).isEqualTo(TITLE); - assertThat(labeledIntent.getDescription()).isEqualTo(DESCRIPTION); - assertThat(labeledIntent.getRequestCode()).isEqualTo( - TextClassifierImpl.LabeledIntent.DEFAULT_REQUEST_CODE); - Intent intent = labeledIntent.getIntent(); + LabeledIntent labeledIntent = intents.get(0); + assertThat(labeledIntent.titleWithoutEntity).isEqualTo(TITLE_WITHOUT_ENTITY); + assertThat(labeledIntent.titleWithEntity).isNull(); + assertThat(labeledIntent.description).isEqualTo(DESCRIPTION); + assertThat(labeledIntent.requestCode).isEqualTo( + LabeledIntent.DEFAULT_REQUEST_CODE); + Intent intent = labeledIntent.intent; assertThat(intent.getAction()).isEqualTo(ACTION); assertThat(intent.getData()).isNull(); assertThat(intent.getType()).isNull(); @@ -161,7 +167,7 @@ public class TemplateIntentFactoryTest { public void invalidTemplate_nullTemplate() { RemoteActionTemplate remoteActionTemplate = null; - List<TextClassifierImpl.LabeledIntent> intents = + List<LabeledIntent> intents = mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate}); assertThat(intents).isEmpty(); @@ -170,7 +176,8 @@ public class TemplateIntentFactoryTest { @Test public void invalidTemplate_nonEmptyPackageName() { RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate( - TITLE, + TITLE_WITHOUT_ENTITY, + TITLE_WITH_ENTITY, DESCRIPTION, ACTION, DATA, @@ -182,7 +189,7 @@ public class TemplateIntentFactoryTest { REQUEST_CODE ); - List<TextClassifierImpl.LabeledIntent> intents = + List<LabeledIntent> intents = mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate}); assertThat(intents).isEmpty(); @@ -192,6 +199,7 @@ public class TemplateIntentFactoryTest { public void invalidTemplate_emptyTitle() { RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate( null, + null, DESCRIPTION, ACTION, null, @@ -203,7 +211,7 @@ public class TemplateIntentFactoryTest { null ); - List<TextClassifierImpl.LabeledIntent> intents = + List<LabeledIntent> intents = mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate}); assertThat(intents).isEmpty(); @@ -212,7 +220,8 @@ public class TemplateIntentFactoryTest { @Test public void invalidTemplate_emptyDescription() { RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate( - TITLE, + TITLE_WITHOUT_ENTITY, + TITLE_WITH_ENTITY, null, ACTION, null, @@ -224,7 +233,7 @@ public class TemplateIntentFactoryTest { null ); - List<TextClassifierImpl.LabeledIntent> intents = + List<LabeledIntent> intents = mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate}); assertThat(intents).isEmpty(); @@ -233,7 +242,8 @@ public class TemplateIntentFactoryTest { @Test public void invalidTemplate_emptyIntentAction() { RemoteActionTemplate remoteActionTemplate = new RemoteActionTemplate( - TITLE, + TITLE_WITHOUT_ENTITY, + TITLE_WITH_ENTITY, DESCRIPTION, null, null, @@ -245,7 +255,7 @@ public class TemplateIntentFactoryTest { null ); - List<TextClassifierImpl.LabeledIntent> intents = + List<LabeledIntent> intents = mTemplateIntentFactory.create(new RemoteActionTemplate[] {remoteActionTemplate}); assertThat(intents).isEmpty(); diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java index 99c959ef57e4..d54402975f65 100644 --- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java +++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java @@ -93,8 +93,8 @@ public class TextClassificationTest { final String id = "id"; final TextClassification reference = new TextClassification.Builder() .setText(text) - .addAction(remoteAction0) - .addAction(remoteAction1) + .addAction(remoteAction0) // Action intent not included. + .addAction(remoteAction1) // Action intent not included. .setEntityType(TextClassifier.TYPE_ADDRESS, 0.3f) .setEntityType(TextClassifier.TYPE_PHONE, 0.7f) .setId(id) @@ -132,6 +132,7 @@ public class TextClassificationTest { // Extras assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY)); + assertNull(ExtrasUtils.getActionsIntents(result)); } @Test diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java index f27f3f9ca427..9fbc1666aed1 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java @@ -57,7 +57,7 @@ import androidx.test.rule.ActivityTestRule; import com.android.internal.R; import com.android.internal.app.ResolverActivity.ResolvedComponentInfo; import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import org.junit.Before; import org.junit.Rule; @@ -524,15 +524,45 @@ public class ChooserActivityTest { waitForIdle(); verify(mockLogger, atLeastOnce()).write(logMakerCaptor.capture()); assertThat(logMakerCaptor.getAllValues().get(0).getCategory(), - is(MetricsProto.MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN)); + is(MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN)); assertThat(logMakerCaptor .getAllValues().get(0) - .getTaggedData(MetricsProto.MetricsEvent.FIELD_TIME_TO_APP_TARGETS), + .getTaggedData(MetricsEvent.FIELD_TIME_TO_APP_TARGETS), is(notNullValue())); assertThat(logMakerCaptor .getAllValues().get(0) - .getTaggedData(MetricsProto.MetricsEvent.FIELD_SHARESHEET_MIMETYPE), + .getTaggedData(MetricsEvent.FIELD_SHARESHEET_MIMETYPE), is("TestType")); + assertThat(logMakerCaptor + .getAllValues().get(0) + .getSubtype(), + is(MetricsEvent.PARENT_PROFILE)); + } + + @Test + public void testOnCreateLoggingFromWorkProfile() { + Intent sendIntent = createSendTextIntent(); + sendIntent.setType("TestType"); + sOverrides.alternateProfileSetting = MetricsEvent.MANAGED_PROFILE; + MetricsLogger mockLogger = sOverrides.metricsLogger; + ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class); + mActivityRule.launchActivity(Intent.createChooser(sendIntent, "logger test")); + waitForIdle(); + verify(mockLogger, atLeastOnce()).write(logMakerCaptor.capture()); + assertThat(logMakerCaptor.getAllValues().get(0).getCategory(), + is(MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN)); + assertThat(logMakerCaptor + .getAllValues().get(0) + .getTaggedData(MetricsEvent.FIELD_TIME_TO_APP_TARGETS), + is(notNullValue())); + assertThat(logMakerCaptor + .getAllValues().get(0) + .getTaggedData(MetricsEvent.FIELD_SHARESHEET_MIMETYPE), + is("TestType")); + assertThat(logMakerCaptor + .getAllValues().get(0) + .getSubtype(), + is(MetricsEvent.MANAGED_PROFILE)); } @Test @@ -547,7 +577,7 @@ public class ChooserActivityTest { verify(mockLogger, Mockito.times(1)).write(logMakerCaptor.capture()); // First invocation is from onCreate assertThat(logMakerCaptor.getAllValues().get(0).getCategory(), - is(MetricsProto.MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN)); + is(MetricsEvent.ACTION_ACTIVITY_CHOOSER_SHOWN)); } @Test @@ -569,7 +599,7 @@ public class ChooserActivityTest { verify(mockLogger, Mockito.times(3)).write(logMakerCaptor.capture()); // First invocation is from onCreate assertThat(logMakerCaptor.getAllValues().get(1).getCategory(), - is(MetricsProto.MetricsEvent.ACTION_SHARE_WITH_PREVIEW)); + is(MetricsEvent.ACTION_SHARE_WITH_PREVIEW)); assertThat(logMakerCaptor.getAllValues().get(1).getSubtype(), is(CONTENT_PREVIEW_TEXT)); } @@ -599,11 +629,11 @@ public class ChooserActivityTest { verify(mockLogger, Mockito.times(3)).write(logMakerCaptor.capture()); // First invocation is from onCreate assertThat(logMakerCaptor.getAllValues().get(1).getCategory(), - is(MetricsProto.MetricsEvent.ACTION_SHARE_WITH_PREVIEW)); + is(MetricsEvent.ACTION_SHARE_WITH_PREVIEW)); assertThat(logMakerCaptor.getAllValues().get(1).getSubtype(), is(CONTENT_PREVIEW_IMAGE)); assertThat(logMakerCaptor.getAllValues().get(2).getCategory(), - is(MetricsProto.MetricsEvent.ACTION_SHARE_WITH_PREVIEW)); + is(MetricsEvent.ACTION_SHARE_WITH_PREVIEW)); assertThat(logMakerCaptor.getAllValues().get(2).getSubtype(), is(CONTENT_PREVIEW_IMAGE)); } diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java index 57c84ff5c8ac..a8dd69a2666d 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java @@ -28,6 +28,7 @@ import android.net.Uri; import android.util.Size; import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import java.util.function.Function; @@ -112,6 +113,14 @@ public class ChooserWrapperActivity extends ChooserActivity { return super.queryResolver(resolver, uri); } + @Override + protected boolean isWorkProfile() { + if (sOverrides.alternateProfileSetting != 0) { + return sOverrides.alternateProfileSetting == MetricsEvent.MANAGED_PROFILE; + } + return super.isWorkProfile(); + } + /** * We cannot directly mock the activity created since instrumentation creates it. * <p> @@ -128,6 +137,7 @@ public class ChooserWrapperActivity extends ChooserActivity { public boolean resolverForceException; public Bitmap previewThumbnail; public MetricsLogger metricsLogger; + public int alternateProfileSetting; public void reset() { onSafelyStartCallback = null; @@ -139,6 +149,7 @@ public class ChooserWrapperActivity extends ChooserActivity { resolverForceException = false; resolverListController = mock(ResolverListController.class); metricsLogger = mock(MetricsLogger.class); + alternateProfileSetting = 0; } } } diff --git a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java index 9b13af2a4357..abfb4fb3cedc 100644 --- a/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/IntentForwarderActivityTest.java @@ -25,6 +25,7 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -40,6 +41,7 @@ import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; +import android.metrics.LogMaker; import android.net.Uri; import android.os.Bundle; import android.os.IBinder; @@ -51,6 +53,9 @@ import androidx.test.InstrumentationRegistry; import androidx.test.rule.ActivityTestRule; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -70,6 +75,11 @@ public class IntentForwarderActivityTest { "android", IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE ); + private static final ComponentName FORWARD_TO_PARENT_COMPONENT_NAME = + new ComponentName( + "android", + IntentForwarderActivity.FORWARD_INTENT_TO_PARENT + ); private static final String TYPE_PLAIN_TEXT = "text/plain"; private static UserInfo MANAGED_PROFILE_INFO = new UserInfo(); @@ -522,6 +532,60 @@ public class IntentForwarderActivityTest { verify(sInjector).showToast(anyInt(), anyInt()); } + @Test + public void forwardToManagedProfile_LoggingTest() throws Exception { + sComponentName = FORWARD_TO_MANAGED_PROFILE_COMPONENT_NAME; + + // Intent can be forwarded. + when(mIPm.canForwardTo( + any(Intent.class), nullable(String.class), anyInt(), anyInt())).thenReturn(true); + + // Managed profile exists. + List<UserInfo> profiles = new ArrayList<>(); + profiles.add(CURRENT_USER_INFO); + profiles.add(MANAGED_PROFILE_INFO); + when(mUserManager.getProfiles(anyInt())).thenReturn(profiles); + + Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class); + intent.setAction(Intent.ACTION_SEND); + intent.setType(TYPE_PLAIN_TEXT); + IntentForwarderWrapperActivity activity = mActivityRule.launchActivity(intent); + + ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class); + verify(activity.getMetricsLogger()).write(logMakerCaptor.capture()); + assertEquals(MetricsEvent.ACTION_SWITCH_SHARE_PROFILE, + logMakerCaptor.getValue().getCategory()); + assertEquals(MetricsEvent.MANAGED_PROFILE, + logMakerCaptor.getValue().getSubtype()); + } + + @Test + public void forwardToParent_LoggingTest() throws Exception { + sComponentName = FORWARD_TO_PARENT_COMPONENT_NAME; + + // Intent can be forwarded. + when(mIPm.canForwardTo( + any(Intent.class), nullable(String.class), anyInt(), anyInt())).thenReturn(true); + + // Managed profile exists. + List<UserInfo> profiles = new ArrayList<>(); + profiles.add(CURRENT_USER_INFO); + profiles.add(MANAGED_PROFILE_INFO); + when(mUserManager.getProfiles(anyInt())).thenReturn(profiles); + + Intent intent = new Intent(mContext, IntentForwarderWrapperActivity.class); + intent.setAction(Intent.ACTION_SEND); + intent.setType(TYPE_PLAIN_TEXT); + IntentForwarderWrapperActivity activity = mActivityRule.launchActivity(intent); + + ArgumentCaptor<LogMaker> logMakerCaptor = ArgumentCaptor.forClass(LogMaker.class); + verify(activity.getMetricsLogger()).write(logMakerCaptor.capture()); + assertEquals(MetricsEvent.ACTION_SWITCH_SHARE_PROFILE, + logMakerCaptor.getValue().getCategory()); + assertEquals(MetricsEvent.PARENT_PROFILE, + logMakerCaptor.getValue().getSubtype()); + } + private void setupShouldSkipDisclosureTest() throws RemoteException { sComponentName = FORWARD_TO_MANAGED_PROFILE_COMPONENT_NAME; sActivityName = "MyTestActivity"; @@ -541,6 +605,7 @@ public class IntentForwarderActivityTest { private Intent mStartActivityIntent; private int mUserIdActivityLaunchedIn; + private MetricsLogger mMetricsLogger = mock(MetricsLogger.class); @Override public void onCreate(@Nullable Bundle savedInstanceState) { @@ -559,6 +624,11 @@ public class IntentForwarderActivityTest { mStartActivityIntent = intent; mUserIdActivityLaunchedIn = userId; } + + @Override + protected MetricsLogger getMetricsLogger() { + return mMetricsLogger; + } } public class TestInjector implements IntentForwarderActivity.Injector { diff --git a/core/tests/utillib/Android.bp b/core/tests/utillib/Android.bp new file mode 100644 index 000000000000..1f742c208ba2 --- /dev/null +++ b/core/tests/utillib/Android.bp @@ -0,0 +1,22 @@ +// 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. + +java_library { + name: "frameworks-core-util-lib", + + srcs: ["**/*.java"], + + static_libs: ["junit"], + libs: ["android.test.base"], +} diff --git a/core/tests/utillib/Android.mk b/core/tests/utillib/Android.mk deleted file mode 100644 index be1ab1f73d9f..000000000000 --- a/core/tests/utillib/Android.mk +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright (C) 2010 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := $(call all-subdir-java-files) - -LOCAL_MODULE := frameworks-core-util-lib -LOCAL_STATIC_JAVA_LIBRARIES := junit -LOCAL_JAVA_LIBRARIES := android.test.base - -include $(BUILD_STATIC_JAVA_LIBRARY) - -# Build the test APKs using their own makefiles -include $(call all-makefiles-under,$(LOCAL_PATH)) - diff --git a/core/tests/utiltests/src/com/android/internal/util/MimeIconUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/MimeIconUtilsTest.java new file mode 100644 index 000000000000..4412c2c6949c --- /dev/null +++ b/core/tests/utiltests/src/com/android/internal/util/MimeIconUtilsTest.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.util; + +import junit.framework.TestCase; + +/** + * Tests for {@link MimeIconUtils}. + */ +public class MimeIconUtilsTest extends TestCase { + public void testSimple() throws Exception { + assertEquals("PNG image", + MimeIconUtils.getTypeInfo("image/png").getLabel()); + assertEquals("Image", + MimeIconUtils.getTypeInfo("image/x-custom").getLabel()); + + assertEquals("ALC file", + MimeIconUtils.getTypeInfo("chemical/x-alchemy").getLabel()); + assertEquals("File", + MimeIconUtils.getTypeInfo("x-custom/x-custom").getLabel()); + + assertEquals("Folder", + MimeIconUtils.getTypeInfo("inode/directory").getLabel()); + + assertEquals("ZIP archive", + MimeIconUtils.getTypeInfo("application/zip").getLabel()); + assertEquals("RAR archive", + MimeIconUtils.getTypeInfo("application/rar").getLabel()); + + assertEquals("TXT document", + MimeIconUtils.getTypeInfo("text/plain").getLabel()); + assertEquals("Document", + MimeIconUtils.getTypeInfo("text/x-custom").getLabel()); + + assertEquals("FLAC audio", + MimeIconUtils.getTypeInfo("audio/flac").getLabel()); + assertEquals("FLAC audio", + MimeIconUtils.getTypeInfo("application/x-flac").getLabel()); + } +} diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java index aaf40b41bc31..83c8b01e6ad6 100644 --- a/graphics/java/android/graphics/BaseCanvas.java +++ b/graphics/java/android/graphics/BaseCanvas.java @@ -599,9 +599,6 @@ public abstract class BaseCanvas { int colorOffset, @Nullable short[] indices, int indexOffset, int indexCount, @NonNull Paint paint) { checkRange(verts.length, vertOffset, vertexCount); - if (isHardwareAccelerated()) { - return; - } if (texs != null) { checkRange(texs.length, texOffset, vertexCount); } diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index 8d3bac8622f6..c064402f8345 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -836,7 +836,7 @@ public final class Bitmap implements Parcelable { * @return A bitmap that represents the specified subset of source * @throws IllegalArgumentException if the x, y, width, height values are * outside of the dimensions of the source bitmap, or width is <= 0, - * or height is <= 0 + * or height is <= 0, or if the source bitmap has already been recycled */ public static Bitmap createBitmap(@NonNull Bitmap source, int x, int y, int width, int height, @Nullable Matrix m, boolean filter) { @@ -849,6 +849,9 @@ public final class Bitmap implements Parcelable { if (y + height > source.getHeight()) { throw new IllegalArgumentException("y + height must be <= bitmap.height()"); } + if (source.isRecycled()) { + throw new IllegalArgumentException("cannot use a recycled source in createBitmap"); + } // check if we can just return our argument unchanged if (!source.isMutable() && x == 0 && y == 0 && width == source.getWidth() && @@ -1270,7 +1273,7 @@ public final class Bitmap implements Parcelable { node.setLeftTopRightBottom(0, 0, width, height); node.setClipToBounds(false); node.setForceDarkAllowed(false); - final RecordingCanvas canvas = node.startRecording(width, height); + final RecordingCanvas canvas = node.beginRecording(width, height); if (source.getWidth() != width || source.getHeight() != height) { canvas.scale(width / (float) source.getWidth(), height / (float) source.getHeight()); @@ -1842,6 +1845,7 @@ public final class Bitmap implements Parcelable { * @throws IllegalStateException if the bitmap's config is {@link Config#HARDWARE} * */ + @NonNull public Color getColor(int x, int y) { checkRecycled("Can't call getColor() on a recycled bitmap"); checkHardware("unable to getColor(), " diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java index b020556cd15f..e6233548651e 100644 --- a/graphics/java/android/graphics/HardwareRenderer.java +++ b/graphics/java/android/graphics/HardwareRenderer.java @@ -258,7 +258,7 @@ public class HardwareRenderer { * and the renderer will draw nothing. */ public void setContentRoot(@Nullable RenderNode content) { - RecordingCanvas canvas = mRootNode.startRecording(); + RecordingCanvas canvas = mRootNode.beginRecording(); if (content != null) { canvas.drawRenderNode(content); } diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java index 62647741dcfa..15d855e9560c 100644 --- a/graphics/java/android/graphics/ImageFormat.java +++ b/graphics/java/android/graphics/ImageFormat.java @@ -16,7 +16,43 @@ package android.graphics; +import android.annotation.IntDef; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + public class ImageFormat { + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = { + UNKNOWN, + RGB_565, + YV12, + Y8, + Y16, + NV16, + NV21, + YUY2, + JPEG, + DEPTH_JPEG, + YUV_420_888, + YUV_422_888, + YUV_444_888, + FLEX_RGB_888, + FLEX_RGBA_8888, + RAW_SENSOR, + RAW_PRIVATE, + RAW10, + RAW12, + DEPTH16, + DEPTH_POINT_CLOUD, + RAW_DEPTH, + PRIVATE, + HEIC + }) + public @interface Format { + } + /* * these constants are chosen to be binary compatible with their previous * location in PixelFormat.java @@ -731,7 +767,7 @@ public class ImageFormat { * @return the number of bits per pixel of the given format or -1 if the * format doesn't exist or is not supported. */ - public static int getBitsPerPixel(int format) { + public static int getBitsPerPixel(@Format int format) { switch (format) { case RGB_565: return 16; @@ -781,7 +817,7 @@ public class ImageFormat { * * @hide */ - public static boolean isPublicFormat(int format) { + public static boolean isPublicFormat(@Format int format) { switch (format) { case RGB_565: case NV16: diff --git a/graphics/java/android/graphics/RecordingCanvas.java b/graphics/java/android/graphics/RecordingCanvas.java index 515532ffda52..30466e1f6b1f 100644 --- a/graphics/java/android/graphics/RecordingCanvas.java +++ b/graphics/java/android/graphics/RecordingCanvas.java @@ -31,7 +31,7 @@ import dalvik.annotation.optimization.FastNative; * Bitmap objects that it draws, preventing the backing memory of Bitmaps from being released while * the RecordingCanvas is still holding a native reference to the memory. * - * This is obtained by calling {@link RenderNode#startRecording()} and is valid until the matching + * This is obtained by calling {@link RenderNode#beginRecording()} and is valid until the matching * {@link RenderNode#endRecording()} is called. It must not be retained beyond that as it is * internally reused. */ diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java index 09b18b771250..42b6acd3b25a 100644 --- a/graphics/java/android/graphics/RenderNode.java +++ b/graphics/java/android/graphics/RenderNode.java @@ -16,6 +16,9 @@ package android.graphics; +import android.annotation.BytesLong; +import android.annotation.ColorInt; +import android.annotation.FloatRange; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -24,6 +27,8 @@ import android.view.RenderNodeAnimator; import android.view.Surface; import android.view.View; +import com.android.internal.util.ArrayUtils; + import dalvik.annotation.optimization.CriticalNative; import dalvik.annotation.optimization.FastNative; @@ -59,7 +64,7 @@ import java.lang.annotation.RetentionPolicy; * <pre class="prettyprint"> * RenderNode renderNode = RenderNode.create("myRenderNode"); * renderNode.setLeftTopRightBottom(0, 0, 50, 50); // Set the size to 50x50 - * RecordingCanvas canvas = renderNode.startRecording(); + * RecordingCanvas canvas = renderNode.beginRecording(); * try { * // Draw with the canvas * canvas.drawRect(...); @@ -71,14 +76,13 @@ import java.lang.annotation.RetentionPolicy; * <h3>Drawing a RenderNode in a View</h3> * <pre class="prettyprint"> * protected void onDraw(Canvas canvas) { - * if (canvas instanceof RecordingCanvas) { - * RecordingCanvas recordingCanvas = (RecordingCanvas) canvas; + * if (canvas.isHardwareAccelerated()) { * // Check that the RenderNode has a display list, re-recording it if it does not. * if (!myRenderNode.hasDisplayList()) { * updateDisplayList(myRenderNode); * } * // Draw the RenderNode into this canvas. - * recordingCanvas.drawRenderNode(myRenderNode); + * canvas.drawRenderNode(myRenderNode); * } * } * </pre> @@ -95,7 +99,7 @@ import java.lang.annotation.RetentionPolicy; * * <h3>Properties</h3> * <p>In addition, a RenderNode offers several properties, such as - * {@link #setScaleX(float)} or {@link #setLeft(int)}, that can be used to affect all + * {@link #setScaleX(float)} or {@link #setTranslationX(float)}, that can be used to affect all * the drawing commands recorded within. For instance, these properties can be used * to move around a large number of images without re-issuing all the individual * <code>canvas.drawBitmap()</code> calls.</p> @@ -104,7 +108,7 @@ import java.lang.annotation.RetentionPolicy; * private void createDisplayList() { * mRenderNode = RenderNode.create("MyRenderNode"); * mRenderNode.setLeftTopRightBottom(0, 0, width, height); - * RecordingCanvas canvas = mRenderNode.startRecording(); + * RecordingCanvas canvas = mRenderNode.beginRecording(); * try { * for (Bitmap b : mBitmaps) { * canvas.drawBitmap(b, 0.0f, 0.0f, null); @@ -116,9 +120,8 @@ import java.lang.annotation.RetentionPolicy; * } * * protected void onDraw(Canvas canvas) { - * if (canvas instanceof RecordingCanvas) { - * RecordingCanvas recordingCanvas = (RecordingCanvas) canvas; - * recordingCanvas.drawRenderNode(mRenderNode); + * if (canvas.isHardwareAccelerated()) + * canvas.drawRenderNode(mRenderNode); * } * } * @@ -178,6 +181,10 @@ public final class RenderNode { private final AnimationHost mAnimationHost; private RecordingCanvas mCurrentRecordingCanvas; + // Will be null if not currently registered + @Nullable + private CompositePositionUpdateListener mCompositePositionUpdateListener; + /** * Creates a new RenderNode that can be used to record batches of * drawing operations, and store / apply render properties when drawn. @@ -247,15 +254,70 @@ public final class RenderNode { } + private static final class CompositePositionUpdateListener implements PositionUpdateListener { + private final PositionUpdateListener[] mListeners; + + CompositePositionUpdateListener(PositionUpdateListener... listeners) { + mListeners = listeners; + } + + public CompositePositionUpdateListener with(PositionUpdateListener listener) { + return new CompositePositionUpdateListener( + ArrayUtils.appendElement(PositionUpdateListener.class, mListeners, listener)); + } + + public CompositePositionUpdateListener without(PositionUpdateListener listener) { + return new CompositePositionUpdateListener( + ArrayUtils.removeElement(PositionUpdateListener.class, mListeners, listener)); + } + + @Override + public void positionChanged(long frameNumber, int left, int top, int right, int bottom) { + for (PositionUpdateListener pul : mListeners) { + pul.positionChanged(frameNumber, left, top, right, bottom); + } + } + + @Override + public void positionLost(long frameNumber) { + for (PositionUpdateListener pul : mListeners) { + pul.positionLost(frameNumber); + } + } + } + /** - * Enable callbacks for position changes. + * Enable callbacks for position changes. Call only from the UI thread or with + * external synchronization. * * @hide */ - public void requestPositionUpdates(PositionUpdateListener listener) { - nRequestPositionUpdates(mNativeRenderNode, listener); + public void addPositionUpdateListener(@NonNull PositionUpdateListener listener) { + CompositePositionUpdateListener comp = mCompositePositionUpdateListener; + if (comp == null) { + comp = new CompositePositionUpdateListener(listener); + } else { + comp = comp.with(listener); + } + mCompositePositionUpdateListener = comp; + nRequestPositionUpdates(mNativeRenderNode, comp); } + /** + * Disable a callback for position changes. Call only from the UI thread or with + * external synchronization. + * + * @param listener Callback to remove + * @hide + */ + public void removePositionUpdateListener(@NonNull PositionUpdateListener listener) { + CompositePositionUpdateListener comp = mCompositePositionUpdateListener; + if (comp != null) { + comp = comp.without(listener); + mCompositePositionUpdateListener = comp; + nRequestPositionUpdates(mNativeRenderNode, comp); + } + } /** * Starts recording a display list for the render node. All @@ -263,19 +325,20 @@ public final class RenderNode { * stored in this display list. * * {@link #endRecording()} must be called when the recording is finished in order to apply - * the updated display list. + * the updated display list. Failing to call {@link #endRecording()} will result in an + * {@link IllegalStateException} if {@link #beginRecording(int, int)} is called again. * * @param width The width of the recording viewport. This will not alter the width of the - * RenderNode itself, that must be set with {@link #setLeft(int)} and - * {@link #setRight(int)} + * RenderNode itself, that must be set with {@link #setPosition(Rect)}. * @param height The height of the recording viewport. This will not alter the height of the - * RenderNode itself, that must be set with {@link #setTop(int)} and - * {@link #setBottom(int)}. + * RenderNode itself, that must be set with {@link #setPosition(Rect)}. * @return A canvas to record drawing operations. + * @throws IllegalStateException If a recording is already in progress. That is, the previous + * call to {@link #beginRecording(int, int)} did not call {@link #endRecording()}. * @see #endRecording() * @see #hasDisplayList() */ - public RecordingCanvas startRecording(int width, int height) { + public RecordingCanvas beginRecording(int width, int height) { if (mCurrentRecordingCanvas != null) { throw new IllegalStateException( "Recording currently in progress - missing #endRecording() call?"); @@ -285,21 +348,18 @@ public final class RenderNode { } /** - * Same as {@link #startRecording(int, int)} with the width & height set + * Same as {@link #beginRecording(int, int)} with the width & height set * to the RenderNode's own width & height. The RenderNode's width & height may be set - * with {@link #setLeftTopRightBottom(int, int, int, int)}. - */ - public RecordingCanvas startRecording() { - return startRecording(nGetWidth(mNativeRenderNode), nGetHeight(mNativeRenderNode)); - } - - /** - * @hide - * @deprecated use {@link #startRecording(int, int)} instead + * with {@link #setPosition(int, int, int, int)}. + * + * @return A canvas to record drawing operations. + * @throws IllegalStateException If a recording is already in progress. That is, the previous + * call to {@link #beginRecording(int, int)} did not call {@link #endRecording()}. + * @see #endRecording() + * @see #hasDisplayList() */ - @Deprecated - public RecordingCanvas start(int width, int height) { - return startRecording(width, height); + public RecordingCanvas beginRecording() { + return beginRecording(nGetWidth(mNativeRenderNode), nGetHeight(mNativeRenderNode)); } /** @@ -307,13 +367,13 @@ public final class RenderNode { * Ends the recording for this display list. Calling this method marks * the display list valid and {@link #hasDisplayList()} will return true. * - * @see #startRecording(int, int) + * @see #beginRecording(int, int) * @see #hasDisplayList() */ public void endRecording() { if (mCurrentRecordingCanvas == null) { throw new IllegalStateException( - "No recording in progress, forgot to call #startRecording()?"); + "No recording in progress, forgot to call #beginRecording()?"); } RecordingCanvas canvas = mCurrentRecordingCanvas; mCurrentRecordingCanvas = null; @@ -324,13 +384,21 @@ public final class RenderNode { /** * @hide + * @deprecated use {@link #beginRecording(int, int)} instead + */ + @Deprecated + public RecordingCanvas start(int width, int height) { + return beginRecording(width, height); + } + + /** + * @hide * @deprecated use {@link #endRecording()} instead */ @Deprecated public void end(RecordingCanvas canvas) { - if (mCurrentRecordingCanvas != canvas) { - throw new IllegalArgumentException( - "Canvas given isn't the one that was returned from #startRecording"); + if (canvas != mCurrentRecordingCanvas) { + throw new IllegalArgumentException("Wrong canvas"); } endRecording(); } @@ -346,7 +414,7 @@ public final class RenderNode { /** * Returns whether the RenderNode has a display list. If this returns false, the RenderNode - * should be re-recorded with {@link #startRecording()} and {@link #endRecording()}. + * should be re-recorded with {@link #beginRecording()} and {@link #endRecording()}. * * A RenderNode without a display list may still be drawn, however it will have no impact * on the rendering content until its display list is updated. @@ -425,18 +493,21 @@ public final class RenderNode { * for performance or required for the current combination of {@link #setAlpha(float)} and * {@link #setHasOverlappingRendering(boolean)}. * - * The usage of this is instead to allow for either overriding of the internal behavior + * <p>The usage of this is instead to allow for either overriding of the internal behavior * if it's measured to be necessary for the particular rendering content in question or, more * usefully, to add a composition effect to the RenderNode via the optional paint parameter. * - * Note: When a RenderNode is using a compositing layer it will also result in + * <p>Note: When a RenderNode is using a compositing layer it will also result in * clipToBounds=true behavior. * * @param forceToLayer if true this forces the RenderNode to use an intermediate buffer. * Default & generally recommended value is false. * @param paint The blend mode, alpha, and ColorFilter to apply to the compositing layer. - * Only applies if forceToLayer is true. - * @return true if anything changed, false otherwise + * Only applies if forceToLayer is true. The paint's alpha is multiplied + * with {@link #getAlpha()} to resolve the final alpha of the RenderNode. + * If null then no additional composition effects are applied on top of the + * composition layer. + * @return True if the value changed, false if the new value was the same as the previous value. */ public boolean setUseCompositingLayer(boolean forceToLayer, @Nullable Paint paint) { boolean didChange = nSetLayerType(mNativeRenderNode, forceToLayer ? 2 : 0); @@ -457,15 +528,22 @@ public final class RenderNode { } /** - * Sets the clip bounds of the RenderNode. If null, the clip bounds is removed from the - * RenderNode. If non-null, the RenderNode will be clipped to this rect. If + * Sets an additional clip on the RenderNode. If null, the extra clip is removed from the + * RenderNode. If non-null, the RenderNode will be clipped to this rect. In addition if * {@link #setClipToBounds(boolean)} is true, then the RenderNode will be clipped to the - * intersection of this rectangle and the bounds of the render node. + * intersection of this rectangle and the bounds of the render node, which is set with + * {@link #setPosition(Rect)}. + * + * <p>This is equivalent to do a {@link Canvas#clipRect(Rect)} at the start of this + * RenderNode's display list. However, as this is a property of the RenderNode instead + * of part of the display list it can be more easily animated for transient additional + * clipping. An example usage of this would be the {@link android.transition.ChangeBounds} + * transition animation with the resizeClip=true option. * - * @param rect the bounds to clip to. If null, the clip bounds are reset - * @return True if the clip bounds changed, false otherwise + * @param rect the bounds to clip to. If null, the additional clip is removed. + * @return True if the value changed, false if the new value was the same as the previous value. */ - public boolean setClipBounds(@Nullable Rect rect) { + public boolean setClipRect(@Nullable Rect rect) { if (rect == null) { return nSetClipBoundsEmpty(mNativeRenderNode); } else { @@ -477,11 +555,12 @@ public final class RenderNode { * Set whether the Render node should clip itself to its bounds. This defaults to true, * and is useful to the renderer in enable quick-rejection of chunks of the tree as well as * better partial invalidation support. Clipping can be further restricted or controlled - * through the combination of this property as well as {@link #setClipBounds(Rect)}, which + * through the combination of this property as well as {@link #setClipRect(Rect)}, which * allows for a different clipping rectangle to be used in addition to or instead of the - * {@link #setLeftTopRightBottom(int, int, int, int)} or the RenderNode. + * {@link #setPosition(int, int, int, int)} or the RenderNode. * * @param clipToBounds true if the display list should clip to its bounds, false otherwise. + * @return True if the value changed, false if the new value was the same as the previous value. */ public boolean setClipToBounds(boolean clipToBounds) { return nSetClipToBounds(mNativeRenderNode, clipToBounds); @@ -489,7 +568,7 @@ public final class RenderNode { /** * Returns whether or not the RenderNode is clipping to its bounds. See - * {@link #setClipToBounds(boolean)} and {@link #setLeftTopRightBottom(int, int, int, int)} + * {@link #setClipToBounds(boolean)} and {@link #setPosition(int, int, int, int)} * * @return true if the render node clips to its bounds, false otherwise. */ @@ -498,20 +577,58 @@ public final class RenderNode { } /** - * Sets whether the RenderNode should be drawn immediately after the + * <p>Sets whether the RenderNode should be drawn immediately after the * closest ancestor RenderNode containing a projection receiver. * + * <p>The default is false, and the rendering of this node happens in the typical draw order. + * + * <p>If true, then at rendering time this rendernode will not be drawn in order with the + * {@link Canvas#drawRenderNode(RenderNode)} command that drew this RenderNode, but instead + * it will be re-positioned in the RenderNode tree to be drawn on the closet ancestor with a + * child rendernode that has {@link #setProjectionReceiver(boolean)} as true. + * + * <p>The typical usage of this is to allow a child RenderNode to draw on a parent's background, + * such as the platform's usage with {@link android.graphics.drawable.RippleDrawable}. Consider + * the following structure, built out of which RenderNode called drawRenderNode on a different + * RenderNode: + * + * <pre> + * +-------------+ + * |RenderNode: P| + * +-+----------++ + * | | + * v v + * +-------+-----+ +-+--------------+ + * |RenderNode: C| |RenderNode: P'BG| + * +-------+-----+ +----------------+ + * | + * | + * +--------+-------+ + * |RenderNode: C'BG| + * +----------------+ + * </pre> + * + * If P'BG is a projection receiver, and C'BG is set to project backwards then C'BG will + * behave as if it was drawn directly by P'BG instead of by C. This includes inheriting P'BG's + * clip instead of C's clip. + * * @param shouldProject true if the display list should be projected onto a - * containing volume. + * containing volume. Default is false. + * @return True if the value changed, false if the new value was the same as the previous value. */ public boolean setProjectBackwards(boolean shouldProject) { return nSetProjectBackwards(mNativeRenderNode, shouldProject); } /** - * Sets whether the RenderNode is a projection receiver - that its parent - * RenderNode should draw any descendent RenderNodes with - * ProjectBackwards=true directly on top of it. Default value is false. + * Sets whether the RenderNode is a projection receiver. If true then this RenderNode's parent + * should draw any descendant RenderNodes with ProjectBackwards=true directly on top of it. + * Default value is false. See + * {@link #setProjectBackwards(boolean)} for a description of what this entails. + * + * @param shouldRecieve True if this RenderNode is a projection receiver, false otherwise. + * Default is false. + * @return True if the value changed, false if the new value was the same as the previous value. */ public boolean setProjectionReceiver(boolean shouldRecieve) { return nSetProjectionReceiver(mNativeRenderNode, shouldRecieve); @@ -526,6 +643,7 @@ public final class RenderNode { * outline for those changes to be applied. * * @param outline The outline to use for this RenderNode. + * @return True if the value changed, false if the new value was the same as the previous value. */ public boolean setOutline(@Nullable Outline outline) { if (outline == null) { @@ -572,8 +690,9 @@ public final class RenderNode { * {@link android.R.attr#spotShadowAlpha} theme attribute * * @param color The color this RenderNode will cast for its elevation spot shadow. + * @return True if the value changed, false if the new value was the same as the previous value. */ - public boolean setSpotShadowColor(int color) { + public boolean setSpotShadowColor(@ColorInt int color) { return nSetSpotShadowColor(mNativeRenderNode, color); } @@ -581,7 +700,7 @@ public final class RenderNode { * @return The shadow color set by {@link #setSpotShadowColor(int)}, or black if nothing * was set */ - public int getSpotShadowColor() { + public @ColorInt int getSpotShadowColor() { return nGetSpotShadowColor(mNativeRenderNode); } @@ -597,8 +716,9 @@ public final class RenderNode { * {@link android.R.attr#ambientShadowAlpha} theme attribute. * * @param color The color this RenderNode will cast for its elevation shadow. + * @return True if the value changed, false if the new value was the same as the previous value. */ - public boolean setAmbientShadowColor(int color) { + public boolean setAmbientShadowColor(@ColorInt int color) { return nSetAmbientShadowColor(mNativeRenderNode, color); } @@ -606,7 +726,7 @@ public final class RenderNode { * @return The shadow color set by {@link #setAmbientShadowColor(int)}, or black if * nothing was set */ - public int getAmbientShadowColor() { + public @ColorInt int getAmbientShadowColor() { return nGetAmbientShadowColor(mNativeRenderNode); } @@ -614,6 +734,8 @@ public final class RenderNode { * Enables or disables clipping to the outline. * * @param clipToOutline true if clipping to the outline. + * @return True if the clipToOutline value changed, false if previous value matched the new + * value. */ public boolean setClipToOutline(boolean clipToOutline) { return nSetClipToOutline(mNativeRenderNode, clipToOutline); @@ -640,7 +762,7 @@ public final class RenderNode { /** * Set the static matrix on the display list. The specified matrix is combined with other - * transforms (such as {@link #setScaleX(float)}, {@link #setRotation(float)}, etc.) + * transforms (such as {@link #setScaleX(float)}, {@link #setRotationZ(float)}, etc.) * * @param matrix A transform matrix to apply to this display list * @hide TODO Do we want this? @@ -669,6 +791,7 @@ public final class RenderNode { * @param alpha The translucency of the display list, must be a value between 0.0f and 1.0f * @see View#setAlpha(float) * @see #getAlpha() + * @return True if the value changed, false if the new value was the same as the previous value. */ public boolean setAlpha(float alpha) { return nSetAlpha(mNativeRenderNode, alpha); @@ -742,7 +865,7 @@ public final class RenderNode { * Sets the base elevation of this RenderNode in pixels * * @param lift the elevation in pixels - * @return true if the elevation changed, false if it was the same + * @return True if the value changed, false if the new value was the same as the previous value. */ public boolean setElevation(float lift) { return nSetElevation(mNativeRenderNode, lift); @@ -763,6 +886,7 @@ public final class RenderNode { * @param translationX The X axis translation value of the display list, in pixels * @see View#setTranslationX(float) * @see #getTranslationX() + * @return True if the value changed, false if the new value was the same as the previous value. */ public boolean setTranslationX(float translationX) { return nSetTranslationX(mNativeRenderNode, translationX); @@ -783,6 +907,7 @@ public final class RenderNode { * @param translationY The Y axis translation value of the display list, in pixels * @see View#setTranslationY(float) * @see #getTranslationY() + * @return True if the value changed, false if the new value was the same as the previous value. */ public boolean setTranslationY(float translationY) { return nSetTranslationY(mNativeRenderNode, translationY); @@ -802,6 +927,7 @@ public final class RenderNode { * * @see View#setTranslationZ(float) * @see #getTranslationZ() + * @return True if the value changed, false if the new value was the same as the previous value. */ public boolean setTranslationZ(float translationZ) { return nSetTranslationZ(mNativeRenderNode, translationZ); @@ -820,19 +946,20 @@ public final class RenderNode { * Sets the rotation value for the display list around the Z axis. * * @param rotation The rotation value of the display list, in degrees - * @see View#setRotation(float) - * @see #getRotation() + * @see View#setRotationZ(float) + * @see #getRotationZ() + * @return True if the value changed, false if the new value was the same as the previous value. */ - public boolean setRotation(float rotation) { + public boolean setRotationZ(float rotation) { return nSetRotation(mNativeRenderNode, rotation); } /** * Returns the rotation value for this display list around the Z axis, in degrees. * - * @see #setRotation(float) + * @see #setRotationZ(float) */ - public float getRotation() { + public float getRotationZ() { return nGetRotation(mNativeRenderNode); } @@ -842,6 +969,7 @@ public final class RenderNode { * @param rotationX The rotation value of the display list, in degrees * @see View#setRotationX(float) * @see #getRotationX() + * @return True if the value changed, false if the new value was the same as the previous value. */ public boolean setRotationX(float rotationX) { return nSetRotationX(mNativeRenderNode, rotationX); @@ -862,6 +990,7 @@ public final class RenderNode { * @param rotationY The rotation value of the display list, in degrees * @see View#setRotationY(float) * @see #getRotationY() + * @return True if the value changed, false if the new value was the same as the previous value. */ public boolean setRotationY(float rotationY) { return nSetRotationY(mNativeRenderNode, rotationY); @@ -882,6 +1011,7 @@ public final class RenderNode { * @param scaleX The scale value of the display list * @see View#setScaleX(float) * @see #getScaleX() + * @return True if the value changed, false if the new value was the same as the previous value. */ public boolean setScaleX(float scaleX) { return nSetScaleX(mNativeRenderNode, scaleX); @@ -902,6 +1032,7 @@ public final class RenderNode { * @param scaleY The scale value of the display list * @see View#setScaleY(float) * @see #getScaleY() + * @return True if the value changed, false if the new value was the same as the previous value. */ public boolean setScaleY(float scaleY) { return nSetScaleY(mNativeRenderNode, scaleY); @@ -922,6 +1053,7 @@ public final class RenderNode { * @param pivotX The pivot value of the display list on the X axis, in pixels * @see View#setPivotX(float) * @see #getPivotX() + * @return True if the value changed, false if the new value was the same as the previous value. */ public boolean setPivotX(float pivotX) { return nSetPivotX(mNativeRenderNode, pivotX); @@ -942,6 +1074,7 @@ public final class RenderNode { * @param pivotY The pivot value of the display list on the Y axis, in pixels * @see View#setPivotY(float) * @see #getPivotY() + * @return True if the value changed, false if the new value was the same as the previous value. */ public boolean setPivotY(float pivotY) { return nSetPivotY(mNativeRenderNode, pivotY); @@ -969,6 +1102,8 @@ public final class RenderNode { * Clears any pivot previously set by a call to {@link #setPivotX(float)} or * {@link #setPivotY(float)}. After calling this {@link #isPivotExplicitlySet()} will be false * and the pivot used for rotation will return to default of being centered on the view. + * + * @return True if the value changed, false if the new value was the same as the previous value. */ public boolean resetPivot() { return nResetPivot(mNativeRenderNode); @@ -997,8 +1132,10 @@ public final class RenderNode { * @param distance The distance in pixels, must always be positive * @see #setRotationX(float) * @see #setRotationY(float) + * @return True if the value changed, false if the new value was the same as the previous value. */ - public boolean setCameraDistance(float distance) { + public boolean setCameraDistance( + @FloatRange(from = 0.0f, to = Float.MAX_VALUE) float distance) { if (!Float.isFinite(distance) || distance < 0.0f) { throw new IllegalArgumentException("distance must be finite & positive, given=" + distance); @@ -1013,7 +1150,7 @@ public final class RenderNode { * @return the distance along the Z axis in pixels. * @see #setCameraDistance(float) */ - public float getCameraDistance() { + public @FloatRange(from = 0.0f, to = Float.MAX_VALUE) float getCameraDistance() { return -nGetCameraDistance(mNativeRenderNode); } @@ -1022,6 +1159,7 @@ public final class RenderNode { * * @param left The left position, in pixels, of the RenderNode * @return true if the value changed, false otherwise + * @hide */ public boolean setLeft(int left) { return nSetLeft(mNativeRenderNode, left); @@ -1032,6 +1170,7 @@ public final class RenderNode { * * @param top The top position, in pixels, of the RenderNode * @return true if the value changed, false otherwise. + * @hide */ public boolean setTop(int top) { return nSetTop(mNativeRenderNode, top); @@ -1042,6 +1181,7 @@ public final class RenderNode { * * @param right The right position, in pixels, of the RenderNode * @return true if the value changed, false otherwise. + * @hide */ public boolean setRight(int right) { return nSetRight(mNativeRenderNode, right); @@ -1052,6 +1192,7 @@ public final class RenderNode { * * @param bottom The bottom position, in pixels, of the RenderNode * @return true if the value changed, false otherwise. + * @hide */ public boolean setBottom(int bottom) { return nSetBottom(mNativeRenderNode, bottom); @@ -1060,8 +1201,6 @@ public final class RenderNode { /** * Gets the left position for the RenderNode. * - * See {@link #setLeft(int)} - * * @return the left position in pixels */ public int getLeft() { @@ -1071,8 +1210,6 @@ public final class RenderNode { /** * Gets the top position for the RenderNode. * - * See {@link #setTop(int)} - * * @return the top position in pixels */ public int getTop() { @@ -1082,8 +1219,6 @@ public final class RenderNode { /** * Gets the right position for the RenderNode. * - * See {@link #setRight(int)} - * * @return the right position in pixels */ public int getRight() { @@ -1093,8 +1228,6 @@ public final class RenderNode { /** * Gets the bottom position for the RenderNode. * - * See {@link #setBottom(int)} - * * @return the bottom position in pixels */ public int getBottom() { @@ -1127,20 +1260,41 @@ public final class RenderNode { * @param right The right position of the RenderNode, in pixels * @param bottom The bottom position of the RenderNode, in pixels * @return true if any values changed, false otherwise. - * @see #setLeft(int) - * @see #setTop(int) - * @see #setRight(int) - * @see #setBottom(int) + * @hide */ public boolean setLeftTopRightBottom(int left, int top, int right, int bottom) { return nSetLeftTopRightBottom(mNativeRenderNode, left, top, right, bottom); } /** + * Sets the position of the RenderNode. + * + * @param left The left position of the RenderNode, in pixels + * @param top The top position of the RenderNode, in pixels + * @param right The right position of the RenderNode, in pixels + * @param bottom The bottom position of the RenderNode, in pixels + * @return True if the value changed, false if the new value was the same as the previous value. + */ + public boolean setPosition(int left, int top, int right, int bottom) { + return nSetLeftTopRightBottom(mNativeRenderNode, left, top, right, bottom); + } + + /** + * Sets the position of the RenderNode. + * + * @param position The position rectangle in pixels + * @return True if the value changed, false if the new value was the same as the previous value. + */ + public boolean setPosition(Rect position) { + return nSetLeftTopRightBottom(mNativeRenderNode, + position.left, position.top, position.right, position.bottom); + } + + /** * Offsets the left and right positions for the RenderNode * * @param offset The amount that the left and right positions are offset in pixels - * @return true if any values changed, false otherwise. + * @return True if the value changed, false if the new value was the same as the previous value. */ public boolean offsetLeftAndRight(int offset) { return nOffsetLeftAndRight(mNativeRenderNode, offset); @@ -1150,7 +1304,7 @@ public final class RenderNode { * Offsets the top and bottom values for the RenderNode * * @param offset The amount that the left and right positions are offset in pixels - * @return true if any values changed, false otherwise. + * @return True if the value changed, false if the new value was the same as the previous value. */ public boolean offsetTopAndBottom(int offset) { return nOffsetTopAndBottom(mNativeRenderNode, offset); @@ -1170,8 +1324,10 @@ public final class RenderNode { * Gets the approximate memory usage of the RenderNode for debug purposes. Does not include * the memory usage of any child RenderNodes nor any bitmaps, only the memory usage of * this RenderNode and any data it owns. + * + * @return Approximate memory usage in bytes. */ - public int computeApproximateMemoryUsage() { + public @BytesLong long computeApproximateMemoryUsage() { return nGetDebugSize(mNativeRenderNode); } @@ -1186,7 +1342,7 @@ public final class RenderNode { * it prevent any 'false' in any of its children. * * @param allow Whether or not to allow force dark. - * @return true If the value has changed, false otherwise. + * @return True if the value changed, false if the new value was the same as the previous value. */ public boolean setForceDarkAllowed(boolean allow) { return nSetAllowForceDark(mNativeRenderNode, allow); diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java index 2ae28f507e0d..08f417662523 100644 --- a/keystore/java/android/security/Credentials.java +++ b/keystore/java/android/security/Credentials.java @@ -20,6 +20,7 @@ import com.android.org.bouncycastle.util.io.pem.PemObject; import com.android.org.bouncycastle.util.io.pem.PemReader; import com.android.org.bouncycastle.util.io.pem.PemWriter; +import android.annotation.UnsupportedAppUsage; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -122,6 +123,7 @@ public class Credentials { * Convert objects to a PEM format which is used for * CA_CERTIFICATE and USER_CERTIFICATE entries. */ + @UnsupportedAppUsage public static byte[] convertToPem(Certificate... objects) throws IOException, CertificateEncodingException { ByteArrayOutputStream bao = new ByteArrayOutputStream(); diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl index 0d32075d20d2..b3cdff7eedf7 100644 --- a/keystore/java/android/security/IKeyChainService.aidl +++ b/keystore/java/android/security/IKeyChainService.aidl @@ -27,6 +27,7 @@ import android.security.keystore.ParcelableKeyGenParameterSpec; */ interface IKeyChainService { // APIs used by KeyChain + @UnsupportedAppUsage String requestPrivateKey(String alias); byte[] getCertificate(String alias); byte[] getCaCertificates(String alias); diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index 66d8542553d2..117d05e837ef 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -35,6 +35,12 @@ #endif #endif +#ifdef __ANDROID__ +#define ANDROID_LOG(x) LOG(x) +#else +#define ANDROID_LOG(x) std::stringstream() +#endif + #include "androidfw/ResourceUtils.h" namespace android { @@ -356,6 +362,7 @@ std::unique_ptr<Asset> AssetManager2::OpenNonAsset(const std::string& filename, ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override, bool /*stop_at_first_match*/, + bool ignore_configuration, FindEntryResult* out_entry) const { // Might use this if density_override != 0. ResTable_config density_override_config; @@ -379,7 +386,8 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri const uint8_t package_idx = package_ids_[package_id]; if (package_idx == 0xff) { - LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.", package_id, resid); + ANDROID_LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.", + package_id, resid); return kInvalidCookie; } @@ -399,7 +407,7 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri // If desired_config is the same as the set configuration, then we can use our filtered list // and we don't need to match the configurations, since they already matched. - const bool use_fast_path = desired_config == &configuration_; + const bool use_fast_path = !ignore_configuration && desired_config == &configuration_; for (size_t pi = 0; pi < package_count; pi++) { const ConfiguredPackage& loaded_package_impl = package_group.packages_[pi]; @@ -475,21 +483,23 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri // ResTable_config, we must copy it. const auto iter_end = type_spec->types + type_spec->type_count; for (auto iter = type_spec->types; iter != iter_end; ++iter) { - ResTable_config this_config; - this_config.copyFromDtoH((*iter)->config); + ResTable_config this_config{}; - if (!this_config.match(*desired_config)) { - continue; - } + if (!ignore_configuration) { + this_config.copyFromDtoH((*iter)->config); + if (!this_config.match(*desired_config)) { + continue; + } - if (best_config == nullptr) { - resolution_type = Resolution::Step::Type::INITIAL; - } else if (this_config.isBetterThan(*best_config, desired_config)) { - resolution_type = Resolution::Step::Type::BETTER_MATCH; - } else if (package_is_overlay && this_config.compare(*best_config) == 0) { - resolution_type = Resolution::Step::Type::OVERLAID; - } else { - continue; + if (best_config == nullptr) { + resolution_type = Resolution::Step::Type::INITIAL; + } else if (this_config.isBetterThan(*best_config, desired_config)) { + resolution_type = Resolution::Step::Type::BETTER_MATCH; + } else if (package_is_overlay && this_config.compare(*best_config) == 0) { + resolution_type = Resolution::Step::Type::OVERLAID; + } else { + continue; + } } // The configuration matches and is better than the previous selection. @@ -506,6 +516,11 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri best_config = &best_config_copy; best_offset = offset; + if (ignore_configuration) { + // Any configuration will suffice, so break. + break; + } + if (resource_resolution_logging_enabled_) { resolution_steps.push_back(Resolution::Step{resolution_type, this_config.toString(), @@ -622,8 +637,9 @@ std::string AssetManager2::GetLastResourceResolution() const { bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) const { FindEntryResult entry; - ApkAssetsCookie cookie = - FindEntry(resid, 0u /* density_override */, true /* stop_at_first_match */, &entry); + ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, + true /* stop_at_first_match */, + true /* ignore_configuration */, &entry); if (cookie == kInvalidCookie) { return false; } @@ -652,13 +668,14 @@ bool AssetManager2::GetResourceName(uint32_t resid, ResourceName* out_name) cons bool AssetManager2::GetResourceFlags(uint32_t resid, uint32_t* out_flags) const { FindEntryResult entry; - ApkAssetsCookie cookie = - FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */, &entry); + ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, + false /* stop_at_first_match */, + true /* ignore_configuration */, &entry); if (cookie != kInvalidCookie) { *out_flags = entry.type_flags; - return cookie; + return true; } - return kInvalidCookie; + return false; } ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, @@ -666,8 +683,8 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, ResTable_config* out_selected_config, uint32_t* out_flags) const { FindEntryResult entry; - ApkAssetsCookie cookie = - FindEntry(resid, density_override, false /* stop_at_first_match */, &entry); + ApkAssetsCookie cookie = FindEntry(resid, density_override, false /* stop_at_first_match */, + false /* ignore_configuration */, &entry); if (cookie == kInvalidCookie) { return kInvalidCookie; } @@ -759,8 +776,10 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>& } FindEntryResult entry; - ApkAssetsCookie cookie = - FindEntry(resid, 0u /* density_override */, false /* stop_at_first_match */, &entry); + ApkAssetsCookie cookie = FindEntry(resid, 0u /* density_override */, + false /* stop_at_first_match */, + false /* ignore_configuration */, + &entry); if (cookie == kInvalidCookie) { return nullptr; } @@ -1387,7 +1406,9 @@ void Theme::SetTo(const Theme& o) { // Find the cookie of the attribute resource id FindEntryResult attribute_entry_result; ApkAssetsCookie attribute_cookie = - o.asset_manager_->FindEntry(make_resid(p, t, e), 0 /* density_override */ , false, + o.asset_manager_->FindEntry(make_resid(p, t, e), 0 /* density_override */ , + true /* stop_at_first_match */, + true /* ignore_configuration */, &attribute_entry_result); // Determine the package id of the attribute in the destination AssetManager diff --git a/libs/androidfw/DisplayEventDispatcher.cpp b/libs/androidfw/DisplayEventDispatcher.cpp index 3b9a348047ba..660614895603 100644 --- a/libs/androidfw/DisplayEventDispatcher.cpp +++ b/libs/androidfw/DisplayEventDispatcher.cpp @@ -135,6 +135,9 @@ bool DisplayEventDispatcher::processPendingEvents( case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG: dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected); break; + case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED: + dispatchConfigChanged(ev.header.timestamp, ev.header.displayId, ev.config.configId); + break; default: ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type); break; diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index fc5aa9c7f1b9..1e2b36cb1703 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -257,11 +257,12 @@ class AssetManager2 { // Creates a new Theme from this AssetManager. std::unique_ptr<Theme> NewTheme(); - template <typename Func> - void ForEachPackage(Func func) const { + void ForEachPackage(const std::function<bool(const std::string&, uint8_t)> func) const { for (const PackageGroup& package_group : package_groups_) { - func(package_group.packages_.front().loaded_package_->GetPackageName(), - package_group.dynamic_ref_table.mAssignedPackageId); + if (!func(package_group.packages_.front().loaded_package_->GetPackageName(), + package_group.dynamic_ref_table.mAssignedPackageId)) { + return; + } } } @@ -282,10 +283,13 @@ class AssetManager2 { // care about the value. In this case, the value of `FindEntryResult::type_flags` is incomplete // and should not be used. // + // When `ignore_configuration` is true, FindEntry will return always select the first entry in + // for the type seen regardless of its configuration. + // // NOTE: FindEntry takes care of ensuring that structs within FindEntryResult have been properly // bounds-checked. Callers of FindEntry are free to trust the data if this method succeeds. ApkAssetsCookie FindEntry(uint32_t resid, uint16_t density_override, bool stop_at_first_match, - FindEntryResult* out_entry) const; + bool ignore_configuration, FindEntryResult* out_entry) const; // Assigns package IDs to all shared library ApkAssets. // Should be called whenever the ApkAssets are changed. diff --git a/libs/androidfw/include/androidfw/DisplayEventDispatcher.h b/libs/androidfw/include/androidfw/DisplayEventDispatcher.h index d2addba61679..5381c0174cb0 100644 --- a/libs/androidfw/include/androidfw/DisplayEventDispatcher.h +++ b/libs/androidfw/include/androidfw/DisplayEventDispatcher.h @@ -40,6 +40,8 @@ private: virtual void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) = 0; virtual void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) = 0; + virtual void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId, + int32_t configId) = 0; virtual int handleEvent(int receiveFd, int events, void* data); bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId, diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp index df1537e2d824..1bd30eb5371b 100644 --- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp +++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp @@ -161,7 +161,7 @@ void RenderNodeDrawable::forceDraw(SkCanvas* canvas) { SkAutoCanvasRestore acr2(canvas, shouldClip); canvas->setMatrix(mProjectedDisplayList->mParentMatrix); if (shouldClip) { - clipOutline(*mProjectedDisplayList->mProjectedOutline, canvas, nullptr); + canvas->clipPath(*mProjectedDisplayList->mProjectedOutline->getPath()); } drawBackwardsProjectedNodes(canvas, *mProjectedDisplayList); } diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp index 6da80628be60..6c04232ab7f5 100644 --- a/libs/hwui/renderthread/CacheManager.cpp +++ b/libs/hwui/renderthread/CacheManager.cpp @@ -117,6 +117,7 @@ void CacheManager::configureContext(GrContextOptions* contextOptions, const void auto& cache = skiapipeline::ShaderCache::get(); cache.initShaderDiskCache(identity, size); contextOptions->fPersistentCache = &cache; + contextOptions->fGpuPathRenderers &= ~GpuPathRenderers::kCoverageCounting; } void CacheManager::trimMemory(TrimMemoryMode mode) { diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index 3b43f1297597..5af660c8738a 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -254,10 +254,36 @@ void VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe float queuePriorities[1] = { 0.0 }; + void* queueNextPtr = nullptr; + + VkDeviceQueueGlobalPriorityCreateInfoEXT queuePriorityCreateInfo; + + if (Properties::contextPriority != 0 + && grExtensions.hasExtension(VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME, 2)) { + memset(&queuePriorityCreateInfo, 0, sizeof(VkDeviceQueueGlobalPriorityCreateInfoEXT)); + queuePriorityCreateInfo.sType = + VK_STRUCTURE_TYPE_DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_EXT; + queuePriorityCreateInfo.pNext = nullptr; + switch (Properties::contextPriority) { + case EGL_CONTEXT_PRIORITY_LOW_IMG: + queuePriorityCreateInfo.globalPriority = VK_QUEUE_GLOBAL_PRIORITY_LOW_EXT; + break; + case EGL_CONTEXT_PRIORITY_MEDIUM_IMG: + queuePriorityCreateInfo.globalPriority = VK_QUEUE_GLOBAL_PRIORITY_MEDIUM_EXT; + break; + case EGL_CONTEXT_PRIORITY_HIGH_IMG: + queuePriorityCreateInfo.globalPriority = VK_QUEUE_GLOBAL_PRIORITY_HIGH_EXT; + break; + default: + LOG_ALWAYS_FATAL("Unsupported context priority"); + } + queueNextPtr = &queuePriorityCreateInfo; + } + const VkDeviceQueueCreateInfo queueInfo[2] = { { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // sType - nullptr, // pNext + queueNextPtr, // pNext 0, // VkDeviceQueueCreateFlags mGraphicsQueueIndex, // queueFamilyIndex 1, // queueCount @@ -265,7 +291,7 @@ void VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe }, { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, // sType - nullptr, // pNext + queueNextPtr, // pNext 0, // VkDeviceQueueCreateFlags mPresentQueueIndex, // queueFamilyIndex 1, // queueCount diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp index 733b866d9c4c..abf083789c23 100644 --- a/libs/input/PointerController.cpp +++ b/libs/input/PointerController.cpp @@ -759,11 +759,11 @@ void PointerController::fadeOutAndReleaseAllSpotsLocked() { void PointerController::loadResourcesLocked() REQUIRES(mLock) { mPolicy->loadPointerResources(&mResources, mLocked.viewport.displayId); + mPolicy->loadPointerIcon(&mLocked.pointerIcon, mLocked.viewport.displayId); + mLocked.additionalMouseResources.clear(); + mLocked.animationResources.clear(); if (mLocked.presentation == PRESENTATION_POINTER) { - mLocked.additionalMouseResources.clear(); - mLocked.animationResources.clear(); - mPolicy->loadPointerIcon(&mLocked.pointerIcon, mLocked.viewport.displayId); mPolicy->loadAdditionalMouseResources(&mLocked.additionalMouseResources, &mLocked.animationResources, mLocked.viewport.displayId); } diff --git a/location/java/android/location/GnssMeasurementCorrections.java b/location/java/android/location/GnssMeasurementCorrections.java index 3ce48b4f7627..9d3211d1b4f8 100644 --- a/location/java/android/location/GnssMeasurementCorrections.java +++ b/location/java/android/location/GnssMeasurementCorrections.java @@ -124,7 +124,7 @@ public final class GnssMeasurementCorrections implements Parcelable { * Gets a set of {@link GnssSingleSatCorrection} each containing measurement corrections for a * satellite in view */ - public @Nullable List<GnssSingleSatCorrection> getSingleSatCorrectionList() { + public @Nullable List<GnssSingleSatCorrection> getSingleSatelliteCorrectionList() { return mSingleSatCorrectionList; } @@ -137,7 +137,7 @@ public final class GnssMeasurementCorrections implements Parcelable { new Creator<GnssMeasurementCorrections>() { @Override public GnssMeasurementCorrections createFromParcel(Parcel parcel) { - GnssMeasurementCorrections.Builder gnssMeasurementCorrectons = + final GnssMeasurementCorrections.Builder gnssMeasurementCorrectons = new Builder() .setLatitudeDegrees(parcel.readDouble()) .setLongitudeDegrees(parcel.readDouble()) @@ -147,7 +147,7 @@ public final class GnssMeasurementCorrections implements Parcelable { .setToaGpsNanosecondsOfWeek(parcel.readLong()); List<GnssSingleSatCorrection> singleSatCorrectionList = new ArrayList<>(); parcel.readTypedList(singleSatCorrectionList, GnssSingleSatCorrection.CREATOR); - gnssMeasurementCorrectons.setSingleSatCorrectionList( + gnssMeasurementCorrectons.setSingleSatelliteCorrectionList( singleSatCorrectionList.isEmpty() ? null : singleSatCorrectionList); return gnssMeasurementCorrectons.build(); } @@ -188,7 +188,7 @@ public final class GnssMeasurementCorrections implements Parcelable { } /** Builder for {@link GnssMeasurementCorrections} */ - public static class Builder { + public static final class Builder { /** * For documentation of below fields, see corresponding fields in {@link * GnssMeasurementCorrections}. @@ -253,7 +253,7 @@ public final class GnssMeasurementCorrections implements Parcelable { * Sets a the list of {@link GnssSingleSatCorrection} containing measurement corrections for * a satellite in view */ - public Builder setSingleSatCorrectionList( + public Builder setSingleSatelliteCorrectionList( @Nullable List<GnssSingleSatCorrection> singleSatCorrectionList) { if (singleSatCorrectionList == null) { mSingleSatCorrectionList = null; diff --git a/location/java/android/location/GnssReflectingPlane.java b/location/java/android/location/GnssReflectingPlane.java index 64b37524e37f..9a106a760ec4 100644 --- a/location/java/android/location/GnssReflectingPlane.java +++ b/location/java/android/location/GnssReflectingPlane.java @@ -116,7 +116,7 @@ public final class GnssReflectingPlane implements Parcelable { } /** Builder for {@link GnssReflectingPlane} */ - public static class Builder { + public static final class Builder { /** For documentation, see corresponding fields in {@link GnssReflectingPlane}. */ private double mLatitudeDegrees; diff --git a/location/java/android/location/GnssSingleSatCorrection.java b/location/java/android/location/GnssSingleSatCorrection.java index 4d5303f18b81..f719e1a985b8 100644 --- a/location/java/android/location/GnssSingleSatCorrection.java +++ b/location/java/android/location/GnssSingleSatCorrection.java @@ -60,6 +60,7 @@ public final class GnssSingleSatCorrection implements Parcelable { private int mSingleSatCorrectionFlags; /** Defines the constellation of the given satellite as defined in {@link GnssStatus}. */ + @GnssStatus.ConstellationType private int mConstellationType; /** @@ -115,7 +116,7 @@ public final class GnssSingleSatCorrection implements Parcelable { } /** Gets a bitmask of fields present in this object */ - public int getSingleSatCorrectionFlags() { + public int getSingleSatelliteCorrectionFlags() { return mSingleSatCorrectionFlags; } @@ -136,7 +137,7 @@ public final class GnssSingleSatCorrection implements Parcelable { * <p>Interpretation depends on {@link #getConstellationType()}. See {@link * GnssStatus#getSvid(int)}. */ - public int getSatId() { + public int getSatelliteId() { return mSatId; } @@ -162,7 +163,7 @@ public final class GnssSingleSatCorrection implements Parcelable { * location. */ @FloatRange(from = 0f, to = 1f) - public float getProbSatIsLos() { + public float getProbabilityLineOfSight() { return mProbSatIsLos; } @@ -189,8 +190,8 @@ public final class GnssSingleSatCorrection implements Parcelable { return mReflectingPlane; } - /** Returns {@code true} if {@link #getProbSatIsLos()} is valid. */ - public boolean hasSatelliteLineOfSight() { + /** Returns {@code true} if {@link #getProbabilityLineOfSight()} is valid. */ + public boolean hasValidSatelliteLineOfSight() { return (mSingleSatCorrectionFlags & HAS_PROB_SAT_IS_LOS_MASK) != 0; } @@ -220,11 +221,11 @@ public final class GnssSingleSatCorrection implements Parcelable { public GnssSingleSatCorrection createFromParcel(Parcel parcel) { GnssSingleSatCorrection singleSatCorrection = new Builder() - .setSingleSatCorrectionFlags(parcel.readInt()) + .setSingleSatelliteCorrectionFlags(parcel.readInt()) .setConstellationType(parcel.readInt()) - .setSatId(parcel.readInt()) + .setSatelliteId(parcel.readInt()) .setCarrierFrequencyHz(parcel.readFloat()) - .setProbSatIsLos(parcel.readFloat()) + .setProbabilityLineOfSight(parcel.readFloat()) .setExcessPathLengthMeters(parcel.readFloat()) .setExcessPathLengthUncertaintyMeters(parcel.readFloat()) .setReflectingPlane( @@ -272,7 +273,7 @@ public final class GnssSingleSatCorrection implements Parcelable { } /** Builder for {@link GnssSingleSatCorrection} */ - public static class Builder { + public static final class Builder { /** * For documentation of below fields, see corresponding fields in {@link @@ -289,19 +290,19 @@ public final class GnssSingleSatCorrection implements Parcelable { private GnssReflectingPlane mReflectingPlane; /** Sets a bitmask of fields present in this object */ - public Builder setSingleSatCorrectionFlags(int singleSatCorrectionFlags) { + public Builder setSingleSatelliteCorrectionFlags(int singleSatCorrectionFlags) { mSingleSatCorrectionFlags = singleSatCorrectionFlags; return this; } /** Sets the constellation type. */ - public Builder setConstellationType(int constellationType) { + public Builder setConstellationType(@GnssStatus.ConstellationType int constellationType) { mConstellationType = constellationType; return this; } - /** Sets the Satellite ID. */ - public Builder setSatId(int satId) { + /** Sets the Satellite ID defined in the ICD of the given constellation. */ + public Builder setSatelliteId(int satId) { mSatId = satId; return this; } @@ -316,7 +317,8 @@ public final class GnssSingleSatCorrection implements Parcelable { * Sets the line-of-sight probability of the satellite at the given location in the range * between 0 and 1. */ - public Builder setProbSatIsLos(@FloatRange(from = 0f, to = 1f) float probSatIsLos) { + public Builder setProbabilityLineOfSight( + @FloatRange(from = 0f, to = 1f) float probSatIsLos) { Preconditions.checkArgumentInRange( probSatIsLos, 0, 1, "probSatIsLos should be between 0 and 1."); mProbSatIsLos = probSatIsLos; diff --git a/location/java/android/location/IGeocodeProvider.aidl b/location/java/android/location/IGeocodeProvider.aidl index aaa70c74778a..7eaf7b84c83a 100644 --- a/location/java/android/location/IGeocodeProvider.aidl +++ b/location/java/android/location/IGeocodeProvider.aidl @@ -26,9 +26,11 @@ import android.location.GeocoderParams; */ interface IGeocodeProvider { + @UnsupportedAppUsage String getFromLocation(double latitude, double longitude, int maxResults, in GeocoderParams params, out List<Address> addrs); + @UnsupportedAppUsage String getFromLocationName(String locationName, double lowerLeftLatitude, double lowerLeftLongitude, double upperRightLatitude, double upperRightLongitude, int maxResults, diff --git a/location/java/android/location/IGeofenceProvider.aidl b/location/java/android/location/IGeofenceProvider.aidl index d4ff0dd70bf2..426ebef86b96 100644 --- a/location/java/android/location/IGeofenceProvider.aidl +++ b/location/java/android/location/IGeofenceProvider.aidl @@ -24,5 +24,6 @@ import android.hardware.location.IGeofenceHardware; * {@hide} */ oneway interface IGeofenceProvider { + @UnsupportedAppUsage void setGeofenceHardware(in IGeofenceHardware proxy); } diff --git a/location/java/android/location/ILocationListener.aidl b/location/java/android/location/ILocationListener.aidl index 180183e77668..ec1134566b24 100644 --- a/location/java/android/location/ILocationListener.aidl +++ b/location/java/android/location/ILocationListener.aidl @@ -25,10 +25,14 @@ import android.os.Bundle; */ oneway interface ILocationListener { + @UnsupportedAppUsage void onLocationChanged(in Location location); + @UnsupportedAppUsage void onProviderEnabled(String provider); + @UnsupportedAppUsage void onProviderDisabled(String provider); // --- deprecated --- + @UnsupportedAppUsage void onStatusChanged(String provider, int status, in Bundle extras); } diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl index 57a0a725fb41..bb75c77f7e64 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -85,6 +85,7 @@ interface ILocationManager boolean stopGnssBatch(); boolean injectLocation(in Location location); + @UnsupportedAppUsage List<String> getAllProviders(); List<String> getProviders(in Criteria criteria, boolean enabledOnly); String getBestProvider(in Criteria criteria, boolean enabledOnly); diff --git a/location/java/android/location/INetInitiatedListener.aidl b/location/java/android/location/INetInitiatedListener.aidl index fc64dd67c116..a9e9136d3317 100644 --- a/location/java/android/location/INetInitiatedListener.aidl +++ b/location/java/android/location/INetInitiatedListener.aidl @@ -22,5 +22,6 @@ package android.location; */ interface INetInitiatedListener { + @UnsupportedAppUsage boolean sendNiResponse(int notifId, int userResponse); } diff --git a/location/java/com/android/internal/location/ILocationProvider.aidl b/location/java/com/android/internal/location/ILocationProvider.aidl index 71b54fb65ae5..a5716304f0d8 100644 --- a/location/java/com/android/internal/location/ILocationProvider.aidl +++ b/location/java/com/android/internal/location/ILocationProvider.aidl @@ -36,6 +36,8 @@ interface ILocationProvider { oneway void sendExtraCommand(String command, in Bundle extras); // --- deprecated and will be removed the future --- + @UnsupportedAppUsage int getStatus(out Bundle extras); + @UnsupportedAppUsage long getStatusUpdateTime(); } diff --git a/location/java/com/android/internal/location/ILocationProviderManager.aidl b/location/java/com/android/internal/location/ILocationProviderManager.aidl index 79166ae3a9b0..85e18ba5ec4b 100644 --- a/location/java/com/android/internal/location/ILocationProviderManager.aidl +++ b/location/java/com/android/internal/location/ILocationProviderManager.aidl @@ -28,9 +28,12 @@ interface ILocationProviderManager { void onSetAdditionalProviderPackages(in List<String> packageNames); + @UnsupportedAppUsage void onSetEnabled(boolean enabled); + @UnsupportedAppUsage void onSetProperties(in ProviderProperties properties); + @UnsupportedAppUsage void onReportLocation(in Location location); } diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt index dbb581fe54b2..d1b39b350d73 100644 --- a/location/lib/api/current.txt +++ b/location/lib/api/current.txt @@ -11,7 +11,7 @@ package com.android.location.provider { method public android.os.IBinder getBinder(); method public boolean isEnabled(); method @Deprecated protected void onDisable(); - method protected void onDump(java.io.FileDescriptor, java.io.PrintWriter, String[]); + method @Deprecated protected void onDump(java.io.FileDescriptor, java.io.PrintWriter, String[]); method @Deprecated protected void onEnable(); method @Deprecated protected int onGetStatus(android.os.Bundle); method @Deprecated protected long onGetStatusUpdateTime(); diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java index 7cd7207c26a0..fa113a8aa3ef 100644 --- a/location/lib/java/com/android/location/provider/LocationProviderBase.java +++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java @@ -240,8 +240,10 @@ public abstract class LocationProviderBase { protected abstract void onSetRequest(ProviderRequestUnbundled request, WorkSource source); /** - * Dump debug information. + * @deprecated This callback will never be invoked on Android Q and above. This method may be + * removed in the future. Prefer to dump provider state via the containing service instead. */ + @Deprecated protected void onDump(FileDescriptor fd, PrintWriter pw, String[] args) {} /** @@ -336,10 +338,5 @@ public abstract class LocationProviderBase { public void sendExtraCommand(String command, Bundle extras) { onSendExtraCommand(command, extras); } - - @Override - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - onDump(fd, pw, args); - } } } diff --git a/location/tests/locationtests/src/android/location/GnssMeasurementCorrectionsTest.java b/location/tests/locationtests/src/android/location/GnssMeasurementCorrectionsTest.java deleted file mode 100644 index 8f46e84195d5..000000000000 --- a/location/tests/locationtests/src/android/location/GnssMeasurementCorrectionsTest.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.location; - -import android.os.Parcel; - -import junit.framework.TestCase; - -import java.util.ArrayList; -import java.util.List; - -/** Unit tests for {@link GnssMeasurementCorrections}. */ -public class GnssMeasurementCorrectionsTest extends TestCase { - public void testDescribeContents() { - GnssMeasurementCorrections measurementCorrections = - new GnssMeasurementCorrections.Builder().build(); - measurementCorrections.describeContents(); - } - - public void testWriteToParcel() { - GnssMeasurementCorrections.Builder measurementCorrections = - new GnssMeasurementCorrections.Builder(); - setTestValues(measurementCorrections); - Parcel parcel = Parcel.obtain(); - measurementCorrections.build().writeToParcel(parcel, 0); - parcel.setDataPosition(0); - GnssMeasurementCorrections newMeasurementCorrection = - GnssMeasurementCorrections.CREATOR.createFromParcel(parcel); - verifyTestValues(newMeasurementCorrection); - parcel.recycle(); - } - - private static void verifyTestValues(GnssMeasurementCorrections measurementCorrections) { - assertEquals(37.386051, measurementCorrections.getLatitudeDegrees()); - assertEquals(-122.083855, measurementCorrections.getLongitudeDegrees()); - assertEquals(32.0, measurementCorrections.getAltitudeMeters()); - assertEquals(9.25, measurementCorrections.getHorizontalPositionUncertaintyMeters()); - assertEquals(2.3, measurementCorrections.getVerticalPositionUncertaintyMeters()); - assertEquals(604000000000000L, measurementCorrections.getToaGpsNanosecondsOfWeek()); - - GnssSingleSatCorrection singleSatCorrection = - measurementCorrections.getSingleSatCorrectionList().get(0); - GnssSingleSatCorrectionsTest.verifyTestValues(singleSatCorrection); - - singleSatCorrection = measurementCorrections.getSingleSatCorrectionList().get(1); - assertEquals(15, singleSatCorrection.getSingleSatCorrectionFlags()); - assertEquals(GnssStatus.CONSTELLATION_GPS, singleSatCorrection.getConstellationType()); - assertEquals(11, singleSatCorrection.getSatId()); - assertEquals(1575430000f, singleSatCorrection.getCarrierFrequencyHz()); - assertEquals(0.9f, singleSatCorrection.getProbSatIsLos()); - assertEquals(50.0f, singleSatCorrection.getExcessPathLengthMeters()); - assertEquals(55.0f, singleSatCorrection.getExcessPathLengthUncertaintyMeters()); - GnssReflectingPlane reflectingPlane = singleSatCorrection.getReflectingPlane(); - assertEquals(37.386054, reflectingPlane.getLatitudeDegrees()); - assertEquals(-122.083855, reflectingPlane.getLongitudeDegrees()); - assertEquals(120.0, reflectingPlane.getAltitudeMeters()); - assertEquals(153.0, reflectingPlane.getAzimuthDegrees()); - } - - private static void setTestValues(GnssMeasurementCorrections.Builder measurementCorrections) { - measurementCorrections - .setLatitudeDegrees(37.386051) - .setLongitudeDegrees(-122.083855) - .setAltitudeMeters(32) - .setHorizontalPositionUncertaintyMeters(9.25) - .setVerticalPositionUncertaintyMeters(2.3) - .setToaGpsNanosecondsOfWeek(604000000000000L); - List<GnssSingleSatCorrection> singleSatCorrectionList = new ArrayList<>(); - singleSatCorrectionList.add(GnssSingleSatCorrectionsTest.generateTestSingleSatCorrection()); - singleSatCorrectionList.add(generateTestSingleSatCorrection()); - measurementCorrections.setSingleSatCorrectionList(singleSatCorrectionList); - } - - private static GnssSingleSatCorrection generateTestSingleSatCorrection() { - GnssSingleSatCorrection.Builder singleSatCorrection = new GnssSingleSatCorrection.Builder(); - singleSatCorrection - .setSingleSatCorrectionFlags(8) - .setConstellationType(GnssStatus.CONSTELLATION_GPS) - .setSatId(11) - .setCarrierFrequencyHz(1575430000f) - .setProbSatIsLos(0.9f) - .setExcessPathLengthMeters(50.0f) - .setExcessPathLengthUncertaintyMeters(55.0f) - .setReflectingPlane(generateTestReflectingPlane()); - return singleSatCorrection.build(); - } - - private static GnssReflectingPlane generateTestReflectingPlane() { - GnssReflectingPlane.Builder reflectingPlane = - new GnssReflectingPlane.Builder() - .setLatitudeDegrees(37.386054) - .setLongitudeDegrees(-122.083855) - .setAltitudeMeters(120.0) - .setAzimuthDegrees(153); - return reflectingPlane.build(); - } -} diff --git a/location/tests/locationtests/src/android/location/GnssReflectingPlaneTest.java b/location/tests/locationtests/src/android/location/GnssReflectingPlaneTest.java deleted file mode 100644 index d7a3378e5a12..000000000000 --- a/location/tests/locationtests/src/android/location/GnssReflectingPlaneTest.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.location; - -import android.os.Parcel; - -import junit.framework.TestCase; - -/** Unit tests for {@link GnssReflectingPlane}. */ -public class GnssReflectingPlaneTest extends TestCase { - public void testDescribeContents() { - GnssReflectingPlane reflectingPlane = new GnssReflectingPlane.Builder().build(); - reflectingPlane.describeContents(); - } - - public void testWriteToParcel() { - GnssReflectingPlane.Builder reflectingPlane = new GnssReflectingPlane.Builder(); - setTestValues(reflectingPlane); - Parcel parcel = Parcel.obtain(); - reflectingPlane.build().writeToParcel(parcel, 0); - parcel.setDataPosition(0); - GnssReflectingPlane newReflectingPlane = - GnssReflectingPlane.CREATOR.createFromParcel(parcel); - verifyTestValues(newReflectingPlane); - parcel.recycle(); - } - - public static void verifyTestValues(GnssReflectingPlane reflectingPlane) { - assertEquals(37.386052, reflectingPlane.getLatitudeDegrees()); - assertEquals(-122.083853, reflectingPlane.getLongitudeDegrees()); - assertEquals(100.0, reflectingPlane.getAltitudeMeters()); - assertEquals(123.0, reflectingPlane.getAzimuthDegrees()); - } - - private static void setTestValues(GnssReflectingPlane.Builder reflectingPlane) { - GnssReflectingPlane refPlane = generateTestReflectingPlane(); - reflectingPlane - .setLatitudeDegrees(refPlane.getLatitudeDegrees()) - .setLongitudeDegrees(refPlane.getLongitudeDegrees()) - .setAltitudeMeters(refPlane.getAltitudeMeters()) - .setAzimuthDegrees(refPlane.getAzimuthDegrees()); - } - - public static GnssReflectingPlane generateTestReflectingPlane() { - GnssReflectingPlane.Builder reflectingPlane = - new GnssReflectingPlane.Builder() - .setLatitudeDegrees(37.386052) - .setLongitudeDegrees(-122.083853) - .setAltitudeMeters(100.0) - .setAzimuthDegrees(123.0); - return reflectingPlane.build(); - } -} diff --git a/location/tests/locationtests/src/android/location/GnssSingleSatCorrectionsTest.java b/location/tests/locationtests/src/android/location/GnssSingleSatCorrectionsTest.java deleted file mode 100644 index f358806d0b26..000000000000 --- a/location/tests/locationtests/src/android/location/GnssSingleSatCorrectionsTest.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.location; - -import android.os.Parcel; - -import junit.framework.TestCase; - -/** Unit tests for {@link GnssSingleSatCorrection}. */ -public class GnssSingleSatCorrectionsTest extends TestCase { - public void testDescribeContents() { - GnssSingleSatCorrection singleSatCorrection = new GnssSingleSatCorrection.Builder().build(); - singleSatCorrection.describeContents(); - } - - public void testWriteToParcel() { - GnssSingleSatCorrection.Builder singleSatCorrection = new GnssSingleSatCorrection.Builder(); - setTestValues(singleSatCorrection); - Parcel parcel = Parcel.obtain(); - singleSatCorrection.build().writeToParcel(parcel, 0); - parcel.setDataPosition(0); - GnssSingleSatCorrection newSingleSatCorrection = - GnssSingleSatCorrection.CREATOR.createFromParcel(parcel); - verifyTestValues(newSingleSatCorrection); - parcel.recycle(); - } - - public static void verifyTestValues(GnssSingleSatCorrection singleSatCorrection) { - assertEquals(15, singleSatCorrection.getSingleSatCorrectionFlags()); - assertEquals(GnssStatus.CONSTELLATION_GALILEO, singleSatCorrection.getConstellationType()); - assertEquals(12, singleSatCorrection.getSatId()); - assertEquals(1575420000f, singleSatCorrection.getCarrierFrequencyHz()); - assertEquals(0.1f, singleSatCorrection.getProbSatIsLos()); - assertEquals(10.0f, singleSatCorrection.getExcessPathLengthMeters()); - assertEquals(5.0f, singleSatCorrection.getExcessPathLengthUncertaintyMeters()); - GnssReflectingPlane reflectingPlane = singleSatCorrection.getReflectingPlane(); - GnssReflectingPlaneTest.verifyTestValues(reflectingPlane); - } - - private static void setTestValues(GnssSingleSatCorrection.Builder singleSatCorrection) { - GnssSingleSatCorrection singleSatCorr = generateTestSingleSatCorrection(); - singleSatCorrection - .setSingleSatCorrectionFlags(singleSatCorr.getSingleSatCorrectionFlags()) - .setConstellationType(singleSatCorr.getConstellationType()) - .setSatId(singleSatCorr.getSatId()) - .setCarrierFrequencyHz(singleSatCorr.getCarrierFrequencyHz()) - .setProbSatIsLos(singleSatCorr.getProbSatIsLos()) - .setExcessPathLengthMeters(singleSatCorr.getExcessPathLengthMeters()) - .setExcessPathLengthUncertaintyMeters( - singleSatCorr.getExcessPathLengthUncertaintyMeters()) - .setReflectingPlane(singleSatCorr.getReflectingPlane()); - } - - public static GnssSingleSatCorrection generateTestSingleSatCorrection() { - GnssSingleSatCorrection.Builder singleSatCorrection = - new GnssSingleSatCorrection.Builder() - .setSingleSatCorrectionFlags(15) - .setConstellationType(GnssStatus.CONSTELLATION_GALILEO) - .setSatId(12) - .setCarrierFrequencyHz(1575420000f) - .setProbSatIsLos(0.1f) - .setExcessPathLengthMeters(10.0f) - .setExcessPathLengthUncertaintyMeters(5.0f) - .setReflectingPlane(GnssReflectingPlaneTest.generateTestReflectingPlane()); - return singleSatCorrection.build(); - } -} diff --git a/media/apex/java/android/media/MediaConstants.java b/media/apex/java/android/media/MediaConstants.java index 65b6f55a068a..776c1ba3f82f 100644 --- a/media/apex/java/android/media/MediaConstants.java +++ b/media/apex/java/android/media/MediaConstants.java @@ -27,6 +27,7 @@ class MediaConstants { static final String KEY_SESSION2LINK = "android.media.key.SESSION2LINK"; static final String KEY_ALLOWED_COMMANDS = "android.media.key.ALLOWED_COMMANDS"; static final String KEY_PLAYBACK_ACTIVE = "android.media.key.PLAYBACK_ACTIVE"; + static final String KEY_TOKEN_EXTRAS = "android.media.key.TOKEN_EXTRAS"; private MediaConstants() { } diff --git a/media/apex/java/android/media/MediaController2.java b/media/apex/java/android/media/MediaController2.java index 4ea384ac70b2..2709df0e060f 100644 --- a/media/apex/java/android/media/MediaController2.java +++ b/media/apex/java/android/media/MediaController2.java @@ -21,6 +21,7 @@ import static android.media.MediaConstants.KEY_PACKAGE_NAME; import static android.media.MediaConstants.KEY_PID; import static android.media.MediaConstants.KEY_PLAYBACK_ACTIVE; import static android.media.MediaConstants.KEY_SESSION2LINK; +import static android.media.MediaConstants.KEY_TOKEN_EXTRAS; import static android.media.Session2Command.RESULT_ERROR_UNKNOWN_ERROR; import static android.media.Session2Command.RESULT_INFO_SKIPPED; import static android.media.Session2Token.TYPE_SESSION; @@ -264,6 +265,7 @@ public class MediaController2 implements AutoCloseable { Session2CommandGroup allowedCommands = connectionResult.getParcelable(KEY_ALLOWED_COMMANDS); boolean playbackActive = connectionResult.getBoolean(KEY_PLAYBACK_ACTIVE); + Bundle tokenExtras = connectionResult.getBundle(KEY_TOKEN_EXTRAS); if (DEBUG) { Log.d(TAG, "notifyConnected sessionBinder=" + sessionBinder + ", allowedCommands=" + allowedCommands); @@ -282,7 +284,7 @@ public class MediaController2 implements AutoCloseable { // so can be used without worrying about deadlock. sessionBinder.linkToDeath(mDeathRecipient, 0); mConnectedToken = new Session2Token(mSessionToken.getUid(), TYPE_SESSION, - mSessionToken.getPackageName(), sessionBinder); + mSessionToken.getPackageName(), sessionBinder, tokenExtras); } mCallbackExecutor.execute(() -> { mCallback.onConnected(MediaController2.this, allowedCommands); diff --git a/media/apex/java/android/media/MediaSession2.java b/media/apex/java/android/media/MediaSession2.java index 4c6945ae8d3f..148e16c68cee 100644 --- a/media/apex/java/android/media/MediaSession2.java +++ b/media/apex/java/android/media/MediaSession2.java @@ -21,6 +21,7 @@ import static android.media.MediaConstants.KEY_PACKAGE_NAME; import static android.media.MediaConstants.KEY_PID; import static android.media.MediaConstants.KEY_PLAYBACK_ACTIVE; import static android.media.MediaConstants.KEY_SESSION2LINK; +import static android.media.MediaConstants.KEY_TOKEN_EXTRAS; import static android.media.Session2Command.RESULT_ERROR_UNKNOWN_ERROR; import static android.media.Session2Command.RESULT_INFO_SKIPPED; import static android.media.Session2Token.TYPE_SESSION; @@ -94,7 +95,8 @@ public class MediaSession2 implements AutoCloseable { private ForegroundServiceEventCallback mForegroundServiceEventCallback; MediaSession2(@NonNull Context context, @NonNull String id, PendingIntent sessionActivity, - @NonNull Executor callbackExecutor, @NonNull SessionCallback callback) { + @NonNull Executor callbackExecutor, @NonNull SessionCallback callback, + Bundle tokenExtras) { synchronized (MediaSession2.class) { if (SESSION_ID_LIST.contains(id)) { throw new IllegalStateException("Session ID must be unique. ID=" + id); @@ -109,7 +111,7 @@ public class MediaSession2 implements AutoCloseable { mCallback = callback; mSessionStub = new Session2Link(this); mSessionToken = new Session2Token(Process.myUid(), TYPE_SESSION, context.getPackageName(), - mSessionStub); + mSessionStub, tokenExtras); mSessionManager = (MediaSessionManager) mContext.getSystemService( Context.MEDIA_SESSION_SERVICE); // NOTE: mResultHandler uses main looper, so this MUST NOT be blocked. @@ -339,6 +341,7 @@ public class MediaSession2 implements AutoCloseable { connectionResult.putParcelable(KEY_ALLOWED_COMMANDS, controllerInfo.mAllowedCommands); connectionResult.putBoolean(KEY_PLAYBACK_ACTIVE, isPlaybackActive()); + connectionResult.putBundle(KEY_TOKEN_EXTRAS, mSessionToken.getExtras()); // Double check if session is still there, because close() can be called in // another thread. @@ -444,6 +447,7 @@ public class MediaSession2 implements AutoCloseable { private PendingIntent mSessionActivity; private Executor mCallbackExecutor; private SessionCallback mCallback; + private Bundle mExtras; /** * Creates a builder for {@link MediaSession2}. @@ -507,6 +511,18 @@ public class MediaSession2 implements AutoCloseable { } /** + * Set extras for the session token. + * + * @return The Builder to allow chaining + * @see Session2Token#getExtras() + */ + @NonNull + public Builder setExtras(@Nullable Bundle extras) { + mExtras = extras; + return this; + } + + /** * Build {@link MediaSession2}. * * @return a new session @@ -525,7 +541,7 @@ public class MediaSession2 implements AutoCloseable { mId = ""; } MediaSession2 session2 = new MediaSession2(mContext, mId, mSessionActivity, - mCallbackExecutor, mCallback); + mCallbackExecutor, mCallback, mExtras); // Notify framework about the newly create session after the constructor is finished. // Otherwise, framework may access the session before the initialization is finished. diff --git a/media/apex/java/android/media/Session2Token.java b/media/apex/java/android/media/Session2Token.java index 238cc2b8ee7d..6680e408ded4 100644 --- a/media/apex/java/android/media/Session2Token.java +++ b/media/apex/java/android/media/Session2Token.java @@ -24,6 +24,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; @@ -88,6 +89,7 @@ public final class Session2Token implements Parcelable { private final String mServiceName; private final Session2Link mSessionLink; private final ComponentName mComponentName; + private final Bundle mExtras; /** * Constructor for the token with type {@link #TYPE_SESSION_SERVICE}. @@ -116,15 +118,18 @@ public final class Session2Token implements Parcelable { mUid = uid; mType = TYPE_SESSION_SERVICE; mSessionLink = null; + mExtras = null; } - Session2Token(int uid, int type, String packageName, Session2Link sessionLink) { + Session2Token(int uid, int type, String packageName, Session2Link sessionLink, + Bundle tokenExtras) { mUid = uid; mType = type; mPackageName = packageName; mServiceName = null; mComponentName = null; mSessionLink = sessionLink; + mExtras = tokenExtras; } Session2Token(Parcel in) { @@ -134,6 +139,7 @@ public final class Session2Token implements Parcelable { mServiceName = in.readString(); mSessionLink = in.readParcelable(null); mComponentName = ComponentName.unflattenFromString(in.readString()); + mExtras = in.readBundle(); } @Override @@ -144,6 +150,7 @@ public final class Session2Token implements Parcelable { dest.writeString(mServiceName); dest.writeParcelable(mSessionLink, flags); dest.writeString(mComponentName == null ? "" : mComponentName.flattenToString()); + dest.writeBundle(mExtras); } @Override @@ -207,6 +214,14 @@ public final class Session2Token implements Parcelable { return mType; } + /** + * @return extras of the token + */ + @Nullable + public Bundle getExtras() { + return mExtras; + } + Session2Link getSessionLink() { return mSessionLink; } diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java index f5a6f8667fa6..ea396c6b9a5e 100644 --- a/media/java/android/media/AudioAttributes.java +++ b/media/java/android/media/AudioAttributes.java @@ -368,6 +368,12 @@ public final class AudioAttributes implements Parcelable { */ public final static int FLAG_DEEP_BUFFER = 0x1 << 9; + /** + * @hide + * Flag specifying that the audio shall not be captured by other apps. + */ + public static final int FLAG_NO_CAPTURE = 0x1 << 10; + private final static int FLAG_ALL = FLAG_AUDIBILITY_ENFORCED | FLAG_SECURE | FLAG_SCO | FLAG_BEACON | FLAG_HW_AV_SYNC | FLAG_HW_HOTWORD | FLAG_BYPASS_INTERRUPTION_POLICY | FLAG_BYPASS_MUTE | FLAG_LOW_LATENCY | FLAG_DEEP_BUFFER; @@ -618,6 +624,22 @@ public final class AudioAttributes implements Parcelable { } /** + * Specifying if audio shall or shall not be captured by other apps. + * By default, capture is allowed. + * @param allowCapture false to forbid capture of the audio by any apps, + * true to allow apps to capture the audio + * @return the same Builder instance + */ + public Builder setAllowCapture(boolean allowCapture) { + if (allowCapture) { + mFlags &= ~FLAG_NO_CAPTURE; + } else { + mFlags |= FLAG_NO_CAPTURE; + } + return this; + } + + /** * @hide * Replaces flags. * @param flags any combination of {@link AudioAttributes#FLAG_ALL}. diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index f996d38c86a9..15f9b47ede09 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -36,6 +36,10 @@ import android.content.Context; import android.content.Intent; import android.media.audiopolicy.AudioPolicy; import android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener; +import android.media.audiopolicy.AudioProductStrategies; +import android.media.audiopolicy.AudioVolumeGroupChangeHandler; +import android.media.audiopolicy.AudioVolumeGroups; +import android.media.projection.MediaProjection; import android.media.session.MediaController; import android.media.session.MediaSession; import android.media.session.MediaSessionLegacyHelper; @@ -59,6 +63,7 @@ import android.util.Pair; import android.view.KeyEvent; import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.Preconditions; import java.io.IOException; import java.lang.annotation.Retention; @@ -88,6 +93,8 @@ public class AudioManager { private static final String TAG = "AudioManager"; private static final boolean DEBUG = false; private static final AudioPortEventHandler sAudioPortEventHandler = new AudioPortEventHandler(); + private static final AudioVolumeGroupChangeHandler sAudioAudioVolumeGroupChangedHandler = + new AudioVolumeGroupChangeHandler(); /** * Broadcast intent, a hint for applications that audio is about to become @@ -1162,6 +1169,93 @@ public class AudioManager { } /** + * Sets the volume index for a particular {@link AudioAttributes}. + * @param attr The {@link AudioAttributes} whose volume index should be set. + * @param index The volume index to set. See + * {@link #getMaxVolumeIndexForAttributes(AudioAttributes)} for the largest valid value + * {@link #getMinVolumeIndexForAttributes(AudioAttributes)} for the lowest valid value. + * @param flags One or more flags. + * @see #getMaxVolumeIndexForAttributes(AudioAttributes) + * @see #getMinVolumeIndexForAttributes(AudioAttributes) + * @see #isVolumeFixed() + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public void setVolumeIndexForAttributes(@NonNull AudioAttributes attr, int index, int flags) { + Preconditions.checkNotNull(attr, "attr must not be null"); + final IAudioService service = getService(); + try { + service.setVolumeIndexForAttributes(attr, index, flags, + getContext().getOpPackageName()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Returns the current volume index for a particular {@link AudioAttributes}. + * + * @param attr The {@link AudioAttributes} whose volume index is returned. + * @return The current volume index for the stream. + * @see #getMaxVolumeIndexForAttributes(AudioAttributes) + * @see #getMinVolumeIndexForAttributes(AudioAttributes) + * @see #setVolumeForAttributes(AudioAttributes, int, int) + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public int getVolumeIndexForAttributes(@NonNull AudioAttributes attr) { + Preconditions.checkNotNull(attr, "attr must not be null"); + final IAudioService service = getService(); + try { + return service.getVolumeIndexForAttributes(attr); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Returns the maximum volume index for a particular {@link AudioAttributes}. + * + * @param attr The {@link AudioAttributes} whose maximum volume index is returned. + * @return The maximum valid volume index for the {@link AudioAttributes}. + * @see #getVolumeIndexForAttributes(AudioAttributes) + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public int getMaxVolumeIndexForAttributes(@NonNull AudioAttributes attr) { + Preconditions.checkNotNull(attr, "attr must not be null"); + final IAudioService service = getService(); + try { + return service.getMaxVolumeIndexForAttributes(attr); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Returns the minimum volume index for a particular {@link AudioAttributes}. + * + * @param attr The {@link AudioAttributes} whose minimum volume index is returned. + * @return The minimum valid volume index for the {@link AudioAttributes}. + * @see #getVolumeIndexForAttributes(AudioAttributes) + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public int getMinVolumeIndexForAttributes(@NonNull AudioAttributes attr) { + Preconditions.checkNotNull(attr, "attr must not be null"); + final IAudioService service = getService(); + try { + return service.getMinVolumeIndexForAttributes(attr); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Solo or unsolo a particular stream. * <p> * Do not use. This method has been deprecated and is now a no-op. @@ -3188,13 +3282,19 @@ public class AudioManager { @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int registerAudioPolicy(@NonNull AudioPolicy policy) { + return registerAudioPolicyStatic(policy); + } + + static int registerAudioPolicyStatic(@NonNull AudioPolicy policy) { if (policy == null) { throw new IllegalArgumentException("Illegal null AudioPolicy argument"); } final IAudioService service = getService(); try { + MediaProjection projection = policy.getMediaProjection(); String regId = service.registerAudioPolicy(policy.getConfig(), policy.cb(), - policy.hasFocusListener(), policy.isFocusPolicy(), policy.isVolumeController()); + policy.hasFocusListener(), policy.isFocusPolicy(), policy.isVolumeController(), + projection == null ? null : projection.getProjection()); if (regId == null) { return ERROR; } else { @@ -3214,6 +3314,10 @@ public class AudioManager { @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicyAsync(@NonNull AudioPolicy policy) { + unregisterAudioPolicyAsyncStatic(policy); + } + + static void unregisterAudioPolicyAsyncStatic(@NonNull AudioPolicy policy) { if (policy == null) { throw new IllegalArgumentException("Illegal null AudioPolicy argument"); } @@ -5210,6 +5314,89 @@ public class AudioManager { return AudioSystem.isHapticPlaybackSupported(); } + /** + * @hide + * Introspection API to retrieve audio product strategies. + * When implementing {Car|Oem}AudioManager, use this method to retrieve the collection of + * audio product strategies, which is indexed by a weakly typed index in order to be extended + * by OEM without any needs of AOSP patches. + * The {Car|Oem}AudioManager can expose API to build {@link AudioAttributes} for a given product + * strategy refered either by its index or human readable string. It will allow clients + * application to start streaming data using these {@link AudioAttributes} on the selected + * device by Audio Policy Engine. + * @return a (possibly zero-length) array of + * {@see android.media.audiopolicy.AudioProductStrategy} objects. + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public @NonNull AudioProductStrategies getAudioProductStrategies() { + final IAudioService service = getService(); + try { + return service.getAudioProductStrategies(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide + * Introspection API to retrieve audio volume groups. + * When implementing {Car|Oem}AudioManager, use this method to retrieve the collection of + * audio volume groups. + * @return a (possibly zero-length) array of + * {@see android.media.audiopolicy.AudioVolumeGroups} objects. + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) + public @NonNull AudioVolumeGroups getAudioVolumeGroups() { + final IAudioService service = getService(); + try { + return service.listAudioVolumeGroups(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * @hide + * Callback registered by client to be notified upon volume group change. + */ + @SystemApi + public abstract static class VolumeGroupCallback { + /** + * Callback method called upon audio volume group change. + * @param group the group for which the volume has changed + */ + public void onAudioVolumeGroupChanged(int group, int flags) {} + } + + /** + * @hide + * Register an audio volume group change listener. + * @param callback the {@link VolumeGroupCallback} to register + */ + @SystemApi + public void registerVolumeGroupCallback( + @NonNull Executor executor, + @NonNull VolumeGroupCallback callback) { + Preconditions.checkNotNull(executor, "executor must not be null"); + Preconditions.checkNotNull(callback, "volume group change cb must not be null"); + sAudioAudioVolumeGroupChangedHandler.init(); + // TODO: make use of executor + sAudioAudioVolumeGroupChangedHandler.registerListener(callback); + } + + /** + * @hide + * Unregister an audio volume group change listener. + * @param callback the {@link VolumeGroupCallback} to unregister + */ + @SystemApi + public void unregisterVolumeGroupCallback( + @NonNull VolumeGroupCallback callback) { + Preconditions.checkNotNull(callback, "volume group change cb must not be null"); + sAudioAudioVolumeGroupChangedHandler.unregisterListener(callback); + } //--------------------------------------------------------- // Inner classes diff --git a/media/java/android/media/AudioPlaybackCaptureConfiguration.java b/media/java/android/media/AudioPlaybackCaptureConfiguration.java new file mode 100644 index 000000000000..9a16aea1e052 --- /dev/null +++ b/media/java/android/media/AudioPlaybackCaptureConfiguration.java @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media; + +import android.annotation.NonNull; +import android.media.audiopolicy.AudioMix; +import android.media.audiopolicy.AudioMixingRule; +import android.media.projection.MediaProjection; +import android.os.RemoteException; + +import com.android.internal.util.Preconditions; + +/** + * Configuration for capturing audio played by other apps. + * + * For privacy and copyright reason, only the following audio can be captured: + * - usage MUST be UNKNOWN or GAME or MEDIA. All other usages CAN NOT be capturable. + * - audio attributes MUST NOT have the FLAG_NO_CAPTURE + * - played by apps that MUST be in the same user profile as the capturing app + * (eg work profile can not capture user profile apps and vice-versa). + * - played by apps that MUST NOT have in their manifest.xml the application + * attribute android:allowAudioPlaybackCapture="false" + * - played by apps that MUST have a targetSdkVersion higher or equal to 29 (Q). + * + * <p>An example for creating a capture configuration for capturing all media playback: + * + * <pre> + * MediaProjection mediaProjection; + * // Retrieve a audio capable projection from the MediaProjectionManager + * AudioAttributes mediaAttr = new AudioAttributes.Builder() + * .setUsage(AudioAttributes.USAGE_MEDIA) + * .build(); + * AudioPlaybackCaptureConfiguration config = + * new AudioPlaybackCaptureConfiguration.Builder(mediaProjection) + * .addMatchingUsage(mediaAttr) + * .build(); + * AudioRecord record = new AudioRecord.Builder() + * .setAudioPlaybackCaptureConfig(config) + * .build(); + * </pre> + * + * @see MediaProjectionManager#getMediaProjection(int, Intent) + * @see AudioRecord.Builder#setAudioPlaybackCaptureConfig(AudioPlaybackCaptureConfiguration) + */ +public final class AudioPlaybackCaptureConfiguration { + + private final AudioMixingRule mAudioMixingRule; + private final MediaProjection mProjection; + + private AudioPlaybackCaptureConfiguration(AudioMixingRule audioMixingRule, + MediaProjection projection) { + mAudioMixingRule = audioMixingRule; + mProjection = projection; + } + + /** + * Returns a mix that routes audio back into the app while still playing it from the speakers. + * + * @param audioFormat The format in which to capture the audio. + */ + AudioMix createAudioMix(AudioFormat audioFormat) { + return new AudioMix.Builder(mAudioMixingRule) + .setFormat(audioFormat) + .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK | AudioMix.ROUTE_FLAG_RENDER) + .build(); + } + MediaProjection getMediaProjection() { + return mProjection; + } + + /** Builder for creating {@link AudioPlaybackCaptureConfiguration} instances. */ + public static final class Builder { + + private static final int MATCH_TYPE_UNSPECIFIED = 0; + private static final int MATCH_TYPE_INCLUSIVE = 1; + private static final int MATCH_TYPE_EXCLUSIVE = 2; + + private static final String ERROR_MESSAGE_MISMATCHED_RULES = + "Inclusive and exclusive usage rules cannot be combined"; + private static final String ERROR_MESSAGE_START_ACTIVITY_FAILED = + "startActivityForResult failed"; + private static final String ERROR_MESSAGE_NON_AUDIO_PROJECTION = + "MediaProjection can not project audio"; + + private final AudioMixingRule.Builder mAudioMixingRuleBuilder; + private final MediaProjection mProjection; + private int mUsageMatchType = MATCH_TYPE_UNSPECIFIED; + private int mUidMatchType = MATCH_TYPE_UNSPECIFIED; + + /** @param projection A MediaProjection that supports audio projection. */ + public Builder(@NonNull MediaProjection projection) { + Preconditions.checkNotNull(projection); + try { + Preconditions.checkArgument(projection.getProjection().canProjectAudio(), + ERROR_MESSAGE_NON_AUDIO_PROJECTION); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + mProjection = projection; + mAudioMixingRuleBuilder = new AudioMixingRule.Builder(); + } + + /** + * Only capture audio output with the given {@link AudioAttributes}. + * + * <p>If called multiple times, will capture audio output that matches any of the given + * attributes. + * + * @throws IllegalStateException if called in conjunction with + * {@link #excludeUsage(AudioAttributes)}. + */ + public Builder addMatchingUsage(@NonNull AudioAttributes audioAttributes) { + Preconditions.checkNotNull(audioAttributes); + Preconditions.checkState( + mUsageMatchType != MATCH_TYPE_EXCLUSIVE, ERROR_MESSAGE_MISMATCHED_RULES); + mAudioMixingRuleBuilder + .addRule(audioAttributes, AudioMixingRule.RULE_MATCH_ATTRIBUTE_USAGE); + mUsageMatchType = MATCH_TYPE_INCLUSIVE; + return this; + } + + /** + * Only capture audio output by app with the matching {@code uid}. + * + * <p>If called multiple times, will capture audio output by apps whose uid is any of the + * given uids. + * + * @throws IllegalStateException if called in conjunction with {@link #excludeUid(int)}. + */ + public Builder addMatchingUid(int uid) { + Preconditions.checkState( + mUidMatchType != MATCH_TYPE_EXCLUSIVE, ERROR_MESSAGE_MISMATCHED_RULES); + mAudioMixingRuleBuilder.addMixRule(AudioMixingRule.RULE_MATCH_UID, uid); + mUidMatchType = MATCH_TYPE_INCLUSIVE; + return this; + } + + /** + * Only capture audio output that does not match the given {@link AudioAttributes}. + * + * <p>If called multiple times, will capture audio output that does not match any of the + * given attributes. + * + * @throws IllegalStateException if called in conjunction with + * {@link #addMatchingUsage(AudioAttributes)}. + */ + public Builder excludeUsage(@NonNull AudioAttributes audioAttributes) { + Preconditions.checkNotNull(audioAttributes); + Preconditions.checkState( + mUsageMatchType != MATCH_TYPE_INCLUSIVE, ERROR_MESSAGE_MISMATCHED_RULES); + mAudioMixingRuleBuilder.excludeRule(audioAttributes, + AudioMixingRule.RULE_MATCH_ATTRIBUTE_USAGE); + mUsageMatchType = MATCH_TYPE_EXCLUSIVE; + return this; + } + + /** + * Only capture audio output by apps that do not have the matching {@code uid}. + * + * <p>If called multiple times, will capture audio output by apps whose uid is not any of + * the given uids. + * + * @throws IllegalStateException if called in conjunction with {@link #addMatchingUid(int)}. + */ + public Builder excludeUid(int uid) { + Preconditions.checkState( + mUidMatchType != MATCH_TYPE_INCLUSIVE, ERROR_MESSAGE_MISMATCHED_RULES); + mAudioMixingRuleBuilder.excludeMixRule(AudioMixingRule.RULE_MATCH_UID, uid); + mUidMatchType = MATCH_TYPE_EXCLUSIVE; + return this; + } + + /** + * Builds the configuration instance. + * + * @throws UnsupportedOperationException if the parameters set are incompatible. + */ + public AudioPlaybackCaptureConfiguration build() { + return new AudioPlaybackCaptureConfiguration(mAudioMixingRuleBuilder.build(), + mProjection); + } + } +} diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java index 24a3a9b32f77..3d5120f86db3 100644 --- a/media/java/android/media/AudioRecord.java +++ b/media/java/android/media/AudioRecord.java @@ -24,6 +24,9 @@ import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.UnsupportedAppUsage; import android.app.ActivityThread; +import android.media.audiopolicy.AudioMix; +import android.media.audiopolicy.AudioPolicy; +import android.media.projection.MediaProjection; import android.os.Binder; import android.os.Handler; import android.os.IBinder; @@ -37,6 +40,7 @@ import android.util.Log; import android.util.Pair; import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.Preconditions; import java.io.IOException; import java.lang.annotation.Retention; @@ -182,6 +186,8 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection, //--------------------------------------------------------- // Member variables //-------------------- + private AudioPolicy mAudioCapturePolicy; + /** * The audio data sampling rate in Hz. * Never {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED}. @@ -396,7 +402,7 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection, int initResult = native_setup( new WeakReference<AudioRecord>(this), mAudioAttributes, sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat, mNativeBufferSizeInBytes, - session, ActivityThread.currentOpPackageName(), 0 /*nativeRecordInJavaObj*/); + session, getCurrentOpPackageName(), 0 /*nativeRecordInJavaObj*/); if (initResult != SUCCESS) { loge("Error code "+initResult+" when initializing native AudioRecord object."); return; // with mState == STATE_UNINITIALIZED @@ -408,6 +414,15 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection, mState = STATE_INITIALIZED; } + private String getCurrentOpPackageName() { + String opPackageName = ActivityThread.currentOpPackageName(); + if (opPackageName != null) { + return opPackageName; + } + // Command line utility + return "uid:" + Binder.getCallingUid(); + } + /** * A constructor which explicitly connects a Native (C++) AudioRecord. For use by * the AudioRecordRoutingProxy subclass. @@ -429,6 +444,16 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection, } /** + * Sets an {@link AudioPolicy} to automatically unregister when the record is released. + * + * <p>This is to prevent users of the audio capture API from having to manually unregister the + * policy that was used to create the record. + */ + private void unregisterAudioPolicyOnRelease(AudioPolicy audioPolicy) { + mAudioCapturePolicy = audioPolicy; + } + + /** * @hide */ /* package */ void deferred_connect(long nativeRecordInJavaObj) { @@ -491,6 +516,11 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection, * the minimum buffer size for the source is used. */ public static class Builder { + + private static final String ERROR_MESSAGE_SOURCE_MISMATCH = + "Cannot both set audio source and set playback capture config"; + + private AudioPlaybackCaptureConfiguration mAudioPlaybackCaptureConfiguration; private AudioAttributes mAttributes; private AudioFormat mFormat; private int mBufferSizeInBytes; @@ -509,6 +539,9 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection, * @throws IllegalArgumentException */ public Builder setAudioSource(int source) throws IllegalArgumentException { + Preconditions.checkState( + mAudioPlaybackCaptureConfiguration == null, + ERROR_MESSAGE_SOURCE_MISMATCH); if ( (source < MediaRecorder.AudioSource.DEFAULT) || (source > MediaRecorder.getAudioSourceMax()) ) { throw new IllegalArgumentException("Invalid audio source " + source); @@ -578,6 +611,25 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection, } /** + * Sets the {@link AudioRecord} to record audio played by other apps. + * + * @param config Defines what apps to record audio from (i.e., via either their uid or + * the type of audio). + * @throws IllegalStateException if called in conjunction with {@link #setAudioSource(int)}. + * @throws NullPointerException if {@code config} is null. + */ + public Builder setAudioPlaybackCaptureConfig( + @NonNull AudioPlaybackCaptureConfiguration config) { + Preconditions.checkNotNull( + config, "Illegal null AudioPlaybackCaptureConfiguration argument"); + Preconditions.checkState( + mAttributes == null, + ERROR_MESSAGE_SOURCE_MISMATCH); + mAudioPlaybackCaptureConfiguration = config; + return this; + } + + /** * @hide * To be only used by system components. * @param sessionId ID of audio session the AudioRecord must be attached to, or @@ -595,6 +647,26 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection, return this; } + private AudioRecord buildAudioPlaybackCaptureRecord() { + AudioMix audioMix = mAudioPlaybackCaptureConfiguration.createAudioMix(mFormat); + MediaProjection projection = mAudioPlaybackCaptureConfiguration.getMediaProjection(); + AudioPolicy audioPolicy = new AudioPolicy.Builder(/*context=*/ null) + .setMediaProjection(projection) + .addMix(audioMix).build(); + + int error = AudioManager.registerAudioPolicyStatic(audioPolicy); + if (error != 0) { + throw new UnsupportedOperationException("Error: could not register audio policy"); + } + + AudioRecord record = audioPolicy.createAudioRecordSink(audioMix); + if (record == null) { + throw new UnsupportedOperationException("Cannot create AudioRecord"); + } + record.unregisterAudioPolicyOnRelease(audioPolicy); + return record; + } + /** * @return a new {@link AudioRecord} instance successfully initialized with all * the parameters set on this <code>Builder</code>. @@ -603,6 +675,10 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection, * or if the device was not available. */ public AudioRecord build() throws UnsupportedOperationException { + if (mAudioPlaybackCaptureConfiguration != null) { + return buildAudioPlaybackCaptureRecord(); + } + if (mFormat == null) { mFormat = new AudioFormat.Builder() .setEncoding(AudioFormat.ENCODING_PCM_16BIT) @@ -757,6 +833,9 @@ public class AudioRecord implements AudioRouting, MicrophoneDirection, } catch(IllegalStateException ise) { // don't raise an exception, we're releasing the resources. } + if (mAudioCapturePolicy != null) { + AudioManager.unregisterAudioPolicyAsyncStatic(mAudioCapturePolicy); + } native_release(); mState = STATE_UNINITIALIZED; } diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 9218e92e51a6..a976d707f5cb 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -40,7 +40,7 @@ import java.util.Map; */ public class AudioSystem { - private static final boolean DEBUG_VOLUME = true; + private static final boolean DEBUG_VOLUME = false; private static final String TAG = "AudioSystem"; /* These values must be kept in sync with system/audio.h */ @@ -920,6 +920,41 @@ public class AudioSystem @UnsupportedAppUsage private static native int setStreamVolumeIndex(int stream, int index, int device); public static native int getStreamVolumeIndex(int stream, int device); + /** + * @hide + * set a volume for the given {@link AudioAttributes} and for all other stream that belong to + * the same volume group. + * @param attributes the {@link AudioAttributes} to be considered + * @param index to be applied + * @param device the volume device to be considered + * @return command completion status. + */ + public static native int setVolumeIndexForAttributes(@NonNull AudioAttributes attributes, + int index, int device); + /** + * @hide + * get the volume index for the given {@link AudioAttributes}. + * @param attributes the {@link AudioAttributes} to be considered + * @param device the volume device to be considered + * @return volume index for the given {@link AudioAttributes} and volume device. + */ + public static native int getVolumeIndexForAttributes(@NonNull AudioAttributes attributes, + int device); + /** + * @hide + * get the minimum volume index for the given {@link AudioAttributes}. + * @param attributes the {@link AudioAttributes} to be considered + * @return minimum volume index for the given {@link AudioAttributes}. + */ + public static native int getMinVolumeIndexForAttributes(@NonNull AudioAttributes attributes); + /** + * @hide + * get the maximum volume index for the given {@link AudioAttributes}. + * @param attributes the {@link AudioAttributes} to be considered + * @return maximum volume index for the given {@link AudioAttributes}. + */ + public static native int getMaxVolumeIndexForAttributes(@NonNull AudioAttributes attributes); + public static native int setMasterVolume(float value); public static native float getMasterVolume(); @UnsupportedAppUsage diff --git a/media/java/android/media/IAudioFocusDispatcher.aidl b/media/java/android/media/IAudioFocusDispatcher.aidl index 3b33c5b7a46a..e3512fa58a7d 100644 --- a/media/java/android/media/IAudioFocusDispatcher.aidl +++ b/media/java/android/media/IAudioFocusDispatcher.aidl @@ -23,6 +23,7 @@ package android.media; */ oneway interface IAudioFocusDispatcher { + @UnsupportedAppUsage void dispatchAudioFocusChange(int focusChange, String clientId); void dispatchFocusResultFromExtPolicy(int requestResult, String clientId); diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index f5aeca717424..1b82fcc48bf8 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -33,7 +33,10 @@ import android.media.IVolumeController; import android.media.PlayerBase; import android.media.VolumePolicy; import android.media.audiopolicy.AudioPolicyConfig; +import android.media.audiopolicy.AudioProductStrategies; +import android.media.audiopolicy.AudioVolumeGroups; import android.media.audiopolicy.IAudioPolicyCallback; +import android.media.projection.IMediaProjection; /** * {@hide} @@ -63,6 +66,7 @@ interface IAudioService { void adjustStreamVolume(int streamType, int direction, int flags, String callingPackage); + @UnsupportedAppUsage void setStreamVolume(int streamType, int index, int flags, String callingPackage); boolean isStreamMute(int streamType); @@ -73,14 +77,28 @@ interface IAudioService { void setMasterMute(boolean mute, int flags, String callingPackage, int userId); + @UnsupportedAppUsage int getStreamVolume(int streamType); int getStreamMinVolume(int streamType); + @UnsupportedAppUsage int getStreamMaxVolume(int streamType); + AudioVolumeGroups listAudioVolumeGroups(); + + void setVolumeIndexForAttributes(in AudioAttributes aa, int index, int flags, String callingPackage); + + int getVolumeIndexForAttributes(in AudioAttributes aa); + + int getMaxVolumeIndexForAttributes(in AudioAttributes aa); + + int getMinVolumeIndexForAttributes(in AudioAttributes aa); + int getLastAudibleStreamVolume(int streamType); + AudioProductStrategies getAudioProductStrategies(); + void setMicrophoneMute(boolean on, String callingPackage, int userId); void setRingerModeExternal(int ringerMode, String caller); @@ -156,6 +174,7 @@ interface IAudioService { int handleBluetoothA2dpActiveDeviceChange(in BluetoothDevice device, int state, int profile, boolean suppressNoisyIntent, int a2dpVolume); + @UnsupportedAppUsage AudioRoutesInfo startWatchingRoutes(in IAudioRoutesObserver observer); boolean isCameraSoundForced(); @@ -176,7 +195,7 @@ interface IAudioService { String registerAudioPolicy(in AudioPolicyConfig policyConfig, in IAudioPolicyCallback pcb, boolean hasFocusListener, boolean isFocusPolicy, - boolean isVolumeController); + boolean isVolumeController, in IMediaProjection projection); oneway void unregisterAudioPolicyAsync(in IAudioPolicyCallback pcb); diff --git a/media/java/android/media/IMediaScannerService.aidl b/media/java/android/media/IMediaScannerService.aidl index c5316461ef1f..24b5595510f6 100644 --- a/media/java/android/media/IMediaScannerService.aidl +++ b/media/java/android/media/IMediaScannerService.aidl @@ -31,6 +31,7 @@ interface IMediaScannerService * @param listener an optional IMediaScannerListener. * If specified, the caller will be notified when scanning is complete via the listener. */ + @UnsupportedAppUsage void requestScanFile(String path, String mimeType, in IMediaScannerListener listener); /** @@ -40,5 +41,6 @@ interface IMediaScannerService * @param mimeType an optional mimeType for the file. * If mimeType is null, then the mimeType will be inferred from the file extension. */ + @UnsupportedAppUsage void scanFile(String path, String mimeType); } diff --git a/media/java/android/media/IRemoteDisplayCallback.aidl b/media/java/android/media/IRemoteDisplayCallback.aidl index 19cf070aa08e..584417d65121 100644 --- a/media/java/android/media/IRemoteDisplayCallback.aidl +++ b/media/java/android/media/IRemoteDisplayCallback.aidl @@ -22,5 +22,6 @@ import android.media.RemoteDisplayState; * {@hide} */ oneway interface IRemoteDisplayCallback { + @UnsupportedAppUsage void onStateChanged(in RemoteDisplayState state); } diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java index 6116429ae561..ba87f2bbffb5 100644 --- a/media/java/android/media/ImageReader.java +++ b/media/java/android/media/ImageReader.java @@ -16,8 +16,12 @@ package android.media; +import android.annotation.IntRange; +import android.annotation.NonNull; import android.graphics.ImageFormat; +import android.graphics.ImageFormat.Format; import android.hardware.HardwareBuffer; +import android.hardware.HardwareBuffer.Usage; import android.os.Handler; import android.os.Looper; import android.os.Message; @@ -120,7 +124,11 @@ public class ImageReader implements AutoCloseable { * Must be greater than 0. * @see Image */ - public static ImageReader newInstance(int width, int height, int format, int maxImages) { + public static @NonNull ImageReader newInstance( + @IntRange(from = 1) int width, + @IntRange(from = 1) int height, + @Format int format, + @IntRange(from = 1) int maxImages) { // If the format is private don't default to USAGE_CPU_READ_OFTEN since it may not // work, and is inscrutable anyway return new ImageReader(width, height, format, maxImages, @@ -210,8 +218,12 @@ public class ImageReader implements AutoCloseable { * @see Image * @see HardwareBuffer */ - public static ImageReader newInstance(int width, int height, int format, int maxImages, - long usage) { + public static @NonNull ImageReader newInstance( + @IntRange(from = 1) int width, + @IntRange(from = 1) int height, + @Format int format, + @IntRange(from = 1) int maxImages, + @Usage long usage) { // TODO: Check this - can't do it just yet because format support is different // Unify formats! The only reliable way to validate usage is to just try it and see. diff --git a/media/java/android/media/ImageWriter.java b/media/java/android/media/ImageWriter.java index dd09afc3ddb0..f813d1b68419 100644 --- a/media/java/android/media/ImageWriter.java +++ b/media/java/android/media/ImageWriter.java @@ -16,7 +16,10 @@ package android.media; +import android.annotation.IntRange; +import android.annotation.NonNull; import android.graphics.ImageFormat; +import android.graphics.ImageFormat.Format; import android.graphics.PixelFormat; import android.graphics.Rect; import android.hardware.camera2.utils.SurfaceUtils; @@ -124,7 +127,8 @@ public class ImageWriter implements AutoCloseable { * {@link #dequeueInputImage()}. * @return a new ImageWriter instance. */ - public static ImageWriter newInstance(Surface surface, int maxImages) { + public static @NonNull ImageWriter newInstance(@NonNull Surface surface, + @IntRange(from = 1) int maxImages) { return new ImageWriter(surface, maxImages, ImageFormat.UNKNOWN); } @@ -169,7 +173,8 @@ public class ImageWriter implements AutoCloseable { * * @return a new ImageWriter instance. */ - public static ImageWriter newInstance(Surface surface, int maxImages, int format) { + public static @NonNull ImageWriter newInstance(@NonNull Surface surface, + @IntRange(from = 1) int maxImages, @Format int format) { if (!ImageFormat.isPublicFormat(format) && !PixelFormat.isPublicFormat(format)) { throw new IllegalArgumentException("Invalid format is specified: " + format); } diff --git a/media/java/android/media/MediaCas.java b/media/java/android/media/MediaCas.java index cf5711d73278..a9e33fd8e472 100644 --- a/media/java/android/media/MediaCas.java +++ b/media/java/android/media/MediaCas.java @@ -19,9 +19,9 @@ package android.media; import android.annotation.NonNull; import android.annotation.Nullable; import android.hardware.cas.V1_0.HidlCasPluginDescriptor; -import android.hardware.cas.V1_1.ICas; +import android.hardware.cas.V1_0.ICas; +import android.hardware.cas.V1_0.IMediaCasService; import android.hardware.cas.V1_1.ICasListener; -import android.hardware.cas.V1_1.IMediaCasService; import android.media.MediaCasException.*; import android.os.Bundle; import android.os.Handler; @@ -99,23 +99,35 @@ import java.util.ArrayList; public final class MediaCas implements AutoCloseable { private static final String TAG = "MediaCas"; private ICas mICas; + private android.hardware.cas.V1_1.ICas mICasV11; private EventListener mListener; private HandlerThread mHandlerThread; private EventHandler mEventHandler; - private static final Singleton<IMediaCasService> gDefault = - new Singleton<IMediaCasService>() { + private static final Singleton<IMediaCasService> sService = new Singleton<IMediaCasService>() { @Override protected IMediaCasService create() { try { - return IMediaCasService.getService(true /*wait*/); - } catch (RemoteException e) {} + Log.d(TAG, "Tried to get cas@1.1 service"); + android.hardware.cas.V1_1.IMediaCasService serviceV11 = + android.hardware.cas.V1_1.IMediaCasService.getService(true /*wait*/); + if (serviceV11 != null) { + return serviceV11; + } + } catch (Exception eV1_1) { + try { + Log.d(TAG, "Tried to get cas@1.0 service"); + return IMediaCasService.getService(true /*wait*/); + } catch (Exception eV1_0) { + Log.d(TAG, "Failed to get cas@1.0 service"); + } + } return null; } }; static IMediaCasService getService() { - return gDefault.get(); + return sService.get(); } private void validateInternalStates() { @@ -126,11 +138,12 @@ public final class MediaCas implements AutoCloseable { private void cleanupAndRethrowIllegalState() { mICas = null; + mICasV11 = null; throw new IllegalStateException(); } - private class EventHandler extends Handler - { + private class EventHandler extends Handler { + private static final int MSG_CAS_EVENT = 0; private static final int MSG_CAS_SESSION_EVENT = 1; private static final String SESSION_KEY = "sessionId"; @@ -164,7 +177,7 @@ public final class MediaCas implements AutoCloseable { } @Override public void onSessionEvent(@NonNull ArrayList<Byte> sessionId, - int event, int arg, @Nullable ArrayList<Byte> data) + int event, int arg, @Nullable ArrayList<Byte> data) throws RemoteException { Message msg = mEventHandler.obtainMessage(); msg.what = EventHandler.MSG_CAS_SESSION_EVENT; @@ -177,7 +190,6 @@ public final class MediaCas implements AutoCloseable { mEventHandler.sendMessage(msg); } }; - /** * Describe a CAS plugin with its CA_system_ID and string name. * @@ -338,9 +350,14 @@ public final class MediaCas implements AutoCloseable { throws MediaCasException { validateInternalStates(); + if (mICasV11 == null) { + Log.d(TAG, "Send Session Event isn't supported by cas@1.0 interface"); + throw new UnsupportedCasException("Send Session Event is not supported"); + } + try { MediaCasException.throwExceptionIfNeeded( - mICas.sendSessionEvent(mSessionId, event, arg, toByteArray(data))); + mICasV11.sendSessionEvent(mSessionId, event, arg, toByteArray(data))); } catch (RemoteException e) { cleanupAndRethrowIllegalState(); } @@ -427,7 +444,16 @@ public final class MediaCas implements AutoCloseable { */ public MediaCas(int CA_system_id) throws UnsupportedCasException { try { - mICas = getService().createPluginExt(CA_system_id, mBinder); + IMediaCasService service = getService(); + android.hardware.cas.V1_1.IMediaCasService serviceV11 = + android.hardware.cas.V1_1.IMediaCasService.castFrom(service); + if (serviceV11 == null) { + Log.d(TAG, "Used cas@1_0 interface to create plugin"); + mICas = service.createPlugin(CA_system_id, mBinder); + } else { + Log.d(TAG, "Used cas@1.1 interface to create plugin"); + mICas = mICasV11 = serviceV11.createPluginExt(CA_system_id, mBinder); + } } catch(Exception e) { Log.e(TAG, "Failed to create plugin: " + e); mICas = null; @@ -528,7 +554,7 @@ public final class MediaCas implements AutoCloseable { } } - private class OpenSessionCallback implements ICas.openSessionCallback { + private class OpenSessionCallback implements android.hardware.cas.V1_1.ICas.openSessionCallback{ public Session mSession; public int mStatus; @Override diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index e4d356b48f6d..08ce9fc87918 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -2645,6 +2645,7 @@ public class MediaPlayer extends PlayerBase */ private synchronized void setSubtitleAnchor() { if ((mSubtitleController == null) && (ActivityThread.currentApplication() != null)) { + getMediaTimeProvider(); final HandlerThread thread = new HandlerThread("SetSubtitleAnchorThread"); thread.start(); Handler handler = new Handler(thread.getLooper()); @@ -2660,7 +2661,7 @@ public class MediaPlayer extends PlayerBase @Override public Looper getSubtitleLooper() { - return Looper.getMainLooper(); + return mTimeProvider.mEventHandler.getLooper(); } }); thread.getLooper().quitSafely(); diff --git a/media/java/android/media/audiopolicy/AudioMix.java b/media/java/android/media/audiopolicy/AudioMix.java index 761b62588e5a..6fd6298adb7d 100644 --- a/media/java/android/media/audiopolicy/AudioMix.java +++ b/media/java/android/media/audiopolicy/AudioMix.java @@ -133,7 +133,8 @@ public class AudioMix { } - int getRouteFlags() { + /** @hide */ + public int getRouteFlags() { return mRouteFlags; } diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java index 65f3294800f5..5f65d586b730 100644 --- a/media/java/android/media/audiopolicy/AudioPolicy.java +++ b/media/java/android/media/audiopolicy/AudioPolicy.java @@ -18,7 +18,9 @@ package android.media.audiopolicy; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; +import android.app.ActivityManager; import android.content.Context; import android.content.pm.PackageManager; import android.media.AudioAttributes; @@ -30,6 +32,7 @@ import android.media.AudioRecord; import android.media.AudioTrack; import android.media.IAudioService; import android.media.MediaRecorder; +import android.media.projection.MediaProjection; import android.os.Binder; import android.os.Handler; import android.os.IBinder; @@ -93,6 +96,8 @@ public class AudioPolicy { private AudioPolicyConfig mConfig; + private final MediaProjection mProjection; + /** @hide */ public AudioPolicyConfig getConfig() { return mConfig; } /** @hide */ @@ -101,13 +106,17 @@ public class AudioPolicy { public boolean isFocusPolicy() { return mIsFocusPolicy; } /** @hide */ public boolean isVolumeController() { return mVolCb != null; } + /** @hide */ + public @Nullable MediaProjection getMediaProjection() { + return mProjection; + } /** * The parameter is guaranteed non-null through the Builder */ private AudioPolicy(AudioPolicyConfig config, Context context, Looper looper, AudioPolicyFocusListener fl, AudioPolicyStatusListener sl, boolean isFocusPolicy, - AudioPolicyVolumeCallback vc) { + AudioPolicyVolumeCallback vc, @Nullable MediaProjection projection) { mConfig = config; mStatus = POLICY_STATUS_UNREGISTERED; mContext = context; @@ -124,6 +133,7 @@ public class AudioPolicy { mStatusListener = sl; mIsFocusPolicy = isFocusPolicy; mVolCb = vc; + mProjection = projection; } /** @@ -138,6 +148,7 @@ public class AudioPolicy { private AudioPolicyStatusListener mStatusListener; private boolean mIsFocusPolicy = false; private AudioPolicyVolumeCallback mVolCb; + private MediaProjection mProjection; /** * Constructs a new Builder with no audio mixes. @@ -222,6 +233,23 @@ public class AudioPolicy { } /** + * Set a media projection obtained through createMediaProjection(). + * + * A MediaProjection that can project audio allows to register an audio + * policy LOOPBACK|RENDER without the MODIFY_AUDIO_ROUTING permission. + * + * @hide + */ + public Builder setMediaProjection(@NonNull MediaProjection projection) { + if (projection == null) { + throw new IllegalArgumentException("Invalid null volume callback"); + } + mProjection = projection; + return this; + + } + + /** * Combines all of the attributes that have been set on this {@code Builder} and returns a * new {@link AudioPolicy} object. * @return a new {@code AudioPolicy} object. @@ -241,7 +269,7 @@ public class AudioPolicy { + "an AudioPolicyFocusListener"); } return new AudioPolicy(new AudioPolicyConfig(mMixes), mContext, mLooper, - mFocusListener, mStatusListener, mIsFocusPolicy, mVolCb); + mFocusListener, mStatusListener, mIsFocusPolicy, mVolCb, mProjection); } } @@ -336,11 +364,10 @@ public class AudioPolicy { * played. * @param uid UID of the application to affect. * @param devices list of devices to which the audio stream of the application may be routed. - * @return {@link AudioManager#SUCCESS} if the change was successful, {@link AudioManager#ERROR} - * otherwise. + * @return true if the change was successful, false otherwise. */ @SystemApi - public int setUidDeviceAffinity(int uid, @NonNull List<AudioDeviceInfo> devices) { + public boolean setUidDeviceAffinity(int uid, @NonNull List<AudioDeviceInfo> devices) { if (devices == null) { throw new IllegalArgumentException("Illegal null list of audio devices"); } @@ -365,10 +392,10 @@ public class AudioPolicy { try { final int status = service.setUidDeviceAffinity(this.cb(), uid, deviceTypes, deviceAdresses); - return status; + return (status == AudioManager.SUCCESS); } catch (RemoteException e) { Log.e(TAG, "Dead object in setUidDeviceAffinity", e); - return AudioManager.ERROR; + return false; } } } @@ -378,11 +405,10 @@ public class AudioPolicy { * Removes audio device affinity previously set by * {@link #setUidDeviceAffinity(int, java.util.List)}. * @param uid UID of the application affected. - * @return {@link AudioManager#SUCCESS} if the change was successful, {@link AudioManager#ERROR} - * otherwise. + * @return true if the change was successful, false otherwise. */ @SystemApi - public int removeUidDeviceAffinity(int uid) { + public boolean removeUidDeviceAffinity(int uid) { synchronized (mLock) { if (mStatus != POLICY_STATUS_REGISTERED) { throw new IllegalStateException("Cannot use unregistered AudioPolicy"); @@ -390,10 +416,10 @@ public class AudioPolicy { final IAudioService service = getService(); try { final int status = service.removeUidDeviceAffinity(this.cb(), uid); - return status; + return (status == AudioManager.SUCCESS); } catch (RemoteException e) { Log.e(TAG, "Dead object in removeUidDeviceAffinity", e); - return AudioManager.ERROR; + return false; } } } @@ -417,24 +443,57 @@ public class AudioPolicy { Log.e(TAG, "Cannot use unregistered AudioPolicy"); return false; } - if (mContext == null) { - Log.e(TAG, "Cannot use AudioPolicy without context"); - return false; - } if (mRegistrationId == null) { Log.e(TAG, "Cannot use unregistered AudioPolicy"); return false; } } - if (!(PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission( - android.Manifest.permission.MODIFY_AUDIO_ROUTING))) { + + // Loopback|capture only need an audio projection, everything else need MODIFY_AUDIO_ROUTING + boolean canModifyAudioRouting = PackageManager.PERMISSION_GRANTED + == checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING); + + boolean canProjectAudio; + try { + canProjectAudio = mProjection != null && mProjection.getProjection().canProjectAudio(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to check if MediaProjection#canProjectAudio"); + throw e.rethrowFromSystemServer(); + } + + if (!((isLoopbackRenderPolicy() && canProjectAudio) || canModifyAudioRouting)) { Slog.w(TAG, "Cannot use AudioPolicy for pid " + Binder.getCallingPid() + " / uid " - + Binder.getCallingUid() + ", needs MODIFY_AUDIO_ROUTING"); + + Binder.getCallingUid() + ", needs MODIFY_AUDIO_ROUTING or " + + "MediaProjection that can project audio."); return false; } return true; } + private boolean isLoopbackRenderPolicy() { + synchronized (mLock) { + return mConfig.mMixes.stream().allMatch(mix -> mix.getRouteFlags() + == (mix.ROUTE_FLAG_RENDER | mix.ROUTE_FLAG_LOOP_BACK)); + } + } + + /** + * Returns {@link PackageManager#PERMISSION_GRANTED} if the caller has the given permission. + */ + private @PackageManager.PermissionResult int checkCallingOrSelfPermission(String permission) { + if (mContext != null) { + return mContext.checkCallingOrSelfPermission(permission); + } + Slog.v(TAG, "Null context, checking permission via ActivityManager"); + int pid = Binder.getCallingPid(); + int uid = Binder.getCallingUid(); + try { + return ActivityManager.getService().checkPermission(permission, pid, uid); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + private void checkMixReadyToUse(AudioMix mix, boolean forTrack) throws IllegalArgumentException{ if (mix == null) { @@ -621,7 +680,6 @@ public class AudioPolicy { * */ public static abstract class AudioPolicyVolumeCallback { - /** @hide */ public AudioPolicyVolumeCallback() {} /** * Called when volume key-related changes are triggered, on the key down event. diff --git a/media/java/android/media/audiopolicy/AudioPolicyConfig.java b/media/java/android/media/audiopolicy/AudioPolicyConfig.java index f725cacf5f21..a56fd31fbaeb 100644 --- a/media/java/android/media/audiopolicy/AudioPolicyConfig.java +++ b/media/java/android/media/audiopolicy/AudioPolicyConfig.java @@ -18,8 +18,6 @@ package android.media.audiopolicy; import android.annotation.NonNull; import android.media.AudioFormat; -import android.media.AudioManager; -import android.media.AudioPatch; import android.media.audiopolicy.AudioMixingRule.AudioMixMatchCriterion; import android.os.Parcel; import android.os.Parcelable; diff --git a/media/java/android/media/audiopolicy/AudioProductStrategies.java b/media/java/android/media/audiopolicy/AudioProductStrategies.java index b8364091ff12..d5938853569c 100644 --- a/media/java/android/media/audiopolicy/AudioProductStrategies.java +++ b/media/java/android/media/audiopolicy/AudioProductStrategies.java @@ -144,8 +144,10 @@ public final class AudioProductStrategies implements Iterable<AudioProductStrate * @hide * @param aa the {@link AudioAttributes} for which stream type is requested * @return the legacy stream type relevant for the given {@link AudioAttributes}. - * If the product strategy is not associated to any stream, it returns STREAM_MUSIC. - * If no product strategy supports the stream type, it returns STREAM_MUSIC. + * If the product strategy is not associated to any stream, it returns + * {@link AudioSystem#STREAM_MUSIC}. + * If no product strategy supports the stream type, it returns + * {@link AudioSystem#STREAM_MUSIC}. */ @SystemApi public int getLegacyStreamTypeForAudioAttributes(@NonNull AudioAttributes aa) { @@ -165,6 +167,62 @@ public final class AudioProductStrategies implements Iterable<AudioProductStrate return AudioSystem.STREAM_MUSIC; } + /** + * @hide + * @param aa the {@link AudioAttributes} to be considered + * @return {@link AudioProductStrategy} supporting the given {@link AudioAttributes}. + * null is returned if no match with given attributes. + */ + @SystemApi + @Nullable + public AudioProductStrategy getProductStrategyForAudioAttributes(@NonNull AudioAttributes aa) { + Preconditions.checkNotNull(aa, "attributes must not be null"); + int productStrategyId = native_get_product_strategies_from_audio_attributes(aa); + if (productStrategyId < 0) { + Log.w(TAG, "no strategy found for Attributes " + aa.toString()); + return null; + } + return getById(productStrategyId); + } + + /** + * @hide + * @param attributes the {@link AudioAttributes} to be considered + * @return volume group associated to the given {@link AudioAttributes}. + * If no group supports the given {@link AudioAttributes}, it returns the volume group + * for the default attributes. + * If no group supports the default attributes, it returns {@link #DEFAULT_VOLUME_GROUP} + */ + @SystemApi + public int getVolumeGroupIdForAttributes(@NonNull AudioAttributes attributes) { + Preconditions.checkNotNull(attributes, "attributes must not be null"); + int volumeGroupId = getVolumeGroupIdForAttributesInt(attributes); + if (volumeGroupId != AudioVolumeGroups.DEFAULT_VOLUME_GROUP) { + return volumeGroupId; + } + // The default volume group is the one hosted by default product strategy, i.e. + // supporting Default Attributes + return getVolumeGroupIdForAttributesInt(AudioProductStrategy.sDefaultAttributes); + } + + /** + * @hide + * @param streamType to be considered + * @return volume group associated to the given stream type. + */ + @SystemApi + public int getVolumeGroupIdForLegacyStreamType(int streamType) { + for (final AudioProductStrategy productStrategy : this) { + int volumeGroupId = productStrategy.getVolumeGroupIdForLegacyStreamType(streamType); + if (volumeGroupId != AudioVolumeGroups.DEFAULT_VOLUME_GROUP) { + return volumeGroupId; + } + } + // The default volume group is the one hosted by default product strategy, i.e. + // supporting Default Attributes + return getVolumeGroupIdForAttributesInt(AudioProductStrategy.sDefaultAttributes); + } + @Override public int describeContents() { return 0; @@ -178,6 +236,21 @@ public final class AudioProductStrategies implements Iterable<AudioProductStrate } } + /** + * @param attributes to be considered + * @return volume group associated to the given {@link AudioAttributes}. + */ + private int getVolumeGroupIdForAttributesInt(@NonNull AudioAttributes attributes) { + Preconditions.checkNotNull(attributes, "attributes must not be null"); + for (final AudioProductStrategy productStrategy : this) { + int volumeGroupId = productStrategy.getVolumeGroupIdForAudioAttributes(attributes); + if (volumeGroupId != AudioVolumeGroups.DEFAULT_VOLUME_GROUP) { + return volumeGroupId; + } + } + return AudioVolumeGroups.DEFAULT_VOLUME_GROUP; + } + public static final Parcelable.Creator<AudioProductStrategies> CREATOR = new Parcelable.Creator<AudioProductStrategies>() { @Override @@ -198,4 +271,7 @@ public final class AudioProductStrategies implements Iterable<AudioProductStrate private static native int native_list_audio_product_strategies( ArrayList<AudioProductStrategy> strategies); + + private static native int native_get_product_strategies_from_audio_attributes( + AudioAttributes attributes); } diff --git a/media/java/android/media/audiopolicy/AudioProductStrategy.java b/media/java/android/media/audiopolicy/AudioProductStrategy.java index af6e8bfdbd83..169484b79bd4 100644 --- a/media/java/android/media/audiopolicy/AudioProductStrategy.java +++ b/media/java/android/media/audiopolicy/AudioProductStrategy.java @@ -157,32 +157,32 @@ public final class AudioProductStrategy implements Parcelable { /** * @hide * @param streamType legacy stream type used for volume operation only - * @return the {@link AudioAttributes} relevant for the given streamType. - * If none is found, it builds the default attributes. + * @return the volume group id relevant for the given streamType. + * If none is found, {@link AudioVolumeGroups#DEFAULT_VOLUME_GROUP} is returned. */ - public int getGroupIdForLegacyStreamType(int streamType) { + public int getVolumeGroupIdForLegacyStreamType(int streamType) { for (final AudioAttributesGroup aag : mAudioAttributesGroups) { if (aag.supportsStreamType(streamType)) { - return aag.getGroupId(); + return aag.getVolumeGroupId(); } } - return DEFAULT_GROUP; + return AudioVolumeGroups.DEFAULT_VOLUME_GROUP; } /** * @hide * @param aa the {@link AudioAttributes} to be considered - * @return the group id associated with the given audio attributes if found, - * default value otherwise. + * @return the volume group id associated with the given audio attributes if found, + * {@link AudioVolumeGroups#DEFAULT_VOLUME_GROUP} otherwise. */ - public int getGroupIdForAudioAttributes(@NonNull AudioAttributes aa) { + public int getVolumeGroupIdForAudioAttributes(@NonNull AudioAttributes aa) { Preconditions.checkNotNull(aa, "AudioAttributes must not be null"); for (final AudioAttributesGroup aag : mAudioAttributesGroups) { if (aag.supportsAttributes(aa)) { - return aag.getGroupId(); + return aag.getVolumeGroupId(); } } - return DEFAULT_GROUP; + return AudioVolumeGroups.DEFAULT_VOLUME_GROUP; } @Override @@ -266,15 +266,14 @@ public final class AudioProductStrategy implements Parcelable { && ((refFormattedTags.length() == 0) || refFormattedTags.equals(cliFormattedTags)); } - private static final class AudioAttributesGroup implements Parcelable { - private int mGroupId; + private int mVolumeGroupId; private int mLegacyStreamType; private final AudioAttributes[] mAudioAttributes; - AudioAttributesGroup(int groupId, int streamType, + AudioAttributesGroup(int volumeGroupId, int streamType, @NonNull AudioAttributes[] audioAttributes) { - mGroupId = groupId; + mVolumeGroupId = volumeGroupId; mLegacyStreamType = streamType; mAudioAttributes = audioAttributes; } @@ -286,7 +285,7 @@ public final class AudioProductStrategy implements Parcelable { AudioAttributesGroup thatAag = (AudioAttributesGroup) o; - return mGroupId == thatAag.mGroupId + return mVolumeGroupId == thatAag.mVolumeGroupId && mLegacyStreamType == thatAag.mLegacyStreamType && mAudioAttributes.equals(thatAag.mAudioAttributes); } @@ -295,8 +294,8 @@ public final class AudioProductStrategy implements Parcelable { return mLegacyStreamType; } - public int getGroupId() { - return mGroupId; + public int getVolumeGroupId() { + return mVolumeGroupId; } public @NonNull AudioAttributes getAudioAttributes() { @@ -331,7 +330,7 @@ public final class AudioProductStrategy implements Parcelable { @Override public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeInt(mGroupId); + dest.writeInt(mVolumeGroupId); dest.writeInt(mLegacyStreamType); dest.writeInt(mAudioAttributes.length); for (AudioAttributes attributes : mAudioAttributes) { @@ -343,14 +342,14 @@ public final class AudioProductStrategy implements Parcelable { new Parcelable.Creator<AudioAttributesGroup>() { @Override public AudioAttributesGroup createFromParcel(@NonNull Parcel in) { - int groupId = in.readInt(); + int volumeGroupId = in.readInt(); int streamType = in.readInt(); int nbAttributes = in.readInt(); AudioAttributes[] aa = new AudioAttributes[nbAttributes]; for (int index = 0; index < nbAttributes; index++) { aa[index] = AudioAttributes.CREATOR.createFromParcel(in); } - return new AudioAttributesGroup(groupId, streamType, aa); + return new AudioAttributesGroup(volumeGroupId, streamType, aa); } @Override @@ -365,8 +364,8 @@ public final class AudioProductStrategy implements Parcelable { StringBuilder s = new StringBuilder(); s.append("\n Legacy Stream Type: "); s.append(Integer.toString(mLegacyStreamType)); - s.append(" Group Id: "); - s.append(Integer.toString(mGroupId)); + s.append(" Volume Group Id: "); + s.append(Integer.toString(mVolumeGroupId)); for (AudioAttributes attribute : mAudioAttributes) { s.append("\n -"); diff --git a/media/java/android/media/audiopolicy/AudioVolumeGroup.aidl b/media/java/android/media/audiopolicy/AudioVolumeGroup.aidl new file mode 100644 index 000000000000..caf1e0db1ad7 --- /dev/null +++ b/media/java/android/media/audiopolicy/AudioVolumeGroup.aidl @@ -0,0 +1,18 @@ +/* Copyright 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.media.audiopolicy; + +parcelable AudioVolumeGroup; diff --git a/media/java/android/media/audiopolicy/AudioVolumeGroup.java b/media/java/android/media/audiopolicy/AudioVolumeGroup.java new file mode 100644 index 000000000000..0b4ba937ad29 --- /dev/null +++ b/media/java/android/media/audiopolicy/AudioVolumeGroup.java @@ -0,0 +1,171 @@ +/* + * 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.media.audiopolicy; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.media.AudioAttributes; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.Preconditions; + +import java.util.Arrays; +import java.util.List; + +/** + * A class to create the association between different playback attributes + * (e.g. media, mapping direction) to a single volume control. + * @hide + */ +@SystemApi +public final class AudioVolumeGroup implements Parcelable { + /** + * Unique identifier of a volume group. + */ + private int mId; + /** + * human-readable name of this volume group. + */ + private final String mName; + + private final AudioAttributes[] mAudioAttributes; + private int[] mLegacyStreamTypes; + + /** + * @param name of the volume group + * @param id of the volume group + * @param followers {@link AudioProductStrategies} strategy following this volume group + */ + AudioVolumeGroup(@NonNull String name, int id, + @NonNull AudioAttributes[] audioAttributes, + @NonNull int[] legacyStreamTypes) { + Preconditions.checkNotNull(name, "name must not be null"); + Preconditions.checkNotNull(audioAttributes, "audioAttributes must not be null"); + Preconditions.checkNotNull(legacyStreamTypes, "legacyStreamTypes must not be null"); + mName = name; + mId = id; + mAudioAttributes = audioAttributes; + mLegacyStreamTypes = legacyStreamTypes; + } + + @Override + public boolean equals(@NonNull Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + AudioVolumeGroup thatAvg = (AudioVolumeGroup) o; + + return mName == thatAvg.mName && mId == thatAvg.mId + && mAudioAttributes.equals(thatAvg.mAudioAttributes); + } + + /** + * @return List of {@link AudioAttributes} involved in this {@link AudioVolumeGroup}. + */ + public List<AudioAttributes> getAudioAttributes() { + return Arrays.asList(mAudioAttributes); + } + + /** + * @return the stream types involved in this {@link AudioVolumeGroup}. + */ + public @NonNull int[] getLegacyStreamTypes() { + return mLegacyStreamTypes; + } + + /** + * @return human-readable name of this volume group. + */ + public @NonNull String name() { + return mName; + } + + /** + * @return the volume group unique identifier id. + */ + public int getId() { + return mId; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString(mName); + dest.writeInt(mId); + dest.writeInt(mAudioAttributes.length); + for (AudioAttributes attributes : mAudioAttributes) { + attributes.writeToParcel(dest, flags | AudioAttributes.FLATTEN_TAGS/*flags*/); + } + dest.writeInt(mLegacyStreamTypes.length); + for (int streamType : mLegacyStreamTypes) { + dest.writeInt(streamType); + } + } + + public static final Parcelable.Creator<AudioVolumeGroup> CREATOR = + new Parcelable.Creator<AudioVolumeGroup>() { + @Override + public @NonNull AudioVolumeGroup createFromParcel(@NonNull Parcel in) { + Preconditions.checkNotNull(in, "in Parcel must not be null"); + String name = in.readString(); + int id = in.readInt(); + int nbAttributes = in.readInt(); + AudioAttributes[] audioAttributes = new AudioAttributes[nbAttributes]; + for (int index = 0; index < nbAttributes; index++) { + audioAttributes[index] = AudioAttributes.CREATOR.createFromParcel(in); + } + int nbStreamTypes = in.readInt(); + int[] streamTypes = new int[nbStreamTypes]; + for (int index = 0; index < nbStreamTypes; index++) { + streamTypes[index] = in.readInt(); + } + return new AudioVolumeGroup(name, id, audioAttributes, streamTypes); + } + + @Override + public @NonNull AudioVolumeGroup[] newArray(int size) { + return new AudioVolumeGroup[size]; + } + }; + + @Override + public @NonNull String toString() { + StringBuilder s = new StringBuilder(); + s.append("\n Name: "); + s.append(mName); + s.append(" Id: "); + s.append(Integer.toString(mId)); + + s.append("\n Supported Audio Attributes:"); + for (AudioAttributes attribute : mAudioAttributes) { + s.append("\n -"); + s.append(attribute.toString()); + } + s.append("\n Supported Legacy Stream Types: { "); + for (int legacyStreamType : mLegacyStreamTypes) { + s.append(Integer.toString(legacyStreamType)); + s.append(" "); + } + s.append("}"); + return s.toString(); + } +} diff --git a/media/java/android/media/audiopolicy/AudioVolumeGroupChangeHandler.java b/media/java/android/media/audiopolicy/AudioVolumeGroupChangeHandler.java new file mode 100644 index 000000000000..074188e01000 --- /dev/null +++ b/media/java/android/media/audiopolicy/AudioVolumeGroupChangeHandler.java @@ -0,0 +1,167 @@ +/* + * 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.media.audiopolicy; + +import android.annotation.NonNull; +import android.media.AudioManager; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Message; + +import com.android.internal.util.Preconditions; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; + +/** + * The AudioVolumeGroupChangeHandler handles AudioManager.OnAudioVolumeGroupChangedListener + * callbacks posted from JNI + * + * TODO: Make use of Executor of callbacks. + * @hide + */ +public class AudioVolumeGroupChangeHandler { + private Handler mHandler; + private HandlerThread mHandlerThread; + private final ArrayList<AudioManager.VolumeGroupCallback> mListeners = + new ArrayList<AudioManager.VolumeGroupCallback>(); + + private static final String TAG = "AudioVolumeGroupChangeHandler"; + + private static final int AUDIOVOLUMEGROUP_EVENT_VOLUME_CHANGED = 1000; + private static final int AUDIOVOLUMEGROUP_EVENT_NEW_LISTENER = 4; + + /** + * Accessed by native methods: JNI Callback context. + */ + @SuppressWarnings("unused") + private long mJniCallback; + + /** + * Initialization + */ + public void init() { + synchronized (this) { + if (mHandler != null) { + return; + } + // create a new thread for our new event handler + mHandlerThread = new HandlerThread(TAG); + mHandlerThread.start(); + + if (mHandlerThread.getLooper() == null) { + mHandler = null; + return; + } + mHandler = new Handler(mHandlerThread.getLooper()) { + @Override + public void handleMessage(Message msg) { + ArrayList<AudioManager.VolumeGroupCallback> listeners; + synchronized (this) { + if (msg.what == AUDIOVOLUMEGROUP_EVENT_NEW_LISTENER) { + listeners = + new ArrayList<AudioManager.VolumeGroupCallback>(); + if (mListeners.contains(msg.obj)) { + listeners.add( + (AudioManager.VolumeGroupCallback) msg.obj); + } + } else { + listeners = mListeners; + } + } + if (listeners.isEmpty()) { + return; + } + + switch (msg.what) { + case AUDIOVOLUMEGROUP_EVENT_VOLUME_CHANGED: + for (int i = 0; i < listeners.size(); i++) { + listeners.get(i).onAudioVolumeGroupChanged((int) msg.arg1, + (int) msg.arg2); + } + break; + + default: + break; + } + } + }; + native_setup(new WeakReference<AudioVolumeGroupChangeHandler>(this)); + } + } + + private native void native_setup(Object moduleThis); + + @Override + protected void finalize() { + native_finalize(); + if (mHandlerThread.isAlive()) { + mHandlerThread.quit(); + } + } + private native void native_finalize(); + + /** + * @param cb the {@link AudioManager.VolumeGroupCallback} to register + */ + public void registerListener(@NonNull AudioManager.VolumeGroupCallback cb) { + Preconditions.checkNotNull(cb, "volume group callback shall not be null"); + synchronized (this) { + mListeners.add(cb); + } + if (mHandler != null) { + Message m = mHandler.obtainMessage( + AUDIOVOLUMEGROUP_EVENT_NEW_LISTENER, 0, 0, cb); + mHandler.sendMessage(m); + } + } + + /** + * @param cb the {@link AudioManager.VolumeGroupCallback} to unregister + */ + public void unregisterListener(@NonNull AudioManager.VolumeGroupCallback cb) { + Preconditions.checkNotNull(cb, "volume group callback shall not be null"); + synchronized (this) { + mListeners.remove(cb); + } + } + + Handler handler() { + return mHandler; + } + + @SuppressWarnings("unused") + private static void postEventFromNative(Object moduleRef, + int what, int arg1, int arg2, Object obj) { + AudioVolumeGroupChangeHandler eventHandler = + (AudioVolumeGroupChangeHandler) ((WeakReference) moduleRef).get(); + if (eventHandler == null) { + return; + } + + if (eventHandler != null) { + Handler handler = eventHandler.handler(); + if (handler != null) { + Message m = handler.obtainMessage(what, arg1, arg2, obj); + if (what != AUDIOVOLUMEGROUP_EVENT_NEW_LISTENER) { + handler.removeMessages(what); + } + handler.sendMessage(m); + } + } + } +} diff --git a/media/java/android/media/audiopolicy/AudioVolumeGroups.aidl b/media/java/android/media/audiopolicy/AudioVolumeGroups.aidl new file mode 100644 index 000000000000..918cac39f19a --- /dev/null +++ b/media/java/android/media/audiopolicy/AudioVolumeGroups.aidl @@ -0,0 +1,18 @@ +/* Copyright 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.media.audiopolicy; + +parcelable AudioVolumeGroups; diff --git a/media/java/android/media/audiopolicy/AudioVolumeGroups.java b/media/java/android/media/audiopolicy/AudioVolumeGroups.java new file mode 100644 index 000000000000..301bec7a10c4 --- /dev/null +++ b/media/java/android/media/audiopolicy/AudioVolumeGroups.java @@ -0,0 +1,135 @@ +/* + * 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.media.audiopolicy; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SystemApi; +import android.media.AudioSystem; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import com.android.internal.util.Preconditions; + +import java.util.ArrayList; +import java.util.Iterator; + +/** + * @hide + * A class to encapsulate a collection of {@link AudioVolumeGroup}. + */ +@SystemApi +public final class AudioVolumeGroups implements Iterable<AudioVolumeGroup>, Parcelable { + + private final ArrayList<AudioVolumeGroup> mAudioVolumeGroupList; + + private static final String TAG = "AudioVolumeGroups"; + + /** + * Volume group value to use when introspection API fails. + */ + public static final int DEFAULT_VOLUME_GROUP = -1; + + public AudioVolumeGroups() { + ArrayList<AudioVolumeGroup> avgList = new ArrayList<AudioVolumeGroup>(); + int status = native_list_audio_volume_groups(avgList); + if (status != AudioSystem.SUCCESS) { + Log.w(TAG, ": listAudioVolumeGroups failed"); + } + mAudioVolumeGroupList = avgList; + } + + private AudioVolumeGroups(@NonNull ArrayList<AudioVolumeGroup> audioVolumeGroupList) { + Preconditions.checkNotNull(audioVolumeGroupList, "audioVolumeGroupList must not be null"); + mAudioVolumeGroupList = audioVolumeGroupList; + } + + /** + * @return number of {@link AudioProductStrategy} objects + */ + public int size() { + return mAudioVolumeGroupList.size(); + } + + /** + * Returns an {@link Iterator} + */ + @Override + public Iterator<AudioVolumeGroup> iterator() { + return mAudioVolumeGroupList.iterator(); + } + + @Override + public boolean equals(@NonNull Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + AudioVolumeGroups that = (AudioVolumeGroups) o; + + return mAudioVolumeGroupList.equals(that.mAudioVolumeGroupList); + } + + /** + * @return the matching {@link AudioVolumeGroup} objects with the given id, + * null object if not found. + */ + public @Nullable AudioVolumeGroup getById(int volumeGroupId) { + for (final AudioVolumeGroup avg : this) { + if (avg.getId() == volumeGroupId) { + return avg; + } + } + Log.e(TAG, ": invalid volume group id: " + volumeGroupId + " requested"); + return null; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeInt(size()); + for (final AudioVolumeGroup volumeGroup : this) { + volumeGroup.writeToParcel(dest, flags); + } + } + + private static native int native_list_audio_volume_groups( + ArrayList<AudioVolumeGroup> groups); + + public static final Parcelable.Creator<AudioVolumeGroups> CREATOR = + new Parcelable.Creator<AudioVolumeGroups>() { + @Override + public @NonNull AudioVolumeGroups createFromParcel(@NonNull Parcel in) { + Preconditions.checkNotNull(in, "in Parcel must not be null"); + ArrayList<AudioVolumeGroup> avgList = new ArrayList<AudioVolumeGroup>(); + int size = in.readInt(); + for (int index = 0; index < size; index++) { + avgList.add(AudioVolumeGroup.CREATOR.createFromParcel(in)); + } + return new AudioVolumeGroups(avgList); + } + + @Override + public @NonNull AudioVolumeGroups[] newArray(int size) { + return new AudioVolumeGroups[size]; + } + }; +} diff --git a/media/java/android/media/projection/IMediaProjectionManager.aidl b/media/java/android/media/projection/IMediaProjectionManager.aidl index 7e10c51fc5cd..d190fcec6c8c 100644 --- a/media/java/android/media/projection/IMediaProjectionManager.aidl +++ b/media/java/android/media/projection/IMediaProjectionManager.aidl @@ -24,6 +24,7 @@ import android.os.IBinder; /** {@hide} */ interface IMediaProjectionManager { + @UnsupportedAppUsage boolean hasProjectionPermission(int uid, String packageName); IMediaProjection createProjection(int uid, String packageName, int type, boolean permanentGrant); diff --git a/media/java/android/media/projection/MediaProjection.java b/media/java/android/media/projection/MediaProjection.java index f9c5b8d729c7..632cfb0f1e30 100644 --- a/media/java/android/media/projection/MediaProjection.java +++ b/media/java/android/media/projection/MediaProjection.java @@ -21,7 +21,6 @@ import android.annotation.Nullable; import android.content.Context; import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; -import android.media.AudioRecord; import android.media.projection.IMediaProjection; import android.media.projection.IMediaProjectionCallback; import android.os.Handler; @@ -140,16 +139,6 @@ public final class MediaProjection { } /** - * Creates an AudioRecord to capture audio played back by the system. - * @hide - */ - public AudioRecord createAudioRecord( - int sampleRateInHz, int channelConfig, - int audioFormat, int bufferSizeInBytes) { - return null; - } - - /** * Stops projection. */ public void stop() { diff --git a/media/java/android/media/session/ControllerLink.java b/media/java/android/media/session/ControllerLink.java index 40c716607697..5828fbdd1700 100644 --- a/media/java/android/media/session/ControllerLink.java +++ b/media/java/android/media/session/ControllerLink.java @@ -158,6 +158,18 @@ public final class ControllerLink implements Parcelable { } /** + * Gets the session info of the connected session. + */ + @Nullable + Bundle getSessionInfo() { + try { + return mISessionController.getSessionInfo(); + } catch (RemoteException e) { + throw new RuntimeException(e); + } + } + + /** * Gets the {@link PendingIntent} for launching UI of the connected session. */ @Nullable @@ -666,6 +678,12 @@ public final class ControllerLink implements Parcelable { return null; } + /** Stub method for ISessionController.getSessionInfo */ + @Nullable + public Bundle getSessionInfo() { + return null; + } + /** Stub method for ISessionController.getLaunchPendingIntent */ @Nullable public PendingIntent getLaunchPendingIntent() { @@ -856,6 +874,11 @@ public final class ControllerLink implements Parcelable { } @Override + public Bundle getSessionInfo() { + return mControllerStub.getSessionInfo(); + } + + @Override public PendingIntent getLaunchPendingIntent() { return mControllerStub.getLaunchPendingIntent(); } diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl index 3e7b4fbdebd3..298085f005da 100644 --- a/media/java/android/media/session/ISessionController.aidl +++ b/media/java/android/media/session/ISessionController.aidl @@ -44,6 +44,7 @@ interface ISessionController { void unregisterCallback(in ControllerCallbackLink cb); String getPackageName(); String getTag(); + Bundle getSessionInfo(); PendingIntent getLaunchPendingIntent(); long getFlags(); MediaController.PlaybackInfo getVolumeAttributes(); diff --git a/media/java/android/media/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl index 8143bfa00dc1..e85461f93731 100644 --- a/media/java/android/media/session/ISessionManager.aidl +++ b/media/java/android/media/session/ISessionManager.aidl @@ -35,7 +35,7 @@ import android.view.KeyEvent; */ interface ISessionManager { SessionLink createSession(String packageName, in SessionCallbackLink sessionCb, String tag, - int userId); + in Bundle sessionInfo, int userId); void notifySession2Created(in Session2Token sessionToken); List<MediaSession.Token> getSessions(in ComponentName compName, int userId); List<Session2Token> getSession2Tokens(int userId); diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java index 9e4199cba47a..fb21f69b2aca 100644 --- a/media/java/android/media/session/MediaController.java +++ b/media/java/android/media/session/MediaController.java @@ -79,6 +79,7 @@ public final class MediaController { private boolean mCbRegistered = false; private String mPackageName; private String mTag; + private Bundle mSessionInfo; private final TransportControls mTransportControls; @@ -410,6 +411,23 @@ public final class MediaController { } /** + * Gets the additional session information which was set when the session was created. + * + * @return The additional session information + */ + @Nullable + public Bundle getSessionInfo() { + if (mSessionInfo == null) { + try { + mSessionInfo = mSessionBinder.getSessionInfo(); + } catch (RuntimeException e) { + Log.d(TAG, "Dead object in getSessionInfo.", e); + } + } + return mSessionInfo; + } + + /** * Get the session's tag for debugging purposes. * * @return The session's tag. diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java index 8ab893b03a62..9fdefa62a35f 100644 --- a/media/java/android/media/session/MediaSession.java +++ b/media/java/android/media/session/MediaSession.java @@ -132,6 +132,27 @@ public final class MediaSession { * @param tag A short name for debugging purposes. */ public MediaSession(@NonNull Context context, @NonNull String tag) { + this(context, tag, null); + } + + /** + * Creates a new session. The session will automatically be registered with + * the system but will not be published until {@link #setActive(boolean) + * setActive(true)} is called. You must call {@link #release()} when + * finished with the session. + * <p> + * The {@code sessionInfo} can include additional unchanging information about this session. + * For example, it can include the version of the application, or the list of the custom + * commands that this session supports. + * + * @param context The context to use to create the session. + * @param tag A short name for debugging purposes. + * @param sessionInfo A bundle for additional information about this session. + * Controllers can get this information by calling + * {@link MediaController#getSessionInfo()}. + */ + public MediaSession(@NonNull Context context, @NonNull String tag, + @Nullable Bundle sessionInfo) { if (context == null) { throw new IllegalArgumentException("context cannot be null."); } @@ -142,7 +163,7 @@ public final class MediaSession { .getSystemService(Context.MEDIA_SESSION_SERVICE); try { SessionCallbackLink cbLink = new SessionCallbackLink(context); - SessionLink sessionLink = manager.createSession(cbLink, tag); + SessionLink sessionLink = manager.createSession(cbLink, tag, sessionInfo); mImpl = new MediaSessionEngine(context, sessionLink, cbLink); mMaxBitmapSize = context.getResources().getDimensionPixelSize( android.R.dimen.config_mediaMetadataBitmapMaxSize); diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java index 46f6c71c5be0..fde4f8837fa5 100644 --- a/media/java/android/media/session/MediaSessionManager.java +++ b/media/java/android/media/session/MediaSessionManager.java @@ -28,6 +28,7 @@ import android.media.AudioManager; import android.media.IRemoteVolumeController; import android.media.MediaSession2; import android.media.Session2Token; +import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; @@ -101,13 +102,15 @@ public final class MediaSessionManager { * Create a new session in the system and get the binder for it. * * @param tag A short name for debugging purposes. + * @param sessionInfo A bundle for additional information about this session. * @return The binder object from the system * @hide */ @NonNull - public SessionLink createSession(@NonNull SessionCallbackLink cbStub, @NonNull String tag) { + public SessionLink createSession(@NonNull SessionCallbackLink cbStub, @NonNull String tag, + @Nullable Bundle sessionInfo) { try { - return mService.createSession(mContext.getPackageName(), cbStub, tag, + return mService.createSession(mContext.getPackageName(), cbStub, tag, sessionInfo, UserHandle.myUserId()); } catch (RemoteException e) { throw new RuntimeException(e); diff --git a/media/java/android/media/tv/ITvRemoteServiceInput.aidl b/media/java/android/media/tv/ITvRemoteServiceInput.aidl index df39299c0f93..a0b6c9bfc8d8 100644 --- a/media/java/android/media/tv/ITvRemoteServiceInput.aidl +++ b/media/java/android/media/tv/ITvRemoteServiceInput.aidl @@ -21,13 +21,22 @@ package android.media.tv; */ oneway interface ITvRemoteServiceInput { // InputBridge related + @UnsupportedAppUsage void openInputBridge(IBinder token, String name, int width, int height, int maxPointers); + @UnsupportedAppUsage void closeInputBridge(IBinder token); + @UnsupportedAppUsage void clearInputBridge(IBinder token); + @UnsupportedAppUsage void sendTimestamp(IBinder token, long timestamp); + @UnsupportedAppUsage void sendKeyDown(IBinder token, int keyCode); + @UnsupportedAppUsage void sendKeyUp(IBinder token, int keyCode); + @UnsupportedAppUsage void sendPointerDown(IBinder token, int pointerId, int x, int y); + @UnsupportedAppUsage void sendPointerUp(IBinder token, int pointerId); + @UnsupportedAppUsage void sendPointerSync(IBinder token); }
\ No newline at end of file diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index e8f188c7a27c..d22a29893540 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -109,7 +109,7 @@ public final class TvInputManager { public @interface VideoUnavailableReason {} static final int VIDEO_UNAVAILABLE_REASON_START = 0; - static final int VIDEO_UNAVAILABLE_REASON_END = 4; + static final int VIDEO_UNAVAILABLE_REASON_END = 5; /** * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and @@ -140,7 +140,14 @@ public final class TvInputManager { * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because * the current TV program is audio-only. */ - public static final int VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY = VIDEO_UNAVAILABLE_REASON_END; + public static final int VIDEO_UNAVAILABLE_REASON_AUDIO_ONLY = 4; + /** + * Reason for {@link TvInputService.Session#notifyVideoUnavailable(int)} and + * {@link TvView.TvInputCallback#onVideoUnavailable(String, int)}: Video is unavailable because + * the source is not physically connected, for example the HDMI cable is not connected. + * @hide + */ + public static final int VIDEO_UNAVAILABLE_REASON_NOT_CONNECTED = VIDEO_UNAVAILABLE_REASON_END; /** @hide */ @Retention(RetentionPolicy.SOURCE) diff --git a/media/jni/Android.bp b/media/jni/Android.bp index 8ed265d674a6..65b58ab6bfef 100644 --- a/media/jni/Android.bp +++ b/media/jni/Android.bp @@ -20,8 +20,8 @@ cc_library_shared { "android_media_MediaScanner.cpp", "android_media_MediaSync.cpp", "android_media_ResampleInputStream.cpp", + "android_media_Streams.cpp", "android_media_SyncParams.cpp", - "android_media_Utils.cpp", "android_mtp_MtpDatabase.cpp", "android_mtp_MtpDevice.cpp", "android_mtp_MtpServer.cpp", @@ -34,6 +34,7 @@ cc_library_shared { "libutils", "libbinder", "libmedia", + "libmedia_jni_utils", "libmedia_omx", "libmediametrics", "libmediadrm", @@ -85,6 +86,36 @@ cc_library_shared { } cc_library_shared { + name: "libmedia_jni_utils", + srcs: [ + "android_media_Utils.cpp", + ], + + shared_libs: [ + "liblog", + "libmedia_omx", + "libnativewindow", + "libui", + "libutils", + "android.hidl.token@1.0-utils", + ], + + include_dirs: [ + "system/media/camera/include", + ], + + export_include_dirs: ["."], + + cflags: [ + "-Wall", + "-Werror", + "-Wno-error=deprecated-declarations", + "-Wunused", + "-Wunreachable-code", + ], +} + +cc_library_shared { name: "libmedia2_jni", srcs: [ @@ -125,7 +156,6 @@ cc_library_shared { "libcrypto", "libcutils", "libjsoncpp", - "libmedia_helper", "libmedia_player2_util", "libmediaplayer2", "libmediaplayer2-protos", @@ -133,7 +163,6 @@ cc_library_shared { "libmediautils", "libprocessgroup", "libprotobuf-cpp-lite", - "libstagefright", "libstagefright_esds", "libstagefright_foundation_without_imemory", "libstagefright_httplive", diff --git a/media/jni/android_media_ImageWriter.cpp b/media/jni/android_media_ImageWriter.cpp index 031e373241cb..cfcba76d3af2 100644 --- a/media/jni/android_media_ImageWriter.cpp +++ b/media/jni/android_media_ImageWriter.cpp @@ -18,8 +18,11 @@ #define LOG_TAG "ImageWriter_JNI" #include "android_media_Utils.h" +#include <utils/Condition.h> #include <utils/Log.h> +#include <utils/Mutex.h> #include <utils/String8.h> +#include <utils/Thread.h> #include <gui/IProducerListener.h> #include <gui/Surface.h> @@ -34,6 +37,8 @@ #include <inttypes.h> #include <android/hardware_buffer_jni.h> +#include <deque> + #define IMAGE_BUFFER_JNI_ID "mNativeBuffer" #define IMAGE_FORMAT_UNKNOWN 0 // This is the same value as ImageFormat#UNKNOWN. // ---------------------------------------------------------------------------- @@ -90,14 +95,124 @@ private: int mFormat; int mWidth; int mHeight; + + // Class for a shared thread used to detach buffers from buffer queues + // to discard buffers after consumers are done using them. + // This is needed because detaching buffers in onBufferReleased callback + // can lead to deadlock when consumer/producer are on the same process. + class BufferDetacher { + public: + // Called by JNIImageWriterContext ctor. Will start the thread for first ref. + void addRef(); + // Called by JNIImageWriterContext dtor. Will stop the thread after ref goes to 0. + void removeRef(); + // Called by onBufferReleased to signal this thread to detach a buffer + void detach(wp<Surface>); + + private: + + class DetachThread : public Thread { + public: + DetachThread() : Thread(/*canCallJava*/false) {}; + + void detach(wp<Surface>); + + virtual void requestExit() override; + + private: + virtual bool threadLoop() override; + + Mutex mLock; + Condition mCondition; + std::deque<wp<Surface>> mQueue; + + static const nsecs_t kWaitDuration = 20000000; // 20 ms + }; + sp<DetachThread> mThread; + + Mutex mLock; + int mRefCount = 0; + }; + + static BufferDetacher sBufferDetacher; }; +JNIImageWriterContext::BufferDetacher JNIImageWriterContext::sBufferDetacher; + +void JNIImageWriterContext::BufferDetacher::addRef() { + Mutex::Autolock l(mLock); + mRefCount++; + if (mRefCount == 1) { + mThread = new DetachThread(); + mThread->run("BufDtchThrd"); + } +} + +void JNIImageWriterContext::BufferDetacher::removeRef() { + Mutex::Autolock l(mLock); + mRefCount--; + if (mRefCount == 0) { + mThread->requestExit(); + mThread->join(); + mThread.clear(); + } +} + +void JNIImageWriterContext::BufferDetacher::detach(wp<Surface> bq) { + Mutex::Autolock l(mLock); + if (mThread == nullptr) { + ALOGE("%s: buffer detach thread is gone!", __FUNCTION__); + return; + } + mThread->detach(bq); +} + +void JNIImageWriterContext::BufferDetacher::DetachThread::detach(wp<Surface> bq) { + Mutex::Autolock l(mLock); + mQueue.push_back(bq); + mCondition.signal(); +} + +void JNIImageWriterContext::BufferDetacher::DetachThread::requestExit() { + Thread::requestExit(); + { + Mutex::Autolock l(mLock); + mQueue.clear(); + } + mCondition.signal(); +} + +bool JNIImageWriterContext::BufferDetacher::DetachThread::threadLoop() { + Mutex::Autolock l(mLock); + mCondition.waitRelative(mLock, kWaitDuration); + + while (!mQueue.empty()) { + if (exitPending()) { + return false; + } + + wp<Surface> wbq = mQueue.front(); + mQueue.pop_front(); + sp<Surface> bq = wbq.promote(); + if (bq != nullptr) { + sp<Fence> fence; + sp<GraphicBuffer> buffer; + ALOGV("%s: One buffer is detached", __FUNCTION__); + mLock.unlock(); + bq->detachNextBuffer(&buffer, &fence); + mLock.lock(); + } + } + return !exitPending(); +} + JNIImageWriterContext::JNIImageWriterContext(JNIEnv* env, jobject weakThiz, jclass clazz) : - mWeakThiz(env->NewGlobalRef(weakThiz)), - mClazz((jclass)env->NewGlobalRef(clazz)), - mFormat(0), - mWidth(-1), - mHeight(-1) { + mWeakThiz(env->NewGlobalRef(weakThiz)), + mClazz((jclass)env->NewGlobalRef(clazz)), + mFormat(0), + mWidth(-1), + mHeight(-1) { + sBufferDetacher.addRef(); } JNIImageWriterContext::~JNIImageWriterContext() { @@ -115,6 +230,7 @@ JNIImageWriterContext::~JNIImageWriterContext() { } mProducer.clear(); + sBufferDetacher.removeRef(); } JNIEnv* JNIImageWriterContext::getJNIEnv(bool* needsDetach) { @@ -153,10 +269,7 @@ void JNIImageWriterContext::onBufferReleased() { // need let this callback give a BufferItem, then only detach if it was attached to this // Writer. Do the detach unconditionally for opaque format now. see b/19977520 if (mFormat == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) { - sp<Fence> fence; - sp<GraphicBuffer> buffer; - ALOGV("%s: One buffer is detached", __FUNCTION__); - mProducer->detachNextBuffer(&buffer, &fence); + sBufferDetacher.detach(mProducer); } env->CallStaticVoidMethod(mClazz, gImageWriterClassInfo.postEventFromNative, mWeakThiz); diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp index f07f1e8fb62b..150b6f918685 100644 --- a/media/jni/android_media_MediaCodec.cpp +++ b/media/jni/android_media_MediaCodec.cpp @@ -23,7 +23,7 @@ #include "android_media_MediaCrypto.h" #include "android_media_MediaDescrambler.h" #include "android_media_MediaMetricsJNI.h" -#include "android_media_Utils.h" +#include "android_media_Streams.h" #include "android_runtime/AndroidRuntime.h" #include "android_runtime/android_view_Surface.h" #include "android_util_Binder.h" diff --git a/media/jni/android_media_MediaCodecList.cpp b/media/jni/android_media_MediaCodecList.cpp index 6b8f7457eab9..923d1d253c6e 100644 --- a/media/jni/android_media_MediaCodecList.cpp +++ b/media/jni/android_media_MediaCodecList.cpp @@ -31,7 +31,7 @@ #include "android_runtime/AndroidRuntime.h" #include "jni.h" #include <nativehelper/JNIHelp.h> -#include "android_media_Utils.h" +#include "android_media_Streams.h" using namespace android; diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp index c6b171bdd6d4..f5ae9d0d5d2f 100644 --- a/media/jni/android_media_MediaExtractor.cpp +++ b/media/jni/android_media_MediaExtractor.cpp @@ -22,7 +22,7 @@ #include "android_media_MediaDataSource.h" #include "android_media_MediaExtractor.h" #include "android_media_MediaMetricsJNI.h" -#include "android_media_Utils.h" +#include "android_media_Streams.h" #include "android_os_HwRemoteBinder.h" #include "android_runtime/AndroidRuntime.h" #include "android_runtime/Log.h" diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp index c1226fa89fe4..a4807843d7d8 100644 --- a/media/jni/android_media_MediaMetadataRetriever.cpp +++ b/media/jni/android_media_MediaMetadataRetriever.cpp @@ -18,6 +18,7 @@ //#define LOG_NDEBUG 0 #define LOG_TAG "MediaMetadataRetrieverJNI" +#include <cmath> #include <assert.h> #include <utils/Log.h> #include <utils/threads.h> @@ -32,7 +33,7 @@ #include <nativehelper/JNIHelp.h> #include "android_runtime/AndroidRuntime.h" #include "android_media_MediaDataSource.h" -#include "android_media_Utils.h" +#include "android_media_Streams.h" #include "android_util_Binder.h" #include "android/graphics/GraphicsJNI.h" diff --git a/media/jni/android_media_MediaMuxer.cpp b/media/jni/android_media_MediaMuxer.cpp index f11452a9d80d..f0aa4c3f1ab6 100644 --- a/media/jni/android_media_MediaMuxer.cpp +++ b/media/jni/android_media_MediaMuxer.cpp @@ -18,7 +18,7 @@ #define LOG_TAG "MediaMuxer-JNI" #include <utils/Log.h> -#include "android_media_Utils.h" +#include "android_media_Streams.h" #include "android_runtime/AndroidRuntime.h" #include "jni.h" #include <nativehelper/JNIHelp.h> diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp index 35b10817c05c..d24edc7552ae 100644 --- a/media/jni/android_media_MediaPlayer.cpp +++ b/media/jni/android_media_MediaPlayer.cpp @@ -44,7 +44,7 @@ #include "android_media_PlaybackParams.h" #include "android_media_SyncParams.h" #include "android_media_VolumeShaper.h" -#include "android_media_Utils.h" +#include "android_media_Streams.h" #include "android_os_Parcel.h" #include "android_util_Binder.h" diff --git a/media/jni/android_media_Streams.cpp b/media/jni/android_media_Streams.cpp new file mode 100644 index 000000000000..b7cbd97409a2 --- /dev/null +++ b/media/jni/android_media_Streams.cpp @@ -0,0 +1,559 @@ +/* + * Copyright 2019, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// #define LOG_NDEBUG 0 +#define LOG_TAG "AndroidMediaStreams" + +#include <utils/Log.h> +#include "android_media_Streams.h" + +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/ABuffer.h> +#include <media/stagefright/foundation/AMessage.h> + +#include <nativehelper/ScopedLocalRef.h> + +namespace android { + +AssetStream::AssetStream(SkStream* stream) + : mStream(stream), mPosition(0) { +} + +AssetStream::~AssetStream() { +} + +piex::Error AssetStream::GetData( + const size_t offset, const size_t length, std::uint8_t* data) { + // Seek first. + if (mPosition != offset) { + if (!mStream->seek(offset)) { + return piex::Error::kFail; + } + } + + // Read bytes. + size_t size = mStream->read((void*)data, length); + mPosition = offset + size; + + return size == length ? piex::Error::kOk : piex::Error::kFail; +} + +BufferedStream::BufferedStream(SkStream* stream) + : mStream(stream) { +} + +BufferedStream::~BufferedStream() { +} + +piex::Error BufferedStream::GetData( + const size_t offset, const size_t length, std::uint8_t* data) { + // Seek first. + if (offset + length > mStreamBuffer.bytesWritten()) { + size_t sizeToRead = offset + length - mStreamBuffer.bytesWritten(); + if (sizeToRead <= kMinSizeToRead) { + sizeToRead = kMinSizeToRead; + } + + void* tempBuffer = malloc(sizeToRead); + if (tempBuffer == NULL) { + return piex::Error::kFail; + } + + size_t bytesRead = mStream->read(tempBuffer, sizeToRead); + if (bytesRead != sizeToRead) { + free(tempBuffer); + return piex::Error::kFail; + } + mStreamBuffer.write(tempBuffer, bytesRead); + free(tempBuffer); + } + + // Read bytes. + if (mStreamBuffer.read((void*)data, offset, length)) { + return piex::Error::kOk; + } else { + return piex::Error::kFail; + } +} + +FileStream::FileStream(const int fd) + : mPosition(0) { + mFile = fdopen(fd, "r"); + if (mFile == NULL) { + return; + } +} + +FileStream::FileStream(const String8 filename) + : mPosition(0) { + mFile = fopen(filename.string(), "r"); + if (mFile == NULL) { + return; + } +} + +FileStream::~FileStream() { + if (mFile != NULL) { + fclose(mFile); + mFile = NULL; + } +} + +piex::Error FileStream::GetData( + const size_t offset, const size_t length, std::uint8_t* data) { + if (mFile == NULL) { + return piex::Error::kFail; + } + + // Seek first. + if (mPosition != offset) { + fseek(mFile, offset, SEEK_SET); + } + + // Read bytes. + size_t size = fread((void*)data, sizeof(std::uint8_t), length, mFile); + mPosition += size; + + // Handle errors and verify the size. + if (ferror(mFile) || size != length) { + ALOGV("GetData read failed: (offset: %zu, length: %zu)", offset, length); + return piex::Error::kFail; + } + return piex::Error::kOk; +} + +bool FileStream::exists() const { + return mFile != NULL; +} + +bool GetExifFromRawImage( + piex::StreamInterface* stream, const String8& filename, + piex::PreviewImageData& image_data) { + // Reset the PreviewImageData to its default. + image_data = piex::PreviewImageData(); + + if (!piex::IsRaw(stream)) { + // Format not supported. + ALOGV("Format not supported: %s", filename.string()); + return false; + } + + piex::Error err = piex::GetPreviewImageData(stream, &image_data); + + if (err != piex::Error::kOk) { + // The input data seems to be broken. + ALOGV("Raw image not detected: %s (piex error code: %d)", filename.string(), (int32_t)err); + return false; + } + + return true; +} + +bool ConvertKeyValueArraysToKeyedVector( + JNIEnv *env, jobjectArray keys, jobjectArray values, + KeyedVector<String8, String8>* keyedVector) { + + int nKeyValuePairs = 0; + bool failed = false; + if (keys != NULL && values != NULL) { + nKeyValuePairs = env->GetArrayLength(keys); + failed = (nKeyValuePairs != env->GetArrayLength(values)); + } + + if (!failed) { + failed = ((keys != NULL && values == NULL) || + (keys == NULL && values != NULL)); + } + + if (failed) { + ALOGE("keys and values arrays have different length"); + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return false; + } + + for (int i = 0; i < nKeyValuePairs; ++i) { + // No need to check on the ArrayIndexOutOfBoundsException, since + // it won't happen here. + jstring key = (jstring) env->GetObjectArrayElement(keys, i); + jstring value = (jstring) env->GetObjectArrayElement(values, i); + + const char* keyStr = env->GetStringUTFChars(key, NULL); + if (!keyStr) { // OutOfMemoryError + return false; + } + + const char* valueStr = env->GetStringUTFChars(value, NULL); + if (!valueStr) { // OutOfMemoryError + env->ReleaseStringUTFChars(key, keyStr); + return false; + } + + keyedVector->add(String8(keyStr), String8(valueStr)); + + env->ReleaseStringUTFChars(key, keyStr); + env->ReleaseStringUTFChars(value, valueStr); + env->DeleteLocalRef(key); + env->DeleteLocalRef(value); + } + return true; +} + +static jobject makeIntegerObject(JNIEnv *env, int32_t value) { + ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Integer")); + CHECK(clazz.get() != NULL); + + jmethodID integerConstructID = + env->GetMethodID(clazz.get(), "<init>", "(I)V"); + CHECK(integerConstructID != NULL); + + return env->NewObject(clazz.get(), integerConstructID, value); +} + +static jobject makeLongObject(JNIEnv *env, int64_t value) { + ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Long")); + CHECK(clazz.get() != NULL); + + jmethodID longConstructID = env->GetMethodID(clazz.get(), "<init>", "(J)V"); + CHECK(longConstructID != NULL); + + return env->NewObject(clazz.get(), longConstructID, value); +} + +static jobject makeFloatObject(JNIEnv *env, float value) { + ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Float")); + CHECK(clazz.get() != NULL); + + jmethodID floatConstructID = + env->GetMethodID(clazz.get(), "<init>", "(F)V"); + CHECK(floatConstructID != NULL); + + return env->NewObject(clazz.get(), floatConstructID, value); +} + +static jobject makeByteBufferObject( + JNIEnv *env, const void *data, size_t size) { + jbyteArray byteArrayObj = env->NewByteArray(size); + env->SetByteArrayRegion(byteArrayObj, 0, size, (const jbyte *)data); + + ScopedLocalRef<jclass> clazz(env, env->FindClass("java/nio/ByteBuffer")); + CHECK(clazz.get() != NULL); + + jmethodID byteBufWrapID = + env->GetStaticMethodID( + clazz.get(), "wrap", "([B)Ljava/nio/ByteBuffer;"); + CHECK(byteBufWrapID != NULL); + + jobject byteBufObj = env->CallStaticObjectMethod( + clazz.get(), byteBufWrapID, byteArrayObj); + + env->DeleteLocalRef(byteArrayObj); byteArrayObj = NULL; + + return byteBufObj; +} + +static void SetMapInt32( + JNIEnv *env, jobject hashMapObj, jmethodID hashMapPutID, + const char *key, int32_t value) { + jstring keyObj = env->NewStringUTF(key); + jobject valueObj = makeIntegerObject(env, value); + + env->CallObjectMethod(hashMapObj, hashMapPutID, keyObj, valueObj); + + env->DeleteLocalRef(valueObj); valueObj = NULL; + env->DeleteLocalRef(keyObj); keyObj = NULL; +} + +status_t ConvertMessageToMap( + JNIEnv *env, const sp<AMessage> &msg, jobject *map) { + ScopedLocalRef<jclass> hashMapClazz( + env, env->FindClass("java/util/HashMap")); + + if (hashMapClazz.get() == NULL) { + return -EINVAL; + } + + jmethodID hashMapConstructID = + env->GetMethodID(hashMapClazz.get(), "<init>", "()V"); + + if (hashMapConstructID == NULL) { + return -EINVAL; + } + + jmethodID hashMapPutID = + env->GetMethodID( + hashMapClazz.get(), + "put", + "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); + + if (hashMapPutID == NULL) { + return -EINVAL; + } + + jobject hashMap = env->NewObject(hashMapClazz.get(), hashMapConstructID); + + for (size_t i = 0; i < msg->countEntries(); ++i) { + AMessage::Type valueType; + const char *key = msg->getEntryNameAt(i, &valueType); + + if (!strncmp(key, "android._", 9)) { + // don't expose private keys (starting with android._) + continue; + } + + jobject valueObj = NULL; + + switch (valueType) { + case AMessage::kTypeInt32: + { + int32_t val; + CHECK(msg->findInt32(key, &val)); + + valueObj = makeIntegerObject(env, val); + break; + } + + case AMessage::kTypeInt64: + { + int64_t val; + CHECK(msg->findInt64(key, &val)); + + valueObj = makeLongObject(env, val); + break; + } + + case AMessage::kTypeFloat: + { + float val; + CHECK(msg->findFloat(key, &val)); + + valueObj = makeFloatObject(env, val); + break; + } + + case AMessage::kTypeString: + { + AString val; + CHECK(msg->findString(key, &val)); + + valueObj = env->NewStringUTF(val.c_str()); + break; + } + + case AMessage::kTypeBuffer: + { + sp<ABuffer> buffer; + CHECK(msg->findBuffer(key, &buffer)); + + valueObj = makeByteBufferObject( + env, buffer->data(), buffer->size()); + break; + } + + case AMessage::kTypeRect: + { + int32_t left, top, right, bottom; + CHECK(msg->findRect(key, &left, &top, &right, &bottom)); + + SetMapInt32( + env, + hashMap, + hashMapPutID, + AStringPrintf("%s-left", key).c_str(), + left); + + SetMapInt32( + env, + hashMap, + hashMapPutID, + AStringPrintf("%s-top", key).c_str(), + top); + + SetMapInt32( + env, + hashMap, + hashMapPutID, + AStringPrintf("%s-right", key).c_str(), + right); + + SetMapInt32( + env, + hashMap, + hashMapPutID, + AStringPrintf("%s-bottom", key).c_str(), + bottom); + break; + } + + default: + break; + } + + if (valueObj != NULL) { + jstring keyObj = env->NewStringUTF(key); + + env->CallObjectMethod(hashMap, hashMapPutID, keyObj, valueObj); + + env->DeleteLocalRef(keyObj); keyObj = NULL; + env->DeleteLocalRef(valueObj); valueObj = NULL; + } + } + + *map = hashMap; + + return OK; +} + +status_t ConvertKeyValueArraysToMessage( + JNIEnv *env, jobjectArray keys, jobjectArray values, + sp<AMessage> *out) { + ScopedLocalRef<jclass> stringClass(env, env->FindClass("java/lang/String")); + CHECK(stringClass.get() != NULL); + ScopedLocalRef<jclass> integerClass(env, env->FindClass("java/lang/Integer")); + CHECK(integerClass.get() != NULL); + ScopedLocalRef<jclass> longClass(env, env->FindClass("java/lang/Long")); + CHECK(longClass.get() != NULL); + ScopedLocalRef<jclass> floatClass(env, env->FindClass("java/lang/Float")); + CHECK(floatClass.get() != NULL); + ScopedLocalRef<jclass> byteBufClass(env, env->FindClass("java/nio/ByteBuffer")); + CHECK(byteBufClass.get() != NULL); + + sp<AMessage> msg = new AMessage; + + jsize numEntries = 0; + + if (keys != NULL) { + if (values == NULL) { + return -EINVAL; + } + + numEntries = env->GetArrayLength(keys); + + if (numEntries != env->GetArrayLength(values)) { + return -EINVAL; + } + } else if (values != NULL) { + return -EINVAL; + } + + for (jsize i = 0; i < numEntries; ++i) { + jobject keyObj = env->GetObjectArrayElement(keys, i); + + if (!env->IsInstanceOf(keyObj, stringClass.get())) { + return -EINVAL; + } + + const char *tmp = env->GetStringUTFChars((jstring)keyObj, NULL); + + if (tmp == NULL) { + return -ENOMEM; + } + + AString key = tmp; + + env->ReleaseStringUTFChars((jstring)keyObj, tmp); + tmp = NULL; + + if (key.startsWith("android._")) { + // don't propagate private keys (starting with android._) + continue; + } + + jobject valueObj = env->GetObjectArrayElement(values, i); + + if (env->IsInstanceOf(valueObj, stringClass.get())) { + const char *value = env->GetStringUTFChars((jstring)valueObj, NULL); + + if (value == NULL) { + return -ENOMEM; + } + + msg->setString(key.c_str(), value); + + env->ReleaseStringUTFChars((jstring)valueObj, value); + value = NULL; + } else if (env->IsInstanceOf(valueObj, integerClass.get())) { + jmethodID intValueID = + env->GetMethodID(integerClass.get(), "intValue", "()I"); + CHECK(intValueID != NULL); + + jint value = env->CallIntMethod(valueObj, intValueID); + + msg->setInt32(key.c_str(), value); + } else if (env->IsInstanceOf(valueObj, longClass.get())) { + jmethodID longValueID = + env->GetMethodID(longClass.get(), "longValue", "()J"); + CHECK(longValueID != NULL); + + jlong value = env->CallLongMethod(valueObj, longValueID); + + msg->setInt64(key.c_str(), value); + } else if (env->IsInstanceOf(valueObj, floatClass.get())) { + jmethodID floatValueID = + env->GetMethodID(floatClass.get(), "floatValue", "()F"); + CHECK(floatValueID != NULL); + + jfloat value = env->CallFloatMethod(valueObj, floatValueID); + + msg->setFloat(key.c_str(), value); + } else if (env->IsInstanceOf(valueObj, byteBufClass.get())) { + jmethodID positionID = + env->GetMethodID(byteBufClass.get(), "position", "()I"); + CHECK(positionID != NULL); + + jmethodID limitID = + env->GetMethodID(byteBufClass.get(), "limit", "()I"); + CHECK(limitID != NULL); + + jint position = env->CallIntMethod(valueObj, positionID); + jint limit = env->CallIntMethod(valueObj, limitID); + + sp<ABuffer> buffer = new ABuffer(limit - position); + + void *data = env->GetDirectBufferAddress(valueObj); + + if (data != NULL) { + memcpy(buffer->data(), + (const uint8_t *)data + position, + buffer->size()); + } else { + jmethodID arrayID = + env->GetMethodID(byteBufClass.get(), "array", "()[B"); + CHECK(arrayID != NULL); + + jbyteArray byteArray = + (jbyteArray)env->CallObjectMethod(valueObj, arrayID); + CHECK(byteArray != NULL); + + env->GetByteArrayRegion( + byteArray, + position, + buffer->size(), + (jbyte *)buffer->data()); + + env->DeleteLocalRef(byteArray); byteArray = NULL; + } + + msg->setBuffer(key.c_str(), buffer); + } + } + + *out = msg; + + return OK; +} + +} // namespace android + diff --git a/media/jni/android_media_Streams.h b/media/jni/android_media_Streams.h new file mode 100644 index 000000000000..d174f9a6650c --- /dev/null +++ b/media/jni/android_media_Streams.h @@ -0,0 +1,116 @@ +/* + * Copyright 2019, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _ANDROID_MEDIA_STREAMS_H_ +#define _ANDROID_MEDIA_STREAMS_H_ + +#include "src/piex_types.h" +#include "src/piex.h" + +#include <jni.h> +#include <nativehelper/JNIHelp.h> +#include <utils/KeyedVector.h> +#include <utils/String8.h> +#include <utils/StrongPointer.h> +#include <SkStream.h> + + +namespace android { + +class AssetStream : public piex::StreamInterface { +private: + SkStream *mStream; + size_t mPosition; + +public: + explicit AssetStream(SkStream* stream); + ~AssetStream(); + + // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer + // provided by the caller, guaranteed to be at least "length" bytes long. + // On 'kOk' the 'data' pointer contains 'length' valid bytes beginning at + // 'offset' bytes from the start of the stream. + // Returns 'kFail' if 'offset' + 'length' exceeds the stream and does not + // change the contents of 'data'. + piex::Error GetData( + const size_t offset, const size_t length, std::uint8_t* data) override; +}; + +class BufferedStream : public piex::StreamInterface { +private: + SkStream *mStream; + // Growable memory stream + SkDynamicMemoryWStream mStreamBuffer; + + // Minimum size to read on filling the buffer. + const size_t kMinSizeToRead = 8192; + +public: + explicit BufferedStream(SkStream* stream); + ~BufferedStream(); + + // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer + // provided by the caller, guaranteed to be at least "length" bytes long. + // On 'kOk' the 'data' pointer contains 'length' valid bytes beginning at + // 'offset' bytes from the start of the stream. + // Returns 'kFail' if 'offset' + 'length' exceeds the stream and does not + // change the contents of 'data'. + piex::Error GetData( + const size_t offset, const size_t length, std::uint8_t* data) override; +}; + +class FileStream : public piex::StreamInterface { +private: + FILE *mFile; + size_t mPosition; + +public: + explicit FileStream(const int fd); + explicit FileStream(const String8 filename); + ~FileStream(); + + // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer + // provided by the caller, guaranteed to be at least "length" bytes long. + // On 'kOk' the 'data' pointer contains 'length' valid bytes beginning at + // 'offset' bytes from the start of the stream. + // Returns 'kFail' if 'offset' + 'length' exceeds the stream and does not + // change the contents of 'data'. + piex::Error GetData( + const size_t offset, const size_t length, std::uint8_t* data) override; + bool exists() const; +}; + +// Reads EXIF metadata from a given raw image via piex. +// And returns true if the operation is successful; otherwise, false. +bool GetExifFromRawImage( + piex::StreamInterface* stream, const String8& filename, piex::PreviewImageData& image_data); + +// Returns true if the conversion is successful; otherwise, false. +bool ConvertKeyValueArraysToKeyedVector( + JNIEnv *env, jobjectArray keys, jobjectArray values, + KeyedVector<String8, String8>* vector); + +struct AMessage; +status_t ConvertMessageToMap( + JNIEnv *env, const sp<AMessage> &msg, jobject *map); + +status_t ConvertKeyValueArraysToMessage( + JNIEnv *env, jobjectArray keys, jobjectArray values, + sp<AMessage> *msg); + +}; // namespace android + +#endif // _ANDROID_MEDIA_STREAMS_H_ diff --git a/media/jni/android_media_Utils.cpp b/media/jni/android_media_Utils.cpp index 01baadb2f024..2ef7b9e22b84 100644 --- a/media/jni/android_media_Utils.cpp +++ b/media/jni/android_media_Utils.cpp @@ -21,12 +21,6 @@ #include <utils/Log.h> #include "android_media_Utils.h" -#include <media/stagefright/foundation/ADebug.h> -#include <media/stagefright/foundation/ABuffer.h> -#include <media/stagefright/foundation/AMessage.h> - -#include <nativehelper/ScopedLocalRef.h> - #define ALIGN(x, mask) ( ((x) + (mask) - 1) & ~((mask) - 1) ) // Must be in sync with the value in HeicCompositeStream.cpp @@ -34,533 +28,6 @@ namespace android { -AssetStream::AssetStream(SkStream* stream) - : mStream(stream), mPosition(0) { -} - -AssetStream::~AssetStream() { -} - -piex::Error AssetStream::GetData( - const size_t offset, const size_t length, std::uint8_t* data) { - // Seek first. - if (mPosition != offset) { - if (!mStream->seek(offset)) { - return piex::Error::kFail; - } - } - - // Read bytes. - size_t size = mStream->read((void*)data, length); - mPosition = offset + size; - - return size == length ? piex::Error::kOk : piex::Error::kFail; -} - -BufferedStream::BufferedStream(SkStream* stream) - : mStream(stream) { -} - -BufferedStream::~BufferedStream() { -} - -piex::Error BufferedStream::GetData( - const size_t offset, const size_t length, std::uint8_t* data) { - // Seek first. - if (offset + length > mStreamBuffer.bytesWritten()) { - size_t sizeToRead = offset + length - mStreamBuffer.bytesWritten(); - if (sizeToRead <= kMinSizeToRead) { - sizeToRead = kMinSizeToRead; - } - - void* tempBuffer = malloc(sizeToRead); - if (tempBuffer == NULL) { - return piex::Error::kFail; - } - - size_t bytesRead = mStream->read(tempBuffer, sizeToRead); - if (bytesRead != sizeToRead) { - free(tempBuffer); - return piex::Error::kFail; - } - mStreamBuffer.write(tempBuffer, bytesRead); - free(tempBuffer); - } - - // Read bytes. - if (mStreamBuffer.read((void*)data, offset, length)) { - return piex::Error::kOk; - } else { - return piex::Error::kFail; - } -} - -FileStream::FileStream(const int fd) - : mPosition(0) { - mFile = fdopen(fd, "r"); - if (mFile == NULL) { - return; - } -} - -FileStream::FileStream(const String8 filename) - : mPosition(0) { - mFile = fopen(filename.string(), "r"); - if (mFile == NULL) { - return; - } -} - -FileStream::~FileStream() { - if (mFile != NULL) { - fclose(mFile); - mFile = NULL; - } -} - -piex::Error FileStream::GetData( - const size_t offset, const size_t length, std::uint8_t* data) { - if (mFile == NULL) { - return piex::Error::kFail; - } - - // Seek first. - if (mPosition != offset) { - fseek(mFile, offset, SEEK_SET); - } - - // Read bytes. - size_t size = fread((void*)data, sizeof(std::uint8_t), length, mFile); - mPosition += size; - - // Handle errors and verify the size. - if (ferror(mFile) || size != length) { - ALOGV("GetData read failed: (offset: %zu, length: %zu)", offset, length); - return piex::Error::kFail; - } - return piex::Error::kOk; -} - -bool FileStream::exists() const { - return mFile != NULL; -} - -bool GetExifFromRawImage( - piex::StreamInterface* stream, const String8& filename, - piex::PreviewImageData& image_data) { - // Reset the PreviewImageData to its default. - image_data = piex::PreviewImageData(); - - if (!piex::IsRaw(stream)) { - // Format not supported. - ALOGV("Format not supported: %s", filename.string()); - return false; - } - - piex::Error err = piex::GetPreviewImageData(stream, &image_data); - - if (err != piex::Error::kOk) { - // The input data seems to be broken. - ALOGV("Raw image not detected: %s (piex error code: %d)", filename.string(), (int32_t)err); - return false; - } - - return true; -} - -bool ConvertKeyValueArraysToKeyedVector( - JNIEnv *env, jobjectArray keys, jobjectArray values, - KeyedVector<String8, String8>* keyedVector) { - - int nKeyValuePairs = 0; - bool failed = false; - if (keys != NULL && values != NULL) { - nKeyValuePairs = env->GetArrayLength(keys); - failed = (nKeyValuePairs != env->GetArrayLength(values)); - } - - if (!failed) { - failed = ((keys != NULL && values == NULL) || - (keys == NULL && values != NULL)); - } - - if (failed) { - ALOGE("keys and values arrays have different length"); - jniThrowException(env, "java/lang/IllegalArgumentException", NULL); - return false; - } - - for (int i = 0; i < nKeyValuePairs; ++i) { - // No need to check on the ArrayIndexOutOfBoundsException, since - // it won't happen here. - jstring key = (jstring) env->GetObjectArrayElement(keys, i); - jstring value = (jstring) env->GetObjectArrayElement(values, i); - - const char* keyStr = env->GetStringUTFChars(key, NULL); - if (!keyStr) { // OutOfMemoryError - return false; - } - - const char* valueStr = env->GetStringUTFChars(value, NULL); - if (!valueStr) { // OutOfMemoryError - env->ReleaseStringUTFChars(key, keyStr); - return false; - } - - keyedVector->add(String8(keyStr), String8(valueStr)); - - env->ReleaseStringUTFChars(key, keyStr); - env->ReleaseStringUTFChars(value, valueStr); - env->DeleteLocalRef(key); - env->DeleteLocalRef(value); - } - return true; -} - -static jobject makeIntegerObject(JNIEnv *env, int32_t value) { - ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Integer")); - CHECK(clazz.get() != NULL); - - jmethodID integerConstructID = - env->GetMethodID(clazz.get(), "<init>", "(I)V"); - CHECK(integerConstructID != NULL); - - return env->NewObject(clazz.get(), integerConstructID, value); -} - -static jobject makeLongObject(JNIEnv *env, int64_t value) { - ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Long")); - CHECK(clazz.get() != NULL); - - jmethodID longConstructID = env->GetMethodID(clazz.get(), "<init>", "(J)V"); - CHECK(longConstructID != NULL); - - return env->NewObject(clazz.get(), longConstructID, value); -} - -static jobject makeFloatObject(JNIEnv *env, float value) { - ScopedLocalRef<jclass> clazz(env, env->FindClass("java/lang/Float")); - CHECK(clazz.get() != NULL); - - jmethodID floatConstructID = - env->GetMethodID(clazz.get(), "<init>", "(F)V"); - CHECK(floatConstructID != NULL); - - return env->NewObject(clazz.get(), floatConstructID, value); -} - -static jobject makeByteBufferObject( - JNIEnv *env, const void *data, size_t size) { - jbyteArray byteArrayObj = env->NewByteArray(size); - env->SetByteArrayRegion(byteArrayObj, 0, size, (const jbyte *)data); - - ScopedLocalRef<jclass> clazz(env, env->FindClass("java/nio/ByteBuffer")); - CHECK(clazz.get() != NULL); - - jmethodID byteBufWrapID = - env->GetStaticMethodID( - clazz.get(), "wrap", "([B)Ljava/nio/ByteBuffer;"); - CHECK(byteBufWrapID != NULL); - - jobject byteBufObj = env->CallStaticObjectMethod( - clazz.get(), byteBufWrapID, byteArrayObj); - - env->DeleteLocalRef(byteArrayObj); byteArrayObj = NULL; - - return byteBufObj; -} - -static void SetMapInt32( - JNIEnv *env, jobject hashMapObj, jmethodID hashMapPutID, - const char *key, int32_t value) { - jstring keyObj = env->NewStringUTF(key); - jobject valueObj = makeIntegerObject(env, value); - - env->CallObjectMethod(hashMapObj, hashMapPutID, keyObj, valueObj); - - env->DeleteLocalRef(valueObj); valueObj = NULL; - env->DeleteLocalRef(keyObj); keyObj = NULL; -} - -status_t ConvertMessageToMap( - JNIEnv *env, const sp<AMessage> &msg, jobject *map) { - ScopedLocalRef<jclass> hashMapClazz( - env, env->FindClass("java/util/HashMap")); - - if (hashMapClazz.get() == NULL) { - return -EINVAL; - } - - jmethodID hashMapConstructID = - env->GetMethodID(hashMapClazz.get(), "<init>", "()V"); - - if (hashMapConstructID == NULL) { - return -EINVAL; - } - - jmethodID hashMapPutID = - env->GetMethodID( - hashMapClazz.get(), - "put", - "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); - - if (hashMapPutID == NULL) { - return -EINVAL; - } - - jobject hashMap = env->NewObject(hashMapClazz.get(), hashMapConstructID); - - for (size_t i = 0; i < msg->countEntries(); ++i) { - AMessage::Type valueType; - const char *key = msg->getEntryNameAt(i, &valueType); - - if (!strncmp(key, "android._", 9)) { - // don't expose private keys (starting with android._) - continue; - } - - jobject valueObj = NULL; - - switch (valueType) { - case AMessage::kTypeInt32: - { - int32_t val; - CHECK(msg->findInt32(key, &val)); - - valueObj = makeIntegerObject(env, val); - break; - } - - case AMessage::kTypeInt64: - { - int64_t val; - CHECK(msg->findInt64(key, &val)); - - valueObj = makeLongObject(env, val); - break; - } - - case AMessage::kTypeFloat: - { - float val; - CHECK(msg->findFloat(key, &val)); - - valueObj = makeFloatObject(env, val); - break; - } - - case AMessage::kTypeString: - { - AString val; - CHECK(msg->findString(key, &val)); - - valueObj = env->NewStringUTF(val.c_str()); - break; - } - - case AMessage::kTypeBuffer: - { - sp<ABuffer> buffer; - CHECK(msg->findBuffer(key, &buffer)); - - valueObj = makeByteBufferObject( - env, buffer->data(), buffer->size()); - break; - } - - case AMessage::kTypeRect: - { - int32_t left, top, right, bottom; - CHECK(msg->findRect(key, &left, &top, &right, &bottom)); - - SetMapInt32( - env, - hashMap, - hashMapPutID, - AStringPrintf("%s-left", key).c_str(), - left); - - SetMapInt32( - env, - hashMap, - hashMapPutID, - AStringPrintf("%s-top", key).c_str(), - top); - - SetMapInt32( - env, - hashMap, - hashMapPutID, - AStringPrintf("%s-right", key).c_str(), - right); - - SetMapInt32( - env, - hashMap, - hashMapPutID, - AStringPrintf("%s-bottom", key).c_str(), - bottom); - break; - } - - default: - break; - } - - if (valueObj != NULL) { - jstring keyObj = env->NewStringUTF(key); - - env->CallObjectMethod(hashMap, hashMapPutID, keyObj, valueObj); - - env->DeleteLocalRef(keyObj); keyObj = NULL; - env->DeleteLocalRef(valueObj); valueObj = NULL; - } - } - - *map = hashMap; - - return OK; -} - -status_t ConvertKeyValueArraysToMessage( - JNIEnv *env, jobjectArray keys, jobjectArray values, - sp<AMessage> *out) { - ScopedLocalRef<jclass> stringClass(env, env->FindClass("java/lang/String")); - CHECK(stringClass.get() != NULL); - ScopedLocalRef<jclass> integerClass(env, env->FindClass("java/lang/Integer")); - CHECK(integerClass.get() != NULL); - ScopedLocalRef<jclass> longClass(env, env->FindClass("java/lang/Long")); - CHECK(longClass.get() != NULL); - ScopedLocalRef<jclass> floatClass(env, env->FindClass("java/lang/Float")); - CHECK(floatClass.get() != NULL); - ScopedLocalRef<jclass> byteBufClass(env, env->FindClass("java/nio/ByteBuffer")); - CHECK(byteBufClass.get() != NULL); - - sp<AMessage> msg = new AMessage; - - jsize numEntries = 0; - - if (keys != NULL) { - if (values == NULL) { - return -EINVAL; - } - - numEntries = env->GetArrayLength(keys); - - if (numEntries != env->GetArrayLength(values)) { - return -EINVAL; - } - } else if (values != NULL) { - return -EINVAL; - } - - for (jsize i = 0; i < numEntries; ++i) { - jobject keyObj = env->GetObjectArrayElement(keys, i); - - if (!env->IsInstanceOf(keyObj, stringClass.get())) { - return -EINVAL; - } - - const char *tmp = env->GetStringUTFChars((jstring)keyObj, NULL); - - if (tmp == NULL) { - return -ENOMEM; - } - - AString key = tmp; - - env->ReleaseStringUTFChars((jstring)keyObj, tmp); - tmp = NULL; - - if (key.startsWith("android._")) { - // don't propagate private keys (starting with android._) - continue; - } - - jobject valueObj = env->GetObjectArrayElement(values, i); - - if (env->IsInstanceOf(valueObj, stringClass.get())) { - const char *value = env->GetStringUTFChars((jstring)valueObj, NULL); - - if (value == NULL) { - return -ENOMEM; - } - - msg->setString(key.c_str(), value); - - env->ReleaseStringUTFChars((jstring)valueObj, value); - value = NULL; - } else if (env->IsInstanceOf(valueObj, integerClass.get())) { - jmethodID intValueID = - env->GetMethodID(integerClass.get(), "intValue", "()I"); - CHECK(intValueID != NULL); - - jint value = env->CallIntMethod(valueObj, intValueID); - - msg->setInt32(key.c_str(), value); - } else if (env->IsInstanceOf(valueObj, longClass.get())) { - jmethodID longValueID = - env->GetMethodID(longClass.get(), "longValue", "()J"); - CHECK(longValueID != NULL); - - jlong value = env->CallLongMethod(valueObj, longValueID); - - msg->setInt64(key.c_str(), value); - } else if (env->IsInstanceOf(valueObj, floatClass.get())) { - jmethodID floatValueID = - env->GetMethodID(floatClass.get(), "floatValue", "()F"); - CHECK(floatValueID != NULL); - - jfloat value = env->CallFloatMethod(valueObj, floatValueID); - - msg->setFloat(key.c_str(), value); - } else if (env->IsInstanceOf(valueObj, byteBufClass.get())) { - jmethodID positionID = - env->GetMethodID(byteBufClass.get(), "position", "()I"); - CHECK(positionID != NULL); - - jmethodID limitID = - env->GetMethodID(byteBufClass.get(), "limit", "()I"); - CHECK(limitID != NULL); - - jint position = env->CallIntMethod(valueObj, positionID); - jint limit = env->CallIntMethod(valueObj, limitID); - - sp<ABuffer> buffer = new ABuffer(limit - position); - - void *data = env->GetDirectBufferAddress(valueObj); - - if (data != NULL) { - memcpy(buffer->data(), - (const uint8_t *)data + position, - buffer->size()); - } else { - jmethodID arrayID = - env->GetMethodID(byteBufClass.get(), "array", "()[B"); - CHECK(arrayID != NULL); - - jbyteArray byteArray = - (jbyteArray)env->CallObjectMethod(valueObj, arrayID); - CHECK(byteArray != NULL); - - env->GetByteArrayRegion( - byteArray, - position, - buffer->size(), - (jbyte *)buffer->data()); - - env->DeleteLocalRef(byteArray); byteArray = NULL; - } - - msg->setBuffer(key.c_str(), buffer); - } - } - - *out = msg; - - return OK; -} - // -----------Utility functions used by ImageReader/Writer JNI----------------- enum { diff --git a/media/jni/android_media_Utils.h b/media/jni/android_media_Utils.h index 19c1b88f78e8..12841c097943 100644 --- a/media/jni/android_media_Utils.h +++ b/media/jni/android_media_Utils.h @@ -17,100 +17,10 @@ #ifndef _ANDROID_MEDIA_UTILS_H_ #define _ANDROID_MEDIA_UTILS_H_ -#include "src/piex_types.h" -#include "src/piex.h" - -#include <android_runtime/AndroidRuntime.h> #include <gui/CpuConsumer.h> -#include <jni.h> -#include <nativehelper/JNIHelp.h> -#include <utils/KeyedVector.h> -#include <utils/String8.h> -#include <SkStream.h> namespace android { -class AssetStream : public piex::StreamInterface { -private: - SkStream *mStream; - size_t mPosition; - -public: - explicit AssetStream(SkStream* stream); - ~AssetStream(); - - // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer - // provided by the caller, guaranteed to be at least "length" bytes long. - // On 'kOk' the 'data' pointer contains 'length' valid bytes beginning at - // 'offset' bytes from the start of the stream. - // Returns 'kFail' if 'offset' + 'length' exceeds the stream and does not - // change the contents of 'data'. - piex::Error GetData( - const size_t offset, const size_t length, std::uint8_t* data) override; -}; - -class BufferedStream : public piex::StreamInterface { -private: - SkStream *mStream; - // Growable memory stream - SkDynamicMemoryWStream mStreamBuffer; - - // Minimum size to read on filling the buffer. - const size_t kMinSizeToRead = 8192; - -public: - explicit BufferedStream(SkStream* stream); - ~BufferedStream(); - - // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer - // provided by the caller, guaranteed to be at least "length" bytes long. - // On 'kOk' the 'data' pointer contains 'length' valid bytes beginning at - // 'offset' bytes from the start of the stream. - // Returns 'kFail' if 'offset' + 'length' exceeds the stream and does not - // change the contents of 'data'. - piex::Error GetData( - const size_t offset, const size_t length, std::uint8_t* data) override; -}; - -class FileStream : public piex::StreamInterface { -private: - FILE *mFile; - size_t mPosition; - -public: - explicit FileStream(const int fd); - explicit FileStream(const String8 filename); - ~FileStream(); - - // Reads 'length' amount of bytes from 'offset' to 'data'. The 'data' buffer - // provided by the caller, guaranteed to be at least "length" bytes long. - // On 'kOk' the 'data' pointer contains 'length' valid bytes beginning at - // 'offset' bytes from the start of the stream. - // Returns 'kFail' if 'offset' + 'length' exceeds the stream and does not - // change the contents of 'data'. - piex::Error GetData( - const size_t offset, const size_t length, std::uint8_t* data) override; - bool exists() const; -}; - -// Reads EXIF metadata from a given raw image via piex. -// And returns true if the operation is successful; otherwise, false. -bool GetExifFromRawImage( - piex::StreamInterface* stream, const String8& filename, piex::PreviewImageData& image_data); - -// Returns true if the conversion is successful; otherwise, false. -bool ConvertKeyValueArraysToKeyedVector( - JNIEnv *env, jobjectArray keys, jobjectArray values, - KeyedVector<String8, String8>* vector); - -struct AMessage; -status_t ConvertMessageToMap( - JNIEnv *env, const sp<AMessage> &msg, jobject *map); - -status_t ConvertKeyValueArraysToMessage( - JNIEnv *env, jobjectArray keys, jobjectArray values, - sp<AMessage> *msg); - // -----------Utility functions used by ImageReader/Writer JNI----------------- typedef CpuConsumer::LockedBuffer LockedImage; diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp index 06a7182e4b1d..1f89d86947a1 100644 --- a/media/jni/android_mtp_MtpDatabase.cpp +++ b/media/jni/android_mtp_MtpDatabase.cpp @@ -18,7 +18,7 @@ #include "utils/Log.h" #include "utils/String8.h" -#include "android_media_Utils.h" +#include "android_media_Streams.h" #include "mtp.h" #include "IMtpDatabase.h" #include "MtpDataPacket.h" diff --git a/native/android/choreographer.cpp b/native/android/choreographer.cpp index 2db575bf5a13..3fecd53b43e2 100644 --- a/native/android/choreographer.cpp +++ b/native/android/choreographer.cpp @@ -70,6 +70,8 @@ private: void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) override; void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override; + void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId, + int32_t configId) override; void scheduleCallbacks(); @@ -164,6 +166,13 @@ void Choreographer::dispatchHotplug(nsecs_t, PhysicalDisplayId displayId, bool c this, displayId, toString(connected)); } +void Choreographer::dispatchConfigChanged(nsecs_t, PhysicalDisplayId displayId, + int32_t configId) { + ALOGV("choreographer %p ~ received config changed event (displayId=%" + ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", configId=%s), ignoring.", + this, displayId, toString(configId)); +} + void Choreographer::handleMessage(const Message& message) { switch (message.what) { case MSG_SCHEDULE_CALLBACKS: diff --git a/packages/CaptivePortalLogin/AndroidManifest.xml b/packages/CaptivePortalLogin/AndroidManifest.xml index 0894ee576a2d..2a4323851068 100644 --- a/packages/CaptivePortalLogin/AndroidManifest.xml +++ b/packages/CaptivePortalLogin/AndroidManifest.xml @@ -17,7 +17,9 @@ */ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.captiveportallogin" > + package="com.android.captiveportallogin" + android:versionCode="10" + android:versionName="Q-initial"> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> diff --git a/packages/CarSystemUI/res/drawable/car_ic_selection_bg.xml b/packages/CarSystemUI/res/drawable/car_ic_selection_bg.xml index 781beaf96264..12993f5b8b7c 100644 --- a/packages/CarSystemUI/res/drawable/car_ic_selection_bg.xml +++ b/packages/CarSystemUI/res/drawable/car_ic_selection_bg.xml @@ -20,7 +20,7 @@ android:viewportHeight="70.0" android:viewportWidth="70.0"> <path - android:fillColor="@color/car_accent" + android:fillColor="?android:attr/colorAccent" android:fillType="evenOdd" android:pathData="M4,0L66,0A4,4 0,0 1,70 4L70,66A4,4 0,0 1,66 70L4,70A4,4 0,0 1,0 66L0,4A4,4 0,0 1,4 0z" android:strokeColor="#00000000" diff --git a/packages/CarSystemUI/res/layout/car_navigation_bar.xml b/packages/CarSystemUI/res/layout/car_navigation_bar.xml index e591ea90c112..93d2b67dcda2 100644 --- a/packages/CarSystemUI/res/layout/car_navigation_bar.xml +++ b/packages/CarSystemUI/res/layout/car_navigation_bar.xml @@ -81,9 +81,9 @@ <com.android.systemui.statusbar.car.CarFacetButton android:id="@+id/phone_nav" style="@style/NavigationBarButton" - systemui:componentNames="com.android.car.dialer/.TelecomActivity" systemui:icon="@drawable/car_ic_phone" - systemui:intent="intent:#Intent;component=com.android.car.dialer/.TelecomActivity;launchFlags=0x14000000;end" + systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;package=com.android.car.dialer;launchFlags=0x10000000;end" + systemui:packages="com.android.car.dialer" systemui:selectedIcon="@drawable/car_ic_phone_selected" systemui:useMoreIcon="false" /> diff --git a/packages/CarSystemUI/src/com/android/systemui/notifications/NotificationsUI.java b/packages/CarSystemUI/src/com/android/systemui/notifications/NotificationsUI.java index 8077431e9da2..efa4387a2f16 100644 --- a/packages/CarSystemUI/src/com/android/systemui/notifications/NotificationsUI.java +++ b/packages/CarSystemUI/src/com/android/systemui/notifications/NotificationsUI.java @@ -16,25 +16,21 @@ package com.android.systemui.notifications; +import android.app.ActivityManager; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.SuppressLint; -import android.app.ActivityManager; import android.car.Car; import android.car.CarNotConnectedException; import android.car.drivingstate.CarUxRestrictionsManager; -import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.res.Configuration; import android.graphics.PixelFormat; import android.os.IBinder; import android.os.ServiceManager; -import android.os.UserHandle; import android.util.Log; import android.view.ContextThemeWrapper; import android.view.GestureDetector; @@ -95,28 +91,12 @@ public class NotificationsUI extends SystemUI private static int sSettleOpenPercentage; private static int sSettleClosePercentage; - private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (action.equals(Intent.ACTION_USER_SWITCHED)) { - mCarNotificationListener.registerAsSystemService(mContext, - mCarUxRestrictionManagerWrapper, - mClickHandlerFactory); - inflateNotificationContent(); - } - } - }; - /** * Inits the window that hosts the notifications and establishes the connections * to the car related services. */ @Override public void start() { - IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_USER_SWITCHED); - mContext.registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, filter, null, null); sSettleOpenPercentage = mContext.getResources().getInteger( R.integer.notification_settle_open_percentage); sSettleClosePercentage = mContext.getResources().getInteger( @@ -133,11 +113,12 @@ public class NotificationsUI extends SystemUI ServiceManager.getService(Context.STATUS_BAR_SERVICE)), launchResult -> { if (launchResult == ActivityManager.START_TASK_TO_FRONT - || launchResult == ActivityManager.START_SUCCESS) { + || launchResult == ActivityManager.START_SUCCESS){ closeCarNotifications(DEFAULT_FLING_VELOCITY); } }); - + mCarNotificationListener.registerAsSystemService(mContext, mCarUxRestrictionManagerWrapper, + mClickHandlerFactory); mCar = Car.createCar(mContext, mCarConnectionListener); mCar.connect(); NotificationGestureListener gestureListener = new NotificationGestureListener(); @@ -150,6 +131,8 @@ public class NotificationsUI extends SystemUI mCarNotificationWindow .setBackgroundColor(mContext.getColor(R.color.notification_shade_background_color)); + inflateNotificationContent(); + WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY, @@ -220,7 +203,7 @@ public class NotificationsUI extends SystemUI // There's a view installed at a higher z-order such that we can intercept the ACTION_DOWN // to set the initial click state. mCarNotificationWindow.findViewById(R.id.glass_pane).setOnTouchListener((v, event) -> { - if (event.getActionMasked() == MotionEvent.ACTION_UP) { + if (event.getActionMasked() == MotionEvent.ACTION_UP ) { mNotificationListAtBottomAtTimeOfTouch = false; } if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { @@ -271,7 +254,7 @@ public class NotificationsUI extends SystemUI public boolean onTouch(View v, MotionEvent event) { // reset mNotificationListAtBottomAtTimeOfTouch here since the "glass pane" will not // get the up event - if (event.getActionMasked() == MotionEvent.ACTION_UP) { + if (event.getActionMasked() == MotionEvent.ACTION_UP ) { mNotificationListAtBottomAtTimeOfTouch = false; } boolean wasScrolledUp = mScrollUpDetector.onTouchEvent(event); @@ -355,7 +338,7 @@ public class NotificationsUI extends SystemUI public boolean onFling(MotionEvent event1, MotionEvent event2, float velocityX, float velocityY) { if (Math.abs(event1.getX() - event2.getX()) > SWIPE_MAX_OFF_PATH - || Math.abs(velocityY) < SWIPE_THRESHOLD_VELOCITY) { + || Math.abs(velocityY) < SWIPE_THRESHOLD_VELOCITY){ // swipe was not vertical or was not fast enough return false; } diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index 369bb9fdd9dd..bd0e0b846abf 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -92,6 +92,10 @@ public class CarStatusBar extends StatusBar implements @Override public void start() { + // get the provisioned state before calling the parent class since it's that flow that + // builds the nav bar + mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class); + mDeviceIsProvisioned = mDeviceProvisionedController.isDeviceProvisioned(); super.start(); mTaskStackListener = new TaskStackListenerImpl(); mActivityManagerWrapper = ActivityManagerWrapper.getInstance(); @@ -105,8 +109,6 @@ public class CarStatusBar extends StatusBar implements mHvacController.connectToCarService(); CarSystemUIFactory factory = SystemUIFactory.getInstance(); - mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class); - mDeviceIsProvisioned = mDeviceProvisionedController.isDeviceProvisioned(); if (!mDeviceIsProvisioned) { mDeviceProvisionedController.addCallback( new DeviceProvisionedController.DeviceProvisionedListener() { diff --git a/packages/ExtServices/src/android/ext/services/notification/AssistantSettings.java b/packages/ExtServices/src/android/ext/services/notification/AssistantSettings.java index 6cf72a45a06b..ba361c40f5c6 100644 --- a/packages/ExtServices/src/android/ext/services/notification/AssistantSettings.java +++ b/packages/ExtServices/src/android/ext/services/notification/AssistantSettings.java @@ -26,6 +26,7 @@ import android.text.TextUtils; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; /** * Observes the settings for {@link Assistant}. @@ -103,7 +104,7 @@ final class AssistantSettings extends ContentObserver { private void registerDeviceConfigs() { DeviceConfig.addOnPropertyChangedListener( - DeviceConfig.NotificationAssistant.NAMESPACE, + DeviceConfig.NAMESPACE_SYSTEMUI, this::postToHandler, this::onDeviceConfigPropertyChanged); @@ -117,7 +118,7 @@ final class AssistantSettings extends ContentObserver { @VisibleForTesting void onDeviceConfigPropertyChanged(String namespace, String name, String value) { - if (!DeviceConfig.NotificationAssistant.NAMESPACE.equals(namespace)) { + if (!DeviceConfig.NAMESPACE_SYSTEMUI.equals(namespace)) { Log.e(LOG_TAG, "Received update from DeviceConfig for unrelated namespace: " + namespace + " " + name + "=" + value); return; @@ -128,17 +129,17 @@ final class AssistantSettings extends ContentObserver { private void updateFromDeviceConfigFlags() { mGenerateReplies = DeviceConfigHelper.getBoolean( - DeviceConfig.NotificationAssistant.GENERATE_REPLIES, DEFAULT_GENERATE_REPLIES); + SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES, DEFAULT_GENERATE_REPLIES); mGenerateActions = DeviceConfigHelper.getBoolean( - DeviceConfig.NotificationAssistant.GENERATE_ACTIONS, DEFAULT_GENERATE_ACTIONS); + SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS, DEFAULT_GENERATE_ACTIONS); mMaxMessagesToExtract = DeviceConfigHelper.getInteger( - DeviceConfig.NotificationAssistant.MAX_MESSAGES_TO_EXTRACT, + SystemUiDeviceConfigFlags.NAS_MAX_MESSAGES_TO_EXTRACT, DEFAULT_MAX_MESSAGES_TO_EXTRACT); mMaxSuggestions = DeviceConfigHelper.getInteger( - DeviceConfig.NotificationAssistant.MAX_SUGGESTIONS, DEFAULT_MAX_SUGGESTIONS); + SystemUiDeviceConfigFlags.NAS_MAX_SUGGESTIONS, DEFAULT_MAX_SUGGESTIONS); mOnUpdateRunnable.run(); } @@ -193,8 +194,7 @@ final class AssistantSettings extends ContentObserver { private static String getValue(String key) { return DeviceConfig.getProperty( - DeviceConfig.NotificationAssistant.NAMESPACE, - key); + DeviceConfig.NAMESPACE_SYSTEMUI, key); } } @@ -202,4 +202,4 @@ final class AssistantSettings extends ContentObserver { AssistantSettings createAndRegister(Handler handler, ContentResolver resolver, int userId, Runnable onUpdateRunnable); } -}
\ No newline at end of file +} diff --git a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantSettingsTest.java b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantSettingsTest.java index d890c1ae81fb..851e2f269a95 100644 --- a/packages/ExtServices/tests/src/android/ext/services/notification/AssistantSettingsTest.java +++ b/packages/ExtServices/tests/src/android/ext/services/notification/AssistantSettingsTest.java @@ -17,7 +17,6 @@ package android.ext.services.notification; import static android.ext.services.notification.AssistantSettings.DEFAULT_MAX_SUGGESTIONS; -import static android.provider.DeviceConfig.NotificationAssistant; import static android.provider.DeviceConfig.setProperty; import static junit.framework.Assert.assertFalse; @@ -30,12 +29,15 @@ import static org.mockito.Mockito.verify; import android.content.ContentResolver; import android.os.Handler; import android.os.Looper; +import android.provider.DeviceConfig; import android.provider.Settings; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import android.support.test.uiautomator.UiDevice; import android.testing.TestableContext; +import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; + import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -49,7 +51,7 @@ import java.io.IOException; @RunWith(AndroidJUnit4.class) public class AssistantSettingsTest { private static final String CLEAR_DEVICE_CONFIG_KEY_CMD = - "device_config delete " + NotificationAssistant.NAMESPACE; + "device_config delete " + DeviceConfig.NAMESPACE_SYSTEMUI; private static final int USER_ID = 5; @@ -87,13 +89,13 @@ public class AssistantSettingsTest { @Test public void testGenerateRepliesDisabled() { setProperty( - NotificationAssistant.NAMESPACE, - NotificationAssistant.GENERATE_REPLIES, + DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES, "false", false /* makeDefault */); mAssistantSettings.onDeviceConfigPropertyChanged( - NotificationAssistant.NAMESPACE, - NotificationAssistant.GENERATE_REPLIES, + DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES, "false"); assertFalse(mAssistantSettings.mGenerateReplies); @@ -102,13 +104,13 @@ public class AssistantSettingsTest { @Test public void testGenerateRepliesEnabled() { setProperty( - NotificationAssistant.NAMESPACE, - NotificationAssistant.GENERATE_REPLIES, + DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES, "true", false /* makeDefault */); mAssistantSettings.onDeviceConfigPropertyChanged( - NotificationAssistant.NAMESPACE, - NotificationAssistant.GENERATE_REPLIES, + DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES, "true"); assertTrue(mAssistantSettings.mGenerateReplies); @@ -117,25 +119,25 @@ public class AssistantSettingsTest { @Test public void testGenerateRepliesEmptyFlag() { setProperty( - NotificationAssistant.NAMESPACE, - NotificationAssistant.GENERATE_REPLIES, + DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES, "false", false /* makeDefault */); mAssistantSettings.onDeviceConfigPropertyChanged( - NotificationAssistant.NAMESPACE, - NotificationAssistant.GENERATE_REPLIES, + DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES, "false"); assertFalse(mAssistantSettings.mGenerateReplies); setProperty( - NotificationAssistant.NAMESPACE, - NotificationAssistant.GENERATE_REPLIES, + DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES, "", false /* makeDefault */); mAssistantSettings.onDeviceConfigPropertyChanged( - NotificationAssistant.NAMESPACE, - NotificationAssistant.GENERATE_REPLIES, + DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES, ""); // Go back to the default value. @@ -145,13 +147,13 @@ public class AssistantSettingsTest { @Test public void testGenerateActionsDisabled() { setProperty( - NotificationAssistant.NAMESPACE, - NotificationAssistant.GENERATE_ACTIONS, + DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS, "false", false /* makeDefault */); mAssistantSettings.onDeviceConfigPropertyChanged( - NotificationAssistant.NAMESPACE, - NotificationAssistant.GENERATE_ACTIONS, + DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS, "false"); assertFalse(mAssistantSettings.mGenerateActions); @@ -160,13 +162,13 @@ public class AssistantSettingsTest { @Test public void testGenerateActionsEnabled() { setProperty( - NotificationAssistant.NAMESPACE, - NotificationAssistant.GENERATE_ACTIONS, + DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS, "true", false /* makeDefault */); mAssistantSettings.onDeviceConfigPropertyChanged( - NotificationAssistant.NAMESPACE, - NotificationAssistant.GENERATE_ACTIONS, + DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS, "true"); assertTrue(mAssistantSettings.mGenerateActions); @@ -175,25 +177,25 @@ public class AssistantSettingsTest { @Test public void testGenerateActionsEmptyFlag() { setProperty( - NotificationAssistant.NAMESPACE, - NotificationAssistant.GENERATE_ACTIONS, + DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS, "false", false /* makeDefault */); mAssistantSettings.onDeviceConfigPropertyChanged( - NotificationAssistant.NAMESPACE, - NotificationAssistant.GENERATE_ACTIONS, + DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS, "false"); assertFalse(mAssistantSettings.mGenerateActions); setProperty( - NotificationAssistant.NAMESPACE, - NotificationAssistant.GENERATE_ACTIONS, + DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS, "", false /* makeDefault */); mAssistantSettings.onDeviceConfigPropertyChanged( - NotificationAssistant.NAMESPACE, - NotificationAssistant.GENERATE_ACTIONS, + DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS, ""); // Go back to the default value. @@ -203,13 +205,13 @@ public class AssistantSettingsTest { @Test public void testMaxMessagesToExtract() { setProperty( - NotificationAssistant.NAMESPACE, - NotificationAssistant.MAX_MESSAGES_TO_EXTRACT, + DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.NAS_MAX_MESSAGES_TO_EXTRACT, "10", false /* makeDefault */); mAssistantSettings.onDeviceConfigPropertyChanged( - NotificationAssistant.NAMESPACE, - NotificationAssistant.MAX_MESSAGES_TO_EXTRACT, + DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.NAS_MAX_MESSAGES_TO_EXTRACT, "10"); assertEquals(10, mAssistantSettings.mMaxMessagesToExtract); @@ -218,13 +220,13 @@ public class AssistantSettingsTest { @Test public void testMaxSuggestions() { setProperty( - NotificationAssistant.NAMESPACE, - NotificationAssistant.MAX_SUGGESTIONS, + DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.NAS_MAX_SUGGESTIONS, "5", false /* makeDefault */); mAssistantSettings.onDeviceConfigPropertyChanged( - NotificationAssistant.NAMESPACE, - NotificationAssistant.MAX_SUGGESTIONS, + DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.NAS_MAX_SUGGESTIONS, "5"); assertEquals(5, mAssistantSettings.mMaxSuggestions); @@ -233,8 +235,8 @@ public class AssistantSettingsTest { @Test public void testMaxSuggestionsEmpty() { mAssistantSettings.onDeviceConfigPropertyChanged( - NotificationAssistant.NAMESPACE, - NotificationAssistant.MAX_SUGGESTIONS, + DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.NAS_MAX_SUGGESTIONS, ""); assertEquals(DEFAULT_MAX_SUGGESTIONS, mAssistantSettings.mMaxSuggestions); @@ -278,13 +280,14 @@ public class AssistantSettingsTest { private static void clearDeviceConfig() throws IOException { UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); uiDevice.executeShellCommand( - CLEAR_DEVICE_CONFIG_KEY_CMD + " " + NotificationAssistant.GENERATE_ACTIONS); + CLEAR_DEVICE_CONFIG_KEY_CMD + " " + SystemUiDeviceConfigFlags.NAS_GENERATE_ACTIONS); uiDevice.executeShellCommand( - CLEAR_DEVICE_CONFIG_KEY_CMD + " " + NotificationAssistant.GENERATE_REPLIES); + CLEAR_DEVICE_CONFIG_KEY_CMD + " " + SystemUiDeviceConfigFlags.NAS_GENERATE_REPLIES); uiDevice.executeShellCommand( - CLEAR_DEVICE_CONFIG_KEY_CMD + " " + NotificationAssistant.MAX_MESSAGES_TO_EXTRACT); + CLEAR_DEVICE_CONFIG_KEY_CMD + " " + + SystemUiDeviceConfigFlags.NAS_MAX_MESSAGES_TO_EXTRACT); uiDevice.executeShellCommand( - CLEAR_DEVICE_CONFIG_KEY_CMD + " " + NotificationAssistant.MAX_SUGGESTIONS); + CLEAR_DEVICE_CONFIG_KEY_CMD + " " + SystemUiDeviceConfigFlags.NAS_MAX_SUGGESTIONS); } } diff --git a/packages/NetworkStack/Android.bp b/packages/NetworkStack/Android.bp index b700bf324817..5f1f26d88171 100644 --- a/packages/NetworkStack/Android.bp +++ b/packages/NetworkStack/Android.bp @@ -28,6 +28,7 @@ java_library { static_libs: [ "netd_aidl_interface-java", "networkstack-aidl-interfaces-java", + "datastallprotosnano", ] } @@ -43,4 +44,4 @@ android_app { jarjar_rules: "jarjar-rules-shared.txt", manifest: "AndroidManifest.xml", required: ["NetworkStackPermissionStub"], -}
\ No newline at end of file +} diff --git a/packages/NetworkStack/AndroidManifest.xml b/packages/NetworkStack/AndroidManifest.xml index 52c209e5f247..90521de6a453 100644 --- a/packages/NetworkStack/AndroidManifest.xml +++ b/packages/NetworkStack/AndroidManifest.xml @@ -18,7 +18,9 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.networkstack" - android:sharedUserId="android.uid.networkstack"> + android:sharedUserId="android.uid.networkstack" + android:versionCode="10" + android:versionName="Q-initial"> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> diff --git a/packages/NetworkStack/src/android/net/metrics/DataStallDetectionStats.java b/packages/NetworkStack/src/android/net/metrics/DataStallDetectionStats.java new file mode 100644 index 000000000000..225dc0f4bfdc --- /dev/null +++ b/packages/NetworkStack/src/android/net/metrics/DataStallDetectionStats.java @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.metrics; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.util.NetworkStackUtils; +import android.net.wifi.WifiInfo; + +import com.android.internal.util.HexDump; +import com.android.server.connectivity.nano.CellularData; +import com.android.server.connectivity.nano.DataStallEventProto; +import com.android.server.connectivity.nano.DnsEvent; +import com.android.server.connectivity.nano.WifiData; + +import com.google.protobuf.nano.MessageNano; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +/** + * Class to record the stats of detection level information for data stall. + * + * @hide + */ +public final class DataStallDetectionStats { + private static final int UNKNOWN_SIGNAL_STRENGTH = -1; + @NonNull + final byte[] mCellularInfo; + @NonNull + final byte[] mWifiInfo; + @NonNull + final byte[] mDns; + final int mEvaluationType; + final int mNetworkType; + + public DataStallDetectionStats(@Nullable byte[] cell, @Nullable byte[] wifi, + @NonNull int[] returnCode, @NonNull long[] dnsTime, int evalType, int netType) { + mCellularInfo = emptyCellDataIfNull(cell); + mWifiInfo = emptyWifiInfoIfNull(wifi); + + DnsEvent dns = new DnsEvent(); + dns.dnsReturnCode = returnCode; + dns.dnsTime = dnsTime; + mDns = MessageNano.toByteArray(dns); + mEvaluationType = evalType; + mNetworkType = netType; + } + + private byte[] emptyCellDataIfNull(@Nullable byte[] cell) { + if (cell != null) return cell; + + CellularData data = new CellularData(); + data.ratType = DataStallEventProto.RADIO_TECHNOLOGY_UNKNOWN; + data.networkMccmnc = ""; + data.simMccmnc = ""; + data.signalStrength = UNKNOWN_SIGNAL_STRENGTH; + return MessageNano.toByteArray(data); + } + + private byte[] emptyWifiInfoIfNull(@Nullable byte[] wifi) { + if (wifi != null) return wifi; + + WifiData data = new WifiData(); + data.wifiBand = DataStallEventProto.AP_BAND_UNKNOWN; + data.signalStrength = UNKNOWN_SIGNAL_STRENGTH; + return MessageNano.toByteArray(data); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("type: ").append(mNetworkType) + .append(", evaluation type: ") + .append(mEvaluationType) + .append(", wifi info: ") + .append(HexDump.toHexString(mWifiInfo)) + .append(", cell info: ") + .append(HexDump.toHexString(mCellularInfo)) + .append(", dns: ") + .append(HexDump.toHexString(mDns)); + return sb.toString(); + } + + @Override + public boolean equals(@Nullable final Object o) { + if (!(o instanceof DataStallDetectionStats)) return false; + final DataStallDetectionStats other = (DataStallDetectionStats) o; + return (mNetworkType == other.mNetworkType) + && (mEvaluationType == other.mEvaluationType) + && Arrays.equals(mWifiInfo, other.mWifiInfo) + && Arrays.equals(mCellularInfo, other.mCellularInfo) + && Arrays.equals(mDns, other.mDns); + } + + @Override + public int hashCode() { + return Objects.hash(mNetworkType, mEvaluationType, mWifiInfo, mCellularInfo, mDns); + } + + /** + * Utility to create an instance of {@Link DataStallDetectionStats} + * + * @hide + */ + public static class Builder { + @Nullable + private byte[] mCellularInfo; + @Nullable + private byte[] mWifiInfo; + @NonNull + private final List<Integer> mDnsReturnCode = new ArrayList<Integer>(); + @NonNull + private final List<Long> mDnsTimeStamp = new ArrayList<Long>(); + private int mEvaluationType; + private int mNetworkType; + + /** + * Add a dns event into Builder. + * + * @param code the return code of the dns event. + * @param timeMs the elapsedRealtime in ms that the the dns event was received from netd. + * @return {@code this} {@link Builder} instance. + */ + public Builder addDnsEvent(int code, long timeMs) { + mDnsReturnCode.add(code); + mDnsTimeStamp.add(timeMs); + return this; + } + + /** + * Set the dns evaluation type into Builder. + * + * @param type the return code of the dns event. + * @return {@code this} {@link Builder} instance. + */ + public Builder setEvaluationType(int type) { + mEvaluationType = type; + return this; + } + + /** + * Set the network type into Builder. + * + * @param type the network type of the logged network. + * @return {@code this} {@link Builder} instance. + */ + public Builder setNetworkType(int type) { + mNetworkType = type; + return this; + } + + /** + * Set the wifi data into Builder. + * + * @param info a {@link WifiInfo} of the connected wifi network. + * @return {@code this} {@link Builder} instance. + */ + public Builder setWiFiData(@Nullable final WifiInfo info) { + WifiData data = new WifiData(); + data.wifiBand = getWifiBand(info); + data.signalStrength = (info != null) ? info.getRssi() : UNKNOWN_SIGNAL_STRENGTH; + mWifiInfo = MessageNano.toByteArray(data); + return this; + } + + private static int getWifiBand(@Nullable final WifiInfo info) { + if (info == null) return DataStallEventProto.AP_BAND_UNKNOWN; + + int freq = info.getFrequency(); + // Refer to ScanResult.is5GHz() and ScanResult.is24GHz(). + if (freq > 4900 && freq < 5900) { + return DataStallEventProto.AP_BAND_5GHZ; + } else if (freq > 2400 && freq < 2500) { + return DataStallEventProto.AP_BAND_2GHZ; + } else { + return DataStallEventProto.AP_BAND_UNKNOWN; + } + } + + /** + * Set the cellular data into Builder. + * + * @param radioType the radio technology of the logged cellular network. + * @param roaming a boolean indicates if logged cellular network is roaming or not. + * @param networkMccmnc the mccmnc of the camped network. + * @param simMccmnc the mccmnc of the sim. + * @return {@code this} {@link Builder} instance. + */ + public Builder setCellData(int radioType, boolean roaming, + @NonNull String networkMccmnc, @NonNull String simMccmnc, int ss) { + CellularData data = new CellularData(); + data.ratType = radioType; + data.isRoaming = roaming; + data.networkMccmnc = networkMccmnc; + data.simMccmnc = simMccmnc; + data.signalStrength = ss; + mCellularInfo = MessageNano.toByteArray(data); + return this; + } + + /** + * Create a new {@Link DataStallDetectionStats}. + */ + public DataStallDetectionStats build() { + return new DataStallDetectionStats(mCellularInfo, mWifiInfo, + NetworkStackUtils.convertToIntArray(mDnsReturnCode), + NetworkStackUtils.convertToLongArray(mDnsTimeStamp), + mEvaluationType, mNetworkType); + } + } +} diff --git a/packages/NetworkStack/src/android/net/metrics/DataStallStatsUtils.java b/packages/NetworkStack/src/android/net/metrics/DataStallStatsUtils.java new file mode 100644 index 000000000000..17a36ad4e6d2 --- /dev/null +++ b/packages/NetworkStack/src/android/net/metrics/DataStallStatsUtils.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.metrics; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.captiveportal.CaptivePortalProbeResult; +import android.util.Log; + +import com.android.internal.util.HexDump; +import com.android.server.connectivity.nano.DataStallEventProto; + +/** + * Collection of utilities for data stall metrics. + * + * To see if the logs are properly sent to statsd, execute following command. + * + * $ adb shell cmd stats print-logs + * $ adb logcat | grep statsd OR $ adb logcat -b stats + * + * @hide + */ +public class DataStallStatsUtils { + private static final String TAG = DataStallStatsUtils.class.getSimpleName(); + private static final boolean DBG = false; + + private static int probeResultToEnum(@Nullable final CaptivePortalProbeResult result) { + if (result == null) return DataStallEventProto.INVALID; + + // TODO: Add partial connectivity support. + if (result.isSuccessful()) { + return DataStallEventProto.VALID; + } else if (result.isPortal()) { + return DataStallEventProto.PORTAL; + } else { + return DataStallEventProto.INVALID; + } + } + + /** + * Write the metric to {@link StatsLog}. + */ + public static void write(@NonNull final DataStallDetectionStats stats, + @NonNull final CaptivePortalProbeResult result) { + int validationResult = probeResultToEnum(result); + if (DBG) { + Log.d(TAG, "write: " + stats + " with result: " + validationResult + + ", dns: " + HexDump.toHexString(stats.mDns)); + } + // TODO(b/124613085): Send to Statsd once the public StatsLog API is ready. + } +} diff --git a/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java b/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java index 98123a5c7261..481dbdadbac0 100644 --- a/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java +++ b/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java @@ -16,8 +16,11 @@ package android.net.util; +import android.annotation.NonNull; + import java.io.FileDescriptor; import java.io.IOException; +import java.util.List; /** * Collection of utilities for the network stack. @@ -40,4 +43,26 @@ public class NetworkStackUtils { } catch (IOException ignored) { } } + + /** + * Returns an int array from the given Integer list. + */ + public static int[] convertToIntArray(@NonNull List<Integer> list) { + int[] array = new int[list.size()]; + for (int i = 0; i < list.size(); i++) { + array[i] = list.get(i); + } + return array; + } + + /** + * Returns a long array from the given long list. + */ + public static long[] convertToLongArray(@NonNull List<Long> list) { + long[] array = new long[list.size()]; + for (int i = 0; i < list.size(); i++) { + array[i] = list.get(i); + } + return array; + } } diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java index 4b846b0fb372..e82a5d7b4881 100644 --- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java +++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java @@ -33,6 +33,7 @@ import static android.net.metrics.ValidationProbeEvent.PROBE_FALLBACK; import static android.net.metrics.ValidationProbeEvent.PROBE_PRIVDNS; import static android.net.util.NetworkStackUtils.isEmpty; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.PendingIntent; import android.content.BroadcastReceiver; @@ -50,6 +51,8 @@ import android.net.TrafficStats; import android.net.Uri; import android.net.captiveportal.CaptivePortalProbeResult; import android.net.captiveportal.CaptivePortalProbeSpec; +import android.net.metrics.DataStallDetectionStats; +import android.net.metrics.DataStallStatsUtils; import android.net.metrics.IpConnectivityLog; import android.net.metrics.NetworkEvent; import android.net.metrics.ValidationProbeEvent; @@ -66,8 +69,10 @@ import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; import android.telephony.AccessNetworkConstants; +import android.telephony.CellSignalStrength; import android.telephony.NetworkRegistrationState; import android.telephony.ServiceState; +import android.telephony.SignalStrength; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; @@ -126,6 +131,9 @@ public class NetworkMonitor extends StateMachine { private static final int DATA_STALL_EVALUATION_TYPE_DNS = 1; private static final int DEFAULT_DATA_STALL_EVALUATION_TYPES = (1 << DATA_STALL_EVALUATION_TYPE_DNS); + // Reevaluate it as intending to increase the number. Larger log size may cause statsd + // log buffer bust and have stats log lost. + private static final int DEFAULT_DNS_LOG_SIZE = 20; enum EvaluationResult { VALIDATED(true), @@ -244,6 +252,7 @@ public class NetworkMonitor extends StateMachine { private final ConnectivityManager mCm; private final IpConnectivityLog mMetricsLog; private final Dependencies mDependencies; + private final DataStallStatsUtils mDetectionStatsUtils; // Configuration values for captive portal detection probes. private final String mCaptivePortalUserAgent; @@ -302,17 +311,19 @@ public class NetworkMonitor extends StateMachine { private final int mDataStallEvaluationType; private final DnsStallDetector mDnsStallDetector; private long mLastProbeTime; + // Set to true if data stall is suspected and reset to false after metrics are sent to statsd. + private boolean mCollectDataStallMetrics = false; public NetworkMonitor(Context context, INetworkMonitorCallbacks cb, Network network, SharedLog validationLog) { this(context, cb, network, new IpConnectivityLog(), validationLog, - Dependencies.DEFAULT); + Dependencies.DEFAULT, new DataStallStatsUtils()); } @VisibleForTesting protected NetworkMonitor(Context context, INetworkMonitorCallbacks cb, Network network, IpConnectivityLog logger, SharedLog validationLogs, - Dependencies deps) { + Dependencies deps, DataStallStatsUtils detectionStatsUtils) { // Add suffix indicating which NetworkMonitor we're talking about. super(TAG + "/" + network.toString()); @@ -325,6 +336,7 @@ public class NetworkMonitor extends StateMachine { mValidationLogs = validationLogs; mCallback = cb; mDependencies = deps; + mDetectionStatsUtils = detectionStatsUtils; mNonPrivateDnsBypassNetwork = network; mNetwork = deps.getPrivateDnsBypassNetwork(network); mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); @@ -656,6 +668,7 @@ public class NetworkMonitor extends StateMachine { case EVENT_DNS_NOTIFICATION: mDnsStallDetector.accumulateConsecutiveDnsTimeoutCount(message.arg1); if (isDataStall()) { + mCollectDataStallMetrics = true; validationLog("Suspecting data stall, reevaluate"); transitionTo(mEvaluatingState); } @@ -667,6 +680,66 @@ public class NetworkMonitor extends StateMachine { } } + private void writeDataStallStats(@NonNull final CaptivePortalProbeResult result) { + /* + * Collect data stall detection level information for each transport type. Collect type + * specific information for cellular and wifi only currently. Generate + * DataStallDetectionStats for each transport type. E.g., if a network supports both + * TRANSPORT_WIFI and TRANSPORT_VPN, two DataStallDetectionStats will be generated. + */ + final int[] transports = mNetworkCapabilities.getTransportTypes(); + + for (int i = 0; i < transports.length; i++) { + DataStallStatsUtils.write(buildDataStallDetectionStats(transports[i]), result); + } + mCollectDataStallMetrics = false; + } + + @VisibleForTesting + protected DataStallDetectionStats buildDataStallDetectionStats(int transport) { + final DataStallDetectionStats.Builder stats = new DataStallDetectionStats.Builder(); + if (VDBG_STALL) log("collectDataStallMetrics: type=" + transport); + stats.setEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS); + stats.setNetworkType(transport); + switch (transport) { + case NetworkCapabilities.TRANSPORT_WIFI: + // TODO: Update it if status query in dual wifi is supported. + final WifiInfo wifiInfo = mWifiManager.getConnectionInfo(); + stats.setWiFiData(wifiInfo); + break; + case NetworkCapabilities.TRANSPORT_CELLULAR: + final boolean isRoaming = !mNetworkCapabilities.hasCapability( + NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING); + final SignalStrength ss = mTelephonyManager.getSignalStrength(); + // TODO(b/120452078): Support multi-sim. + stats.setCellData( + mTelephonyManager.getDataNetworkType(), + isRoaming, + mTelephonyManager.getNetworkOperator(), + mTelephonyManager.getSimOperator(), + (ss != null) + ? ss.getLevel() : CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN); + break; + default: + // No transport type specific information for the other types. + break; + } + addDnsEvents(stats); + + return stats.build(); + } + + @VisibleForTesting + protected void addDnsEvents(@NonNull final DataStallDetectionStats.Builder stats) { + final int size = mDnsStallDetector.mResultIndices.size(); + for (int i = 1; i <= DEFAULT_DNS_LOG_SIZE && i <= size; i++) { + final int index = mDnsStallDetector.mResultIndices.indexOf(size - i); + stats.addDnsEvent(mDnsStallDetector.mDnsEvents[index].mReturnCode, + mDnsStallDetector.mDnsEvents[index].mTimeStamp); + } + } + + // Being in the MaybeNotifyState State indicates the user may have been notified that sign-in // is required. This State takes care to clear the notification upon exit from the State. private class MaybeNotifyState extends State { @@ -972,6 +1045,11 @@ public class NetworkMonitor extends StateMachine { final CaptivePortalProbeResult probeResult = (CaptivePortalProbeResult) message.obj; mLastProbeTime = SystemClock.elapsedRealtime(); + + if (mCollectDataStallMetrics) { + writeDataStallStats(probeResult); + } + if (probeResult.isSuccessful()) { // Transit EvaluatingPrivateDnsState to get to Validated // state (even if no Private DNS validation required). @@ -1617,7 +1695,6 @@ public class NetworkMonitor extends StateMachine { */ @VisibleForTesting protected class DnsStallDetector { - private static final int DEFAULT_DNS_LOG_SIZE = 50; private int mConsecutiveTimeoutCount = 0; private int mSize; final DnsResult[] mDnsEvents; diff --git a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java index 9a16bb77182e..ddb7030d314a 100644 --- a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java +++ b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java @@ -39,6 +39,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.annotation.NonNull; import android.content.Context; import android.net.ConnectivityManager; import android.net.INetworkMonitorCallbacks; @@ -48,8 +49,11 @@ import android.net.Network; import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.captiveportal.CaptivePortalProbeResult; +import android.net.metrics.DataStallDetectionStats; +import android.net.metrics.DataStallStatsUtils; import android.net.metrics.IpConnectivityLog; import android.net.util.SharedLog; +import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.Bundle; import android.os.ConditionVariable; @@ -58,6 +62,7 @@ import android.os.SystemClock; import android.provider.Settings; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; +import android.telephony.CellSignalStrength; import android.telephony.TelephonyManager; import android.util.ArrayMap; @@ -98,6 +103,8 @@ public class NetworkMonitorTest { private @Mock NetworkMonitor.Dependencies mDependencies; private @Mock INetworkMonitorCallbacks mCallbacks; private @Spy Network mNetwork = new Network(TEST_NETID); + private @Mock DataStallStatsUtils mDataStallStatsUtils; + private @Mock WifiInfo mWifiInfo; private static final int TEST_NETID = 4242; @@ -105,10 +112,12 @@ public class NetworkMonitorTest { private static final String TEST_HTTPS_URL = "https://www.google.com/gen_204"; private static final String TEST_FALLBACK_URL = "http://fallback.google.com/gen_204"; private static final String TEST_OTHER_FALLBACK_URL = "http://otherfallback.google.com/gen_204"; + private static final String TEST_MCCMNC = "123456"; private static final int DATA_STALL_EVALUATION_TYPE_DNS = 1; private static final int RETURN_CODE_DNS_SUCCESS = 0; private static final int RETURN_CODE_DNS_TIMEOUT = 255; + private static final int DEFAULT_DNS_TIMEOUT_THRESHOLD = 5; private static final int HANDLER_TIMEOUT_MS = 1000; @@ -186,9 +195,9 @@ public class NetworkMonitorTest { private long mProbeTime = 0; WrappedNetworkMonitor(Context context, Network network, IpConnectivityLog logger, - Dependencies deps) { + Dependencies deps, DataStallStatsUtils statsUtils) { super(context, mCallbacks, network, logger, - new SharedLog("test_nm"), deps); + new SharedLog("test_nm"), deps, statsUtils); } @Override @@ -199,11 +208,16 @@ public class NetworkMonitorTest { protected void setLastProbeTime(long time) { mProbeTime = time; } + + @Override + protected void addDnsEvents(@NonNull final DataStallDetectionStats.Builder stats) { + generateTimeoutDnsEvent(stats, DEFAULT_DNS_TIMEOUT_THRESHOLD); + } } private WrappedNetworkMonitor makeMeteredWrappedNetworkMonitor() { final WrappedNetworkMonitor nm = new WrappedNetworkMonitor( - mContext, mNetwork, mLogger, mDependencies); + mContext, mNetwork, mLogger, mDependencies, mDataStallStatsUtils); when(mCm.getNetworkCapabilities(any())).thenReturn(METERED_CAPABILITIES); nm.start(); waitForIdle(nm.getHandler()); @@ -212,7 +226,7 @@ public class NetworkMonitorTest { private WrappedNetworkMonitor makeNotMeteredWrappedNetworkMonitor() { final WrappedNetworkMonitor nm = new WrappedNetworkMonitor( - mContext, mNetwork, mLogger, mDependencies); + mContext, mNetwork, mLogger, mDependencies, mDataStallStatsUtils); when(mCm.getNetworkCapabilities(any())).thenReturn(NOT_METERED_CAPABILITIES); nm.start(); waitForIdle(nm.getHandler()); @@ -222,7 +236,7 @@ public class NetworkMonitorTest { private NetworkMonitor makeMonitor() { final NetworkMonitor nm = new NetworkMonitor( mContext, mCallbacks, mNetwork, mLogger, mValidationLogger, - mDependencies); + mDependencies, mDataStallStatsUtils); nm.start(); waitForIdle(nm.getHandler()); return nm; @@ -384,7 +398,7 @@ public class NetworkMonitorTest { public void testIsDataStall_EvaluationDnsOnNotMeteredNetwork() { WrappedNetworkMonitor wrappedMonitor = makeNotMeteredWrappedNetworkMonitor(); wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100); - makeDnsTimeoutEvent(wrappedMonitor, 5); + makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD); assertTrue(wrappedMonitor.isDataStall()); } @@ -395,7 +409,7 @@ public class NetworkMonitorTest { assertFalse(wrappedMonitor.isDataStall()); wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); - makeDnsTimeoutEvent(wrappedMonitor, 5); + makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD); assertTrue(wrappedMonitor.isDataStall()); } @@ -429,7 +443,7 @@ public class NetworkMonitorTest { // Test dns events happened in valid dns time threshold. WrappedNetworkMonitor wrappedMonitor = makeMeteredWrappedNetworkMonitor(); wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100); - makeDnsTimeoutEvent(wrappedMonitor, 5); + makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD); assertFalse(wrappedMonitor.isDataStall()); wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); assertTrue(wrappedMonitor.isDataStall()); @@ -438,7 +452,7 @@ public class NetworkMonitorTest { setValidDataStallDnsTimeThreshold(0); wrappedMonitor = makeMeteredWrappedNetworkMonitor(); wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 100); - makeDnsTimeoutEvent(wrappedMonitor, 5); + makeDnsTimeoutEvent(wrappedMonitor, DEFAULT_DNS_TIMEOUT_THRESHOLD); assertFalse(wrappedMonitor.isDataStall()); wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); assertFalse(wrappedMonitor.isDataStall()); @@ -505,6 +519,59 @@ public class NetworkMonitorTest { .notifyNetworkTested(NETWORK_TEST_RESULT_VALID, null); } + @Test + public void testDataStall_StallSuspectedAndSendMetrics() throws IOException { + WrappedNetworkMonitor wrappedMonitor = makeNotMeteredWrappedNetworkMonitor(); + wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); + makeDnsTimeoutEvent(wrappedMonitor, 5); + assertTrue(wrappedMonitor.isDataStall()); + verify(mDataStallStatsUtils, times(1)).write(any(), any()); + } + + @Test + public void testDataStall_NoStallSuspectedAndSendMetrics() throws IOException { + WrappedNetworkMonitor wrappedMonitor = makeNotMeteredWrappedNetworkMonitor(); + wrappedMonitor.setLastProbeTime(SystemClock.elapsedRealtime() - 1000); + makeDnsTimeoutEvent(wrappedMonitor, 3); + assertFalse(wrappedMonitor.isDataStall()); + verify(mDataStallStatsUtils, never()).write(any(), any()); + } + + @Test + public void testCollectDataStallMetrics() { + WrappedNetworkMonitor wrappedMonitor = makeNotMeteredWrappedNetworkMonitor(); + + when(mTelephony.getDataNetworkType()).thenReturn(TelephonyManager.NETWORK_TYPE_LTE); + when(mTelephony.getNetworkOperator()).thenReturn(TEST_MCCMNC); + when(mTelephony.getSimOperator()).thenReturn(TEST_MCCMNC); + + DataStallDetectionStats.Builder stats = + new DataStallDetectionStats.Builder() + .setEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS) + .setNetworkType(NetworkCapabilities.TRANSPORT_CELLULAR) + .setCellData(TelephonyManager.NETWORK_TYPE_LTE /* radioType */, + true /* roaming */, + TEST_MCCMNC /* networkMccmnc */, + TEST_MCCMNC /* simMccmnc */, + CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN /* signalStrength */); + generateTimeoutDnsEvent(stats, DEFAULT_DNS_TIMEOUT_THRESHOLD); + + assertEquals(wrappedMonitor.buildDataStallDetectionStats( + NetworkCapabilities.TRANSPORT_CELLULAR), stats.build()); + + when(mWifi.getConnectionInfo()).thenReturn(mWifiInfo); + + stats = new DataStallDetectionStats.Builder() + .setEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS) + .setNetworkType(NetworkCapabilities.TRANSPORT_WIFI) + .setWiFiData(mWifiInfo); + generateTimeoutDnsEvent(stats, DEFAULT_DNS_TIMEOUT_THRESHOLD); + + assertEquals( + wrappedMonitor.buildDataStallDetectionStats(NetworkCapabilities.TRANSPORT_WIFI), + stats.build()); + } + private void makeDnsTimeoutEvent(WrappedNetworkMonitor wrappedMonitor, int count) { for (int i = 0; i < count; i++) { wrappedMonitor.getDnsStallDetector().accumulateConsecutiveDnsTimeoutCount( @@ -594,5 +661,11 @@ public class NetworkMonitorTest { private void setStatus(HttpURLConnection connection, int status) throws IOException { doReturn(status).when(connection).getResponseCode(); } + + private void generateTimeoutDnsEvent(DataStallDetectionStats.Builder stats, int num) { + for (int i = 0; i < num; i++) { + stats.addDnsEvent(RETURN_CODE_DNS_TIMEOUT, 123456789 /* timeMs */); + } + } } diff --git a/packages/NetworkStackPermissionStub/AndroidManifest.xml b/packages/NetworkStackPermissionStub/AndroidManifest.xml index a8742d7ab34f..ba8a178eaea4 100644 --- a/packages/NetworkStackPermissionStub/AndroidManifest.xml +++ b/packages/NetworkStackPermissionStub/AndroidManifest.xml @@ -18,7 +18,9 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.networkstack.permissionstub" - android:sharedUserId="android.uid.networkstack"> + android:sharedUserId="android.uid.networkstack" + android:versionCode="10" + android:versionName="Q-initial"> <!-- This package only exists to define the below permissions, and enforce that they are only granted to apps sharing the same signature. diff --git a/packages/OsuLogin/src/com/android/hotspot2/osu/OsuLoginActivity.java b/packages/OsuLogin/src/com/android/hotspot2/osu/OsuLoginActivity.java index 28b05396acce..82e33cc4aff9 100644 --- a/packages/OsuLogin/src/com/android/hotspot2/osu/OsuLoginActivity.java +++ b/packages/OsuLogin/src/com/android/hotspot2/osu/OsuLoginActivity.java @@ -46,8 +46,6 @@ import com.android.hotspot2.R; import java.net.MalformedURLException; import java.net.URL; - - /** * Online Sign Up Login Web View launched during Provision Process of Hotspot 2.0 rel2. */ @@ -64,6 +62,7 @@ public class OsuLoginActivity extends Activity { private WebView mWebView; private SwipeRefreshLayout mSwipeRefreshLayout; private ProgressBar mProgressBar; + private boolean mForceDisconnect = true; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { @@ -141,6 +140,7 @@ public class OsuLoginActivity extends Activity { if (DBG) { Log.d(TAG, "Lost for the current Network, close the browser"); } + mForceDisconnect = false; // It is already disconnected. if (mNetwork.equals(network)) { finishAndRemoveTask(); } @@ -193,12 +193,6 @@ public class OsuLoginActivity extends Activity { mWebView.goBack(); return true; } - - // In case of back key, it needs to disconnect current connection with OSU AP to - // abort current Provisioning flow. - if (mWifiManager != null) { - mWifiManager.disconnect(); - } } return super.onKeyDown(keyCode, event); } @@ -207,6 +201,11 @@ public class OsuLoginActivity extends Activity { protected void onDestroy() { if (mNetworkCallback != null) { mCm.unregisterNetworkCallback(mNetworkCallback); + mNetworkCallback = null; + } + if (mWifiManager != null && mForceDisconnect) { + mWifiManager.disconnect(); + mWifiManager = null; } super.onDestroy(); } @@ -232,6 +231,7 @@ public class OsuLoginActivity extends Activity { private class OsuWebViewClient extends WebViewClient { boolean mPageError = false; + boolean mRedirectResponseReceived = false; @Override public void onPageStarted(WebView view, String urlString, Bitmap favicon) { @@ -247,6 +247,10 @@ public class OsuLoginActivity extends Activity { // Do not show the page error on UI. if (mPageError) { + if (mRedirectResponseReceived) { + // Do not disconnect current connection while provisioning is in progress. + mForceDisconnect = false; + } finishAndRemoveTask(); } } @@ -255,6 +259,7 @@ public class OsuLoginActivity extends Activity { public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) { if (request.getUrl().toString().startsWith("http://127.0.0.1")) { + mRedirectResponseReceived = true; view.stopLoading(); } @@ -266,5 +271,4 @@ public class OsuLoginActivity extends Activity { } } } - } diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp index caa928f43cc9..730e9e134e3d 100644 --- a/packages/SettingsLib/Android.bp +++ b/packages/SettingsLib/Android.bp @@ -29,7 +29,7 @@ android_library { resource_dirs: ["res"], - srcs: ["src/**/*.java"], + srcs: ["src/**/*.java", "src/**/*.kt"], min_sdk_version: "21", diff --git a/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_view.xml b/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_view.xml index b053317b9de1..4bc68b333cc6 100644 --- a/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_view.xml +++ b/packages/SettingsLib/BarChartPreference/res/layout/settings_bar_view.xml @@ -25,8 +25,7 @@ <View android:id="@+id/bar_view" android:layout_width="8dp" - android:layout_height="wrap_content" - android:background="?android:attr/colorAccent"/> + android:layout_height="wrap_content"/> <ImageView android:id="@+id/icon_view" diff --git a/packages/SettingsLib/BarChartPreference/res/values/styles.xml b/packages/SettingsLib/BarChartPreference/res/values/styles.xml index 647d0800fe82..4876cb6e1f28 100644 --- a/packages/SettingsLib/BarChartPreference/res/values/styles.xml +++ b/packages/SettingsLib/BarChartPreference/res/values/styles.xml @@ -18,7 +18,7 @@ <resources> <style name="BarViewStyle"> <item name="android:layout_width">0dp</item> - <item name="android:layout_height">wrap_content</item> + <item name="android:layout_height">226dp</item> <item name="android:layout_weight">1</item> <item name="android:layout_marginStart">8dp</item> <item name="android:layout_marginEnd">8dp</item> diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java index 3b87fcabb3fc..1003c974deb3 100644 --- a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java +++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarChartPreference.java @@ -160,8 +160,11 @@ public class BarChartPreference extends Preference { // If the state is loading, we just show a blank view. if (mIsLoading) { + holder.itemView.setVisibility(View.INVISIBLE); return; } + holder.itemView.setVisibility(View.VISIBLE); + // We must show title of bar chart. bindChartTitleView(holder); diff --git a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java index 6bf61ae70312..3ef0235940b1 100644 --- a/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java +++ b/packages/SettingsLib/BarChartPreference/src/com/android/settingslib/widget/BarView.java @@ -89,7 +89,7 @@ public class BarView extends LinearLayout { private void init() { LayoutInflater.from(getContext()).inflate(R.layout.settings_bar_view, this); setOrientation(LinearLayout.VERTICAL); - setGravity(Gravity.CENTER); + setGravity(Gravity.CENTER | Gravity.BOTTOM); mBarView = findViewById(R.id.bar_view); mIcon = findViewById(R.id.icon_view); diff --git a/packages/SettingsLib/EntityHeaderWidgets/src/com/android/settingslib/widget/AppEntitiesHeaderController.java b/packages/SettingsLib/EntityHeaderWidgets/src/com/android/settingslib/widget/AppEntitiesHeaderController.java index 330049fc6673..6e95a0ed106a 100644 --- a/packages/SettingsLib/EntityHeaderWidgets/src/com/android/settingslib/widget/AppEntitiesHeaderController.java +++ b/packages/SettingsLib/EntityHeaderWidgets/src/com/android/settingslib/widget/AppEntitiesHeaderController.java @@ -90,6 +90,7 @@ public class AppEntitiesHeaderController { private int mHeaderTitleRes; private int mHeaderDetailsRes; private int mHeaderEmptyRes; + private CharSequence mHeaderDetails; private View.OnClickListener mDetailsOnClickListener; /** @@ -148,6 +149,14 @@ public class AppEntitiesHeaderController { } /** + * Set the text for app entities header details. + */ + public AppEntitiesHeaderController setHeaderDetails(CharSequence detailsText) { + mHeaderDetails = detailsText; + return this; + } + + /** * Register a callback to be invoked when header details view is clicked. */ public AppEntitiesHeaderController setHeaderDetailsClickListener( @@ -232,11 +241,13 @@ public class AppEntitiesHeaderController { } private void bindHeaderDetailsView() { - CharSequence detailsText = ""; - try { - detailsText = mContext.getText(mHeaderDetailsRes); - } catch (Resources.NotFoundException e) { - Log.e(TAG, "Resource of header details can't not be found!", e); + CharSequence detailsText = mHeaderDetails; + if (TextUtils.isEmpty(detailsText)) { + try { + detailsText = mContext.getText(mHeaderDetailsRes); + } catch (Resources.NotFoundException e) { + Log.e(TAG, "Resource of header details can't not be found!", e); + } } mHeaderDetailsView.setText(detailsText); mHeaderDetailsView.setVisibility( diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index f865563200e1..68f1e6fb6f13 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -136,8 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"আঁতৰোৱা এপ্সমূহ"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"আঁতৰোৱা এপ্ আৰু ব্যৱহাৰকাৰীসমূহ"</string> - <!-- no translation found for data_usage_ota (5377889154805560860) --> - <skip /> + <string name="data_usage_ota" msgid="5377889154805560860">"ছিষ্টেম আপডে’ট"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"ইউএছবি টেডাৰিং"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"প\'ৰ্টেবল হটস্পট"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ব্লুটুথ টেডাৰিং"</string> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index ef8dd3de56a9..4f2913101c38 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -136,8 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"সরানো অ্যাপ্লিকেশানগুলি"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"সরানো অ্যাপ্লিকেশানগুলি এবং ব্যবহারকারীগণ"</string> - <!-- no translation found for data_usage_ota (5377889154805560860) --> - <skip /> + <string name="data_usage_ota" msgid="5377889154805560860">"সিস্টেম আপডেট"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB টিথারিং"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"পোর্টেবল হটস্পট"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ব্লুটুথ টিথারিং"</string> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index 97b9036157a3..36e04bb5f2bf 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -136,8 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"દૂર કરેલી ઍપ્લિકેશનો"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"દૂર કરેલી ઍપ્લિકેશનો અને વપરાશકર્તાઓ"</string> - <!-- no translation found for data_usage_ota (5377889154805560860) --> - <skip /> + <string name="data_usage_ota" msgid="5377889154805560860">"સિસ્ટમ અપડેટ"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB ટિથરિંગ"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"પોર્ટેબલ હૉટસ્પૉટ"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"બ્લૂટૂથ ટિથરિંગ"</string> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index 6e37c9a6eeda..89d6361395eb 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -136,8 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"אפליקציות שהוסרו"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"אפליקציות ומשתמשים שהוסרו"</string> - <!-- no translation found for data_usage_ota (5377889154805560860) --> - <skip /> + <string name="data_usage_ota" msgid="5377889154805560860">"עדכוני מערכת"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"שיתוף אינטרנט דרך USB"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"נקודה לשיתוף אינטרנט"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"שיתוף אינטרנט דרך Bluetooth"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index 41b0fbda408f..1e9a0d51ee67 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -136,8 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"ತೆಗೆದುಹಾಕಲಾದ ಅಪ್ಲಿಕೇಶನ್ಗಳು"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"ಅಪ್ಲಿಕೇಶನ್ಗಳು ಮತ್ತು ಬಳಕೆದಾರರನ್ನು ತೆಗೆದುಹಾಕಲಾಗಿದೆ"</string> - <!-- no translation found for data_usage_ota (5377889154805560860) --> - <skip /> + <string name="data_usage_ota" msgid="5377889154805560860">"ಸಿಸ್ಟಂ ಅಪ್ಡೇಟ್ಗಳು"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB ಟೆಥರಿಂಗ್"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ಪೋರ್ಟಬಲ್ ಹಾಟ್ಸ್ಪಾಟ್"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ಬ್ಲೂಟೂತ್ ಟೆಥರಿಂಗ್"</string> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index 0292cabb73bf..ae97fb24f573 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -136,8 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"നീക്കംചെയ്ത അപ്ലിക്കേഷനുകൾ"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"നീക്കംചെയ്ത അപ്ലിക്കേഷനുകളും ഉപയോക്താക്കളും"</string> - <!-- no translation found for data_usage_ota (5377889154805560860) --> - <skip /> + <string name="data_usage_ota" msgid="5377889154805560860">"സിസ്റ്റം അപ്ഡേറ്റുകൾ"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB ടെതറിംഗ്"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"പോർട്ടബിൾ ഹോട്ട്സ്പോട്ട്"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ബ്ലൂടൂത്ത് ടെതറിംഗ്"</string> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index 0c2f0eefb0e7..88de8f43b888 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -136,8 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"काढलेले अॅप्स"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"काढलेले अॅप्स आणि वापरकर्ते"</string> - <!-- no translation found for data_usage_ota (5377889154805560860) --> - <skip /> + <string name="data_usage_ota" msgid="5377889154805560860">"सिस्टम अपडेट"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB टेदरिंग"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"पोर्टेबल हॉटस्पॉट"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ब्लूटूथ टेदरिंग"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index 7daf9564cc9b..ac15f1b55564 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -136,8 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"एन्ड्रोइड OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"हटाइएका अनुप्रयोगहरू"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"अनुप्रयोगहरू र प्रयोगकर्ताहरू हटाइयो।"</string> - <!-- no translation found for data_usage_ota (5377889154805560860) --> - <skip /> + <string name="data_usage_ota" msgid="5377889154805560860">"प्रणालीसम्बन्धी अद्यावधिकहरू"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB टेथर गर्दै"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"पोर्टेबल हटस्पट"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ब्लुटुथ टेथर गर्दै"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index 54ddf845847e..847586e06cfc 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -136,8 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"କଢ଼ାଯାଇଥିବା ଆପ୍ଗୁଡ଼ିକ"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"ଆପ୍ ଏବଂ ଉପଯୋଗକର୍ତ୍ତା ବାହାର କରାଗଲା"</string> - <!-- no translation found for data_usage_ota (5377889154805560860) --> - <skip /> + <string name="data_usage_ota" msgid="5377889154805560860">"ସିଷ୍ଟମ୍ ଅପ୍ଡେଟ୍"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB ଟିଥରିଙ୍ଗ"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ପୋର୍ଟବଲ୍ ହଟସ୍ପଟ୍"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ବ୍ଲୁଟୂଥ ଟିଥରିଙ୍ଗ"</string> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index dcf3e3cb46ec..0675b6be525e 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -136,8 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"ਹਟਾਏ ਗਏ ਐਪਸ"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"ਹਟਾਏ ਗਏ ਐਪਸ ਅਤੇ ਉਪਭੋਗਤਾ"</string> - <!-- no translation found for data_usage_ota (5377889154805560860) --> - <skip /> + <string name="data_usage_ota" msgid="5377889154805560860">"ਸਿਸਟਮ ਅੱਪਡੇਟ"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB ਟੈਦਰਿੰਗ"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"ਪੋਰਟੇਬਲ ਹੌਟਸਪੌਟ"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"ਬਲੂਟੁੱਥ ਟੈਦਰਿੰਗ"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index 71716cec6886..3cf43c844354 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -136,8 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"அகற்றப்பட்ட பயன்பாடுகள்"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"அகற்றப்பட்ட பயன்பாடுகள் மற்றும் பயனர்கள்"</string> - <!-- no translation found for data_usage_ota (5377889154805560860) --> - <skip /> + <string name="data_usage_ota" msgid="5377889154805560860">"சிஸ்டம் புதுப்பிப்புகள்"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB டெதெரிங்"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"போர்ட்டபிள் ஹாட்ஸ்பாட்"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"புளூடூத் டெதெரிங்"</string> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index 65ed110d3c46..c22362927e31 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -136,8 +136,7 @@ <string name="process_kernel_label" msgid="3916858646836739323">"Android OS"</string> <string name="data_usage_uninstalled_apps" msgid="614263770923231598">"ہٹائی گئی ایپس"</string> <string name="data_usage_uninstalled_apps_users" msgid="7986294489899813194">"ہٹائی گئی ایپس اور صارفین"</string> - <!-- no translation found for data_usage_ota (5377889154805560860) --> - <skip /> + <string name="data_usage_ota" msgid="5377889154805560860">"سسٹم اپ ڈیٹس"</string> <string name="tether_settings_title_usb" msgid="6688416425801386511">"USB ٹیدرنگ"</string> <string name="tether_settings_title_wifi" msgid="3277144155960302049">"پورٹیبل ہاٹ اسپاٹ"</string> <string name="tether_settings_title_bluetooth" msgid="355855408317564420">"بلوٹوتھ ٹیدرنگ"</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java index 4f18d450462d..402ce90d6ec5 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java @@ -420,14 +420,20 @@ public class BluetoothEventManager { private class ClassChangedHandler implements Handler { public void onReceive(Context context, Intent intent, BluetoothDevice device) { - mDeviceManager.onBtClassChanged(device); + CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device); + if (cachedDevice != null) { + cachedDevice.refresh(); + } } } private class UuidChangedHandler implements Handler { public void onReceive(Context context, Intent intent, BluetoothDevice device) { - mDeviceManager.onUuidChanged(device); + CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device); + if (cachedDevice != null) { + cachedDevice.onUuidChanged(); + } } } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java index 5b4a8b4f259e..33e754044873 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java @@ -206,20 +206,6 @@ public class CachedBluetoothDeviceManager { } } - public synchronized void onBtClassChanged(BluetoothDevice device) { - CachedBluetoothDevice cachedDevice = findDevice(device); - if (cachedDevice != null) { - cachedDevice.dispatchAttributesChanged(); - } - } - - public synchronized void onUuidChanged(BluetoothDevice device) { - CachedBluetoothDevice cachedDevice = findDevice(device); - if (cachedDevice != null) { - cachedDevice.onUuidChanged(); - } - } - public synchronized void onBluetoothStateChanged(int bluetoothState) { // When Bluetooth is turning off, we need to clear the non-bonded devices // Otherwise, they end up showing up on the next BT enable diff --git a/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt b/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt new file mode 100644 index 000000000000..911cfcd115f4 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/graph/ThemedBatteryDrawable.kt @@ -0,0 +1,412 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.android.settingslib.graph + +import android.content.Context +import android.graphics.BlendMode +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.ColorFilter +import android.graphics.Matrix +import android.graphics.Paint +import android.graphics.Path +import android.graphics.PixelFormat +import android.graphics.Rect +import android.graphics.RectF +import android.graphics.drawable.Drawable +import android.util.PathParser +import android.util.TypedValue + +import com.android.settingslib.R +import com.android.settingslib.Utils + +/** + * A battery meter drawable that respects paths configured in + * frameworks/base/core/res/res/values/config.xml to allow for an easily overrideable battery icon + */ +open class ThemedBatteryDrawable(private val context: Context, frameColor: Int) : Drawable() { + + // Need to load: + // 1. perimeter shape + // 2. fill mask (if smaller than perimeter, this would create a fill that + // doesn't touch the walls + private val perimeterPath = Path() + private val scaledPerimeter = Path() + // Fill will cover the whole bounding rect of the fillMask, and be masked by the path + private val fillMask = Path() + private val scaledFill = Path() + // Based off of the mask, the fill will interpolate across this space + private val fillRect = RectF() + // Top of this rect changes based on level, 100% == fillRect + private val levelRect = RectF() + private val levelPath = Path() + // Updates the transform of the paths when our bounds change + private val scaleMatrix = Matrix() + private val padding = Rect() + // The net result of fill + perimeter paths + private val unifiedPath = Path() + + // Bolt path (used while charging) + private val boltPath = Path() + private val scaledBolt = Path() + + // Plus sign (used for power save mode) + private val plusPath = Path() + private val scaledPlus = Path() + + private var intrinsicHeight: Int + private var intrinsicWidth: Int + + // To implement hysteresis, keep track of the need to invert the interior icon of the battery + private var invertFillIcon = false + + // Colors can be configured based on battery level (see res/values/arrays.xml) + private var colorLevels: IntArray + + private var fillColor: Int = Color.MAGENTA + private var backgroundColor: Int = Color.MAGENTA + // updated whenever level changes + private var levelColor: Int = Color.MAGENTA + + // Dual tone implies that battery level is a clipped overlay over top of the whole shape + private var dualTone = false + + private val invalidateRunnable: () -> Unit = { + invalidateSelf() + } + + open var criticalLevel: Int = context.resources.getInteger( + com.android.internal.R.integer.config_criticalBatteryWarningLevel) + + var charging = false + set(value) { + field = value + postInvalidate() + } + + var powerSaveEnabled = false + set(value) { + field = value + postInvalidate() + } + + private val fillColorStrokePaint = Paint(Paint.ANTI_ALIAS_FLAG).also { p -> + p.color = frameColor + p.isDither = true + p.strokeWidth = 5f + p.style = Paint.Style.STROKE + p.blendMode = BlendMode.SRC + p.strokeMiter = 5f + p.strokeJoin = Paint.Join.ROUND + } + + private val fillColorStrokeProtection = Paint(Paint.ANTI_ALIAS_FLAG).also { p -> + p.isDither = true + p.strokeWidth = 5f + p.style = Paint.Style.STROKE + p.blendMode = BlendMode.CLEAR + p.strokeMiter = 5f + p.strokeJoin = Paint.Join.ROUND + } + + private val fillPaint = Paint(Paint.ANTI_ALIAS_FLAG).also { p -> + p.color = frameColor + p.alpha = 255 + p.isDither = true + p.strokeWidth = 0f + p.style = Paint.Style.FILL_AND_STROKE + } + + // Only used if dualTone is set to true + private val dualToneBackgroundFill = Paint(Paint.ANTI_ALIAS_FLAG).also { p -> + p.color = frameColor + p.alpha = 255 + p.isDither = true + p.strokeWidth = 0f + p.style = Paint.Style.FILL_AND_STROKE + } + + init { + val density = context.resources.displayMetrics.density + intrinsicHeight = (Companion.HEIGHT * density).toInt() + intrinsicWidth = (Companion.WIDTH * density).toInt() + + val res = context.resources + val levels = res.obtainTypedArray(R.array.batterymeter_color_levels) + val colors = res.obtainTypedArray(R.array.batterymeter_color_values) + val N = levels.length() + colorLevels = IntArray(2 * N) + for (i in 0 until N) { + colorLevels[2 * i] = levels.getInt(i, 0) + if (colors.getType(i) == TypedValue.TYPE_ATTRIBUTE) { + colorLevels[2 * i + 1] = Utils.getColorAttrDefaultColor(context, + colors.getThemeAttributeId(i, 0)) + } else { + colorLevels[2 * i + 1] = colors.getColor(i, 0) + } + } + levels.recycle() + colors.recycle() + + loadPaths() + } + + override fun draw(c: Canvas) { + unifiedPath.reset() + levelPath.reset() + levelRect.set(fillRect) + val fillFraction = level / 100f + val fillTop = + if (level >= 95) + fillRect.top + else + fillRect.top + (fillRect.height() * (1 - fillFraction)) + + levelRect.top = Math.floor(fillTop.toDouble()).toFloat() + levelPath.addRect(levelRect, Path.Direction.CCW) + + // The perimeter should never change + unifiedPath.addPath(scaledPerimeter) + // IF drawing dual tone, the level is used only to clip the whole drawable path + if (!dualTone) { + unifiedPath.op(levelPath, Path.Op.UNION) + } + + fillPaint.color = levelColor + + // Deal with unifiedPath clipping before it draws + if (charging) { + // Clip out the bolt shape + unifiedPath.op(scaledBolt, Path.Op.DIFFERENCE) + if (!invertFillIcon) { + c.drawPath(scaledBolt, fillPaint) + } + } else if (powerSaveEnabled) { + // Clip out the plus shape + unifiedPath.op(scaledPlus, Path.Op.DIFFERENCE) + if (!invertFillIcon) { + c.drawPath(scaledPlus, fillPaint) + } + } + + if (dualTone) { + // Dual tone means we draw the shape again, clipped to the charge level + c.drawPath(unifiedPath, dualToneBackgroundFill) + c.save() + c.clipRect(0f, + bounds.bottom - bounds.height() * fillFraction, + bounds.right.toFloat(), + bounds.bottom.toFloat()) + c.drawPath(unifiedPath, fillPaint) + c.restore() + } else { + // Non dual-tone means we draw the perimeter (with the level fill), and potentially + // draw the fill again with a critical color + fillPaint.color = fillColor + c.drawPath(unifiedPath, fillPaint) + fillPaint.color = levelColor + + // Show colorError below this level + if (level <= Companion.CRITICAL_LEVEL && !charging) { + c.save() + c.clipPath(scaledFill) + c.drawPath(levelPath, fillPaint) + c.restore() + } + } + + if (charging) { + c.clipOutPath(scaledBolt) + if (invertFillIcon) { + c.drawPath(scaledBolt, fillColorStrokePaint) + } else { + c.drawPath(scaledBolt, fillColorStrokeProtection) + } + } else if (powerSaveEnabled) { + c.clipOutPath(scaledPlus) + if (invertFillIcon) { + c.drawPath(scaledPlus, fillColorStrokePaint) + } else { + c.drawPath(scaledPlus, fillColorStrokeProtection) + } + } + } + + private fun batteryColorForLevel(level: Int): Int { + return when { + charging || powerSaveEnabled -> fillColor + else -> getColorForLevel(level) + } + } + + private fun getColorForLevel(level: Int): Int { + var thresh: Int + var color = 0 + var i = 0 + while (i < colorLevels.size) { + thresh = colorLevels[i] + color = colorLevels[i + 1] + if (level <= thresh) { + + // Respect tinting for "normal" level + return if (i == colorLevels.size - 2) { + fillColor + } else { + color + } + } + i += 2 + } + return color + } + + /** + * Alpha is unused internally, and should be defined in the colors passed to {@link setColors}. + * Further, setting an alpha for a dual tone battery meter doesn't make sense without bounds + * defining the minimum background fill alpha. This is because fill + background must be equal + * to the net alpha passed in here. + */ + override fun setAlpha(alpha: Int) { + } + + override fun setColorFilter(colorFilter: ColorFilter?) { + fillPaint.colorFilter = colorFilter + fillColorStrokePaint.colorFilter = colorFilter + dualToneBackgroundFill.colorFilter = colorFilter + } + + /** + * Deprecated, but required by Drawable + */ + override fun getOpacity(): Int { + return PixelFormat.OPAQUE + } + + override fun getIntrinsicHeight(): Int { + return intrinsicHeight + } + + override fun getIntrinsicWidth(): Int { + return intrinsicWidth + } + + /** + * Set the fill level + */ + public open fun setBatteryLevel(l: Int) { + invertFillIcon = if (l >= 67) true else if (l <= 33) false else invertFillIcon + level = l + levelColor = batteryColorForLevel(level) + invalidateSelf() + } + + public fun getBatteryLevel(): Int { + return level + } + + override fun onBoundsChange(bounds: Rect?) { + super.onBoundsChange(bounds) + updateSize() + } + + fun setPadding(left: Int, top: Int, right: Int, bottom: Int) { + padding.left = left + padding.top = top + padding.right = right + padding.bottom = bottom + + updateSize() + } + + fun setColors(fgColor: Int, bgColor: Int, singleToneColor: Int) { + fillColor = if (dualTone) fgColor else singleToneColor + + fillPaint.color = fillColor + fillColorStrokePaint.color = fillColor + + backgroundColor = bgColor + dualToneBackgroundFill.color = bgColor + + // Also update the level color, since fillColor may have changed + levelColor = batteryColorForLevel(level) + + invalidateSelf() + } + + private fun postInvalidate() { + unscheduleSelf(invalidateRunnable) + scheduleSelf(invalidateRunnable, 0) + } + + private fun updateSize() { + val b = bounds + if (b.isEmpty) { + scaleMatrix.setScale(1f, 1f) + } else { + scaleMatrix.setScale((b.right / WIDTH), (b.bottom / HEIGHT)) + } + + perimeterPath.transform(scaleMatrix, scaledPerimeter) + fillMask.transform(scaleMatrix, scaledFill) + scaledFill.computeBounds(fillRect, true) + boltPath.transform(scaleMatrix, scaledBolt) + plusPath.transform(scaleMatrix, scaledPlus) + + // It is expected that this view only ever scale by the same factor in each dimension, so + // just pick one to scale the strokeWidths + val scaledStrokeWidth = + Math.max(b.right / WIDTH * PROTECTION_STROKE_WIDTH, PROTECTION_MIN_STROKE_WIDTH) + + fillColorStrokePaint.strokeWidth = scaledStrokeWidth + fillColorStrokeProtection.strokeWidth = scaledStrokeWidth + } + + private fun loadPaths() { + val pathString = context.resources.getString( + com.android.internal.R.string.config_batterymeterPerimeterPath) + perimeterPath.set(PathParser.createPathFromPathData(pathString)) + val b = RectF() + perimeterPath.computeBounds(b, true) + + val fillMaskString = context.resources.getString( + com.android.internal.R.string.config_batterymeterFillMask) + fillMask.set(PathParser.createPathFromPathData(fillMaskString)) + // Set the fill rect so we can calculate the fill properly + fillMask.computeBounds(fillRect, true) + + val boltPathString = context.resources.getString( + com.android.internal.R.string.config_batterymeterBoltPath) + boltPath.set(PathParser.createPathFromPathData(boltPathString)) + + val plusPathString = context.resources.getString( + com.android.internal.R.string.config_batterymeterPowersavePath) + plusPath.set(PathParser.createPathFromPathData(plusPathString)) + + dualTone = context.resources.getBoolean( + com.android.internal.R.bool.config_batterymeterDualTone) + } + + companion object { + private const val TAG = "ThemedBatteryDrawable" + private const val WIDTH = 12f + private const val HEIGHT = 20f + private const val CRITICAL_LEVEL = 15 + // On a 12x20 grid, how wide to make the fill protection stroke. + // Scales when our size changes + private const val PROTECTION_STROKE_WIDTH = 1.4f + // Arbitrarily chosen for visibility at small sizes + private const val PROTECTION_MIN_STROKE_WIDTH = 5f + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreference.java b/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreference.java index 2a1281027169..a31b71e2cd0b 100644 --- a/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreference.java +++ b/packages/SettingsLib/src/com/android/settingslib/widget/FooterPreference.java @@ -59,6 +59,5 @@ public class FooterPreference extends Preference { setIcon(R.drawable.ic_info_outline_24); setKey(KEY_FOOTER); setOrder(ORDER_FOOTER); - setSelectable(false); } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java index 43b289464e1d..dc47de8546db 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManagerTest.java @@ -369,21 +369,6 @@ public class CachedBluetoothDeviceManagerTest { } /** - * Test to verify onBtClassChanged(). - */ - @Test - public void onBtClassChanged_validBtClass_classChanged() { - CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1); - assertThat(cachedDevice1).isNotNull(); - assertThat(cachedDevice1.getBtClass()).isEqualTo(DEVICE_CLASS_1); - - final BluetoothClass newBluetoothClass = DEVICE_CLASS_2; - when(mDevice1.getBluetoothClass()).thenReturn(newBluetoothClass); - mCachedDeviceManager.onBtClassChanged(mDevice1); - assertThat(cachedDevice1.getBtClass()).isEqualTo(newBluetoothClass); - } - - /** * Test to verify onDeviceDisappeared(). */ @Test diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppEntitiesHeaderControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppEntitiesHeaderControllerTest.java index 63a958eeb5bb..9a07ca885914 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppEntitiesHeaderControllerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppEntitiesHeaderControllerTest.java @@ -85,6 +85,23 @@ public class AppEntitiesHeaderControllerTest { } @Test + public void setHeaderDetails_onlyDetailsTextSet_shouldSetToDetailsView() { + mController.setHeaderDetails(TITLE).apply(); + final TextView view = mAppEntitiesHeaderView.findViewById(R.id.header_details); + + assertThat(view.getText()).isEqualTo(TITLE); + } + + @Test + public void setHeaderDetails_detailsTextAndResBothSet_shouldSetTextToDetailsView() { + mController.setHeaderDetailsRes(R.string.expand_button_title); + mController.setHeaderDetails(TITLE).apply(); + final TextView view = mAppEntitiesHeaderView.findViewById(R.id.header_details); + + assertThat(view.getText()).isEqualTo(TITLE); + } + + @Test public void setHeaderDetailsClickListener_setClickListener_detailsViewAttachClickListener() { mController.setHeaderDetailsClickListener(v -> { }).apply(); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java index 1080cf49e551..3acca2a7ef75 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/BarChartPreferenceTest.java @@ -305,7 +305,7 @@ public class BarChartPreferenceTest { } @Test - public void onBindViewHolder_loadingStateIsTrue_shouldNotInitAnyView() { + public void onBindViewHolder_loadingStateIsTrue_shouldHideAllViews() { final BarViewInfo viewInfo = new BarViewInfo(mIcon, 30 /* barNumber */, R.string.debug_app); viewInfo.setClickListener(v -> { }); @@ -317,8 +317,7 @@ public class BarChartPreferenceTest { mPreference.onBindViewHolder(mHolder); - assertThat(TextUtils.isEmpty(mTitleView.getText())).isTrue(); - assertThat(TextUtils.isEmpty(mDetailsView.getText())).isTrue(); + assertThat(mHolder.itemView.getVisibility()).isEqualTo(View.INVISIBLE); } @Test @@ -334,6 +333,7 @@ public class BarChartPreferenceTest { mPreference.onBindViewHolder(mHolder); + assertThat(mHolder.itemView.getVisibility()).isEqualTo(View.VISIBLE); assertThat(TextUtils.isEmpty(mTitleView.getText())).isFalse(); assertThat(TextUtils.isEmpty(mDetailsView.getText())).isFalse(); } diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml index 26ea6ab2e8be..65e0c0fbc991 100644 --- a/packages/SettingsProvider/res/values/defaults.xml +++ b/packages/SettingsProvider/res/values/defaults.xml @@ -40,8 +40,8 @@ <bool name="def_wifi_display_on">false</bool> <bool name="def_install_non_market_apps">false</bool> <bool name="def_package_verifier_enable">true</bool> - <!-- Comma-separated list of location providers --> - <string name="def_location_providers_allowed" translatable="false">gps,network</string> + <!-- 0 == off, 3 == on --> + <integer name="def_location_mode">3</integer> <bool name="assisted_gps_enabled">true</bool> <bool name="def_netstats_enabled">true</bool> <bool name="def_usb_mass_storage_enabled">true</bool> diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index ef90dc981870..5e2b7c86ee93 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -23,6 +23,7 @@ import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; +import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; @@ -2339,9 +2340,6 @@ class DatabaseHelper extends SQLiteOpenHelper { stmt = db.compileStatement("INSERT OR IGNORE INTO secure(name,value)" + " VALUES(?,?);"); - loadStringSetting(stmt, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, - R.string.def_location_providers_allowed); - // Don't do this. The SystemServer will initialize ADB_ENABLED from a // persistent system property instead. //loadSetting(stmt, Settings.Secure.ADB_ENABLED, 0); @@ -2440,6 +2438,7 @@ class DatabaseHelper extends SQLiteOpenHelper { private void loadGlobalSettings(SQLiteDatabase db) { SQLiteStatement stmt = null; + final Resources res = mContext.getResources(); try { stmt = db.compileStatement("INSERT OR IGNORE INTO global(name,value)" + " VALUES(?,?);"); @@ -2468,7 +2467,7 @@ class DatabaseHelper extends SQLiteOpenHelper { loadSetting(stmt, Settings.Global.STAY_ON_WHILE_PLUGGED_IN, ("1".equals(SystemProperties.get("ro.kernel.qemu")) || - mContext.getResources().getBoolean(R.bool.def_stay_on_while_plugged_in)) + res.getBoolean(R.bool.def_stay_on_while_plugged_in)) ? 1 : 0); loadIntegerSetting(stmt, Settings.Global.WIFI_SLEEP_POLICY, @@ -2505,14 +2504,14 @@ class DatabaseHelper extends SQLiteOpenHelper { loadBooleanSetting(stmt, Settings.Global.DEVICE_PROVISIONED, R.bool.def_device_provisioned); - final int maxBytes = mContext.getResources().getInteger( + final int maxBytes = res.getInteger( R.integer.def_download_manager_max_bytes_over_mobile); if (maxBytes > 0) { loadSetting(stmt, Settings.Global.DOWNLOAD_MAX_BYTES_OVER_MOBILE, Integer.toString(maxBytes)); } - final int recommendedMaxBytes = mContext.getResources().getInteger( + final int recommendedMaxBytes = res.getInteger( R.integer.def_download_manager_recommended_max_bytes_over_mobile); if (recommendedMaxBytes > 0) { loadSetting(stmt, Settings.Global.DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE, @@ -2609,6 +2608,20 @@ class DatabaseHelper extends SQLiteOpenHelper { loadSetting(stmt, Settings.Global.DEVICE_NAME, getDefaultDeviceName()); + // Set default lid/cover behaviour according to legacy device config + final int defaultLidBehavior; + if (res.getBoolean(com.android.internal.R.bool.config_lidControlsSleep)) { + // WindowManagerFuncs.LID_BEHAVIOR_SLEEP + defaultLidBehavior = 1; + } else if (res.getBoolean(com.android.internal.R.bool.config_lidControlsScreenLock)) { + // WindowManagerFuncs.LID_BEHAVIOR_LOCK + defaultLidBehavior = 2; + } else { + // WindowManagerFuncs.LID_BEHAVIOR_NONE + defaultLidBehavior = 0; + } + loadSetting(stmt, Settings.Global.LID_BEHAVIOR, defaultLidBehavior); + /* * IMPORTANT: Do not add any more upgrade steps here as the global, * secure, and system settings are no longer stored in a database diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 81b304dc2ce4..0f8fd9265eaa 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -1353,11 +1353,11 @@ class SettingsProtoDumpUtil { Settings.Global.SHOW_TEMPERATURE_WARNING, GlobalSettingsProto.TemperatureWarning.SHOW_TEMPERATURE_WARNING); dumpSetting(s, p, + Settings.Global.SHOW_USB_TEMPERATURE_ALARM, + GlobalSettingsProto.TemperatureWarning.SHOW_USB_TEMPERATURE_ALARM); + dumpSetting(s, p, Settings.Global.WARNING_TEMPERATURE, GlobalSettingsProto.TemperatureWarning.WARNING_TEMPERATURE_LEVEL); - dumpSetting(s, p, - Settings.Global.USB_ALARM_TEMPERATURE, - GlobalSettingsProto.TemperatureWarning.USB_ALARM_TEMPERATURE_LEVEL); p.end(tempWarningToken); final long tetherToken = p.start(GlobalSettingsProto.TETHER); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index a7ad2239b402..d6c33a3b077c 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -969,6 +969,11 @@ public class SettingsProvider extends ContentProvider { try { synchronized (mLock) { Setting setting = getSecureSetting( + Settings.Secure.LOCATION_MODE, userId); + updateSecureSetting(Settings.Secure.LOCATION_MODE, + setting != null ? setting.getValue() : null, null, + true, userId, true); + setting = getSecureSetting( Settings.Secure.LOCATION_PROVIDERS_ALLOWED, userId); updateSecureSetting(Settings.Secure.LOCATION_PROVIDERS_ALLOWED, setting != null ? setting.getValue() : null, null, @@ -1379,8 +1384,8 @@ public class SettingsProvider extends ContentProvider { } } if (enableOverride) { - if (Secure.LOCATION_PROVIDERS_ALLOWED.equals(name)) { - final Setting overridden = getLocationProvidersAllowedSetting(owningUserId); + if (Secure.LOCATION_MODE.equals(name)) { + final Setting overridden = getLocationModeSetting(owningUserId); if (overridden != null) { return overridden; } @@ -1475,7 +1480,7 @@ public class SettingsProvider extends ContentProvider { return null; } - private Setting getLocationProvidersAllowedSetting(int owningUserId) { + private Setting getLocationModeSetting(int owningUserId) { synchronized (mLock) { final Setting setting = getGlobalSetting( Global.LOCATION_GLOBAL_KILL_SWITCH); @@ -1486,7 +1491,7 @@ public class SettingsProvider extends ContentProvider { final SettingsState settingsState = mSettingsRegistry.getSettingsLocked( SETTINGS_TYPE_SECURE, owningUserId); return settingsState.new Setting( - Secure.LOCATION_PROVIDERS_ALLOWED, + Secure.LOCATION_MODE, "", // value "", // tag "", // default value @@ -1497,7 +1502,7 @@ public class SettingsProvider extends ContentProvider { @Override public boolean update(String value, boolean setDefault, String packageName, String tag, boolean forceNonSystemPackage) { - Slog.wtf(LOG_TAG, "update shoudln't be called on this instance."); + Slog.wtf(LOG_TAG, "update shouldn't be called on this instance."); return false; } }; @@ -3115,7 +3120,7 @@ public class SettingsProvider extends ContentProvider { final int key = makeKey(SETTINGS_TYPE_SECURE, userId); mGenerationRegistry.incrementGeneration(key); - final Uri uri = getNotificationUriFor(key, Secure.LOCATION_PROVIDERS_ALLOWED); + final Uri uri = getNotificationUriFor(key, Secure.LOCATION_MODE); mHandler.obtainMessage(MyHandler.MSG_NOTIFY_URI_CHANGED, userId, 0, uri).sendToTarget(); } @@ -4229,23 +4234,18 @@ public class SettingsProvider extends ContentProvider { final Setting locationProvidersAllowed = secureSettings.getSettingLocked( Secure.LOCATION_PROVIDERS_ALLOWED); - String defLocationMode = Integer.toString( - !TextUtils.isEmpty(locationProvidersAllowed.getValue()) - ? Secure.LOCATION_MODE_ON - : Secure.LOCATION_MODE_OFF); - secureSettings.insertSettingLocked( - Secure.LOCATION_MODE, defLocationMode, - null, true, SettingsState.SYSTEM_PACKAGE_NAME); - - // also reset LOCATION_PROVIDERS_ALLOWED back to the default value - this - // setting is now only for debug/test purposes, and will likely be removed - // in a later release. LocationManagerService is responsible for adjusting - // these settings to the proper state. - - String defLocationProvidersAllowed = getContext().getResources().getString( - R.string.def_location_providers_allowed); + final int defLocationMode; + if (locationProvidersAllowed.isNull()) { + defLocationMode = getContext().getResources().getInteger( + R.integer.def_location_mode); + } else { + defLocationMode = + !TextUtils.isEmpty(locationProvidersAllowed.getValue()) + ? Secure.LOCATION_MODE_ON + : Secure.LOCATION_MODE_OFF; + } secureSettings.insertSettingLocked( - Secure.LOCATION_PROVIDERS_ALLOWED, defLocationProvidersAllowed, + Secure.LOCATION_MODE, Integer.toString(defLocationMode), null, true, SettingsState.SYSTEM_PACKAGE_NAME); } diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 0da3b10778e9..fc6ef2bcede2 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -146,6 +146,7 @@ <uses-permission android:name="android.permission.SET_TIME_ZONE" /> <uses-permission android:name="android.permission.DISABLE_HIDDEN_API_CHECKS" /> <uses-permission android:name="android.permission.MANAGE_ROLE_HOLDERS" /> + <uses-permission android:name="android.permission.OBSERVE_ROLE_HOLDERS" /> <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" /> <!-- Permission needed to rename bugreport notifications (so they're not shown as Shell) --> <uses-permission android:name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" /> diff --git a/packages/SystemUI/res-keyguard/drawable/bubble_hour_hand.xml b/packages/SystemUI/res-keyguard/drawable/bubble_hour_hand.xml index d3c3a518f6ac..a2404b0834e9 100644 --- a/packages/SystemUI/res-keyguard/drawable/bubble_hour_hand.xml +++ b/packages/SystemUI/res-keyguard/drawable/bubble_hour_hand.xml @@ -1,9 +1,9 @@ <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:height="200dp" - android:width="200dp" - android:viewportHeight="100" - android:viewportWidth="100"> + android:height="350dp" + android:width="350dp" + android:viewportHeight="254.66145" + android:viewportWidth="254.66145"> <path android:fillColor="#000000" - android:pathData="M50.082,14.199m-13.985,0a13.985,13.985 0,1 1,27.97 0a13.985,13.985 0,1 1,-27.97 0"/> + android:pathData="M127.331,40.481m-10.914,0a10.914,10.914 0,1 1,21.828 0a10.914,10.914 0,1 1,-21.828 0"/> </vector> diff --git a/packages/SystemUI/res-keyguard/drawable/bubble_minute_hand.xml b/packages/SystemUI/res-keyguard/drawable/bubble_minute_hand.xml index a4417fb65da4..be10b5d12af7 100644 --- a/packages/SystemUI/res-keyguard/drawable/bubble_minute_hand.xml +++ b/packages/SystemUI/res-keyguard/drawable/bubble_minute_hand.xml @@ -1,9 +1,11 @@ <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:height="200dp" - android:width="200dp" - android:viewportHeight="100" - android:viewportWidth="100" > + android:height="350dp" + android:width="350dp" + android:viewportHeight="254.66145" + android:viewportWidth="254.66145" > <path - android:fillColor="#000000" - android:pathData="M50.082,0.025L50.082,0.025A13.985,15.63 0,0 1,64.067 15.656L64.067,49.029A13.985,15.63 0,0 1,50.082 64.659L50.082,64.659A13.985,15.63 0,0 1,36.097 49.029L36.097,15.656A13.985,15.63 0,0 1,50.082 0.025z"/> + android:strokeColor="#000000" + android:strokeWidth="5" + android:pathData="M125.923,29.692L128.739,29.692A27.108,30.579 0,0 1,155.847 60.271L155.847,125.268A27.108,30.579 0,0 1,128.739 155.847L125.923,155.847A27.108,30.579 0,0 1,98.815 125.268L98.815,60.271A27.108,30.579 0,0 1,125.923 29.692z"/> </vector> + diff --git a/packages/SystemUI/res-keyguard/layout/bubble_clock.xml b/packages/SystemUI/res-keyguard/layout/bubble_clock.xml index c8dc8e43893c..0bc13199d71e 100644 --- a/packages/SystemUI/res-keyguard/layout/bubble_clock.xml +++ b/packages/SystemUI/res-keyguard/layout/bubble_clock.xml @@ -42,15 +42,15 @@ > <ImageView android:id="@+id/minute_hand" - android:layout_width="300dp" - android:layout_height="300dp" + android:layout_width="350dp" + android:layout_height="350dp" android:src="@drawable/bubble_minute_hand" android:tint="@color/bubbleMinuteHandColor" /> <ImageView android:id="@+id/hour_hand" - android:layout_width="300dp" - android:layout_height="300dp" + android:layout_width="350dp" + android:layout_height="350dp" android:src="@drawable/bubble_hour_hand" android:tint="@color/bubbleHourHandColor" /> diff --git a/packages/SystemUI/res-keyguard/layout/default_clock_preview.xml b/packages/SystemUI/res-keyguard/layout/default_clock_preview.xml new file mode 100644 index 000000000000..3e6dd13e4b9f --- /dev/null +++ b/packages/SystemUI/res-keyguard/layout/default_clock_preview.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<RelativeLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + > + <TextClock + android:id="@+id/time" + style="@style/widget_big" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_above="@id/date" + android:letterSpacing="0.03" + android:gravity="center_horizontal" + android:format12Hour="@string/keyguard_widget_12_hours_format" + android:format24Hour="@string/keyguard_widget_24_hours_format" + /> + + <TextClock + android:id="@+id/date" + style="@stype/widget_big" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_centerInParent="true" + android:letterSpacing="0.03" + android:gravity="center_horizontal" + android:format12Hour="EEE, MMM d" + android:format24Hour="EEE, MMM d" + /> +</RelativeLayout> diff --git a/packages/SystemUI/res/drawable/bubble_expanded_header_bg.xml b/packages/SystemUI/res/drawable/bubble_expanded_header_bg.xml deleted file mode 100644 index a76b7b1c8f7b..000000000000 --- a/packages/SystemUI/res/drawable/bubble_expanded_header_bg.xml +++ /dev/null @@ -1,26 +0,0 @@ -<?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 - --> -<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> - <item> - <shape android:shape="rectangle"> - <solid android:color="?android:attr/colorBackgroundFloating"/> - <corners - android:topLeftRadius="@dimen/corner_size" - android:topRightRadius="@dimen/corner_size"/> - </shape> - </item> -</layer-list>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/ic_tune_black_16dp.xml b/packages/SystemUI/res/drawable/ic_tune_black_16dp.xml new file mode 100644 index 000000000000..339cb70e3ed0 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_tune_black_16dp.xml @@ -0,0 +1,25 @@ +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="16dp" + android:height="16dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FF000000" + android:pathData="M3,17v2h6v-2L3,17zM3,5v2h10L13,5L3,5zM13,21v-2h8v-2h-8v-2h-2v6h2zM7,9v2L3,11v2h4v2h2L9,9L7,9zM21,13v-2L11,11v2h10zM15,9h2L17,7h4L21,5h-4L17,3h-2v6z"/> +</vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/qs_customize_tile_decoration.xml b/packages/SystemUI/res/drawable/qs_customize_tile_decoration.xml new file mode 100644 index 000000000000..f086cec01234 --- /dev/null +++ b/packages/SystemUI/res/drawable/qs_customize_tile_decoration.xml @@ -0,0 +1,17 @@ +<!-- + Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<color xmlns:android="http://schemas.android.com/apk/res/android" + android:color="@color/qs_customize_decoration"/>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/qs_customizer_background_primary.xml b/packages/SystemUI/res/drawable/qs_customizer_background_primary.xml index abe1429697ac..215dee41d548 100644 --- a/packages/SystemUI/res/drawable/qs_customizer_background_primary.xml +++ b/packages/SystemUI/res/drawable/qs_customizer_background_primary.xml @@ -15,7 +15,7 @@ --> <inset xmlns:android="http://schemas.android.com/apk/res/android"> <shape> - <solid android:color="?android:attr/colorPrimary"/> + <solid android:color="@color/qs_customize_background"/> <corners android:radius="?android:attr/dialogCornerRadius" /> </shape> </inset> diff --git a/packages/SystemUI/res/drawable/qs_customizer_toolbar.xml b/packages/SystemUI/res/drawable/qs_customizer_toolbar.xml index 557cae150303..648d45b4f903 100644 --- a/packages/SystemUI/res/drawable/qs_customizer_toolbar.xml +++ b/packages/SystemUI/res/drawable/qs_customizer_toolbar.xml @@ -15,7 +15,7 @@ --> <inset xmlns:android="http://schemas.android.com/apk/res/android"> <shape> - <solid android:color="?android:attr/colorSecondary"/> + <solid android:color="@color/qs_customize_background"/> <corners android:topLeftRadius="?android:attr/dialogCornerRadius" android:topRightRadius="?android:attr/dialogCornerRadius" /> diff --git a/packages/SystemUI/res/layout-sw600dp/navigation_layout_rot90.xml b/packages/SystemUI/res/layout-sw600dp/navigation_layout_rot90.xml deleted file mode 100644 index 78304fd894dd..000000000000 --- a/packages/SystemUI/res/layout-sw600dp/navigation_layout_rot90.xml +++ /dev/null @@ -1,44 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2016 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<FrameLayout - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:systemui="http://schemas.android.com/apk/res-auto" - android:layout_width="match_parent" - android:layout_height="match_parent"> - - <FrameLayout - android:id="@+id/nav_buttons" - android:layout_width="match_parent" - android:layout_height="match_parent"> - - <LinearLayout - android:id="@+id/ends_group" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="horizontal" - android:clipChildren="false" /> - - <LinearLayout - android:id="@+id/center_group" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:gravity="center" - android:orientation="horizontal" - android:clipChildren="false" /> - - </FrameLayout> - -</FrameLayout> diff --git a/packages/SystemUI/res/layout/bubble_expanded_view.xml b/packages/SystemUI/res/layout/bubble_expanded_view.xml index f664c0581d7e..56a3cd5499e3 100644 --- a/packages/SystemUI/res/layout/bubble_expanded_view.xml +++ b/packages/SystemUI/res/layout/bubble_expanded_view.xml @@ -29,36 +29,22 @@ <FrameLayout android:id="@+id/header_permission_wrapper" - android:layout_width="wrap_content" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:animateLayoutChanges="true" - android:background="@drawable/bubble_expanded_header_bg"> + android:animateLayoutChanges="true"> <LinearLayout android:id="@+id/header_layout" android:layout_height="@dimen/bubble_expanded_header_height" android:layout_width="match_parent" android:animateLayoutChanges="true" + android:gravity="end|center_vertical" android:orientation="horizontal"> - <TextView - android:id="@+id/header_text" - android:textAppearance="@*android:style/TextAppearance.Material.Title" - android:textSize="18sp" - android:layout_weight="1" - android:layout_width="0dp" - android:layout_height="match_parent" - android:gravity="start|center_vertical" - android:singleLine="true" - android:paddingLeft="@dimen/bubble_expanded_header_horizontal_padding" - android:paddingRight="@dimen/bubble_expanded_header_horizontal_padding" - /> - <ImageButton android:id="@+id/deep_link_button" android:layout_width="@dimen/bubble_header_icon_size" android:layout_height="@dimen/bubble_header_icon_size" - android:gravity="end|center_vertical" android:src="@drawable/ic_open_in_new" android:scaleType="center" android:tint="?android:attr/colorForeground" @@ -70,7 +56,6 @@ android:layout_width="@dimen/bubble_header_icon_size" android:layout_height="@dimen/bubble_header_icon_size" android:src="@drawable/ic_settings" - android:gravity="end|center_vertical" android:scaleType="center" android:tint="?android:attr/colorForeground" android:background="?android:attr/selectableItemBackground" diff --git a/packages/SystemUI/res/layout/bubble_permission_view.xml b/packages/SystemUI/res/layout/bubble_permission_view.xml index 7fbb78a6feee..c9d8a9128d7c 100644 --- a/packages/SystemUI/res/layout/bubble_permission_view.xml +++ b/packages/SystemUI/res/layout/bubble_permission_view.xml @@ -17,7 +17,7 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" - android:layout_height="wrap_content" + android:layout_height="@dimen/bubble_permission_height" android:animateLayoutChanges="true" android:orientation="vertical" android:paddingStart="@dimen/bubble_expanded_header_horizontal_padding" diff --git a/packages/SystemUI/res/layout/keyguard_status_bar.xml b/packages/SystemUI/res/layout/keyguard_status_bar.xml index 5b9816d91965..66c0c5c275ed 100644 --- a/packages/SystemUI/res/layout/keyguard_status_bar.xml +++ b/packages/SystemUI/res/layout/keyguard_status_bar.xml @@ -71,7 +71,7 @@ android:gravity="center_vertical" android:ellipsize="marquee" android:textDirection="locale" - android:textAppearance="?android:attr/textAppearanceSmall" + android:textAppearance="@style/TextAppearance.StatusBar.Clock" android:textColor="?attr/wallpaperTextColorSecondary" android:singleLine="true" systemui:showMissingSim="true" diff --git a/packages/SystemUI/res/layout/nav_bar_tuner_inflater.xml b/packages/SystemUI/res/layout/nav_bar_tuner_inflater.xml index b2376333a30e..133b2158c771 100644 --- a/packages/SystemUI/res/layout/nav_bar_tuner_inflater.xml +++ b/packages/SystemUI/res/layout/nav_bar_tuner_inflater.xml @@ -20,8 +20,8 @@ android:layout_width="match_parent" android:layout_height="@dimen/navigation_bar_size"> - <include android:id="@+id/rot0" layout="@layout/navigation_layout" /> + <include android:id="@+id/horizontal" layout="@layout/navigation_layout" /> - <include android:id="@+id/rot90" layout="@layout/navigation_layout_rot90" /> + <include android:id="@+id/vertical" layout="@layout/navigation_layout_vertical" /> </com.android.systemui.tuner.PreviewNavInflater> diff --git a/packages/SystemUI/res/layout/navigation_layout.xml b/packages/SystemUI/res/layout/navigation_layout.xml index d72021e27e0b..db1c79d24c54 100644 --- a/packages/SystemUI/res/layout/navigation_layout.xml +++ b/packages/SystemUI/res/layout/navigation_layout.xml @@ -22,7 +22,8 @@ android:layout_marginStart="@dimen/rounded_corner_content_padding" android:layout_marginEnd="@dimen/rounded_corner_content_padding" android:paddingStart="@dimen/nav_content_padding" - android:paddingEnd="@dimen/nav_content_padding"> + android:paddingEnd="@dimen/nav_content_padding" + android:id="@+id/horizontal"> <com.android.systemui.statusbar.phone.NearestTouchFrame android:id="@+id/nav_buttons" diff --git a/packages/SystemUI/res/layout/navigation_layout_rot90.xml b/packages/SystemUI/res/layout/navigation_layout_vertical.xml index 24a0c71f3bad..285c5c4e0a01 100644 --- a/packages/SystemUI/res/layout/navigation_layout_rot90.xml +++ b/packages/SystemUI/res/layout/navigation_layout_vertical.xml @@ -22,7 +22,8 @@ android:layout_marginTop="@dimen/rounded_corner_content_padding" android:layout_marginBottom="@dimen/rounded_corner_content_padding" android:paddingTop="@dimen/nav_content_padding" - android:paddingBottom="@dimen/nav_content_padding"> + android:paddingBottom="@dimen/nav_content_padding" + android:id="@+id/vertical"> <com.android.systemui.statusbar.phone.NearestTouchFrame android:id="@+id/nav_buttons" diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml index 91353d7fb8ba..863c1ccd7eaf 100644 --- a/packages/SystemUI/res/layout/notification_info.xml +++ b/packages/SystemUI/res/layout/notification_info.xml @@ -178,6 +178,7 @@ asked for it --> android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" + android:maxWidth="100dp" style="@style/TextAppearance.NotificationInfo.Button"/> <LinearLayout @@ -186,6 +187,7 @@ asked for it --> android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_alignParentEnd="true" + android:maxWidth="200dp" android:orientation="horizontal"> <TextView android:id="@+id/deliver_silently" @@ -195,6 +197,7 @@ asked for it --> android:layout_centerVertical="true" android:layout_marginStart="@dimen/notification_guts_button_horizontal_spacing" android:paddingRight="24dp" + android:maxWidth="125dp" style="@style/TextAppearance.NotificationInfo.Button"/> <TextView android:id="@+id/block" @@ -203,6 +206,7 @@ asked for it --> android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" + android:maxWidth="75dp" android:layout_marginStart="@dimen/notification_guts_button_horizontal_spacing" style="@style/TextAppearance.NotificationInfo.Button"/> <TextView @@ -212,6 +216,7 @@ asked for it --> android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" + android:maxWidth="75dp" android:layout_marginStart="@dimen/notification_guts_button_horizontal_spacing" style="@style/TextAppearance.NotificationInfo.Button"/> </LinearLayout> diff --git a/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml b/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml index bc15f2c46b47..665fc3fbf3da 100644 --- a/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml +++ b/packages/SystemUI/res/layout/ongoing_privacy_dialog_content.xml @@ -54,9 +54,6 @@ android:orientation="vertical" android:gravity="start" /> - - <include android:id="@+id/overflow" layout="@layout/ongoing_privacy_dialog_item" - android:visibility="gone" /> </LinearLayout> </LinearLayout> diff --git a/packages/SystemUI/res/layout/qs_customize_divider.xml b/packages/SystemUI/res/layout/qs_customize_divider.xml index 6fcfa8d27a7a..d6664fefe2da 100644 --- a/packages/SystemUI/res/layout/qs_customize_divider.xml +++ b/packages/SystemUI/res/layout/qs_customize_divider.xml @@ -24,5 +24,4 @@ android:paddingTop="20dp" android:paddingBottom="13dp" android:textAppearance="@style/TextAppearance.QSEdit.Headers" - android:textColor="?android:attr/colorAccent" android:text="@string/drag_to_add_tiles" /> diff --git a/packages/SystemUI/res/layout/qs_customize_header.xml b/packages/SystemUI/res/layout/qs_customize_header.xml index d54dfee8c670..58066a3ef19c 100644 --- a/packages/SystemUI/res/layout/qs_customize_header.xml +++ b/packages/SystemUI/res/layout/qs_customize_header.xml @@ -24,5 +24,4 @@ android:gravity="center" android:minHeight="@dimen/qs_customize_header_min_height" android:textAppearance="@style/TextAppearance.QSEdit.Headers" - android:textColor="?android:attr/colorAccent" android:text="@string/drag_to_rearrange_tiles" />
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/qs_customize_panel_content.xml b/packages/SystemUI/res/layout/qs_customize_panel_content.xml index 87b455129835..09f512f72f84 100644 --- a/packages/SystemUI/res/layout/qs_customize_panel_content.xml +++ b/packages/SystemUI/res/layout/qs_customize_panel_content.xml @@ -37,7 +37,8 @@ android:layout_height="wrap_content" android:background="@drawable/qs_customizer_toolbar" android:navigationContentDescription="@*android:string/action_bar_up_description" - style="?android:attr/toolbarStyle" /> + style="@style/QSCustomizeToolbar" + /> <androidx.recyclerview.widget.RecyclerView android:id="@android:id/list" diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml index e67bb60555f3..ca34c23eca1f 100644 --- a/packages/SystemUI/res/layout/volume_dialog.xml +++ b/packages/SystemUI/res/layout/volume_dialog.xml @@ -87,7 +87,7 @@ android:background="@drawable/rounded_bg_bottom_background"> <com.android.keyguard.AlphaOptimizedImageButton android:id="@+id/settings" - android:src="@drawable/ic_settings_16dp" + android:src="@drawable/ic_tune_black_16dp" android:layout_width="@dimen/volume_dialog_tap_target_size" android:layout_height="@dimen/volume_dialog_tap_target_size" android:layout_gravity="center" diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml index 2c5120dd06f3..5803bf129c24 100644 --- a/packages/SystemUI/res/values-night/colors.xml +++ b/packages/SystemUI/res/values-night/colors.xml @@ -41,4 +41,10 @@ <!-- The color of the text inside a notification --> <color name="notification_primary_text_color">@*android:color/notification_primary_text_color_dark</color> + + <!-- The color of the background in the top part of QSCustomizer --> + <color name="qs_customize_background">@color/GM2_grey_900</color> + + <!-- The color of the background in the bottom part of QSCustomizer --> + <color name="qs_customize_decoration">@color/GM2_grey_800</color> </resources>
\ No newline at end of file diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index d5f29badd893..0e4ffee81165 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -35,6 +35,8 @@ <color name="status_bar_clock_color">#FFFFFFFF</color> <color name="qs_user_detail_icon_muted">#FFFFFFFF</color> <!-- not so muted after all --> <color name="qs_tile_disabled_color">#9E9E9E</color> <!-- 38% black --> + <color name="qs_customize_background">@color/GM2_grey_50</color> + <color name="qs_customize_decoration">@color/GM2_grey_100</color> <!-- Tint color for the content on the notification overflow card. --> <color name="keyguard_overflow_content_color">#ff686868</color> @@ -72,7 +74,7 @@ <color name="notification_primary_text_color">@*android:color/notification_primary_text_color_light</color> <!-- The "inside" of a notification, reached via longpress --> - <color name="notification_guts_bg_color">#f8f9fa</color> + <color name="notification_guts_bg_color">@color/GM2_grey_50</color> <color name="assist_orb_color">#ffffff</color> @@ -123,7 +125,7 @@ <color name="zen_introduction">#ffffffff</color> - <color name="smart_reply_button_text">#5F6368</color> + <color name="smart_reply_button_text">@color/GM2_grey_700</color> <color name="smart_reply_button_text_dark_bg">@*android:color/notification_primary_text_color_dark</color> <color name="smart_reply_button_background">#ffffffff</color> <color name="smart_reply_button_stroke">#ffdadce0</color> @@ -134,4 +136,17 @@ <!-- Logout button --> <color name="logout_button_bg_color">#ccffffff</color> + + <!-- GM2 colors --> + <color name="GM2_grey_50">#F8F9FA</color> + <color name="GM2_grey_100">#F1F3F4</color> + <color name="GM2_grey_200">#E8EAED</color> + <color name="GM2_grey_300">#DADCE0</color> + <color name="GM2_grey_400">#BDC1C6</color> + <color name="GM2_grey_500">#9AA0A6</color> + <color name="GM2_grey_600">#80868B</color> + <color name="GM2_grey_650">#70757A</color> + <color name="GM2_grey_700">#5F6368</color> + <color name="GM2_grey_800">#3C4043</color> + <color name="GM2_grey_900">#202124</color> </resources> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 4396a42929ba..b4dc0eff0761 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -329,16 +329,11 @@ <bool name="quick_settings_show_full_alarm">false</bool> - <!-- Whether to show a warning notification when the device reaches a certain temperature. --> + <!-- Whether to show a warning notification when device's skin temperature is high. --> <integer name="config_showTemperatureWarning">0</integer> - <!-- Temp at which to show a warning notification if config_showTemperatureWarning is true. - If < 0, uses the skin temperature sensor shutdown value from - HardwarePropertiesManager#getDeviceTemperatures - config_warningTemperatureTolerance. --> - <integer name="config_warningTemperature">-1</integer> - - <!-- Fudge factor for how much below the shutdown temp to show the warning. --> - <integer name="config_warningTemperatureTolerance">2</integer> + <!-- Whether to show a alarm dialog when device's usb port is overheating. --> + <integer name="config_showUsbPortAlarm">0</integer> <!-- Accessibility actions --> <item type="id" name="action_split_task_to_left" /> @@ -485,4 +480,5 @@ </string-array> <integer name="ongoing_appops_dialog_max_apps">5</integer> + </resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 371a0604c45b..50cf37beb878 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -44,8 +44,9 @@ <!-- Height of the battery icon in the status bar. --> <dimen name="status_bar_battery_icon_height">13.0dp</dimen> - <!-- Width of the battery icon in the status bar. --> - <dimen name="status_bar_battery_icon_width">8.5dp</dimen> + <!-- Width of the battery icon in the status bar. The battery drawable assumes a 12x20 canvas, + so the width of the icon should be 13.0dp * (12.0 / 20.0) --> + <dimen name="status_bar_battery_icon_width">7.8dp</dimen> <!-- The font size for the clock in the status bar. --> <dimen name="status_bar_clock_size">14sp</dimen> @@ -1012,7 +1013,7 @@ <!-- How much to inset the icon in the circle --> <dimen name="bubble_icon_inset">16dp</dimen> <!-- Padding around the view displayed when the bubble is expanded --> - <dimen name="bubble_expanded_view_padding">8dp</dimen> + <dimen name="bubble_expanded_view_padding">4dp</dimen> <!-- Default (and minimum) height of the expanded view shown when the bubble is expanded --> <dimen name="bubble_expanded_default_height">180dp</dimen> <!-- Height of the triangle that points to the expanded bubble --> @@ -1045,4 +1046,11 @@ <dimen name="bubble_header_icon_size">48dp</dimen> <!-- Size of the app icon shown in the bubble permission view --> <dimen name="bubble_permission_icon_size">24dp</dimen> + <!-- Space between the pointer triangle and the bubble expanded view --> + <dimen name="bubble_pointer_margin">8dp</dimen> + <!-- Height of the permission prompt shown with bubbles --> + <dimen name="bubble_permission_height">120dp</dimen> + + <!-- Size of the RAT type for CellularTile --> + <dimen name="celltile_rat_type_size">10sp</dimen> </resources> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 0fde2de1e2f1..d53c78543b65 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1579,7 +1579,7 @@ <string name="inline_blocking_helper">You usually dismiss these notifications. \nKeep showing them?</string> - <!-- Notification Inline controls: button to dismiss the blocking helper [CHAR_LIMIT=25] --> + <!-- Notification Inline controls: button to dismiss the blocking helper [CHAR_LIMIT=20] --> <string name="inline_done_button">Done</string> <!-- Notification Inline controls: continue receiving notifications prompt, channel level --> @@ -1588,7 +1588,7 @@ <!-- Notification inline controls: block notifications button [CHAR_LIMIT=25] --> <string name="inline_stop_button">Stop notifications</string> - <!-- Notification inline controls: button to deliver notifications silently from this channel [CHAR_LIMIT=35] --> + <!-- Notification inline controls: button to deliver notifications silently from this channel [CHAR_LIMIT=30] --> <string name="inline_deliver_silently_button">Deliver Silently</string> <!-- Notification inline controls: button to block notifications from this channel [CHAR_LIMIT=20] --> @@ -2118,6 +2118,14 @@ <string name="high_temp_notif_message">Some features limited while phone cools down</string> <!-- Text body for dialog alerting user that their phone has reached a certain temperature and may start to slow down in order to cool down. [CHAR LIMIT=300] --> <string name="high_temp_dialog_message">Your phone will automatically try to cool down. You can still use your phone, but it may run slower.\n\nOnce your phone has cooled down, it will run normally.</string> + <!-- Title for alarm dialog alerting user the usb adapter has reached a certain temperature that should disconnect charging cable immediately. [CHAR LIMIT=30] --> + <string name="high_temp_alarm_title">Unplug charger</string> + <!-- Text body for dialog alerting user the usb adapter has reached a certain temperature that should disconnect charging cable immediately. [CHAR LIMIT=300] --> + <string name="high_temp_alarm_notify_message">There\u2019s an issue charging this device. Unplug the power adapter, and take care as the cable may be warm.</string> + <!-- Text for See care steps button [CHAR LIMIT=300] --> + <string name="high_temp_alarm_help_care_steps">See care steps</string> + <!-- Text link directs to usb overheat help page. --> + <string name="high_temp_alarm_help_url" translatable="false">help_uri_usb_warm</string> <!-- SysUI Tuner: Button to select lock screen shortcut [CHAR LIMIT=60] --> <string name="lockscreen_shortcut_left">Left shortcut</string> @@ -2378,5 +2386,4 @@ <string name="no_bubbles">Block</string> <!-- Text used for button allowing user to approve / enable bubbles [CHAR LIMIT=20] --> <string name="yes_bubbles">Allow</string> - </resources> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 96cdbd38ed0b..30dbc8bd1d20 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -216,6 +216,12 @@ <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item> </style> + <!-- This is hard coded to be sans-serif-condensed to match the icons --> + <style name="TextAppearance.RATBadge" parent="@style/TextAppearance.QS.TileLabel.Secondary"> + <item name="android:fontFamily">sans-serif-condensed</item> + <item name="android:textSize">@dimen/celltile_rat_type_size</item> + </style> + <style name="TextAppearance.QS.CarrierInfo"> <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item> <item name="android:textSize">@dimen/qs_carrier_info_text_size</item> @@ -458,8 +464,15 @@ </style> <style name="TextAppearance.QSEdit.Headers" - parent="@*android:style/TextAppearance.Material.Body2"> - <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item> + parent="@*android:style/TextAppearance.DeviceDefault.Body2"> + <item name="android:textSize">11sp</item> + <item name="android:textColor">?android:attr/textColorSecondary</item> + <item name="android:textAllCaps">true</item> + </style> + + <style name="QSCustomizeToolbar" parent="@*android:style/Widget.DeviceDefault.Toolbar"> + <item name="android:textColor">?android:attr/textColorPrimary</item> + <item name="android:elevation">10dp</item> </style> <style name="edit_theme" parent="qs_theme"> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java index 0cde9daa81b3..e554dcd36100 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java @@ -27,26 +27,26 @@ import android.os.RemoteException; import android.util.Log; import android.view.BatchedInputEventReceiver; import android.view.Choreographer; +import android.view.IWindowManager; import android.view.InputChannel; import android.view.InputEvent; -import android.view.IWindowManager; -import android.view.MotionEvent; import android.view.WindowManagerGlobal; import java.io.PrintWriter; /** - * Manages the input consumer that allows the SystemUI to directly receive touch input. + * Manages the input consumer that allows the SystemUI to directly receive input. */ public class InputConsumerController { private static final String TAG = InputConsumerController.class.getSimpleName(); /** - * Listener interface for callers to subscribe to touch events. + * Listener interface for callers to subscribe to input events. */ - public interface TouchListener { - boolean onTouchEvent(MotionEvent ev); + public interface InputListener { + /** Handles any input event. */ + boolean onInputEvent(InputEvent ev); } /** @@ -71,9 +71,8 @@ public class InputConsumerController { public void onInputEvent(InputEvent event) { boolean handled = true; try { - if (mListener != null && event instanceof MotionEvent) { - MotionEvent ev = (MotionEvent) event; - handled = mListener.onTouchEvent(ev); + if (mListener != null) { + handled = mListener.onInputEvent(event); } } finally { finishInputEvent(event, handled); @@ -86,7 +85,7 @@ public class InputConsumerController { private final String mName; private InputEventReceiver mInputEventReceiver; - private TouchListener mListener; + private InputListener mListener; private RegistrationListener mRegistrationListener; /** @@ -115,9 +114,9 @@ public class InputConsumerController { } /** - * Sets the touch listener. + * Sets the input listener. */ - public void setTouchListener(TouchListener listener) { + public void setInputListener(InputListener listener) { mListener = listener; } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java new file mode 100644 index 000000000000..3ae2df5b97bf --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/PinnedStackListenerForwarder.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.shared.system; + +import android.content.pm.ParceledListSlice; +import android.graphics.Rect; +import android.os.RemoteException; +import android.view.IPinnedStackController; +import android.view.IPinnedStackListener; + +import java.util.ArrayList; +import java.util.List; + +/** + * PinnedStackListener that simply forwards all calls to each listener added via + * {@link #addListener}. This is necessary since calling + * {@link com.android.server.wm.WindowManagerService#registerPinnedStackListener} replaces any + * previously set listener. + */ +public class PinnedStackListenerForwarder extends IPinnedStackListener.Stub { + private List<IPinnedStackListener> mListeners = new ArrayList<>(); + + /** Adds a listener to receive updates from the WindowManagerService. */ + public void addListener(IPinnedStackListener listener) { + mListeners.add(listener); + } + + /** Removes a listener so it will no longer receive updates from the WindowManagerService. */ + public void removeListener(IPinnedStackListener listener) { + mListeners.remove(listener); + } + + @Override + public void onListenerRegistered(IPinnedStackController controller) throws RemoteException { + for (IPinnedStackListener listener : mListeners) { + listener.onListenerRegistered(controller); + } + } + + @Override + public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds, Rect animatingBounds, + boolean fromImeAdjustment, boolean fromShelfAdjustment, int displayRotation) + throws RemoteException { + for (IPinnedStackListener listener : mListeners) { + listener.onMovementBoundsChanged( + insetBounds, normalBounds, animatingBounds, + fromImeAdjustment, fromShelfAdjustment, displayRotation); + } + } + + @Override + public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) throws RemoteException { + for (IPinnedStackListener listener : mListeners) { + listener.onImeVisibilityChanged(imeVisible, imeHeight); + } + } + + @Override + public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) + throws RemoteException { + for (IPinnedStackListener listener : mListeners) { + listener.onShelfVisibilityChanged(shelfVisible, shelfHeight); + } + } + + @Override + public void onMinimizedStateChanged(boolean isMinimized) throws RemoteException { + for (IPinnedStackListener listener : mListeners) { + listener.onMinimizedStateChanged(isMinimized); + } + } + + @Override + public void onActionsChanged(ParceledListSlice actions) throws RemoteException { + for (IPinnedStackListener listener : mListeners) { + listener.onActionsChanged(actions); + } + } +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/StatsLogCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/StatsLogCompat.java index 5bc1f2511411..d17725f1e219 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/StatsLogCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/StatsLogCompat.java @@ -32,4 +32,17 @@ public class StatsLogCompat { StatsLog.write(19, action, srcState, dstState, extension, swipeUpEnabled); } + + /** + * StatsLog.write(StatsLog.STYLE_EVENT, action, colorPackageHash, + * fontPackageHash, shapePackageHash, clockPackageHash, + * launcherGrid, wallpaperCategoryHash, wallpaperIdHash); + */ + public static void write(int action, int colorPackageHash, + int fontPackageHash, int shapePackageHash, int clockPackageHash, + int launcherGrid, int wallpaperCategoryHash, int wallpaperIdHash) { + StatsLog.write(179, action, colorPackageHash, + fontPackageHash, shapePackageHash, clockPackageHash, + launcherGrid, wallpaperCategoryHash, wallpaperIdHash); + } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java index e6acfbe8df53..39da19423ee5 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java @@ -51,7 +51,7 @@ public class SyncRtSurfaceTransactionApplierCompat { * this method to avoid synchronization issues. */ public void scheduleApply(final SyncRtSurfaceTransactionApplierCompat.SurfaceParams... params) { - if (mTargetViewRootImpl == null) { + if (mTargetViewRootImpl == null || mTargetViewRootImpl.getView() == null) { return; } mTargetViewRootImpl.registerRtFrameCallback(new HardwareRenderer.FrameDrawingCallback() { diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java index f9aa8da5e11e..c54a4699964d 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListener.java @@ -16,7 +16,7 @@ package com.android.systemui.shared.system; -import android.app.ActivityManager.TaskSnapshot; +import android.app.ActivityManager.RunningTaskInfo; import android.content.ComponentName; import android.os.UserHandle; import android.util.Log; @@ -43,10 +43,35 @@ public abstract class TaskStackChangeListener { public void onActivityForcedResizable(String packageName, int taskId, int reason) { } public void onActivityDismissingDockedStack() { } public void onActivityLaunchOnSecondaryDisplayFailed() { } + + public void onActivityLaunchOnSecondaryDisplayFailed(RunningTaskInfo taskInfo) { + onActivityLaunchOnSecondaryDisplayFailed(); + } + + /** + * @see #onActivityLaunchOnSecondaryDisplayRerouted(RunningTaskInfo taskInfo) + */ + public void onActivityLaunchOnSecondaryDisplayRerouted() { } + + /** + * Called when an activity was requested to be launched on a secondary display but was rerouted + * to default display. + * + * @param taskInfo info about the Activity's task + */ + public void onActivityLaunchOnSecondaryDisplayRerouted(RunningTaskInfo taskInfo) { + onActivityLaunchOnSecondaryDisplayRerouted(); + } + public void onTaskProfileLocked(int taskId, int userId) { } public void onTaskCreated(int taskId, ComponentName componentName) { } public void onTaskRemoved(int taskId) { } public void onTaskMovedToFront(int taskId) { } + + public void onTaskMovedToFront(RunningTaskInfo taskInfo) { + onTaskMovedToFront(taskInfo.taskId); + } + public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) { } /** diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java index 628b3c6e21dc..7e4ab854adcf 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/TaskStackChangeListeners.java @@ -16,8 +16,9 @@ package com.android.systemui.shared.system; -import android.app.ActivityTaskManager; +import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityManager.TaskSnapshot; +import android.app.ActivityTaskManager; import android.app.IActivityManager; import android.app.TaskStackListener; import android.content.ComponentName; @@ -132,8 +133,18 @@ public class TaskStackChangeListeners extends TaskStackListener { } @Override - public void onActivityLaunchOnSecondaryDisplayFailed() throws RemoteException { - mHandler.sendEmptyMessage(H.ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED); + public void onActivityLaunchOnSecondaryDisplayFailed(RunningTaskInfo taskInfo, + int requestedDisplayId) throws RemoteException { + mHandler.obtainMessage(H.ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED, requestedDisplayId, + 0 /* unused */, + taskInfo).sendToTarget(); + } + + @Override + public void onActivityLaunchOnSecondaryDisplayRerouted(RunningTaskInfo taskInfo, + int requestedDisplayId) throws RemoteException { + mHandler.obtainMessage(H.ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED, + requestedDisplayId, 0 /* unused */, taskInfo).sendToTarget(); } @Override @@ -157,8 +168,9 @@ public class TaskStackChangeListeners extends TaskStackListener { } @Override - public void onTaskMovedToFront(int taskId) throws RemoteException { - mHandler.obtainMessage(H.ON_TASK_MOVED_TO_FRONT, taskId, 0).sendToTarget(); + public void onTaskMovedToFront(RunningTaskInfo taskInfo) + throws RemoteException { + mHandler.obtainMessage(H.ON_TASK_MOVED_TO_FRONT, taskInfo).sendToTarget(); } @Override @@ -184,6 +196,7 @@ public class TaskStackChangeListeners extends TaskStackListener { private static final int ON_TASK_REMOVED = 13; private static final int ON_TASK_MOVED_TO_FRONT = 14; private static final int ON_ACTIVITY_REQUESTED_ORIENTATION_CHANGE = 15; + private static final int ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED = 16; public H(Looper looper) { @@ -258,8 +271,18 @@ public class TaskStackChangeListeners extends TaskStackListener { break; } case ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED: { + final RunningTaskInfo info = (RunningTaskInfo) msg.obj; + for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { + mTaskStackListeners.get(i) + .onActivityLaunchOnSecondaryDisplayFailed(info); + } + break; + } + case ON_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED: { + final RunningTaskInfo info = (RunningTaskInfo) msg.obj; for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { - mTaskStackListeners.get(i).onActivityLaunchOnSecondaryDisplayFailed(); + mTaskStackListeners.get(i) + .onActivityLaunchOnSecondaryDisplayRerouted(info); } break; } @@ -283,15 +306,16 @@ public class TaskStackChangeListeners extends TaskStackListener { break; } case ON_TASK_MOVED_TO_FRONT: { + final RunningTaskInfo info = (RunningTaskInfo) msg.obj; for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { - mTaskStackListeners.get(i).onTaskMovedToFront(msg.arg1); + mTaskStackListeners.get(i).onTaskMovedToFront(info); } break; } case ON_ACTIVITY_REQUESTED_ORIENTATION_CHANGE: { for (int i = mTaskStackListeners.size() - 1; i >= 0; i--) { - mTaskStackListeners.get(i).onActivityRequestedOrientationChanged( - msg.arg1, msg.arg2); + mTaskStackListeners.get(i) + .onActivityRequestedOrientationChanged(msg.arg1, msg.arg2); } break; } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java index 8a251ae51f38..10996e884fb5 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java @@ -27,6 +27,7 @@ import android.graphics.Rect; import android.os.Handler; import android.os.RemoteException; import android.util.Log; +import android.view.IPinnedStackListener; import android.view.WindowManager; import android.view.WindowManagerGlobal; @@ -81,6 +82,13 @@ public class WindowManagerWrapper { private static final WindowManagerWrapper sInstance = new WindowManagerWrapper(); + /** + * Forwarder to which we can add multiple pinned stack listeners. Each listener will receive + * updates from the window manager service. + */ + private PinnedStackListenerForwarder mPinnedStackListenerForwarder = + new PinnedStackListenerForwarder(); + public static WindowManagerWrapper getInstance() { return sInstance; } @@ -199,4 +207,14 @@ public class WindowManagerWrapper { Log.w(TAG, "Failed to register docked stack listener"); } } + + /** + * Adds a pinned stack listener, which will receive updates from the window manager service + * along with any other pinned stack listeners that were added via this method. + */ + public void addPinnedStackListener(IPinnedStackListener listener) throws RemoteException { + mPinnedStackListenerForwarder.addListener(listener); + WindowManagerGlobal.getWindowManagerService().registerPinnedStackListener( + DEFAULT_DISPLAY, mPinnedStackListenerForwarder); + } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java index a055950a5522..4cb8d90927fd 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java @@ -34,6 +34,8 @@ import com.android.internal.util.LatencyTracker; import com.android.internal.widget.LockPatternChecker; import com.android.internal.widget.LockPatternUtils; +import java.util.Arrays; + /** * Base class for PIN and password unlock screens. */ @@ -124,18 +126,19 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout protected void verifyPasswordAndUnlock() { if (mDismissing) return; // already verified but haven't been dismissed; don't do it again. - final String entry = getPasswordText(); + final byte[] entry = getPasswordText(); setPasswordEntryInputEnabled(false); if (mPendingLockCheck != null) { mPendingLockCheck.cancel(false); } final int userId = KeyguardUpdateMonitor.getCurrentUser(); - if (entry.length() <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) { + if (entry.length <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) { // to avoid accidental lockout, only count attempts that are long enough to be a // real password. This may require some tweaking. setPasswordEntryInputEnabled(true); onPasswordChecked(userId, false /* matched */, 0, false /* not valid - too short */); + Arrays.fill(entry, (byte) 0); return; } @@ -157,6 +160,7 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout } onPasswordChecked(userId, true /* matched */, 0 /* timeoutMs */, true /* isValidPassword */); + Arrays.fill(entry, (byte) 0); } @Override @@ -171,6 +175,7 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout onPasswordChecked(userId, false /* matched */, timeoutMs, true /* isValidPassword */); } + Arrays.fill(entry, (byte) 0); } @Override @@ -181,6 +186,7 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout LatencyTracker.getInstance(mContext).onActionEnd( ACTION_CHECK_CREDENTIAL_UNLOCKED); } + Arrays.fill(entry, (byte) 0); } }); } @@ -211,7 +217,7 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout } protected abstract void resetPasswordText(boolean animate, boolean announce); - protected abstract String getPasswordText(); + protected abstract byte[] getPasswordText(); protected abstract void setPasswordEntryEnabled(boolean enabled); protected abstract void setPasswordEntryInputEnabled(boolean enabled); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java index 583dac7996ae..9dd97170437d 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java @@ -17,7 +17,6 @@ package com.android.keyguard; import static android.view.Display.DEFAULT_DISPLAY; -import android.annotation.Nullable; import android.app.Presentation; import android.content.Context; import android.graphics.Point; @@ -32,15 +31,11 @@ import android.view.DisplayInfo; import android.view.View; import android.view.WindowManager; -import java.util.function.BooleanSupplier; - // TODO(multi-display): Support multiple external displays public class KeyguardDisplayManager { protected static final String TAG = "KeyguardDisplayManager"; private static boolean DEBUG = KeyguardConstants.DEBUG; - private final ViewMediatorCallback mCallback; - private final MediaRouter mMediaRouter; private final DisplayManager mDisplayService; private final Context mContext; @@ -57,7 +52,7 @@ public class KeyguardDisplayManager { public void onDisplayAdded(int displayId) { final Display display = mDisplayService.getDisplay(displayId); if (mShowing) { - notifyIfChanged(() -> showPresentation(display)); + showPresentation(display); } } @@ -76,13 +71,12 @@ public class KeyguardDisplayManager { @Override public void onDisplayRemoved(int displayId) { - notifyIfChanged(() -> hidePresentation(displayId)); + hidePresentation(displayId); } }; - public KeyguardDisplayManager(Context context, ViewMediatorCallback callback) { + public KeyguardDisplayManager(Context context) { mContext = context; - mCallback = callback; mMediaRouter = mContext.getSystemService(MediaRouter.class); mDisplayService = mContext.getSystemService(DisplayManager.class); mDisplayService.registerDisplayListener(mDisplayListener, null /* handler */); @@ -138,42 +132,13 @@ public class KeyguardDisplayManager { /** * @param displayId The id of the display to hide the presentation off. - * @return {@code true} if the a presentation was removed. - * {@code false} if the presentation was not added before. */ - private boolean hidePresentation(int displayId) { + private void hidePresentation(int displayId) { final Presentation presentation = mPresentations.get(displayId); if (presentation != null) { presentation.dismiss(); mPresentations.remove(displayId); - return true; - } - return false; - } - - private void notifyIfChanged(BooleanSupplier updateMethod) { - if (updateMethod.getAsBoolean()) { - final int[] displayList = getPresentationDisplayIds(); - mCallback.onSecondaryDisplayShowingChanged(displayList); - } - } - - /** - * @return An array of displayId's on which a {@link KeyguardPresentation} is showing on. - */ - @Nullable - private int[] getPresentationDisplayIds() { - final int size = mPresentations.size(); - if (size == 0) return null; - - final int[] displayIds = new int[size]; - for (int i = mPresentations.size() - 1; i >= 0; i--) { - final Presentation presentation = mPresentations.valueAt(i); - if (presentation != null) { - displayIds[i] = presentation.getDisplay().getDisplayId(); - } } - return displayIds; } public void show() { @@ -181,7 +146,7 @@ public class KeyguardDisplayManager { if (DEBUG) Log.v(TAG, "show"); mMediaRouter.addCallback(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY, mMediaRouterCallback, MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY); - notifyIfChanged(() -> updateDisplays(true /* showing */)); + updateDisplays(true /* showing */); } mShowing = true; } @@ -190,7 +155,7 @@ public class KeyguardDisplayManager { if (mShowing) { if (DEBUG) Log.v(TAG, "hide"); mMediaRouter.removeCallback(mMediaRouterCallback); - notifyIfChanged(() -> updateDisplays(false /* showing */)); + updateDisplays(false /* showing */); } mShowing = false; } @@ -200,19 +165,19 @@ public class KeyguardDisplayManager { @Override public void onRouteSelected(MediaRouter router, int type, RouteInfo info) { if (DEBUG) Log.d(TAG, "onRouteSelected: type=" + type + ", info=" + info); - notifyIfChanged(() -> updateDisplays(mShowing)); + updateDisplays(mShowing); } @Override public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) { if (DEBUG) Log.d(TAG, "onRouteUnselected: type=" + type + ", info=" + info); - notifyIfChanged(() -> updateDisplays(mShowing)); + updateDisplays(mShowing); } @Override public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo info) { if (DEBUG) Log.d(TAG, "onRoutePresentationDisplayChanged: info=" + info); - notifyIfChanged(() -> updateDisplays(mShowing)); + updateDisplays(mShowing); } }; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java index 261f391839b3..185edbfa9856 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java @@ -241,8 +241,8 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView } @Override - protected String getPasswordText() { - return mPasswordEntry.getText().toString(); + protected byte[] getPasswordText() { + return charSequenceToByteArray(mPasswordEntry.getText()); } @Override @@ -377,4 +377,18 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView return getContext().getString( com.android.internal.R.string.keyguard_accessibility_password_unlock); } + + /* + * This method avoids creating a new string when getting a byte array from EditView#getText(). + */ + private static byte[] charSequenceToByteArray(CharSequence chars) { + if (chars == null) { + return null; + } + byte[] bytes = new byte[chars.length()]; + for (int i = 0; i < chars.length(); i++) { + bytes[i] = (byte) chars.charAt(i); + } + return bytes; + } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java index 3cc18ddf3b2a..ecafc3408224 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java @@ -165,8 +165,8 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView } @Override - protected String getPasswordText() { - return mPasswordEntry.getText(); + protected byte[] getPasswordText() { + return charSequenceToByteArray(mPasswordEntry.getText()); } @Override @@ -264,4 +264,18 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView return getContext().getString( com.android.internal.R.string.keyguard_accessibility_pin_unlock); } + + /* + * This method avoids creating a new string when getting a byte array from EditView#getText(). + */ + private static byte[] charSequenceToByteArray(CharSequence chars) { + if (chars == null) { + return null; + } + byte[] bytes = new byte[chars.length()]; + for (int i = 0; i < chars.length(); i++) { + bytes[i] = (byte) chars.charAt(i); + } + return bytes; + } } diff --git a/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java b/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java index a07c5cbde956..6e0613036678 100644 --- a/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java +++ b/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java @@ -96,11 +96,6 @@ public interface ViewMediatorCallback { int getBouncerPromptReason(); /** - * Invoked when the secondary displays showing a keyguard window changes. - */ - void onSecondaryDisplayShowingChanged(int[] displayId); - - /** * Consumes a message that was enqueued to be displayed on the next time the bouncer shows up. * @return Message that should be displayed above the challenge. */ diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java index c5dc3244d0a3..42a6e22c2641 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java @@ -34,6 +34,7 @@ import android.util.DisplayMetrics; import android.view.LayoutInflater; import android.view.View; import android.view.View.MeasureSpec; +import android.view.ViewGroup; import androidx.annotation.VisibleForTesting; @@ -59,6 +60,9 @@ import javax.inject.Singleton; @Singleton public final class ClockManager { + private static final String TAG = "ClockOptsProvider"; + private static final String DEFAULT_CLOCK_ID = "default"; + private final List<ClockInfo> mClockInfos = new ArrayList<>(); /** * Map from expected value stored in settings to supplier of custom clock face. @@ -66,6 +70,7 @@ public final class ClockManager { private final Map<String, Supplier<ClockPlugin>> mClocks = new ArrayMap<>(); @Nullable private ClockPlugin mCurrentClock; + private final LayoutInflater mLayoutInflater; private final ContentResolver mContentResolver; private final SettingsWrapper mSettingsWrapper; /** @@ -121,11 +126,11 @@ public final class ClockManager { Resources res = context.getResources(); mClockInfos.add(ClockInfo.builder() - .setName("default") + .setName(DEFAULT_CLOCK_ID) .setTitle(res.getString(R.string.clock_title_default)) - .setId("default") + .setId(DEFAULT_CLOCK_ID) .setThumbnail(() -> BitmapFactory.decodeResource(res, R.drawable.default_thumbnail)) - .setPreview(() -> BitmapFactory.decodeResource(res, R.drawable.default_preview)) + .setPreview(() -> getClockPreview(DEFAULT_CLOCK_ID)) .build()); mClockInfos.add(ClockInfo.builder() .setName("bubble") @@ -150,12 +155,15 @@ public final class ClockManager { .build()); LayoutInflater layoutInflater = injectionInflater.injectable(LayoutInflater.from(context)); + mClocks.put(DEFAULT_CLOCK_ID, + () -> DefaultClockController.build(layoutInflater)); mClocks.put(BubbleClockController.class.getName(), () -> BubbleClockController.build(layoutInflater)); mClocks.put(StretchAnalogClockController.class.getName(), () -> StretchAnalogClockController.build(layoutInflater)); mClocks.put(TypeClockController.class.getName(), () -> TypeClockController.build(layoutInflater)); + mLayoutInflater = layoutInflater; // Store the size of the display for generation of clock preview. DisplayMetrics dm = res.getDisplayMetrics(); @@ -217,6 +225,7 @@ public final class ClockManager { */ @Nullable private Bitmap getClockPreview(String clockId) { + Bitmap bitmap = Bitmap.createBitmap(mWidth, mHeight, Config.ARGB_8888); Supplier<ClockPlugin> supplier = mClocks.get(clockId); if (supplier == null) { return null; @@ -239,17 +248,34 @@ public final class ClockManager { plugin.dozeTimeTick(); // Draw clock view hierarchy to canvas. - Bitmap bitmap = Bitmap.createBitmap(mWidth, mHeight, Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); + canvas.drawColor(Color.BLACK); + dispatchVisibilityAggregated(clockView, true); clockView.measure(MeasureSpec.makeMeasureSpec(mWidth, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(mHeight, MeasureSpec.EXACTLY)); clockView.layout(0, 0, mWidth, mHeight); - canvas.drawColor(Color.BLACK); clockView.draw(canvas); - return bitmap; } + private void dispatchVisibilityAggregated(View view, boolean isVisible) { + // Similar to View.dispatchVisibilityAggregated implementation. + final boolean thisVisible = view.getVisibility() == View.VISIBLE; + if (thisVisible || !isVisible) { + view.onVisibilityAggregated(isVisible); + } + + if (view instanceof ViewGroup) { + isVisible = thisVisible && isVisible; + ViewGroup vg = (ViewGroup) view; + int count = vg.getChildCount(); + + for (int i = 0; i < count; i++) { + dispatchVisibilityAggregated(vg.getChildAt(i), isVisible); + } + } + } + private void notifyClockChanged(ClockPlugin plugin) { for (int i = 0; i < mListeners.size(); i++) { // It probably doesn't make sense to supply the same plugin instances to multiple @@ -279,7 +305,11 @@ public final class ClockManager { private void reload() { mCurrentClock = getClockPlugin(); - notifyClockChanged(mCurrentClock); + if (mCurrentClock instanceof DefaultClockController) { + notifyClockChanged(null); + } else { + notifyClockChanged(mCurrentClock); + } } private ClockPlugin getClockPlugin() { diff --git a/packages/SystemUI/src/com/android/keyguard/clock/DefaultClockController.java b/packages/SystemUI/src/com/android/keyguard/clock/DefaultClockController.java new file mode 100644 index 000000000000..a4766dba599f --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/clock/DefaultClockController.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.keyguard.clock; + +import android.graphics.Paint.Style; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.TextView; + +import com.android.keyguard.R; +import com.android.systemui.plugins.ClockPlugin; + +import java.util.TimeZone; + +/** + * Plugin for the default clock face used only to provide a preview. + */ +public class DefaultClockController implements ClockPlugin { + + /** + * Root view of preview. + */ + private View mView; + /** + * Text clock in preview view hierarchy. + */ + private TextView mTextTime; + private TextView mTextDate; + + private DefaultClockController() {} + + /** + * Create a DefaultClockController instance. + * + * @param inflater Inflater used to inflate custom clock views. + */ + public static DefaultClockController build(LayoutInflater inflater) { + DefaultClockController controller = new DefaultClockController(); + controller.createViews(inflater); + return controller; + } + + private void createViews(LayoutInflater inflater) { + mView = inflater.inflate(R.layout.default_clock_preview, null); + mTextTime = mView.findViewById(R.id.time); + mTextDate = mView.findViewById(R.id.date); + } + + @Override + public View getView() { + return null; + } + + @Override + public View getBigClockView() { + return mView; + } + + @Override + public void setStyle(Style style) {} + + @Override + public void setTextColor(int color) { + mTextTime.setTextColor(color); + mTextDate.setTextColor(color); + } + + @Override + public void setColorPalette(boolean supportsDarkText, int[] colorPalette) {} + + @Override + public void dozeTimeTick() { + } + + @Override + public void setDarkAmount(float darkAmount) {} + + @Override + public void onTimeZoneChanged(TimeZone timeZone) {} + + @Override + public boolean shouldShowStatusArea() { + return true; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java index 200679432200..592b6039d69d 100644 --- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java @@ -46,7 +46,7 @@ import android.widget.LinearLayout; import android.widget.TextView; import com.android.settingslib.Utils; -import com.android.settingslib.graph.BatteryMeterDrawableBase; +import com.android.settingslib.graph.ThemedBatteryDrawable; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; import com.android.systemui.settings.CurrentUserTracker; @@ -76,7 +76,7 @@ public class BatteryMeterView extends LinearLayout implements public static final int MODE_OFF = 2; public static final int MODE_ESTIMATE = 3; - private final BatteryMeterDrawableBase mDrawable; + private final ThemedBatteryDrawable mDrawable; private final String mSlotBattery; private final ImageView mBatteryIconView; private final CurrentUserTracker mUserTracker; @@ -94,9 +94,11 @@ public class BatteryMeterView extends LinearLayout implements private boolean mIsSubscribedForTunerUpdates; private boolean mCharging; + private int mDarkModeSingleToneColor; private int mDarkModeBackgroundColor; private int mDarkModeFillColor; + private int mLightModeSingleToneColor; private int mLightModeBackgroundColor; private int mLightModeFillColor; private int mUser; @@ -106,6 +108,7 @@ public class BatteryMeterView extends LinearLayout implements */ private boolean mUseWallpaperTextColors; + private int mNonAdaptedSingleToneColor; private int mNonAdaptedForegroundColor; private int mNonAdaptedBackgroundColor; @@ -127,7 +130,7 @@ public class BatteryMeterView extends LinearLayout implements defStyle, 0); final int frameColor = atts.getColor(R.styleable.BatteryMeterView_frameColor, context.getColor(R.color.meter_background_color)); - mDrawable = new BatteryMeterDrawableBase(context, frameColor); + mDrawable = new ThemedBatteryDrawable(context, frameColor); atts.recycle(); mSettingObserver = new SettingObserver(new Handler(context.getMainLooper())); @@ -169,6 +172,10 @@ public class BatteryMeterView extends LinearLayout implements setClipChildren(false); setClipToPadding(false); Dependency.get(ConfigurationController.class).observe(viewAttachLifecycle(this), this); + + // Needed for PorderDuff.Mode.CLEAR operations to work properly, but redraws don't happen + // enough to justify a hardware layer. + setLayerType(LAYER_TYPE_SOFTWARE, null); } public void setForceShowPercent(boolean show) { @@ -243,9 +250,11 @@ public class BatteryMeterView extends LinearLayout implements if (mUseWallpaperTextColors) { updateColors( Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor), - Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColorSecondary)); + Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColorSecondary), + Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor)); } else { - updateColors(mNonAdaptedForegroundColor, mNonAdaptedBackgroundColor); + updateColors(mNonAdaptedForegroundColor, mNonAdaptedBackgroundColor, + mNonAdaptedSingleToneColor); } } @@ -258,10 +267,14 @@ public class BatteryMeterView extends LinearLayout implements Utils.getThemeAttr(context, R.attr.darkIconTheme)); Context dualToneLightTheme = new ContextThemeWrapper(context, Utils.getThemeAttr(context, R.attr.lightIconTheme)); + mDarkModeSingleToneColor = Utils.getColorAttrDefaultColor(dualToneDarkTheme, + R.attr.singleToneColor); mDarkModeBackgroundColor = Utils.getColorAttrDefaultColor(dualToneDarkTheme, R.attr.backgroundColor); mDarkModeFillColor = Utils.getColorAttrDefaultColor(dualToneDarkTheme, R.attr.fillColor); + mLightModeSingleToneColor = Utils.getColorAttrDefaultColor(dualToneLightTheme, + R.attr.singleToneColor); mLightModeBackgroundColor = Utils.getColorAttrDefaultColor(dualToneLightTheme, R.attr.backgroundColor); mLightModeFillColor = Utils.getColorAttrDefaultColor(dualToneLightTheme, R.attr.fillColor); @@ -303,8 +316,8 @@ public class BatteryMeterView extends LinearLayout implements @Override public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) { - mDrawable.setBatteryLevel(level); mDrawable.setCharging(pluggedIn); + mDrawable.setBatteryLevel(level); mCharging = pluggedIn; mLevel = level; updatePercentText(); @@ -315,7 +328,7 @@ public class BatteryMeterView extends LinearLayout implements @Override public void onPowerSaveChanged(boolean isPowerSave) { - mDrawable.setPowerSave(isPowerSave); + mDrawable.setPowerSaveEnabled(isPowerSave); } private TextView loadPercentView() { @@ -406,21 +419,24 @@ public class BatteryMeterView extends LinearLayout implements @Override public void onDarkChanged(Rect area, float darkIntensity, int tint) { float intensity = DarkIconDispatcher.isInArea(area, this) ? darkIntensity : 0; + mNonAdaptedSingleToneColor = getColorForDarkIntensity( + intensity, mLightModeSingleToneColor, mDarkModeSingleToneColor); mNonAdaptedForegroundColor = getColorForDarkIntensity( intensity, mLightModeFillColor, mDarkModeFillColor); mNonAdaptedBackgroundColor = getColorForDarkIntensity( intensity, mLightModeBackgroundColor,mDarkModeBackgroundColor); if (!mUseWallpaperTextColors) { - updateColors(mNonAdaptedForegroundColor, mNonAdaptedBackgroundColor); + updateColors(mNonAdaptedForegroundColor, mNonAdaptedBackgroundColor, + mNonAdaptedSingleToneColor); } } - private void updateColors(int foregroundColor, int backgroundColor) { - mDrawable.setColors(foregroundColor, backgroundColor); - mTextColor = foregroundColor; + private void updateColors(int foregroundColor, int backgroundColor, int singleToneColor) { + mDrawable.setColors(foregroundColor, backgroundColor, singleToneColor); + mTextColor = singleToneColor; if (mBatteryPercentView != null) { - mBatteryPercentView.setTextColor(foregroundColor); + mBatteryPercentView.setTextColor(singleToneColor); } } @@ -429,7 +445,7 @@ public class BatteryMeterView extends LinearLayout implements } public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - String powerSave = mDrawable == null ? null : mDrawable.getPowerSave() + ""; + String powerSave = mDrawable == null ? null : mDrawable.getPowerSaveEnabled() + ""; CharSequence percent = mBatteryPercentView == null ? null : mBatteryPercentView.getText(); pw.println(" BatteryMeterView:"); pw.println(" mDrawable.getPowerSave: " + powerSave); diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index 567207376996..87f004fc12e6 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -61,7 +61,6 @@ import com.android.systemui.statusbar.notification.NotificationAlertingManager; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationFilter; import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; -import com.android.systemui.statusbar.notification.NotificationRowBinder; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment; import com.android.systemui.statusbar.notification.logging.NotificationLogger; @@ -266,7 +265,6 @@ public class Dependency extends SystemUI { @Inject Lazy<NotificationListener> mNotificationListener; @Inject Lazy<NotificationLogger> mNotificationLogger; @Inject Lazy<NotificationViewHierarchyManager> mNotificationViewHierarchyManager; - @Inject Lazy<NotificationRowBinder> mNotificationRowBinder; @Inject Lazy<NotificationFilter> mNotificationFilter; @Inject Lazy<NotificationInterruptionStateProvider> mNotificationInterruptionStateProvider; @Inject Lazy<KeyguardDismissUtil> mKeyguardDismissUtil; @@ -440,7 +438,6 @@ public class Dependency extends SystemUI { mProviders.put(NotificationLogger.class, mNotificationLogger::get); mProviders.put(NotificationViewHierarchyManager.class, mNotificationViewHierarchyManager::get); - mProviders.put(NotificationRowBinder.class, mNotificationRowBinder::get); mProviders.put(NotificationFilter.class, mNotificationFilter::get); mProviders.put(NotificationInterruptionStateProvider.class, mNotificationInterruptionStateProvider::get); diff --git a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java index 2a1d066d356e..f5451e952dd9 100644 --- a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java +++ b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java @@ -63,6 +63,9 @@ public class HardwareUiLayout extends MultiListLayout implements Tunable { public HardwareUiLayout(Context context, AttributeSet attrs) { super(context, attrs); + // Manually re-initialize mRotation to portrait-mode, since this view must always + // be constructed in portrait mode and rotated into the correct initial position. + mRotation = ROTATION_NONE; updateSettings(); } @@ -172,6 +175,10 @@ public class HardwareUiLayout extends MultiListLayout implements Tunable { mSeparatedView.setBackground(mSeparatedViewBackground); updateEdgeMargin(mEdgeBleed ? 0 : getEdgePadding()); mOldHeight = mList.getMeasuredHeight(); + + // Must be called to initialize view rotation correctly. + // Requires LayoutParams, hence why this isn't called during the constructor. + updateRotation(); } else { return; } @@ -189,7 +196,18 @@ public class HardwareUiLayout extends MultiListLayout implements Tunable { mSwapOrientation = swapOrientation; } - @Override + private void updateRotation() { + int rotation = RotationUtils.getRotation(getContext()); + if (rotation != mRotation) { + rotate(mRotation, rotation); + mRotation = rotation; + } + } + + /** + * Requires LayoutParams to be set to work correctly, and therefore must be run after after + * the HardwareUILayout has been added to the view hierarchy. + */ protected void rotate(int from, int to) { super.rotate(from, to); if (from != ROTATION_NONE && to != ROTATION_NONE) { @@ -522,4 +540,4 @@ public class HardwareUiLayout extends MultiListLayout implements Tunable { inoutInfo.contentInsets.set(mList.getLeft(), mList.getTop(), 0, getBottom() - mList.getBottom()); }; -} +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java index 9efa656b3ed3..60e6083790dc 100644 --- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java @@ -474,8 +474,6 @@ public class SwipeHelper implements Gefingerpoken { if (anim == null) { return; } - int duration = SNAP_ANIM_LEN; - anim.setDuration(duration); anim.addListener(new AnimatorListenerAdapter() { boolean wasCancelled = false; @@ -495,6 +493,9 @@ public class SwipeHelper implements Gefingerpoken { }); prepareSnapBackAnimation(animView, anim); mSnappingChild = true; + float maxDistance = Math.abs(targetLeft - getTranslation(animView)); + mFlingAnimationUtils.apply(anim, getTranslation(animView), targetLeft, velocity, + maxDistance); anim.start(); } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index 0832296b7dab..471619e897c5 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -25,17 +25,21 @@ import static com.android.systemui.statusbar.notification.NotificationAlertingMa import android.annotation.Nullable; import android.app.ActivityManager; +import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityTaskManager; import android.app.IActivityTaskManager; import android.app.INotificationManager; import android.app.Notification; import android.content.Context; +import android.content.pm.ParceledListSlice; import android.graphics.Rect; import android.os.RemoteException; import android.os.ServiceManager; import android.provider.Settings; import android.service.notification.StatusBarNotification; import android.view.Display; +import android.view.IPinnedStackController; +import android.view.IPinnedStackListener; import android.view.ViewGroup; import android.widget.FrameLayout; @@ -48,6 +52,7 @@ import com.android.systemui.R; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.TaskStackChangeListener; +import com.android.systemui.shared.system.WindowManagerWrapper; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; @@ -75,16 +80,21 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe // Enables some subset of notifs to automatically become bubbles private static final boolean DEBUG_ENABLE_AUTO_BUBBLE = false; - // Secure settings flags - // Feature level flag + /** Flag to enable or disable the entire feature */ private static final String ENABLE_BUBBLES = "experiment_enable_bubbles"; - // Auto bubble flags set whether different notification types should be presented as a bubble + /** Auto bubble flags set whether different notif types should be presented as a bubble */ private static final String ENABLE_AUTO_BUBBLE_MESSAGES = "experiment_autobubble_messaging"; private static final String ENABLE_AUTO_BUBBLE_ONGOING = "experiment_autobubble_ongoing"; private static final String ENABLE_AUTO_BUBBLE_ALL = "experiment_autobubble_all"; - // Use an activity view for an auto-bubbled notification if it has an appropriate content intent + + /** Use an activityView for an auto-bubbled notifs if it has an appropriate content intent */ private static final String ENABLE_BUBBLE_CONTENT_INTENT = "experiment_bubble_content_intent"; + /** Whether the row of bubble circles are anchored to the top or bottom of the screen. */ + private static final String ENABLE_BUBBLES_AT_TOP = "experiment_enable_top_bubbles"; + /** Flag to position the header below the activity view */ + private static final String ENABLE_BUBBLE_FOOTER = "experiment_enable_bubble_footer"; + private final Context mContext; private final NotificationEntryManager mNotificationEntryManager; private final IActivityTaskManager mActivityTaskManager; @@ -172,6 +182,12 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe mTaskStackListener = new BubbleTaskStackListener(); ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener); + try { + WindowManagerWrapper.getInstance().addPinnedStackListener(new BubblesImeListener()); + } catch (RemoteException e) { + e.printStackTrace(); + } + mBubbleData = data; } @@ -491,15 +507,8 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe } @Override - public void onTaskMovedToFront(int taskId) { - ActivityManager.StackInfo stackInfo = null; - try { - stackInfo = findStackInfo(taskId); - } catch (RemoteException e) { - e.rethrowAsRuntimeException(); - } - if (stackInfo != null && stackInfo.displayId == Display.DEFAULT_DISPLAY - && mStackView != null) { + public void onTaskMovedToFront(RunningTaskInfo taskInfo) { + if (mStackView != null && taskInfo.displayId == Display.DEFAULT_DISPLAY) { mStackView.collapseStack(); } } @@ -510,9 +519,9 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe * ultimately ended up, displays an error message toast, and calls this method instead of * onTaskMovedToFront. */ - // TODO(b/124058588): add requestedDisplayId to this callback, ignore unless matches @Override - public void onActivityLaunchOnSecondaryDisplayFailed() { + public void onActivityLaunchOnSecondaryDisplayFailed(RunningTaskInfo taskInfo) { + // TODO(b/124058588): move to ActivityView.StateCallback, filter on virtualDisplay ID if (mStackView != null) { mStackView.collapseStack(); } @@ -543,4 +552,53 @@ public class BubbleController implements BubbleExpandedView.OnBubbleBlockedListe return Settings.Secure.getInt(context.getContentResolver(), ENABLE_BUBBLES, 1) != 0; } + + /** + * Whether bubbles should be positioned at the top of the screen or not. + */ + public static boolean showBubblesAtTop(Context context) { + return Settings.Secure.getInt(context.getContentResolver(), + ENABLE_BUBBLES_AT_TOP, 0) != 0; + } + + /** + * Whether the bubble chrome should display as a footer or not (in which case it's a header). + */ + public static boolean useFooter(Context context) { + return Settings.Secure.getInt(context.getContentResolver(), + ENABLE_BUBBLE_FOOTER, 0) != 0; + } + + /** PinnedStackListener that dispatches IME visibility updates to the stack. */ + private class BubblesImeListener extends IPinnedStackListener.Stub { + + @Override + public void onListenerRegistered(IPinnedStackController controller) throws RemoteException { + } + + @Override + public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds, + Rect animatingBounds, boolean fromImeAdjustment, boolean fromShelfAdjustment, + int displayRotation) throws RemoteException {} + + @Override + public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) + throws RemoteException { + if (mStackView != null) { + mStackView.post(() -> { + mStackView.onImeVisibilityChanged(imeVisible, imeHeight); + }); + } + } + + @Override + public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) + throws RemoteException {} + + @Override + public void onMinimizedStateChanged(boolean isMinimized) throws RemoteException {} + + @Override + public void onActionsChanged(ParceledListSlice actions) throws RemoteException {} + } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java index c2327ad41d3e..603b3b9766d9 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java @@ -39,6 +39,7 @@ import android.graphics.Color; import android.graphics.Insets; import android.graphics.Point; import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.ShapeDrawable; import android.os.RemoteException; import android.os.ServiceManager; @@ -48,7 +49,6 @@ import android.util.AttributeSet; import android.util.Log; import android.util.StatsLog; import android.view.View; -import android.view.ViewGroup; import android.view.WindowInsets; import android.widget.FrameLayout; import android.widget.ImageButton; @@ -70,12 +70,16 @@ import com.android.systemui.statusbar.notification.stack.ExpandableViewState; public class BubbleExpandedView extends LinearLayout implements View.OnClickListener { private static final String TAG = "BubbleExpandedView"; + // Configurable via bubble settings; just for testing + private boolean mUseFooter; + private boolean mShowOnTop; + // The triangle pointing to the expanded view private View mPointerView; + private int mPointerMargin; // Header private View mHeaderView; - private TextView mHeaderTextView; private ImageButton mDeepLinkIcon; private ImageButton mSettingsIcon; @@ -91,6 +95,8 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList private int mMinHeight; private int mHeaderHeight; + private int mBubbleHeight; + private int mPermissionHeight; private NotificationEntry mEntry; private PackageManager mPm; @@ -151,6 +157,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList mPm = context.getPackageManager(); mMinHeight = getResources().getDimensionPixelSize( R.dimen.bubble_expanded_default_height); + mPointerMargin = getResources().getDimensionPixelSize(R.dimen.bubble_pointer_margin); try { mNotificationManagerService = INotificationManager.Stub.asInterface( ServiceManager.getServiceOrThrow(Context.NOTIFICATION_SERVICE)); @@ -173,12 +180,17 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList int bgColor = ta.getColor(0, Color.WHITE); ta.recycle(); + mShowOnTop = BubbleController.showBubblesAtTop(getContext()); + mUseFooter = BubbleController.useFooter(getContext()); + ShapeDrawable triangleDrawable = new ShapeDrawable( - TriangleShape.create(width, height, true /* pointUp */)); + TriangleShape.create(width, height, mShowOnTop /* pointUp */)); triangleDrawable.setTint(bgColor); mPointerView.setBackground(triangleDrawable); FrameLayout viewWrapper = findViewById(R.id.header_permission_wrapper); + viewWrapper.setBackground(createHeaderPermissionBackground(bgColor)); + LayoutTransition transition = new LayoutTransition(); transition.setDuration(200); @@ -194,10 +206,12 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList viewWrapper.setLayoutTransition(transition); viewWrapper.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING); + mHeaderHeight = getContext().getResources().getDimensionPixelSize( R.dimen.bubble_expanded_header_height); + mPermissionHeight = getContext().getResources().getDimensionPixelSize( + R.dimen.bubble_permission_height); mHeaderView = findViewById(R.id.header_layout); - mHeaderTextView = findViewById(R.id.header_text); mDeepLinkIcon = findViewById(R.id.deep_link_button); mSettingsIcon = findViewById(R.id.settings_button); mDeepLinkIcon.setOnClickListener(this); @@ -228,6 +242,37 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList activityView.setForwardedInsets(Insets.of(0, 0, 0, insetsBottom)); return view.onApplyWindowInsets(insets); }); + + if (!mShowOnTop) { + removeView(mPointerView); + if (mUseFooter) { + View divider = findViewById(R.id.divider); + viewWrapper.removeView(divider); + removeView(viewWrapper); + addView(divider); + addView(viewWrapper); + } + addView(mPointerView); + } + } + + /** + * Creates a background with corners rounded based on how the view is configured to display + */ + private Drawable createHeaderPermissionBackground(int bgColor) { + TypedArray ta2 = getContext().obtainStyledAttributes( + new int[] {android.R.attr.dialogCornerRadius}); + final float cr = ta2.getDimension(0, 0f); + ta2.recycle(); + + float[] radii = mUseFooter + ? new float[] {0, 0, 0, 0, cr, cr, cr, cr} + : new float[] {cr, cr, cr, cr, 0, 0, 0, 0}; + GradientDrawable chromeBackground = new GradientDrawable(); + chromeBackground.setShape(GradientDrawable.RECTANGLE); + chromeBackground.setCornerRadii(radii); + chromeBackground.setColor(bgColor); + return chromeBackground; } /** @@ -295,13 +340,6 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList R.string.bubbles_settings_button_description, mAppName)); mDeepLinkIcon.setContentDescription(getResources().getString( R.string.bubbles_deep_link_button_description, mAppName)); - if (mEntry != null && mEntry.getBubbleMetadata() != null) { - mHeaderTextView.setText(mEntry.getBubbleMetadata().getTitle()); - } else { - // This should only happen if we're auto-bubbling notification content that isn't - // explicitly a bubble - mHeaderTextView.setText(mAppName); - } } private void updatePermissionView() { @@ -321,6 +359,8 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList mPermissionView.setVisibility(VISIBLE); ((ImageView) mPermissionView.findViewById(R.id.pkgicon)).setImageDrawable(mAppIcon); ((TextView) mPermissionView.findViewById(R.id.pkgname)).setText(mAppName); + logBubbleClickEvent(mEntry.notification, + StatsLog.BUBBLE_UICHANGED__ACTION__PERMISSION_DIALOG_SHOWN); } } @@ -339,7 +379,11 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList // Use notification view mNotifRow = mEntry.getRow(); - addView(mNotifRow); + if (mShowOnTop) { + addView(mNotifRow); + } else { + addView(mNotifRow, mUseFooter ? 0 : 1); + } } updateView(); } @@ -352,6 +396,17 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList return true; } + /** + * @return total height that the expanded view occupies. + */ + int getExpandedSize() { + int chromeHeight = mPermissionView.getVisibility() != View.VISIBLE + ? mHeaderHeight + : mPermissionHeight; + return mBubbleHeight + mPointerView.getHeight() + mPointerMargin + + chromeHeight; + } + void updateHeight() { if (usingActivityView()) { Notification.BubbleMetadata data = mEntry.getBubbleMetadata(); @@ -365,12 +420,19 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList ? data.getDesiredHeight() : mMinHeight; } - int max = mStackView.getMaxExpandedHeight() - mHeaderHeight; + int chromeHeight = mPermissionView.getVisibility() != View.VISIBLE + ? mHeaderHeight + : mPermissionHeight; + int max = mStackView.getMaxExpandedHeight() - chromeHeight - mPointerView.getHeight() + - mPointerMargin; int height = Math.min(desiredHeight, max); height = Math.max(height, mMinHeight); LayoutParams lp = (LayoutParams) mActivityView.getLayoutParams(); lp.height = height; + mBubbleHeight = height; mActivityView.setLayoutParams(lp); + } else { + mBubbleHeight = mNotifRow != null ? mNotifRow.getIntrinsicHeight() : mMinHeight; } } @@ -419,6 +481,7 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList } else if (mOnBubbleBlockedListener != null) { mOnBubbleBlockedListener.onBubbleBlocked(mEntry); } + mStackView.onExpandedHeightChanged(); logBubbleClickEvent(mEntry.notification, allowed ? StatsLog.BUBBLE_UICHANGED__ACTION__PERMISSION_OPT_IN : StatsLog.BUBBLE_UICHANGED__ACTION__PERMISSION_OPT_OUT); @@ -453,23 +516,14 @@ public class BubbleExpandedView extends LinearLayout implements View.OnClickList /** * Removes and releases an ActivityView if one was previously created for this bubble. */ - public void destroyActivityView(ViewGroup tmpParent) { + public void destroyActivityView() { if (mActivityView == null) { return; } - if (!mActivityViewReady) { - // release not needed, never initialized? - mActivityView = null; - return; - } - // HACK: release() will crash if the view is not attached. - if (!isAttachedToWindow()) { - mActivityView.setVisibility(View.GONE); - tmpParent.addView(this); + if (mActivityViewReady) { + mActivityView.release(); } - - mActivityView.release(); - ((ViewGroup) getParent()).removeView(this); + removeView(mActivityView); mActivityView = null; mActivityViewReady = false; } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index 5546e4c50174..8235d8de3257 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -100,6 +100,7 @@ public class BubbleStackView extends FrameLayout { private int mExpandedAnimateYDistance; private int mStatusBarHeight; private int mPipDismissHeight; + private int mImeOffset; private Bubble mExpandedBubble; private boolean mIsExpanded; @@ -162,6 +163,7 @@ public class BubbleStackView extends FrameLayout { res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); mPipDismissHeight = mContext.getResources().getDimensionPixelSize( R.dimen.pip_dismiss_gradient_height); + mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset); mDisplaySize = new Point(); WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); @@ -171,7 +173,7 @@ public class BubbleStackView extends FrameLayout { int elevation = res.getDimensionPixelSize(R.dimen.bubble_elevation); mStackAnimationController = new StackAnimationController(); - mExpandedAnimationController = new ExpandedAnimationController(); + mExpandedAnimationController = new ExpandedAnimationController(mDisplaySize); mBubbleContainer = new PhysicsAnimationLayout(context); mBubbleContainer.setMaxRenderedChildren( @@ -346,7 +348,7 @@ public class BubbleStackView extends FrameLayout { // Remove it from the views int removedIndex = mBubbleContainer.indexOfChild(b.iconView); - b.expandedView.destroyActivityView(this /* tmpParent */); + b.expandedView.destroyActivityView(); mBubbleContainer.removeView(b.iconView); int bubbleCount = mBubbleContainer.getChildCount(); @@ -375,7 +377,7 @@ public class BubbleStackView extends FrameLayout { public void stackDismissed() { for (Bubble bubble : mBubbleData.getBubbles()) { bubble.entry.setBubbleDismissed(true); - bubble.expandedView.destroyActivityView(this /* tmpParent */); + bubble.expandedView.destroyActivityView(); } mBubbleData.clear(); collapseStack(); @@ -511,8 +513,7 @@ public class BubbleStackView extends FrameLayout { final float yStart = Math.min( mStackAnimationController.getStackPosition().y, mExpandedAnimateYDistance); - final float yDest = getStatusBarHeight() - + mExpandedBubble.iconView.getHeight() + mBubblePadding; + final float yDest = getYPositionForExpandedView(); if (shouldExpand) { mExpandedViewContainer.setTranslationX(xStart); @@ -550,8 +551,15 @@ public class BubbleStackView extends FrameLayout { : null; } - public PointF getStackPosition() { - return mStackAnimationController.getStackPosition(); + /** Moves the bubbles out of the way if they're going to be over the keyboard. */ + public void onImeVisibilityChanged(boolean visible, int height) { + if (!mIsExpanded) { + if (visible) { + mStackAnimationController.updateBoundsForVisibleImeAndAnimate(height + mImeOffset); + } else { + mStackAnimationController.updateBoundsForInvisibleImeAndAnimate(); + } + } } /** Called when a drag operation on an individual bubble has started. */ @@ -659,13 +667,39 @@ public class BubbleStackView extends FrameLayout { * y position when the bubbles are expanded as well as the bounds of the dismiss target. */ int getMaxExpandedHeight() { + boolean showOnTop = BubbleController.showBubblesAtTop(getContext()); int expandedY = (int) mExpandedAnimationController.getExpandedY(); - int bubbleContainerHeight = mBubbleContainer.getChildAt(0) != null - ? mBubbleContainer.getChildAt(0).getHeight() - : 0; - // PIP dismiss view uses FLAG_LAYOUT_IN_SCREEN so we need to subtract the bottom inset - int pipDismissHeight = mPipDismissHeight - getBottomInset(); - return mDisplaySize.y - expandedY - mBubbleSize - pipDismissHeight; + if (showOnTop) { + // PIP dismiss view uses FLAG_LAYOUT_IN_SCREEN so we need to subtract the bottom inset + int pipDismissHeight = mPipDismissHeight - getBottomInset(); + return mDisplaySize.y - expandedY - mBubbleSize - pipDismissHeight; + } else { + return expandedY - getStatusBarHeight(); + } + } + + /** + * Calculates the y position of the expanded view when it is expanded. + */ + float getYPositionForExpandedView() { + boolean showOnTop = BubbleController.showBubblesAtTop(getContext()); + if (showOnTop) { + return getStatusBarHeight() + mBubbleSize + mBubblePadding; + } else { + return mExpandedAnimationController.getExpandedY() + - mExpandedBubble.expandedView.getExpandedSize() - mBubblePadding; + } + } + + /** + * Called when the height of the currently expanded view has changed (not via an + * update to the bubble's desired height but for some other reason, e.g. permission view + * goes away). + */ + void onExpandedHeightChanged() { + if (mIsExpanded) { + requestUpdate(); + } } /** @@ -742,6 +776,8 @@ public class BubbleStackView extends FrameLayout { mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE); if (mIsExpanded) { + final float y = getYPositionForExpandedView(); + mExpandedViewContainer.setTranslationY(y); mExpandedBubble.expandedView.updateView(); } @@ -786,7 +822,10 @@ public class BubbleStackView extends FrameLayout { * @return the index of the bubble view within the bubble stack. The range of the position * is between 0 and the bubble count minus 1. */ - int getBubbleIndex(Bubble bubble) { + int getBubbleIndex(@Nullable Bubble bubble) { + if (bubble == null) { + return 0; + } return mBubbleContainer.indexOfChild(bubble.iconView); } @@ -808,6 +847,10 @@ public class BubbleStackView extends FrameLayout { .floatValue(); } + public PointF getStackPosition() { + return mStackAnimationController.getStackPosition(); + } + /** * Logs the bubble UI event. * diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java index f0d9be1e484a..f7896b0b1201 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java @@ -17,6 +17,7 @@ package com.android.systemui.bubbles.animation; import android.content.res.Resources; +import android.graphics.Point; import android.graphics.PointF; import android.view.View; import android.view.WindowInsets; @@ -25,6 +26,7 @@ import androidx.dynamicanimation.animation.DynamicAnimation; import androidx.dynamicanimation.animation.SpringForce; import com.android.systemui.R; +import com.android.systemui.bubbles.BubbleController; import com.google.android.collect.Sets; @@ -61,6 +63,14 @@ public class ExpandedAnimationController private float mBubbleSizePx; /** Height of the status bar. */ private float mStatusBarHeight; + /** Size of display. */ + private Point mDisplaySize; + /** Size of dismiss target at bottom of screen. */ + private float mPipDismissHeight; + + public ExpandedAnimationController(Point displaySize) { + mDisplaySize = displaySize; + } /** * Whether the individual bubble has been dragged out of the row of bubbles far enough to cause @@ -88,6 +98,7 @@ public class ExpandedAnimationController mBubbleSizePx = res.getDimensionPixelSize(R.dimen.individual_bubble_size); mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); + mPipDismissHeight = res.getDimensionPixelSize(R.dimen.pip_dismiss_gradient_height); } /** @@ -204,16 +215,19 @@ public class ExpandedAnimationController /** The Y value of the row of expanded bubbles. */ public float getExpandedY() { + boolean showOnTop = mLayout != null + && BubbleController.showBubblesAtTop(mLayout.getContext()); final WindowInsets insets = mLayout != null ? mLayout.getRootWindowInsets() : null; - if (insets != null) { + if (showOnTop && insets != null) { return mBubblePaddingPx + Math.max( mStatusBarHeight, insets.getDisplayCutout() != null ? insets.getDisplayCutout().getSafeInsetTop() : 0); + } else { + int bottomInset = insets != null ? insets.getSystemWindowInsetBottom() : 0; + return mDisplaySize.y - mBubbleSizePx - (mPipDismissHeight - bottomInset); } - - return mBubblePaddingPx; } /** Runs the given Runnable after all translation-related animations have ended. */ diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java index 7dfb21cf384f..f47fbe0d149f 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java @@ -66,6 +66,15 @@ public class StackAnimationController extends */ private PointF mStackPosition = new PointF(); + /** The height of the most recently visible IME. */ + private float mImeHeight = 0f; + + /** + * The Y position of the stack before the IME became visible, or {@link Float#MIN_VALUE} if the + * IME is not visible or the user moved the stack since the IME became visible. + */ + private float mPreImeY = Float.MIN_VALUE; + /** * Animations on the stack position itself, which would have been started in * {@link #flingThenSpringFirstBubbleWithStackFollowing}. These animations dispatch to @@ -108,6 +117,10 @@ public class StackAnimationController extends * it with the 'following' effect. */ public void moveFirstBubbleWithStackFollowing(float x, float y) { + // If we manually move the bubbles with the IME open, clear the return point since we don't + // want the stack to snap away from the new position. + mPreImeY = Float.MIN_VALUE; + moveFirstBubbleWithStackFollowing(DynamicAnimation.TRANSLATION_X, x); moveFirstBubbleWithStackFollowing(DynamicAnimation.TRANSLATION_Y, y); } @@ -189,6 +202,44 @@ public class StackAnimationController extends } /** + * Save the IME height so that the allowable stack bounds reflect the now-visible IME, and + * animate the stack out of the way if necessary. + */ + public void updateBoundsForVisibleImeAndAnimate(int imeHeight) { + mImeHeight = imeHeight; + + final float maxBubbleY = getAllowableStackPositionRegion().bottom; + if (mStackPosition.y > maxBubbleY && mPreImeY == Float.MIN_VALUE) { + mPreImeY = mStackPosition.y; + + springFirstBubbleWithStackFollowing( + DynamicAnimation.TRANSLATION_Y, + getSpringForce(DynamicAnimation.TRANSLATION_Y, /* view */ null) + .setStiffness(SpringForce.STIFFNESS_LOW), + /* startVel */ 0f, + maxBubbleY); + } + } + + /** + * Clear the IME height from the bounds and animate the stack back to its original position, + * assuming it wasn't moved in the meantime. + */ + public void updateBoundsForInvisibleImeAndAnimate() { + mImeHeight = 0; + + if (mPreImeY > Float.MIN_VALUE) { + springFirstBubbleWithStackFollowing( + DynamicAnimation.TRANSLATION_Y, + getSpringForce(DynamicAnimation.TRANSLATION_Y, /* view */ null) + .setStiffness(SpringForce.STIFFNESS_LOW), + /* startVel */ 0f, + mPreImeY); + mPreImeY = Float.MIN_VALUE; + } + } + + /** * Returns the region within which the stack is allowed to rest. This goes slightly off the left * and right sides of the screen, below the status bar/cutout and above the navigation bar. * While the stack is not allowed to rest outside of these bounds, it can temporarily be @@ -228,6 +279,7 @@ public class StackAnimationController extends mLayout.getHeight() - mIndividualBubbleSize - mBubblePadding + - (mImeHeight > Float.MIN_VALUE ? mImeHeight + mBubblePadding : 0f) - Math.max( insets.getSystemWindowInsetBottom(), insets.getDisplayCutout() != null @@ -389,8 +441,8 @@ public class StackAnimationController extends float vel, float finalPosition) { Log.d(TAG, String.format("Springing %s to final position %f.", - PhysicsAnimationLayout.getReadablePropertyName(property), - finalPosition)); + PhysicsAnimationLayout.getReadablePropertyName(property), + finalPosition)); StackPositionProperty firstBubbleProperty = new StackPositionProperty(property); SpringAnimation springAnimation = diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java index 5353ee6c8ab3..06dbdbf1efa8 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java @@ -17,11 +17,11 @@ package com.android.systemui.doze; import android.content.Context; +import android.hardware.display.AmbientDisplayConfiguration; import android.os.Handler; import android.os.UserHandle; import android.util.Log; -import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.systemui.dock.DockManager; import com.android.systemui.doze.DozeMachine.State; diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java index 325018227692..36e28dc0156d 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java @@ -21,9 +21,9 @@ import android.app.Application; import android.content.Context; import android.hardware.Sensor; import android.hardware.SensorManager; +import android.hardware.display.AmbientDisplayConfiguration; import android.os.Handler; -import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.Dependency; import com.android.systemui.R; diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java index 4d89a391a14f..6c4be0617043 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java @@ -17,12 +17,12 @@ package com.android.systemui.doze; import android.annotation.MainThread; +import android.hardware.display.AmbientDisplayConfiguration; import android.os.Trace; import android.os.UserHandle; import android.util.Log; import android.view.Display; -import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.internal.util.Preconditions; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.util.Assert; diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java index 2d1dba6f79c8..77180f8cc07a 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java @@ -30,6 +30,7 @@ import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.hardware.TriggerEvent; import android.hardware.TriggerEventListener; +import android.hardware.display.AmbientDisplayConfiguration; import android.net.Uri; import android.os.Handler; import android.os.SystemClock; @@ -38,7 +39,8 @@ import android.provider.Settings; import android.text.TextUtils; import android.util.Log; -import com.android.internal.hardware.AmbientDisplayConfiguration; +import androidx.annotation.VisibleForTesting; + import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; import com.android.systemui.plugins.SensorManagerPlugin; @@ -60,7 +62,6 @@ public class DozeSensors { private final Context mContext; private final AlarmManager mAlarmManager; private final SensorManager mSensorManager; - private final TriggerSensor[] mSensors; private final ContentResolver mResolver; private final TriggerSensor mPickupSensor; private final DozeParameters mDozeParameters; @@ -68,10 +69,12 @@ public class DozeSensors { private final WakeLock mWakeLock; private final Consumer<Boolean> mProxCallback; private final Callback mCallback; + @VisibleForTesting + protected final TriggerSensor[] mSensors; private final Handler mHandler = new Handler(); private final ProxSensor mProxSensor; - + private long mDebounceFrom; public DozeSensors(Context context, AlarmManager alarmManager, SensorManager sensorManager, DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock, @@ -134,13 +137,21 @@ public class DozeSensors { mConfig.wakeScreenGestureAvailable() && alwaysOn, DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN, false /* reports touch coordinates */, - false /* touchscreen */), + false /* touchscreen */, mConfig.getWakeLockScreenDebounce()), }; mProxSensor = new ProxSensor(policy); mCallback = callback; } + /** + * Temporarily disable some sensors to avoid turning on the device while the user is + * turning it off. + */ + public void requestTemporaryDisable() { + mDebounceFrom = SystemClock.uptimeMillis(); + } + private Sensor findSensorWithType(String type) { return findSensorWithType(mSensorManager, type); } @@ -179,15 +190,6 @@ public class DozeSensors { } } - public void reregisterAllSensors() { - for (TriggerSensor s : mSensors) { - s.setListening(false); - } - for (TriggerSensor s : mSensors) { - s.setListening(true); - } - } - public void onUserSwitched() { for (TriggerSensor s : mSensors) { s.updateListener(); @@ -320,7 +322,8 @@ public class DozeSensors { } } - private class TriggerSensor extends TriggerEventListener { + @VisibleForTesting + class TriggerSensor extends TriggerEventListener { final Sensor mSensor; final boolean mConfigured; final int mPulseReason; @@ -467,23 +470,25 @@ public class DozeSensors { /** * A Sensor that is injected via plugin. */ - private class PluginSensor extends TriggerSensor { + @VisibleForTesting + class PluginSensor extends TriggerSensor implements SensorManagerPlugin.SensorEventListener { - private final SensorManagerPlugin.Sensor mPluginSensor; - private final SensorManagerPlugin.SensorEventListener mTriggerEventListener = (event) -> { - DozeLog.traceSensor(mContext, mPulseReason); - mHandler.post(mWakeLock.wrap(() -> { - if (DEBUG) Log.d(TAG, "onSensorEvent: " + triggerEventToString(event)); - mCallback.onSensorPulse(mPulseReason, true /* sensorPerformsProxCheck */, -1, -1, - event.getValues()); - })); - }; + final SensorManagerPlugin.Sensor mPluginSensor; + private long mDebounce; PluginSensor(SensorManagerPlugin.Sensor sensor, String setting, boolean configured, int pulseReason, boolean reportsTouchCoordinates, boolean requiresTouchscreen) { + this(sensor, setting, configured, pulseReason, reportsTouchCoordinates, + requiresTouchscreen, 0L /* debounce */); + } + + PluginSensor(SensorManagerPlugin.Sensor sensor, String setting, boolean configured, + int pulseReason, boolean reportsTouchCoordinates, boolean requiresTouchscreen, + long debounce) { super(null, setting, configured, pulseReason, reportsTouchCoordinates, requiresTouchscreen); mPluginSensor = sensor; + mDebounce = debounce; } @Override @@ -492,11 +497,11 @@ public class DozeSensors { AsyncSensorManager asyncSensorManager = (AsyncSensorManager) mSensorManager; if (mRequested && !mDisabled && (enabledBySetting() || mIgnoresSetting) && !mRegistered) { - asyncSensorManager.registerPluginListener(mPluginSensor, mTriggerEventListener); + asyncSensorManager.registerPluginListener(mPluginSensor, this); mRegistered = true; if (DEBUG) Log.d(TAG, "registerPluginListener"); } else if (mRegistered) { - asyncSensorManager.unregisterPluginListener(mPluginSensor, mTriggerEventListener); + asyncSensorManager.unregisterPluginListener(mPluginSensor, this); mRegistered = false; if (DEBUG) Log.d(TAG, "unregisterPluginListener"); } @@ -524,6 +529,21 @@ public class DozeSensors { } return sb.append(']').toString(); } + + @Override + public void onSensorChanged(SensorManagerPlugin.SensorEvent event) { + DozeLog.traceSensor(mContext, mPulseReason); + mHandler.post(mWakeLock.wrap(() -> { + final long now = SystemClock.uptimeMillis(); + if (now < mDebounceFrom + mDebounce) { + if (DEBUG) Log.d(TAG, "onSensorEvent dropped: " + triggerEventToString(event)); + return; + } + if (DEBUG) Log.d(TAG, "onSensorEvent: " + triggerEventToString(event)); + mCallback.onSensorPulse(mPulseReason, true /* sensorPerformsProxCheck */, -1, -1, + event.getValues()); + })); + } } public interface Callback { diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index 70bf903cd712..b2f707f76c10 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -27,6 +27,7 @@ import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; +import android.hardware.display.AmbientDisplayConfiguration; import android.os.Handler; import android.os.SystemClock; import android.os.UserHandle; @@ -34,7 +35,6 @@ import android.text.format.Formatter; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.internal.util.Preconditions; import com.android.systemui.dock.DockManager; import com.android.systemui.statusbar.phone.DozeParameters; @@ -143,8 +143,12 @@ public class DozeTriggers implements DozeMachine.Part { if (isWakeDisplay) { onWakeScreen(wakeEvent, mMachine.getState()); - } else if (isLongPress || isWakeLockScreen) { + } else if (isLongPress) { requestPulse(pulseReason, sensorPerformedProxCheck); + } else if (isWakeLockScreen) { + if (wakeEvent) { + requestPulse(pulseReason, sensorPerformedProxCheck); + } } else { proximityCheckThenCall((result) -> { if (result == ProximityCheck.RESULT_NEAR) { @@ -228,14 +232,12 @@ public class DozeTriggers implements DozeMachine.Part { if (mDockManager != null) { mDockManager.addListener(mDockEventListener); } + mDozeSensors.requestTemporaryDisable(); checkTriggersAtInit(); break; case DOZE: case DOZE_AOD: mDozeSensors.setProxListening(newState != DozeMachine.State.DOZE); - if (oldState != DozeMachine.State.INITIALIZED) { - mDozeSensors.reregisterAllSensors(); - } mDozeSensors.setListening(true); if (newState == DozeMachine.State.DOZE_AOD && !sWakeDisplaySensorState) { onWakeScreen(false, newState); @@ -250,6 +252,9 @@ public class DozeTriggers implements DozeMachine.Part { mDozeSensors.setTouchscreenSensorsListening(false); mDozeSensors.setProxListening(true); break; + case DOZE_PULSE_DONE: + mDozeSensors.requestTemporaryDisable(); + break; case FINISH: mBroadcastReceiver.unregister(mContext); mDozeHost.removeCallback(mHostCallback); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 66cfadf9d6e8..172746e2ffbe 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -21,7 +21,6 @@ import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT; import static com.android.internal.telephony.IccCardConstants.State.ABSENT; import static com.android.internal.telephony.IccCardConstants.State.PIN_REQUIRED; import static com.android.internal.telephony.IccCardConstants.State.PUK_REQUIRED; -import static com.android.internal.telephony.IccCardConstants.State.READY; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT; @@ -95,7 +94,6 @@ import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.Arrays; /** * Mediates requests related to the keyguard. This includes queries about the @@ -247,9 +245,6 @@ public class KeyguardViewMediator extends SystemUI { // AOD is enabled and status bar is in AOD state. private boolean mAodShowing; - // display ids of the external display on which we have put a keyguard window - private int[] mSecondaryDisplaysShowing; - /** Cached value of #isInputRestricted */ private boolean mInputRestricted; @@ -687,13 +682,6 @@ public class KeyguardViewMediator extends SystemUI { mCustomMessage = null; return message; } - - @Override - public void onSecondaryDisplayShowingChanged(int[] displayIds) { - synchronized (KeyguardViewMediator.this) { - setShowingLocked(mShowing, mAodShowing, displayIds, false); - } - } }; public void userActivity() { @@ -722,7 +710,7 @@ public class KeyguardViewMediator extends SystemUI { mContext.registerReceiver(mDelayedLockBroadcastReceiver, delayedActionFilter, SYSTEMUI_PERMISSION, null /* scheduler */); - mKeyguardDisplayManager = new KeyguardDisplayManager(mContext, mViewMediatorCallback); + mKeyguardDisplayManager = new KeyguardDisplayManager(mContext); mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); @@ -738,10 +726,10 @@ public class KeyguardViewMediator extends SystemUI { setShowingLocked(!shouldWaitForProvisioning() && !mLockPatternUtils.isLockScreenDisabled( KeyguardUpdateMonitor.getCurrentUser()), - mAodShowing, mSecondaryDisplaysShowing, true /* forceCallbacks */); + mAodShowing, true /* forceCallbacks */); } else { // The system's keyguard is disabled or missing. - setShowingLocked(false, mAodShowing, mSecondaryDisplaysShowing, true); + setShowingLocked(false, mAodShowing, true); } mStatusBarKeyguardViewManager = @@ -1764,12 +1752,10 @@ public class KeyguardViewMediator extends SystemUI { playSound(mTrustedSoundId); } - private void updateActivityLockScreenState(boolean showing, boolean aodShowing, - int[] secondaryDisplaysShowing) { + private void updateActivityLockScreenState(boolean showing, boolean aodShowing) { mUiOffloadThread.submit(() -> { try { - ActivityTaskManager.getService().setLockScreenShown(showing, aodShowing, - secondaryDisplaysShowing); + ActivityTaskManager.getService().setLockScreenShown(showing, aodShowing); } catch (RemoteException e) { } }); @@ -1892,8 +1878,7 @@ public class KeyguardViewMediator extends SystemUI { if (!mHiding) { // Tell ActivityManager that we canceled the keyguardExitAnimation. - setShowingLocked(mShowing, mAodShowing, mSecondaryDisplaysShowing, - true /* force */); + setShowingLocked(mShowing, mAodShowing, true /* force */); return; } mHiding = false; @@ -2163,23 +2148,19 @@ public class KeyguardViewMediator extends SystemUI { } private void setShowingLocked(boolean showing, boolean aodShowing) { - setShowingLocked(showing, aodShowing, mSecondaryDisplaysShowing, - false /* forceCallbacks */); + setShowingLocked(showing, aodShowing, false /* forceCallbacks */); } - private void setShowingLocked(boolean showing, boolean aodShowing, - int[] secondaryDisplaysShowing, boolean forceCallbacks) { + private void setShowingLocked(boolean showing, boolean aodShowing, boolean forceCallbacks) { final boolean notifyDefaultDisplayCallbacks = showing != mShowing || aodShowing != mAodShowing || forceCallbacks; - if (notifyDefaultDisplayCallbacks - || !Arrays.equals(secondaryDisplaysShowing, mSecondaryDisplaysShowing)) { + if (notifyDefaultDisplayCallbacks) { mShowing = showing; mAodShowing = aodShowing; - mSecondaryDisplaysShowing = secondaryDisplaysShowing; if (notifyDefaultDisplayCallbacks) { notifyDefaultDisplayCallbacks(showing); } - updateActivityLockScreenState(showing, aodShowing, secondaryDisplaysShowing); + updateActivityLockScreenState(showing, aodShowing); } } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java index 3346ad28bee8..17402905b21a 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java @@ -16,8 +16,6 @@ package com.android.systemui.pip.phone; -import static android.view.Display.DEFAULT_DISPLAY; - import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.app.IActivityManager; @@ -33,8 +31,6 @@ import android.util.Log; import android.util.Pair; import android.view.IPinnedStackController; import android.view.IPinnedStackListener; -import android.view.IWindowManager; -import android.view.WindowManagerGlobal; import com.android.systemui.Dependency; import com.android.systemui.UiOffloadThread; @@ -57,7 +53,6 @@ public class PipManager implements BasePipManager { private Context mContext; private IActivityManager mActivityManager; private IActivityTaskManager mActivityTaskManager; - private IWindowManager mWindowManager; private Handler mHandler = new Handler(); private final PinnedStackListener mPinnedStackListener = new PinnedStackListener(); @@ -178,10 +173,9 @@ public class PipManager implements BasePipManager { mContext = context; mActivityManager = ActivityManager.getService(); mActivityTaskManager = ActivityTaskManager.getService(); - mWindowManager = WindowManagerGlobal.getWindowManagerService(); try { - mWindowManager.registerPinnedStackListener(DEFAULT_DISPLAY, mPinnedStackListener); + WindowManagerWrapper.getInstance().addPinnedStackListener(mPinnedStackListener); } catch (RemoteException e) { Log.e(TAG, "Failed to register pinned stack listener", e); } diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java index cef1b6b1d93b..3140e6db2442 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -37,6 +37,7 @@ import android.os.RemoteException; import android.util.Log; import android.util.Size; import android.view.IPinnedStackController; +import android.view.InputEvent; import android.view.MotionEvent; import android.view.ViewConfiguration; import android.view.accessibility.AccessibilityEvent; @@ -206,7 +207,7 @@ public class PipTouchHandler { mEnableDimissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge); // Register the listener for input consumer touch events - inputConsumerController.setTouchListener(this::handleTouchEvent); + inputConsumerController.setInputListener(this::handleTouchEvent); inputConsumerController.setRegistrationListener(this::onRegistrationChanged); onRegistrationChanged(inputConsumerController.isRegistered()); } @@ -370,11 +371,16 @@ public class PipTouchHandler { mMovementBounds, true /* allowMenuTimeout */, willResizeMenu()); } - private boolean handleTouchEvent(MotionEvent ev) { + private boolean handleTouchEvent(InputEvent inputEvent) { + // Skip any non motion events + if (!(inputEvent instanceof MotionEvent)) { + return true; + } // Skip touch handling until we are bound to the controller if (mPinnedStackController == null) { return true; } + MotionEvent ev = (MotionEvent) inputEvent; // Update the touch state mTouchState.onTouchEvent(ev); diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java index 50dda1c1de6b..fdb0b36ee51e 100644 --- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java +++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java @@ -16,6 +16,7 @@ package com.android.systemui.power; +import android.app.KeyguardManager; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; @@ -41,6 +42,7 @@ import android.text.style.URLSpan; import android.util.Log; import android.util.Slog; import android.view.View; +import android.view.WindowManager; import androidx.annotation.VisibleForTesting; @@ -48,10 +50,13 @@ import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.settingslib.Utils; import com.android.settingslib.fuelgauge.BatterySaverUtils; import com.android.settingslib.utils.PowerUtil; +import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.SystemUI; +import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.util.NotificationChannels; +import com.android.systemui.volume.Events; import java.io.PrintWriter; import java.text.NumberFormat; @@ -118,6 +123,7 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { private final Context mContext; private final NotificationManager mNoMan; private final PowerManager mPowerMan; + private final KeyguardManager mKeyguard; private final Handler mHandler = new Handler(Looper.getMainLooper()); private final Receiver mReceiver = new Receiver(); private final Intent mOpenBatterySettings = settings(Intent.ACTION_POWER_USAGE_SUMMARY); @@ -141,6 +147,7 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { private boolean mHighTempWarning; private SystemUIDialog mHighTempDialog; private SystemUIDialog mThermalShutdownDialog; + @VisibleForTesting SystemUIDialog mUsbHighTempDialog; /** */ @@ -149,6 +156,7 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { mContext = context; mNoMan = mContext.getSystemService(NotificationManager.class); mPowerMan = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + mKeyguard = mContext.getSystemService(KeyguardManager.class); mReceiver.init(); } @@ -165,6 +173,8 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { pw.print("mHighTempDialog="); pw.println(mHighTempDialog != null ? "not null" : null); pw.print("mThermalShutdownDialog="); pw.println(mThermalShutdownDialog != null ? "not null" : null); + pw.print("mUsbHighTempDialog="); + pw.println(mUsbHighTempDialog != null ? "not null" : null); } private int getLowBatteryAutoTriggerDefaultLevel() { @@ -434,6 +444,53 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI { } @Override + public void showUsbHighTemperatureAlarm() { + mHandler.post(() -> showUsbHighTemperatureAlarmInternal()); + } + + private void showUsbHighTemperatureAlarmInternal() { + if (mUsbHighTempDialog != null) { + return; + } + + final SystemUIDialog d = new SystemUIDialog(mContext, R.style.Theme_SystemUI_Dialog_Alert); + d.setCancelable(false); + d.setIconAttribute(android.R.attr.alertDialogIcon); + d.setTitle(R.string.high_temp_alarm_title); + d.setShowForAllUsers(true); + d.setMessage(mContext.getString(R.string.high_temp_alarm_notify_message, "")); + d.setPositiveButton((com.android.internal.R.string.ok), + (dialogInterface, which) -> mUsbHighTempDialog = null); + d.setNegativeButton((R.string.high_temp_alarm_help_care_steps), + (dialogInterface, which) -> { + final String contextString = mContext.getString( + R.string.high_temp_alarm_help_url); + final Intent helpIntent = new Intent(); + helpIntent.setClassName("com.android.settings", + "com.android.settings.HelpTrampoline"); + helpIntent.putExtra(Intent.EXTRA_TEXT, contextString); + Dependency.get(ActivityStarter.class).startActivity(helpIntent, + true /* dismissShade */, resultCode -> { + mUsbHighTempDialog = null; + }); + }); + d.setOnDismissListener(dialogInterface -> { + mUsbHighTempDialog = null; + Events.writeEvent(mContext, Events.EVENT_DISMISS_USB_OVERHEAT_ALARM, + Events.DISMISS_REASON_USB_OVERHEAD_ALARM_CHANGED, + mKeyguard.isKeyguardLocked()); + }); + d.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON + | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + d.show(); + mUsbHighTempDialog = d; + + Events.writeEvent(mContext, Events.EVENT_SHOW_USB_OVERHEAT_ALARM, + Events.SHOW_REASON_USB_OVERHEAD_ALARM_CHANGED, + mKeyguard.isKeyguardLocked()); + } + + @Override public void updateLowBatteryWarning() { updateNotification(); } diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java index c43f5728aaa2..e27c25efd88f 100644 --- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java +++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java @@ -27,7 +27,6 @@ import android.content.res.Resources; import android.database.ContentObserver; import android.os.BatteryManager; import android.os.Handler; -import android.os.HardwarePropertiesManager; import android.os.IBinder; import android.os.IThermalEventListener; import android.os.IThermalService; @@ -43,7 +42,6 @@ import android.util.Log; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.logging.MetricsLogger; import com.android.settingslib.utils.ThreadUtils; import com.android.systemui.Dependency; import com.android.systemui.R; @@ -71,7 +69,6 @@ public class PowerUI extends SystemUI { final Receiver mReceiver = new Receiver(); private PowerManager mPowerManager; - private HardwarePropertiesManager mHardwarePropertiesManager; private WarningsUI mWarnings; private final Configuration mLastConfiguration = new Configuration(); private long mTimeRemaining = Long.MAX_VALUE; @@ -82,30 +79,21 @@ public class PowerUI extends SystemUI { private boolean mLowWarningShownThisChargeCycle; private boolean mSevereWarningShownThisChargeCycle; private Future mLastShowWarningTask; + private boolean mEnableSkinTemperatureWarning; + private boolean mEnableUsbTemperatureAlarm; private int mLowBatteryAlertCloseLevel; private final int[] mLowBatteryReminderLevels = new int[2]; private long mScreenOffTime = -1; - private float mThresholdTemp; - private float[] mRecentTemps = new float[MAX_RECENT_TEMPS]; - private int mNumTemps; - private long mNextLogTime; @VisibleForTesting IThermalService mThermalService; @VisibleForTesting int mBatteryLevel = 100; @VisibleForTesting int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN; - // by using the same instance (method references are not guaranteed to be the same object - // We create a method reference here so that we are guaranteed that we can remove a callback - // each time they are created). - private final Runnable mUpdateTempCallback = this::updateTemperatureWarning; - public void start() { mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); - mHardwarePropertiesManager = (HardwarePropertiesManager) - mContext.getSystemService(Context.HARDWARE_PROPERTIES_SERVICE); mScreenOffTime = mPowerManager.isScreenOn() ? -1 : SystemClock.elapsedRealtime(); mWarnings = Dependency.get(WarningsUI.class); mEnhancedEstimates = Dependency.get(EnhancedEstimates.class); @@ -128,7 +116,7 @@ public class PowerUI extends SystemUI { // to the temperature being too high. showThermalShutdownDialog(); - initTemperatureWarning(); + initTemperature(); } @Override @@ -137,7 +125,7 @@ public class PowerUI extends SystemUI { // Safe to modify mLastConfiguration here as it's only updated by the main thread (here). if ((mLastConfiguration.updateFrom(newConfig) & mask) != 0) { - mHandler.post(this::initTemperatureWarning); + mHandler.post(this::initTemperature); } } @@ -383,30 +371,16 @@ public class PowerUI extends SystemUI { return canShowWarning || canShowSevereWarning; } - private void initTemperatureWarning() { + private void initTemperature() { ContentResolver resolver = mContext.getContentResolver(); Resources resources = mContext.getResources(); - if (Settings.Global.getInt(resolver, Settings.Global.SHOW_TEMPERATURE_WARNING, - resources.getInteger(R.integer.config_showTemperatureWarning)) == 0) { - return; - } - mThresholdTemp = Settings.Global.getFloat(resolver, Settings.Global.WARNING_TEMPERATURE, - resources.getInteger(R.integer.config_warningTemperature)); - - if (mThresholdTemp < 0f) { - // Get the shutdown temperature, adjust for warning tolerance. - float[] throttlingTemps = mHardwarePropertiesManager.getDeviceTemperatures( - HardwarePropertiesManager.DEVICE_TEMPERATURE_SKIN, - HardwarePropertiesManager.TEMPERATURE_SHUTDOWN); - if (throttlingTemps == null - || throttlingTemps.length == 0 - || throttlingTemps[0] == HardwarePropertiesManager.UNDEFINED_TEMPERATURE) { - return; - } - mThresholdTemp = throttlingTemps[0] - - resources.getInteger(R.integer.config_warningTemperatureTolerance); - } + mEnableSkinTemperatureWarning = Settings.Global.getInt(resolver, + Settings.Global.SHOW_TEMPERATURE_WARNING, + resources.getInteger(R.integer.config_showTemperatureWarning)) != 0; + mEnableUsbTemperatureAlarm = Settings.Global.getInt(resolver, + Settings.Global.SHOW_USB_TEMPERATURE_ALARM, + resources.getInteger(R.integer.config_showUsbPortAlarm)) != 0; if (mThermalService == null) { // Enable push notifications of throttling from vendor thermal @@ -416,103 +390,34 @@ public class PowerUI extends SystemUI { if (b != null) { mThermalService = IThermalService.Stub.asInterface(b); - try { - mThermalService.registerThermalEventListenerWithType( - new ThermalEventListener(), Temperature.TYPE_SKIN); - } catch (RemoteException e) { - // Should never happen. - } + registerThermalEventListener(); } else { Slog.w(TAG, "cannot find thermalservice, no throttling push notifications"); } } - - setNextLogTime(); - - // We have passed all of the checks, start checking the temp - mHandler.post(mUpdateTempCallback); - } - - private void showThermalShutdownDialog() { - if (mPowerManager.getLastShutdownReason() - == PowerManager.SHUTDOWN_REASON_THERMAL_SHUTDOWN) { - mWarnings.showThermalShutdownWarning(); - } } @VisibleForTesting - protected void updateTemperatureWarning() { - float[] temps = mHardwarePropertiesManager.getDeviceTemperatures( - HardwarePropertiesManager.DEVICE_TEMPERATURE_SKIN, - HardwarePropertiesManager.TEMPERATURE_CURRENT); - if (temps.length != 0) { - float temp = temps[0]; - mRecentTemps[mNumTemps++] = temp; - - StatusBar statusBar = getComponent(StatusBar.class); - if (statusBar != null && !statusBar.isDeviceInVrMode() - && temp >= mThresholdTemp) { - logAtTemperatureThreshold(temp); - mWarnings.showHighTemperatureWarning(); - } else { - mWarnings.dismissHighTemperatureWarning(); + void registerThermalEventListener() { + try { + if (mEnableSkinTemperatureWarning) { + mThermalService.registerThermalEventListenerWithType( + new ThermalEventSkinListener(), Temperature.TYPE_SKIN); } - } - - logTemperatureStats(); - - // Remove any pending callbacks as we only want to enable one - mHandler.removeCallbacks(mUpdateTempCallback); - mHandler.postDelayed(mUpdateTempCallback, TEMPERATURE_INTERVAL); - } - - private void logAtTemperatureThreshold(float temp) { - StringBuilder sb = new StringBuilder(); - sb.append("currentTemp=").append(temp) - .append(",thresholdTemp=").append(mThresholdTemp) - .append(",batteryStatus=").append(mBatteryStatus) - .append(",recentTemps="); - for (int i = 0; i < mNumTemps; i++) { - sb.append(mRecentTemps[i]).append(','); - } - Slog.i(TAG, sb.toString()); - } - - /** - * Calculates and logs min, max, and average - * {@link HardwarePropertiesManager#DEVICE_TEMPERATURE_SKIN} over the past - * {@link #TEMPERATURE_LOGGING_INTERVAL}. - */ - private void logTemperatureStats() { - if (mNextLogTime > System.currentTimeMillis() && mNumTemps != MAX_RECENT_TEMPS) { - return; - } - - if (mNumTemps > 0) { - float sum = mRecentTemps[0], min = mRecentTemps[0], max = mRecentTemps[0]; - for (int i = 1; i < mNumTemps; i++) { - float temp = mRecentTemps[i]; - sum += temp; - if (temp > max) { - max = temp; - } - if (temp < min) { - min = temp; - } + if (mEnableUsbTemperatureAlarm) { + mThermalService.registerThermalEventListenerWithType( + new ThermalEventUsbListener(), Temperature.TYPE_USB_PORT); } - - float avg = sum / mNumTemps; - Slog.i(TAG, "avg=" + avg + ",min=" + min + ",max=" + max); - MetricsLogger.histogram(mContext, "device_skin_temp_avg", (int) avg); - MetricsLogger.histogram(mContext, "device_skin_temp_min", (int) min); - MetricsLogger.histogram(mContext, "device_skin_temp_max", (int) max); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to register thermal callback.", e); } - setNextLogTime(); - mNumTemps = 0; } - private void setNextLogTime() { - mNextLogTime = System.currentTimeMillis() + TEMPERATURE_LOGGING_INTERVAL; + private void showThermalShutdownDialog() { + if (mPowerManager.getLastShutdownReason() + == PowerManager.SHUTDOWN_REASON_THERMAL_SHUTDOWN) { + mWarnings.showThermalShutdownWarning(); + } } public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { @@ -541,34 +446,80 @@ public class PowerUI extends SystemUI { Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0)); pw.print("bucket: "); pw.println(Integer.toString(findBatteryLevelBucket(mBatteryLevel))); - pw.print("mThresholdTemp="); - pw.println(Float.toString(mThresholdTemp)); - pw.print("mNextLogTime="); - pw.println(Long.toString(mNextLogTime)); + pw.print("mEnableSkinTemperatureWarning="); + pw.println(mEnableSkinTemperatureWarning); + pw.print("mEnableUsbTemperatureAlarm="); + pw.println(mEnableUsbTemperatureAlarm); mWarnings.dump(pw); } public interface WarningsUI { void update(int batteryLevel, int bucket, long screenOffTime); + void updateEstimate(Estimate estimate); + void updateThresholds(long lowThreshold, long severeThreshold); + void dismissLowBatteryWarning(); + void showLowBatteryWarning(boolean playSound); + void dismissInvalidChargerWarning(); + void showInvalidChargerWarning(); + void updateLowBatteryWarning(); + boolean isInvalidChargerWarningShowing(); + void dismissHighTemperatureWarning(); + void showHighTemperatureWarning(); + + /** + * Display USB overheat alarm + */ + void showUsbHighTemperatureAlarm(); + void showThermalShutdownWarning(); + void dump(PrintWriter pw); + void userSwitched(); } - // Thermal event received from vendor thermal management subsystem - private final class ThermalEventListener extends IThermalEventListener.Stub { + // Thermal event received from thermal service manager subsystem + @VisibleForTesting + final class ThermalEventSkinListener extends IThermalEventListener.Stub { @Override public void notifyThrottling(Temperature temp) { - mHandler.post(mUpdateTempCallback); + int status = temp.getStatus(); + + if (status >= Temperature.THROTTLING_EMERGENCY) { + StatusBar statusBar = getComponent(StatusBar.class); + if (statusBar != null && !statusBar.isDeviceInVrMode()) { + mWarnings.showHighTemperatureWarning(); + Slog.d(TAG, "ThermalEventSkinListener: notifyThrottling was called " + + ", current skin status = " + status + + ", temperature = " + temp.getValue()); + } + } else { + mWarnings.dismissHighTemperatureWarning(); + } + } + } + + // Thermal event received from thermal service manager subsystem + @VisibleForTesting + final class ThermalEventUsbListener extends IThermalEventListener.Stub { + @Override public void notifyThrottling(Temperature temp) { + int status = temp.getStatus(); + + if (status >= Temperature.THROTTLING_EMERGENCY) { + mWarnings.showUsbHighTemperatureAlarm(); + Slog.d(TAG, "ThermalEventUsbListener: notifyThrottling was called " + + ", current usb port status = " + status + + ", temperature = " + temp.getValue()); + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt index 75b8a056df1d..84a344648a39 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyDialog.kt @@ -42,14 +42,10 @@ class OngoingPrivacyDialog constructor( private val iconSize = context.resources.getDimensionPixelSize( R.dimen.ongoing_appops_dialog_icon_size) - private val plusSize = context.resources.getDimensionPixelSize( - R.dimen.ongoing_appops_dialog_app_plus_size) private val iconColor = context.resources.getColor( com.android.internal.R.color.text_color_primary, context.theme) - private val plusColor: Int private val iconMargin = context.resources.getDimensionPixelSize( R.dimen.ongoing_appops_dialog_icon_margin) - private val MAX_ITEMS = context.resources.getInteger(R.integer.ongoing_appops_dialog_max_apps) private val iconFactory = IconDrawableFactory.newInstance(context, true) private var dismissDialog: (() -> Unit)? = null private val appsAndTypes = dialogBuilder.appsAndTypes @@ -57,13 +53,6 @@ class OngoingPrivacyDialog constructor( { it.second.min() }, { it.first })) - init { - val a = context.theme.obtainStyledAttributes( - intArrayOf(com.android.internal.R.attr.colorAccent)) - plusColor = a.getColor(0, 0) - a.recycle() - } - fun createDialog(): Dialog { val builder = AlertDialog.Builder(context).apply { setPositiveButton(R.string.ongoing_privacy_dialog_ok, null) @@ -96,33 +85,9 @@ class OngoingPrivacyDialog constructor( val numItems = appsAndTypes.size for (i in 0..(numItems - 1)) { - if (i >= MAX_ITEMS) break val item = appsAndTypes[i] addAppItem(appsList, item.first, item.second, dialogBuilder.types.size > 1) } - - if (numItems > MAX_ITEMS) { - val overflow = contentView.findViewById(R.id.overflow) as LinearLayout - overflow.visibility = View.VISIBLE - val overflowText = overflow.findViewById(R.id.app_name) as TextView - overflowText.text = context.resources.getQuantityString( - R.plurals.ongoing_privacy_dialog_overflow_text, - numItems - MAX_ITEMS, - numItems - MAX_ITEMS - ) - val overflowPlus = overflow.findViewById(R.id.app_icon) as ImageView - val lp = overflowPlus.layoutParams.apply { - height = plusSize - width = plusSize - } - overflowPlus.layoutParams = lp - overflowPlus.apply { - val plus = context.getDrawable(R.drawable.plus) - imageTintList = ColorStateList.valueOf(plusColor) - setImageDrawable(plus) - } - } - return contentView } diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt index a6e48f8835c7..3f581c4de5fb 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt @@ -18,6 +18,8 @@ import android.content.Context import android.content.pm.ApplicationInfo import android.content.pm.PackageManager import android.graphics.drawable.Drawable +import android.os.UserHandle +import android.util.IconDrawableFactory import com.android.systemui.R typealias Privacy = PrivacyType @@ -46,14 +48,21 @@ data class PrivacyApplication(val packageName: String, val uid: Int, val context private val applicationInfo: ApplicationInfo? by lazy { try { - context.packageManager.getApplicationInfo(packageName, 0) + val userHandle = UserHandle.getUserHandleForUid(uid) + context.createPackageContextAsUser(packageName, 0, userHandle).getPackageManager() + .getApplicationInfo(packageName, 0) } catch (_: PackageManager.NameNotFoundException) { null } } val icon: Drawable by lazy { applicationInfo?.let { - context.packageManager.getApplicationIcon(it) + try { + val iconFactory = IconDrawableFactory.newInstance(context, true) + iconFactory.getBadgedIcon(it, UserHandle.getUserId(uid)) + } catch (_: Exception) { + null + } } ?: context.getDrawable(android.R.drawable.sym_def_app_icon) } diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java index 64ad95c6eaea..c209b315b197 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java @@ -38,7 +38,6 @@ import androidx.recyclerview.widget.RecyclerView; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; -import com.android.settingslib.Utils; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.plugins.qs.QS; @@ -103,10 +102,6 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene mToolbar.getMenu().add(Menu.NONE, MENU_RESET, 0, mContext.getString(com.android.internal.R.string.reset)); mToolbar.setTitle(R.string.qs_edit); - int accentColor = Utils.getColorAttrDefaultColor(context, android.R.attr.colorAccent); - mToolbar.setTitleTextColor(accentColor); - mToolbar.getNavigationIcon().setTint(accentColor); - mToolbar.getOverflowIcon().setTint(accentColor); mRecyclerView = findViewById(android.R.id.list); mTransparentView = findViewById(R.id.customizer_transparent_view); mTileAdapter = new TileAdapter(getContext()); diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java index a29e93a57c69..608f646e77a4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java @@ -19,9 +19,8 @@ import android.app.AlertDialog.Builder; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; -import android.content.res.TypedArray; import android.graphics.Canvas; -import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; import android.os.Handler; import android.view.LayoutInflater; import android.view.View; @@ -505,13 +504,10 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta }; private class TileItemDecoration extends ItemDecoration { - private final ColorDrawable mDrawable; + private final Drawable mDrawable; private TileItemDecoration(Context context) { - TypedArray ta = - context.obtainStyledAttributes(new int[]{android.R.attr.colorSecondary}); - mDrawable = new ColorDrawable(ta.getColor(0, 0)); - ta.recycle(); + mDrawable = context.getDrawable(R.drawable.qs_customize_tile_decoration); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java index 7f76900bba21..c664a2090c04 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java @@ -15,14 +15,11 @@ */ package com.android.systemui.qs.tiles; -import android.content.Context; import android.content.Intent; -import android.graphics.drawable.Drawable; import android.service.quicksettings.Tile; import android.widget.Switch; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.settingslib.graph.BatteryMeterDrawableBase; import com.android.systemui.R; import com.android.systemui.plugins.qs.QSTile.BooleanState; import com.android.systemui.qs.QSHost; @@ -41,6 +38,8 @@ public class BatterySaverTile extends QSTileImpl<BooleanState> implements private boolean mCharging; private boolean mPluggedIn; + private Icon mIcon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_battery_saver); + @Inject public BatterySaverTile(QSHost host, BatteryController batteryController) { super(host); @@ -84,9 +83,7 @@ public class BatterySaverTile extends QSTileImpl<BooleanState> implements protected void handleUpdateState(BooleanState state, Object arg) { state.state = mPluggedIn ? Tile.STATE_UNAVAILABLE : mPowerSave ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; - BatterySaverIcon bsi = new BatterySaverIcon(); - bsi.mState = state.state; - state.icon = bsi; + state.icon = mIcon; state.label = mContext.getString(R.string.battery_detail_switch_title); state.contentDescription = state.label; state.value = mPowerSave; @@ -106,48 +103,4 @@ public class BatterySaverTile extends QSTileImpl<BooleanState> implements mPowerSave = isPowerSave; refreshState(null); } - - public static class BatterySaverIcon extends Icon { - private int mState; - - @Override - public Drawable getDrawable(Context context) { - BatterySaverDrawable b = - new BatterySaverDrawable(context, QSTileImpl.getColorForState(context, mState)); - b.mState = mState; - final int pad = context.getResources() - .getDimensionPixelSize(R.dimen.qs_tile_divider_height); - b.setPadding(pad, pad, pad, pad); - return b; - } - } - - private static class BatterySaverDrawable extends BatteryMeterDrawableBase { - private int mState; - private static final int MAX_BATTERY = 100; - - BatterySaverDrawable(Context context, int frameColor) { - super(context, frameColor); - // Show as full so it's always uniform color - super.setBatteryLevel(MAX_BATTERY); - setPowerSave(true); - setCharging(false); - setPowerSaveAsColorError(false); - mPowerSaveAsColorError = true; - mFramePaint.setColor(0); - mPowersavePaint.setColor(frameColor); - mFramePaint.setStrokeWidth(mPowersavePaint.getStrokeWidth()); - mPlusPaint.setColor(frameColor); - } - - @Override - protected int batteryColorForLevel(int level) { - return 0; - } - - @Override - public void setBatteryLevel(int val) { - // Don't change the actual level, otherwise this won't draw correctly - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java index e1becdbb42a6..c587a39f49e3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java @@ -25,7 +25,11 @@ import android.content.Intent; import android.content.res.Resources; import android.provider.Settings; import android.service.quicksettings.Tile; +import android.text.SpannableString; +import android.text.SpannableStringBuilder; +import android.text.Spanned; import android.text.TextUtils; +import android.text.style.TextAppearanceSpan; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -188,7 +192,8 @@ public class CellularTile extends QSTileImpl<SignalState> { state.secondaryLabel = r.getString(R.string.status_bar_airplane); } else if (mobileDataEnabled) { state.state = Tile.STATE_ACTIVE; - state.secondaryLabel = getMobileDataSubscriptionName(cb); + state.secondaryLabel = appendMobileDataType(getMobileDataSubscriptionName(cb), + cb.dataContentDescription); } else { state.state = Tile.STATE_INACTIVE; state.secondaryLabel = r.getString(R.string.cell_data_off); @@ -207,6 +212,18 @@ public class CellularTile extends QSTileImpl<SignalState> { state.contentDescription = state.label + ", " + contentDescriptionSuffix; } + private CharSequence appendMobileDataType(CharSequence current, CharSequence dataType) { + if (TextUtils.isEmpty(dataType)) { + return current; + } + SpannableString type = new SpannableString(dataType); + SpannableStringBuilder builder = new SpannableStringBuilder(current); + builder.append(" "); + builder.append(type, new TextAppearanceSpan(mContext, R.style.TextAppearance_RATBadge), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + return builder; + } + private CharSequence getMobileDataSubscriptionName(CallbackInfo cb) { if (cb.roaming && !TextUtils.isEmpty(cb.dataSubscriptionName)) { String roaming = mContext.getString(R.string.data_connection_roaming); @@ -232,6 +249,7 @@ public class CellularTile extends QSTileImpl<SignalState> { private static final class CallbackInfo { boolean airplaneModeEnabled; CharSequence dataSubscriptionName; + CharSequence dataContentDescription; boolean activityIn; boolean activityOut; boolean noSim; @@ -250,6 +268,7 @@ public class CellularTile extends QSTileImpl<SignalState> { return; } mInfo.dataSubscriptionName = mController.getMobileDataNetworkName(); + mInfo.dataContentDescription = (description != null) ? typeContentDescription : null; mInfo.activityIn = activityIn; mInfo.activityOut = activityOut; mInfo.roaming = roaming; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java index 52a88145c7ed..3e40cfc0cda2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java @@ -22,6 +22,7 @@ import android.content.pm.PackageManager; import android.content.res.Resources; import android.provider.Settings; import android.service.quicksettings.Tile; +import android.text.TextUtils; import android.util.Log; import android.view.View; import android.view.ViewGroup; @@ -208,6 +209,9 @@ public class WifiTile extends QSTileImpl<SignalState> { if (wifiConnected) { minimalContentDescription.append(cb.wifiSignalContentDescription).append(","); minimalContentDescription.append(removeDoubleQuotes(cb.ssid)); + if (!TextUtils.isEmpty(state.secondaryLabel)) { + minimalContentDescription.append(",").append(state.secondaryLabel); + } } } state.contentDescription = minimalContentDescription.toString(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java index 22d1d5b233ce..d4272605924f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/FlingAnimationUtils.java @@ -32,6 +32,8 @@ import com.android.systemui.statusbar.phone.StatusBar; */ public class FlingAnimationUtils { + private static final String TAG = "FlingAnimationUtils"; + private static final float LINEAR_OUT_SLOW_IN_X2 = 0.35f; private static final float LINEAR_OUT_SLOW_IN_X2_MAX = 0.68f; private static final float LINEAR_OUT_FASTER_IN_X2 = 0.5f; @@ -195,6 +197,10 @@ public class FlingAnimationUtils { } private Interpolator getInterpolator(float startGradient, float velocityFactor) { + if (Float.isNaN(velocityFactor)) { + Log.e(TAG, "Invalid velocity factor", new Throwable()); + return Interpolators.LINEAR_OUT_SLOW_IN; + } if (startGradient != mCachedStartGradient || velocityFactor != mCachedVelocityFactor) { float speedup = mSpeedUpFactor * (1.0f - velocityFactor); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java index c945afd1cb39..f34b912a255c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java @@ -15,7 +15,6 @@ */ package com.android.systemui.statusbar; -import com.android.systemui.statusbar.notification.NotificationRowBinder; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -27,8 +26,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow * want to perform some action before doing so). */ public interface NotificationPresenter extends ExpandableNotificationRow.OnExpandClickListener, - ActivatableNotificationView.OnActivatedListener, - NotificationRowBinder.BindRowCallback { + ActivatableNotificationView.OnActivatedListener { /** * Returns true if the presenter is not visible. For example, it may not be necessary to do * animations if this returns true. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index 3fbc64163759..4ed9ae4d5142 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -35,6 +35,7 @@ import com.android.systemui.statusbar.NotificationUpdateHandler; import com.android.systemui.statusbar.notification.collection.NotificationData; import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.NotificationRowBinder; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.NotificationContentInflater; import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag; @@ -124,16 +125,7 @@ public class NotificationEntryManager implements return mRemoteInputManager; } - private NotificationRowBinder getRowBinder() { - if (mNotificationRowBinder == null) { - mNotificationRowBinder = Dependency.get(NotificationRowBinder.class); - } - return mNotificationRowBinder; - } - - // TODO: Remove this once we can always use a mocked row binder in our tests - @VisibleForTesting - void setRowBinder(NotificationRowBinder notificationRowBinder) { + public void setRowBinder(NotificationRowBinder notificationRowBinder) { mNotificationRowBinder = notificationRowBinder; } @@ -345,7 +337,7 @@ public class NotificationEntryManager implements Dependency.get(LeakDetector.class).trackInstance(entry); // Construct the expanded view. - getRowBinder().inflateViews(entry, () -> performRemoveNotification(notification)); + requireBinder().inflateViews(entry, () -> performRemoveNotification(notification)); abortExistingInflation(key); @@ -386,7 +378,7 @@ public class NotificationEntryManager implements listener.onPreEntryUpdated(entry); } - getRowBinder().inflateViews(entry, () -> performRemoveNotification(notification)); + requireBinder().inflateViews(entry, () -> performRemoveNotification(notification)); updateNotifications(); if (DEBUG) { @@ -440,7 +432,7 @@ public class NotificationEntryManager implements // By comparing the old and new UI adjustments, reinflate the view accordingly. for (NotificationEntry entry : entries) { - getRowBinder().onNotificationRankingUpdated( + requireBinder().onNotificationRankingUpdated( entry, oldImportances.get(entry.key), oldAdjustments.get(entry.key), @@ -486,4 +478,12 @@ public class NotificationEntryManager implements activeExtender.setShouldManageLifetime(entry, false); } } + + private NotificationRowBinder requireBinder() { + if (mNotificationRowBinder == null) { + throw new RuntimeException("You must initialize NotificationEntryManager by calling" + + "setRowBinder() before using."); + } + return mNotificationRowBinder; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinder.java new file mode 100644 index 000000000000..7504e863ca73 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinder.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.collection; + +import android.annotation.Nullable; + +import com.android.systemui.statusbar.NotificationUiAdjustment; +import com.android.systemui.statusbar.notification.InflationException; +import com.android.systemui.statusbar.notification.NotificationEntryManager; + +/** + * Used by the {@link NotificationEntryManager}. When notifications are added or updated, the binder + * is asked to (re)inflate and prepare their views. This inflation must occur off the main thread. + */ +public interface NotificationRowBinder { + /** + * Called when a notification has been added or updated. The binder must asynchronously inflate + * and bind the views associated with the notification. + * + * TODO: The caller is notified when the inflation completes, but this is currently a very + * roundabout business. Add an explicit completion/failure callback to this method. + */ + void inflateViews( + NotificationEntry entry, + Runnable onDismissRunnable) + throws InflationException; + + /** + * Called when the ranking has been updated (but not add or remove has been done). The binder + * should inspect the old and new adjustments and re-inflate the entry's views if necessary + * (e.g. if something important changed). + */ + void onNotificationRankingUpdated( + NotificationEntry entry, + @Nullable Integer oldImportance, + NotificationUiAdjustment oldAdjustment, + NotificationUiAdjustment newAdjustment); +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java index 6f5baf9faf39..b91cdaf9ae80 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationRowBinder.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRowBinderImpl.java @@ -14,10 +14,9 @@ * limitations under the License. */ -package com.android.systemui.statusbar.notification; +package com.android.systemui.statusbar.notification.collection; import static com.android.internal.util.Preconditions.checkNotNull; -import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME; import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT; import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_AMBIENT; import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_HEADS_UP; @@ -39,7 +38,9 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationUiAdjustment; -import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.InflationException; +import com.android.systemui.statusbar.notification.NotificationClicker; +import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationContentInflater; @@ -50,13 +51,8 @@ import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.HeadsUpManager; -import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; - /** Handles inflating and updating views for notifications. */ -@Singleton -public class NotificationRowBinder { +public class NotificationRowBinderImpl implements NotificationRowBinder { private static final String TAG = "NotificationViewManager"; @@ -84,9 +80,7 @@ public class NotificationRowBinder { private NotificationClicker mNotificationClicker; private final NotificationLogger mNotificationLogger = Dependency.get(NotificationLogger.class); - @Inject - public NotificationRowBinder(Context context, - @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress) { + public NotificationRowBinderImpl(Context context, boolean allowLongPress) { mContext = context; mMessagingUtil = new NotificationMessagingUtil(context); mAllowLongPress = allowLongPress; @@ -122,6 +116,7 @@ public class NotificationRowBinder { /** * Inflates the views for the given entry (possibly asynchronously). */ + @Override public void inflateViews( NotificationEntry entry, Runnable onDismissRunnable) @@ -192,6 +187,7 @@ public class NotificationRowBinder { * Updates the views bound to an entry when the entry's ranking changes, either in-place or by * reinflating them. */ + @Override public void onNotificationRankingUpdated( NotificationEntry entry, @Nullable Integer oldImportance, @@ -264,7 +260,7 @@ public class NotificationRowBinder { } private void logNotificationExpansion(String key, boolean userAction, boolean expanded) { - mNotificationLogger.onExpansionChanged(key, userAction, expanded); + mNotificationLogger.onExpansionChanged(key, userAction, expanded); } /** Callback for when a row is bound to an entry. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java index b65c4a5f71d8..b4dd1144e761 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java @@ -34,12 +34,17 @@ import android.widget.RemoteViews; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.widget.ImageMessageConsumer; +import com.android.systemui.Dependency; import com.android.systemui.statusbar.InflationTask; +import com.android.systemui.statusbar.SmartReplyController; import com.android.systemui.statusbar.notification.InflationException; import com.android.systemui.statusbar.notification.MediaNotificationProcessor; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper; import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.policy.InflatedSmartReplies; +import com.android.systemui.statusbar.policy.SmartReplyConstants; import com.android.systemui.util.Assert; import java.lang.annotation.Retention; @@ -278,6 +283,8 @@ public class NotificationContentInflater { InflationProgress result = createRemoteViews(reInflateFlags, builder, mIsLowPriority, mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, mRedactAmbient, packageContext); + result = inflateSmartReplyViews(result, reInflateFlags, mRow.getEntry(), + mRow.getContext(), mRow.getHeadsUpManager()); apply( inflateSynchronously, result, @@ -306,6 +313,7 @@ public class NotificationContentInflater { if (mRow.getPrivateLayout().isContentViewInactive(VISIBLE_TYPE_HEADSUP)) { mRow.getPrivateLayout().setHeadsUpChild(null); mCachedContentViews.remove(FLAG_CONTENT_VIEW_HEADS_UP); + mRow.getPrivateLayout().setHeadsUpInflatedSmartReplies(null); } break; case FLAG_CONTENT_VIEW_AMBIENT: @@ -336,12 +344,33 @@ public class NotificationContentInflater { } } + private static InflationProgress inflateSmartReplyViews(InflationProgress result, + @InflationFlag int reInflateFlags, NotificationEntry entry, Context context, + HeadsUpManager headsUpManager) { + SmartReplyConstants smartReplyConstants = Dependency.get(SmartReplyConstants.class); + SmartReplyController smartReplyController = Dependency.get(SmartReplyController.class); + if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0 && result.newExpandedView != null) { + result.expandedInflatedSmartReplies = + InflatedSmartReplies.inflate( + context, entry, smartReplyConstants, smartReplyController, + headsUpManager); + } + if ((reInflateFlags & FLAG_CONTENT_VIEW_HEADS_UP) != 0 && result.newHeadsUpView != null) { + result.headsUpInflatedSmartReplies = + InflatedSmartReplies.inflate( + context, entry, smartReplyConstants, smartReplyController, + headsUpManager); + } + return result; + } + private static InflationProgress createRemoteViews(@InflationFlag int reInflateFlags, Notification.Builder builder, boolean isLowPriority, boolean isChildInGroup, boolean usesIncreasedHeight, boolean usesIncreasedHeadsUpHeight, boolean redactAmbient, Context packageContext) { InflationProgress result = new InflationProgress(); isLowPriority = isLowPriority && !isChildInGroup; + if ((reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0) { result.newContentView = createContentView(builder, isLowPriority, usesIncreasedHeight); } @@ -661,6 +690,12 @@ public class NotificationContentInflater { } else if (result.newExpandedView == null) { privateLayout.setExpandedChild(null); } + if (result.newExpandedView != null) { + privateLayout.setExpandedInflatedSmartReplies( + result.expandedInflatedSmartReplies); + } else { + privateLayout.setExpandedInflatedSmartReplies(null); + } cachedContentViews.put(FLAG_CONTENT_VIEW_EXPANDED, result.newExpandedView); row.setExpandable(result.newExpandedView != null); } @@ -671,6 +706,12 @@ public class NotificationContentInflater { } else if (result.newHeadsUpView == null) { privateLayout.setHeadsUpChild(null); } + if (result.newHeadsUpView != null) { + privateLayout.setHeadsUpInflatedSmartReplies( + result.headsUpInflatedSmartReplies); + } else { + privateLayout.setHeadsUpInflatedSmartReplies(null); + } cachedContentViews.put(FLAG_CONTENT_VIEW_HEADS_UP, result.newHeadsUpView); } @@ -846,9 +887,12 @@ public class NotificationContentInflater { packageContext); processor.processNotification(notification, recoveredBuilder); } - return createRemoteViews(mReInflateFlags, recoveredBuilder, mIsLowPriority, + InflationProgress inflationProgress = createRemoteViews(mReInflateFlags, + recoveredBuilder, mIsLowPriority, mIsChildInGroup, mUsesIncreasedHeight, mUsesIncreasedHeadsUpHeight, mRedactAmbient, packageContext); + return inflateSmartReplyViews(inflationProgress, mReInflateFlags, mRow.getEntry(), + mRow.getContext(), mRow.getHeadsUpManager()); } catch (Exception e) { mError = e; return null; @@ -927,6 +971,9 @@ public class NotificationContentInflater { private View inflatedPublicView; private CharSequence headsUpStatusBarText; private CharSequence headsUpStatusBarTextPublic; + + private InflatedSmartReplies expandedInflatedSmartReplies; + private InflatedSmartReplies headsUpInflatedSmartReplies; } @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java index 1dc48d4b18b9..646617c72128 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java @@ -19,7 +19,6 @@ package com.android.systemui.statusbar.notification.row; import android.annotation.Nullable; import android.app.Notification; import android.app.PendingIntent; -import android.app.RemoteInput; import android.content.Context; import android.graphics.Rect; import android.os.Build; @@ -28,7 +27,6 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.AttributeSet; import android.util.Log; -import android.util.Pair; import android.view.MotionEvent; import android.view.NotificationHeaderView; import android.view.View; @@ -39,7 +37,6 @@ import android.widget.ImageView; import android.widget.LinearLayout; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.ArrayUtils; import com.android.internal.util.ContrastColorUtil; import com.android.systemui.Dependency; import com.android.systemui.R; @@ -52,13 +49,14 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.wrapper.NotificationCustomViewWrapper; import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper; import com.android.systemui.statusbar.phone.NotificationGroupManager; +import com.android.systemui.statusbar.policy.InflatedSmartReplies; +import com.android.systemui.statusbar.policy.InflatedSmartReplies.SmartRepliesAndActions; import com.android.systemui.statusbar.policy.RemoteInputView; import com.android.systemui.statusbar.policy.SmartReplyConstants; import com.android.systemui.statusbar.policy.SmartReplyView; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.util.List; /** * A frame layout containing the actual payload of the notification, including the contracted, @@ -95,6 +93,8 @@ public class NotificationContentView extends FrameLayout { private SmartReplyView mExpandedSmartReplyView; private SmartReplyView mHeadsUpSmartReplyView; private SmartReplyController mSmartReplyController; + private InflatedSmartReplies mExpandedInflatedSmartReplies; + private InflatedSmartReplies mHeadsUpInflatedSmartReplies; private NotificationViewWrapper mContractedWrapper; private NotificationViewWrapper mExpandedWrapper; @@ -1318,8 +1318,22 @@ public class NotificationContentView extends FrameLayout { return; } - SmartRepliesAndActions smartRepliesAndActions = - chooseSmartRepliesAndActions(mSmartReplyConstants, entry); + applyRemoteInput(entry, InflatedSmartReplies.hasFreeformRemoteInput(entry)); + + if (mExpandedInflatedSmartReplies == null && mHeadsUpInflatedSmartReplies == null) { + if (DEBUG) { + Log.d(TAG, "Both expanded, and heads-up InflatedSmartReplies are null, " + + "don't add smart replies."); + } + return; + } + // The inflated smart-reply objects for the expanded view and the heads-up view both contain + // the same SmartRepliesAndActions to avoid discrepancies between the two views. We here + // reuse that object for our local SmartRepliesAndActions to avoid discrepancies between + // this class and the InflatedSmartReplies classes. + SmartRepliesAndActions smartRepliesAndActions = mExpandedInflatedSmartReplies != null + ? mExpandedInflatedSmartReplies.getSmartRepliesAndActions() + : mHeadsUpInflatedSmartReplies.getSmartRepliesAndActions(); if (DEBUG) { Log.d(TAG, String.format("Adding suggestions for %s, %d actions, and %d replies.", entry.notification.getKey(), @@ -1328,86 +1342,9 @@ public class NotificationContentView extends FrameLayout { smartRepliesAndActions.smartReplies == null ? 0 : smartRepliesAndActions.smartReplies.choices.length)); } - - applyRemoteInput(entry, smartRepliesAndActions.hasFreeformRemoteInput); applySmartReplyView(smartRepliesAndActions, entry); } - /** - * Chose what smart replies and smart actions to display. App generated suggestions take - * precedence. So if the app provides any smart replies, we don't show any - * replies or actions generated by the NotificationAssistantService (NAS), and if the app - * provides any smart actions we also don't show any NAS-generated replies or actions. - */ - @VisibleForTesting - static SmartRepliesAndActions chooseSmartRepliesAndActions( - SmartReplyConstants smartReplyConstants, - final NotificationEntry entry) { - Notification notification = entry.notification.getNotification(); - Pair<RemoteInput, Notification.Action> remoteInputActionPair = - notification.findRemoteInputActionPair(false /* freeform */); - Pair<RemoteInput, Notification.Action> freeformRemoteInputActionPair = - notification.findRemoteInputActionPair(true /* freeform */); - - if (!smartReplyConstants.isEnabled()) { - if (DEBUG) { - Log.d(TAG, "Smart suggestions not enabled, not adding suggestions for " - + entry.notification.getKey()); - } - return new SmartRepliesAndActions(null, null, freeformRemoteInputActionPair != null); - } - // Only use smart replies from the app if they target P or above. We have this check because - // the smart reply API has been used for other things (Wearables) in the past. The API to - // add smart actions is new in Q so it doesn't require a target-sdk check. - boolean enableAppGeneratedSmartReplies = (!smartReplyConstants.requiresTargetingP() - || entry.targetSdk >= Build.VERSION_CODES.P); - - boolean appGeneratedSmartRepliesExist = - enableAppGeneratedSmartReplies - && remoteInputActionPair != null - && !ArrayUtils.isEmpty(remoteInputActionPair.first.getChoices()) - && remoteInputActionPair.second.actionIntent != null; - - List<Notification.Action> appGeneratedSmartActions = notification.getContextualActions(); - boolean appGeneratedSmartActionsExist = !appGeneratedSmartActions.isEmpty(); - - SmartReplyView.SmartReplies smartReplies = null; - SmartReplyView.SmartActions smartActions = null; - if (appGeneratedSmartRepliesExist) { - smartReplies = new SmartReplyView.SmartReplies( - remoteInputActionPair.first.getChoices(), - remoteInputActionPair.first, - remoteInputActionPair.second.actionIntent, - false /* fromAssistant */); - } - if (appGeneratedSmartActionsExist) { - smartActions = new SmartReplyView.SmartActions(appGeneratedSmartActions, - false /* fromAssistant */); - } - // Apps didn't provide any smart replies / actions, use those from NAS (if any). - if (!appGeneratedSmartRepliesExist && !appGeneratedSmartActionsExist) { - boolean useGeneratedReplies = !ArrayUtils.isEmpty(entry.systemGeneratedSmartReplies) - && freeformRemoteInputActionPair != null - && freeformRemoteInputActionPair.second.getAllowGeneratedReplies() - && freeformRemoteInputActionPair.second.actionIntent != null; - if (useGeneratedReplies) { - smartReplies = new SmartReplyView.SmartReplies( - entry.systemGeneratedSmartReplies, - freeformRemoteInputActionPair.first, - freeformRemoteInputActionPair.second.actionIntent, - true /* fromAssistant */); - } - boolean useSmartActions = !ArrayUtils.isEmpty(entry.systemGeneratedSmartActions) - && notification.getAllowSystemGeneratedContextualActions(); - if (useSmartActions) { - smartActions = new SmartReplyView.SmartActions( - entry.systemGeneratedSmartActions, true /* fromAssistant */); - } - } - return new SmartRepliesAndActions( - smartReplies, smartActions, freeformRemoteInputActionPair != null); - } - private void applyRemoteInput(NotificationEntry entry, boolean hasFreeformRemoteInput) { View bigContentView = mExpandedChild; if (bigContentView != null) { @@ -1507,11 +1444,12 @@ public class NotificationContentView extends FrameLayout { return null; } - private void applySmartReplyView(SmartRepliesAndActions smartRepliesAndActions, + private void applySmartReplyView( + SmartRepliesAndActions smartRepliesAndActions, NotificationEntry entry) { if (mExpandedChild != null) { - mExpandedSmartReplyView = - applySmartReplyView(mExpandedChild, smartRepliesAndActions, entry); + mExpandedSmartReplyView = applySmartReplyView(mExpandedChild, smartRepliesAndActions, + entry, mExpandedInflatedSmartReplies); if (mExpandedSmartReplyView != null) { if (smartRepliesAndActions.smartReplies != null || smartRepliesAndActions.smartActions != null) { @@ -1533,65 +1471,79 @@ public class NotificationContentView extends FrameLayout { } } if (mHeadsUpChild != null && mSmartReplyConstants.getShowInHeadsUp()) { - mHeadsUpSmartReplyView = - applySmartReplyView(mHeadsUpChild, smartRepliesAndActions, entry); + mHeadsUpSmartReplyView = applySmartReplyView(mHeadsUpChild, smartRepliesAndActions, + entry, mHeadsUpInflatedSmartReplies); } } + @Nullable private SmartReplyView applySmartReplyView(View view, - SmartRepliesAndActions smartRepliesAndActions, NotificationEntry entry) { + SmartRepliesAndActions smartRepliesAndActions, + NotificationEntry entry, InflatedSmartReplies inflatedSmartReplyView) { View smartReplyContainerCandidate = view.findViewById( com.android.internal.R.id.smart_reply_container); if (!(smartReplyContainerCandidate instanceof LinearLayout)) { return null; } + LinearLayout smartReplyContainer = (LinearLayout) smartReplyContainerCandidate; - // If there are no smart replies and no smart actions - early out. - if (smartRepliesAndActions.smartReplies == null - && smartRepliesAndActions.smartActions == null) { - smartReplyContainer.setVisibility(View.GONE); - return null; - } - // If we are showing the spinner we don't want to add the buttons. - boolean showingSpinner = entry.notification.getNotification() - .extras.getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false); - if (showingSpinner) { - smartReplyContainer.setVisibility(View.GONE); - return null; - } - // If we are keeping the notification around while sending we don't want to add the buttons. - boolean hideSmartReplies = entry.notification.getNotification() - .extras.getBoolean(Notification.EXTRA_HIDE_SMART_REPLIES, false); - if (hideSmartReplies) { + if (!InflatedSmartReplies.shouldShowSmartReplyView(entry, smartRepliesAndActions)) { smartReplyContainer.setVisibility(View.GONE); return null; } + SmartReplyView smartReplyView = null; - if (smartReplyContainer.getChildCount() == 0) { - smartReplyView = SmartReplyView.inflate(mContext, smartReplyContainer); + if (smartReplyContainer.getChildCount() == 1 + && smartReplyContainer.getChildAt(0) instanceof SmartReplyView) { + // If we already have a SmartReplyView - replace it with the newly inflated one. The + // newly inflated one is connected to the new inflated smart reply/action buttons. + smartReplyContainer.removeAllViews(); + } + if (smartReplyContainer.getChildCount() == 0 + && inflatedSmartReplyView != null + && inflatedSmartReplyView.getSmartReplyView() != null) { + smartReplyView = inflatedSmartReplyView.getSmartReplyView(); smartReplyContainer.addView(smartReplyView); - } else if (smartReplyContainer.getChildCount() == 1) { - View child = smartReplyContainer.getChildAt(0); - if (child instanceof SmartReplyView) { - smartReplyView = (SmartReplyView) child; - } } if (smartReplyView != null) { smartReplyView.resetSmartSuggestions(smartReplyContainer); - if (smartRepliesAndActions.smartReplies != null) { - smartReplyView.addRepliesFromRemoteInput( - smartRepliesAndActions.smartReplies, mSmartReplyController, entry); - } - if (smartRepliesAndActions.smartActions != null) { - smartReplyView.addSmartActions( - smartRepliesAndActions.smartActions, mSmartReplyController, entry, - mContainingNotification.getHeadsUpManager()); - } + smartReplyView.addPreInflatedButtons( + inflatedSmartReplyView.getSmartSuggestionButtons()); smartReplyContainer.setVisibility(View.VISIBLE); } return smartReplyView; } + /** + * Set pre-inflated views necessary to display smart replies and actions in the expanded + * notification state. + * + * @param inflatedSmartReplies the pre-inflated state to add to this view. If null the existing + * {@link SmartReplyView} related to the expanded notification state is cleared. + */ + public void setExpandedInflatedSmartReplies( + @Nullable InflatedSmartReplies inflatedSmartReplies) { + mExpandedInflatedSmartReplies = inflatedSmartReplies; + if (inflatedSmartReplies == null) { + mExpandedSmartReplyView = null; + } + } + + /** + * Set pre-inflated views necessary to display smart replies and actions in the heads-up + * notification state. + * + * @param inflatedSmartReplies the pre-inflated state to add to this view. If null the existing + * {@link SmartReplyView} related to the heads-up notification state is cleared. + */ + public void setHeadsUpInflatedSmartReplies( + @Nullable InflatedSmartReplies inflatedSmartReplies) { + mHeadsUpInflatedSmartReplies = inflatedSmartReplies; + if (inflatedSmartReplies == null) { + mHeadsUpSmartReplyView = null; + } + } + public void closeRemoteInput() { if (mHeadsUpRemoteInput != null) { mHeadsUpRemoteInput.close(); @@ -2005,22 +1957,4 @@ public class NotificationContentView extends FrameLayout { } pw.println(); } - - @VisibleForTesting - static class SmartRepliesAndActions { - @Nullable - public final SmartReplyView.SmartReplies smartReplies; - @Nullable - public final SmartReplyView.SmartActions smartActions; - public final boolean hasFreeformRemoteInput; - - SmartRepliesAndActions( - @Nullable SmartReplyView.SmartReplies smartReplies, - @Nullable SmartReplyView.SmartActions smartActions, - boolean hasFreeformRemoteInput) { - this.smartReplies = smartReplies; - this.smartActions = smartActions; - this.hasFreeformRemoteInput = hasFreeformRemoteInput; - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java index c9be2c8f6703..007c50c8765d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java @@ -25,6 +25,8 @@ import com.android.systemui.Dependency; import com.android.systemui.qs.AutoAddTracker; import com.android.systemui.qs.QSTileHost; import com.android.systemui.qs.SecureSetting; +import com.android.systemui.statusbar.policy.CastController; +import com.android.systemui.statusbar.policy.CastController.CastDevice; import com.android.systemui.statusbar.policy.DataSaverController; import com.android.systemui.statusbar.policy.DataSaverController.Listener; import com.android.systemui.statusbar.policy.HotspotController; @@ -42,6 +44,7 @@ public class AutoTileManager { public static final String INVERSION = "inversion"; public static final String WORK = "work"; public static final String NIGHT = "night"; + public static final String CAST = "cast"; private final Context mContext; private final QSTileHost mHost; @@ -51,6 +54,7 @@ public class AutoTileManager { private final DataSaverController mDataSaverController; private final ManagedProfileController mManagedProfileController; private final NightDisplayListener mNightDisplayListener; + private final CastController mCastController; @Inject public AutoTileManager(Context context, AutoAddTracker autoAddTracker, QSTileHost host, @@ -58,7 +62,8 @@ public class AutoTileManager { HotspotController hotspotController, DataSaverController dataSaverController, ManagedProfileController managedProfileController, - NightDisplayListener nightDisplayListener) { + NightDisplayListener nightDisplayListener, + CastController castController) { mAutoTracker = autoAddTracker; mContext = context; mHost = host; @@ -67,6 +72,7 @@ public class AutoTileManager { mDataSaverController = dataSaverController; mManagedProfileController = managedProfileController; mNightDisplayListener = nightDisplayListener; + mCastController = castController; if (!mAutoTracker.isAdded(HOTSPOT)) { hotspotController.addCallback(mHotspotCallback); } @@ -95,6 +101,9 @@ public class AutoTileManager { && ColorDisplayManager.isNightDisplayAvailable(mContext)) { nightDisplayListener.setCallback(mNightDisplayCallback); } + if (!mAutoTracker.isAdded(CAST)) { + castController.addCallback(mCastCallback); + } } public void destroy() { @@ -108,6 +117,7 @@ public class AutoTileManager { if (ColorDisplayManager.isNightDisplayAvailable(mContext)) { mNightDisplayListener.setCallback(null); } + mCastController.removeCallback(mCastCallback); } public void unmarkTileAsAutoAdded(String tabSpec) { @@ -181,4 +191,27 @@ public class AutoTileManager { mHandler.post(() -> mNightDisplayListener.setCallback(null)); } }; + + @VisibleForTesting + final CastController.Callback mCastCallback = new CastController.Callback() { + @Override + public void onCastDevicesChanged() { + if (mAutoTracker.isAdded(CAST)) return; + + boolean isCasting = false; + for (CastDevice device : mCastController.getCastDevices()) { + if (device.state == CastDevice.STATE_CONNECTED + || device.state == CastDevice.STATE_CONNECTING) { + isCasting = true; + break; + } + } + + if (isCasting) { + mHost.addTile(CAST); + mAutoTracker.setTileAdded(CAST); + mHandler.post(() -> mCastController.removeCallback(mCastCallback)); + } + } + }; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java index 10497734d0b1..a17e04259fe3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.phone; import android.content.Context; +import android.hardware.display.AmbientDisplayConfiguration; import android.os.PowerManager; import android.os.SystemProperties; import android.os.UserHandle; @@ -26,7 +27,6 @@ import android.util.MathUtils; import android.util.SparseBooleanArray; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.doze.AlwaysOnDisplayPolicy; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java index cf3f89ef8788..409d60fa6c17 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavBarTintController.java @@ -135,7 +135,7 @@ public class NavBarTintController { final Bitmap hardBitmap = SurfaceControl .screenshot(new Rect(), mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels, mNavigationBarView.getContext().getDisplay().getRotation()); - if (cropRect.bottom <= hardBitmap.getHeight() + if (hardBitmap != null && cropRect.bottom <= hardBitmap.getHeight() && cropRect.left + cropRect.width() <= hardBitmap.getWidth()) { final Bitmap cropBitmap = Bitmap.createBitmap(hardBitmap, cropRect.left, cropRect.top, cropRect.width(), cropRect.height()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java index 64209a7b9e73..fcf5893eafcc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarEdgePanel.java @@ -103,7 +103,7 @@ public class NavigationBarEdgePanel extends View { public static NavigationBarEdgePanel create(@NonNull Context context, int width, int height, int gravity) { final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, height, - WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, + WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java index c9fa89e7cb97..faa2ab105816 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java @@ -23,18 +23,15 @@ import android.graphics.drawable.Icon; import android.util.AttributeSet; import android.util.Log; import android.util.SparseArray; -import android.view.Display; -import android.view.Display.Mode; import android.view.Gravity; import android.view.LayoutInflater; -import android.view.Surface; import android.view.View; import android.view.ViewGroup; -import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.Space; +import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.plugins.PluginListener; @@ -84,21 +81,21 @@ public class NavigationBarInflaterView extends FrameLayout private static final String WEIGHT_CENTERED_SUFFIX = "WC"; private final List<NavBarButtonProvider> mPlugins = new ArrayList<>(); - private final Display mDisplay; protected LayoutInflater mLayoutInflater; protected LayoutInflater mLandscapeInflater; - protected FrameLayout mRot0; - protected FrameLayout mRot90; - private boolean isRot0Landscape; + protected FrameLayout mHorizontal; + protected FrameLayout mVertical; - private SparseArray<ButtonDispatcher> mButtonDispatchers; + @VisibleForTesting + SparseArray<ButtonDispatcher> mButtonDispatchers; private String mCurrentLayout; private View mLastPortrait; private View mLastLandscape; + private boolean mIsVertical; private boolean mAlternativeOrder; private boolean mUsingCustomLayout; @@ -107,14 +104,11 @@ public class NavigationBarInflaterView extends FrameLayout public NavigationBarInflaterView(Context context, AttributeSet attrs) { super(context, attrs); createInflaters(); - mDisplay = ((WindowManager) - context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); - Mode displayMode = mDisplay.getMode(); - isRot0Landscape = displayMode.getPhysicalWidth() > displayMode.getPhysicalHeight(); mOverviewProxyService = Dependency.get(OverviewProxyService.class); } - private void createInflaters() { + @VisibleForTesting + void createInflaters() { mLayoutInflater = LayoutInflater.from(mContext); Configuration landscape = new Configuration(); landscape.setTo(mContext.getResources().getConfiguration()); @@ -132,13 +126,12 @@ public class NavigationBarInflaterView extends FrameLayout private void inflateChildren() { removeAllViews(); - mRot0 = (FrameLayout) mLayoutInflater.inflate(R.layout.navigation_layout, this, false); - mRot0.setId(R.id.rot0); - addView(mRot0); - mRot90 = (FrameLayout) mLayoutInflater.inflate(R.layout.navigation_layout_rot90, this, - false); - mRot90.setId(R.id.rot90); - addView(mRot90); + mHorizontal = (FrameLayout) mLayoutInflater.inflate(R.layout.navigation_layout, + this /* root */, false /* attachToRoot */); + addView(mHorizontal); + mVertical = (FrameLayout) mLayoutInflater.inflate(R.layout.navigation_layout_vertical, + this /* root */, false /* attachToRoot */); + addView(mVertical); updateAlternativeOrder(); } @@ -198,12 +191,9 @@ public class NavigationBarInflaterView extends FrameLayout } } - public void updateButtonDispatchersCurrentView() { + void updateButtonDispatchersCurrentView() { if (mButtonDispatchers != null) { - final int rotation = mDisplay.getRotation(); - final boolean portrait = rotation == Surface.ROTATION_0 - || rotation == Surface.ROTATION_180; - final View view = portrait ? mRot0 : mRot90; + View view = mIsVertical ? mVertical : mHorizontal; for (int i = 0; i < mButtonDispatchers.size(); i++) { final ButtonDispatcher dispatcher = mButtonDispatchers.valueAt(i); dispatcher.setCurrentView(view); @@ -211,7 +201,13 @@ public class NavigationBarInflaterView extends FrameLayout } } - public void setAlternativeOrder(boolean alternativeOrder) { + void setVertical(boolean vertical) { + if (vertical != mIsVertical) { + mIsVertical = vertical; + } + } + + void setAlternativeOrder(boolean alternativeOrder) { if (alternativeOrder != mAlternativeOrder) { mAlternativeOrder = alternativeOrder; updateAlternativeOrder(); @@ -219,10 +215,10 @@ public class NavigationBarInflaterView extends FrameLayout } private void updateAlternativeOrder() { - updateAlternativeOrder(mRot0.findViewById(R.id.ends_group)); - updateAlternativeOrder(mRot0.findViewById(R.id.center_group)); - updateAlternativeOrder(mRot90.findViewById(R.id.ends_group)); - updateAlternativeOrder(mRot90.findViewById(R.id.center_group)); + updateAlternativeOrder(mHorizontal.findViewById(R.id.ends_group)); + updateAlternativeOrder(mHorizontal.findViewById(R.id.center_group)); + updateAlternativeOrder(mVertical.findViewById(R.id.ends_group)); + updateAlternativeOrder(mVertical.findViewById(R.id.center_group)); } private void updateAlternativeOrder(View v) { @@ -232,10 +228,10 @@ public class NavigationBarInflaterView extends FrameLayout } private void initiallyFill(ButtonDispatcher buttonDispatcher) { - addAll(buttonDispatcher, (ViewGroup) mRot0.findViewById(R.id.ends_group)); - addAll(buttonDispatcher, (ViewGroup) mRot0.findViewById(R.id.center_group)); - addAll(buttonDispatcher, (ViewGroup) mRot90.findViewById(R.id.ends_group)); - addAll(buttonDispatcher, (ViewGroup) mRot90.findViewById(R.id.center_group)); + addAll(buttonDispatcher, mHorizontal.findViewById(R.id.ends_group)); + addAll(buttonDispatcher, mHorizontal.findViewById(R.id.center_group)); + addAll(buttonDispatcher, mVertical.findViewById(R.id.ends_group)); + addAll(buttonDispatcher, mVertical.findViewById(R.id.center_group)); } private void addAll(ButtonDispatcher buttonDispatcher, ViewGroup parent) { @@ -267,17 +263,23 @@ public class NavigationBarInflaterView extends FrameLayout String[] center = sets[1].split(BUTTON_SEPARATOR); String[] end = sets[2].split(BUTTON_SEPARATOR); // Inflate these in start to end order or accessibility traversal will be messed up. - inflateButtons(start, mRot0.findViewById(R.id.ends_group), isRot0Landscape, true); - inflateButtons(start, mRot90.findViewById(R.id.ends_group), !isRot0Landscape, true); + inflateButtons(start, mHorizontal.findViewById(R.id.ends_group), + false /* landscape */, true /* start */); + inflateButtons(start, mVertical.findViewById(R.id.ends_group), + true /* landscape */, true /* start */); - inflateButtons(center, mRot0.findViewById(R.id.center_group), isRot0Landscape, false); - inflateButtons(center, mRot90.findViewById(R.id.center_group), !isRot0Landscape, false); + inflateButtons(center, mHorizontal.findViewById(R.id.center_group), + false /* landscape */, false /* start */); + inflateButtons(center, mVertical.findViewById(R.id.center_group), + true /* landscape */, false /* start */); - addGravitySpacer(mRot0.findViewById(R.id.ends_group)); - addGravitySpacer(mRot90.findViewById(R.id.ends_group)); + addGravitySpacer(mHorizontal.findViewById(R.id.ends_group)); + addGravitySpacer(mVertical.findViewById(R.id.ends_group)); - inflateButtons(end, mRot0.findViewById(R.id.ends_group), isRot0Landscape, false); - inflateButtons(end, mRot90.findViewById(R.id.ends_group), !isRot0Landscape, false); + inflateButtons(end, mHorizontal.findViewById(R.id.ends_group), + false /* landscape */, false /* start */); + inflateButtons(end, mVertical.findViewById(R.id.ends_group), + true /* landscape */, false /* start */); updateButtonDispatchersCurrentView(); } @@ -472,8 +474,8 @@ public class NavigationBarInflaterView extends FrameLayout mButtonDispatchers.valueAt(i).clear(); } } - clearAllChildren(mRot0.findViewById(R.id.nav_buttons)); - clearAllChildren(mRot90.findViewById(R.id.nav_buttons)); + clearAllChildren(mHorizontal.findViewById(R.id.nav_buttons)); + clearAllChildren(mVertical.findViewById(R.id.nav_buttons)); } private void clearAllChildren(ViewGroup group) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index d6d3d0807659..f82b05e78ff9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -116,9 +116,11 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav final static boolean ALTERNATE_CAR_MODE_UI = false; View mCurrentView = null; - View[] mRotatedViews = new View[4]; + private View mVertical; + private View mHorizontal; - boolean mVertical; + /** Indicates that navigation bar is vertical. */ + private boolean mIsVertical; private int mCurrentRotation = -1; boolean mLongClickableAccessibilityButton; @@ -350,7 +352,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav public NavigationBarView(Context context, AttributeSet attrs) { super(context, attrs); - mVertical = false; + mIsVertical = false; mLongClickableAccessibilityButton = false; // Set up the context group of buttons @@ -471,7 +473,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav public void setOnVerticalChangedListener(OnVerticalChangedListener onVerticalChangedListener) { mOnVerticalChangedListener = onVerticalChangedListener; - notifyVerticalChangedListener(mVertical); + notifyVerticalChangedListener(mIsVertical); } @Override @@ -547,10 +549,6 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav return mCurrentView; } - public View[] getAllViews() { - return mRotatedViews; - } - public ButtonDispatcher getRecentsButton() { return mButtonDispatchers.get(R.id.recent_apps); } @@ -657,7 +655,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav // Animate the back button's rotation to the new degrees and only in portrait move up the // back button to line up with the other buttons - float targetY = !mOverviewProxyService.shouldShowSwipeUpUI() && !mVertical && useAltBack + float targetY = !mOverviewProxyService.shouldShowSwipeUpUI() && !mIsVertical && useAltBack ? - getResources().getDimension(R.dimen.navbar_back_button_ime_offset) : 0; ObjectAnimator navBarAnimator = ObjectAnimator.ofPropertyValuesHolder(drawable, @@ -669,7 +667,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav } private void orientHomeButton(KeyButtonDrawable drawable) { - drawable.setRotation(mVertical ? 90 : 0); + drawable.setRotation(mIsVertical ? 90 : 0); } private KeyButtonDrawable chooseNavigationIconDrawable(@DrawableRes int icon, @@ -964,7 +962,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener); DockedStackExistsListener.register(mDockedListener); - updateRotatedViews(); + updateOrientationViews(); reloadNavIcons(); } @@ -1028,34 +1026,35 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav view.setTranslationY(posY); } - private void updateRotatedViews() { - mRotatedViews[Surface.ROTATION_0] = - mRotatedViews[Surface.ROTATION_180] = findViewById(R.id.rot0); - mRotatedViews[Surface.ROTATION_270] = - mRotatedViews[Surface.ROTATION_90] = findViewById(R.id.rot90); + private void updateOrientationViews() { + mHorizontal = findViewById(R.id.horizontal); + mVertical = findViewById(R.id.vertical); updateCurrentView(); } - public boolean needsReorient(int rotation) { + boolean needsReorient(int rotation) { return mCurrentRotation != rotation; } private void updateCurrentView() { - final int rot = getContextDisplay().getRotation(); - for (int i=0; i<4; i++) { - mRotatedViews[i].setVisibility(View.GONE); - } - mCurrentView = mRotatedViews[rot]; + resetViews(); + mCurrentView = mIsVertical ? mVertical : mHorizontal; mCurrentView.setVisibility(View.VISIBLE); - mNavigationInflaterView.setAlternativeOrder(rot == Surface.ROTATION_90); + mNavigationInflaterView.setVertical(mIsVertical); + mCurrentRotation = getContextDisplay().getRotation(); + mNavigationInflaterView.setAlternativeOrder(mCurrentRotation == Surface.ROTATION_90); mNavigationInflaterView.updateButtonDispatchersCurrentView(); updateLayoutTransitionsEnabled(); - mCurrentRotation = rot; + } + + private void resetViews() { + mHorizontal.setVisibility(View.GONE); + mVertical.setVisibility(View.GONE); } private void updateRecentsIcon() { - mDockedIcon.setRotation(mDockedStackExists && mVertical ? 90 : 0); + mDockedIcon.setRotation(mDockedStackExists && mIsVertical ? 90 : 0); getRecentsButton().setImageDrawable(mDockedStackExists ? mDockedIcon : mRecentIcon); mBarTransitions.reapplyDarkIntensity(); } @@ -1077,7 +1076,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav } public boolean isVertical() { - return mVertical; + return mIsVertical; } public void reorient() { @@ -1101,7 +1100,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav updateTaskSwitchHelper(); updateNavButtonIcons(); - getHomeButton().setVertical(mVertical); + getHomeButton().setVertical(mIsVertical); } private void updateTaskSwitchHelper() { @@ -1134,9 +1133,12 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav "onSizeChanged: (%dx%d) old: (%dx%d)", w, h, oldw, oldh)); final boolean newVertical = w > 0 && h > w; - if (newVertical != mVertical) { - mVertical = newVertical; - //Log.v(TAG, String.format("onSizeChanged: h=%d, w=%d, vert=%s", h, w, mVertical?"y":"n")); + if (newVertical != mIsVertical) { + mIsVertical = newVertical; + if (DEBUG) { + Log.d(TAG, String.format("onSizeChanged: h=%d, w=%d, vert=%s", h, w, + mIsVertical ? "y" : "n")); + } reorient(); notifyVerticalChangedListener(newVertical); } @@ -1347,7 +1349,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav pw.println(String.format(" disabled=0x%08x vertical=%s menu=%s darkIntensity=%.2f", mDisabledFlags, - mVertical ? "true" : "false", + mIsVertical ? "true" : "false", getMenuButton().isVisible() ? "true" : "false", getLightTransitionsController().getCurrentDarkIntensity())); 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 13d448922ab1..41580f6146de 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -195,9 +195,9 @@ import com.android.systemui.statusbar.notification.NotificationClicker; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; import com.android.systemui.statusbar.notification.NotificationListController; -import com.android.systemui.statusbar.notification.NotificationRowBinder; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl; import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; @@ -390,7 +390,6 @@ public class StatusBar extends SystemUI implements DemoMode, protected NotificationEntryManager mEntryManager; private NotificationListController mNotificationListController; private NotificationInterruptionStateProvider mNotificationInterruptionStateProvider; - private NotificationRowBinder mNotificationRowBinder; protected NotificationViewHierarchyManager mViewHierarchyManager; protected ForegroundServiceController mForegroundServiceController; protected AppOpsController mAppOpsController; @@ -620,7 +619,6 @@ public class StatusBar extends SystemUI implements DemoMode, mEntryManager = Dependency.get(NotificationEntryManager.class); mNotificationInterruptionStateProvider = Dependency.get(NotificationInterruptionStateProvider.class); - mNotificationRowBinder = Dependency.get(NotificationRowBinder.class); mViewHierarchyManager = Dependency.get(NotificationViewHierarchyManager.class); mForegroundServiceController = Dependency.get(ForegroundServiceController.class); mAppOpsController = Dependency.get(AppOpsController.class); @@ -1032,10 +1030,15 @@ public class StatusBar extends SystemUI implements DemoMode, mStatusBarWindow, this, mNotificationPanel, (NotificationListContainer) mStackScroller); + final NotificationRowBinderImpl rowBinder = + new NotificationRowBinderImpl( + mContext, + SystemUIFactory.getInstance().provideAllowNotificationLongPress()); + mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanel, mHeadsUpManager, mStatusBarWindow, mStackScroller, mDozeScrimController, mScrimController, mActivityLaunchAnimator, mStatusBarKeyguardViewManager, - mNotificationAlertingManager); + mNotificationAlertingManager, rowBinder); mNotificationListController = new NotificationListController( @@ -1051,7 +1054,9 @@ public class StatusBar extends SystemUI implements DemoMode, mNotificationActivityStarter = new StatusBarNotificationActivityStarter( mContext, mNotificationPanel, mPresenter, mHeadsUpManager, mActivityLaunchAnimator); mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter); - mNotificationRowBinder.setNotificationClicker(new NotificationClicker( + + mEntryManager.setRowBinder(rowBinder); + rowBinder.setNotificationClicker(new NotificationClicker( this, Dependency.get(BubbleController.class), mNotificationActivityStarter)); mGroupAlertTransferHelper.bind(mEntryManager, mGroupManager); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java index 3ce66c5de372..6fe89645ef19 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java @@ -64,9 +64,9 @@ import com.android.systemui.statusbar.notification.NotificationAlertingManager; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider; -import com.android.systemui.statusbar.notification.NotificationRowBinder; import com.android.systemui.statusbar.notification.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; @@ -79,7 +79,8 @@ import com.android.systemui.statusbar.policy.KeyguardMonitor; import java.util.ArrayList; public class StatusBarNotificationPresenter implements NotificationPresenter, - ConfigurationController.ConfigurationListener { + ConfigurationController.ConfigurationListener, + NotificationRowBinderImpl.BindRowCallback { private final LockscreenGestureLogger mLockscreenGestureLogger = Dependency.get(LockscreenGestureLogger.class); @@ -97,8 +98,6 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, (SysuiStatusBarStateController) Dependency.get(StatusBarStateController.class); private final NotificationEntryManager mEntryManager = Dependency.get(NotificationEntryManager.class); - private final NotificationRowBinder mNotificationRowBinder = - Dependency.get(NotificationRowBinder.class); private final NotificationInterruptionStateProvider mNotificationInterruptionStateProvider = Dependency.get(NotificationInterruptionStateProvider.class); private final NotificationMediaManager mMediaManager = @@ -140,7 +139,8 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, ScrimController scrimController, ActivityLaunchAnimator activityLaunchAnimator, StatusBarKeyguardViewManager statusBarKeyguardViewManager, - NotificationAlertingManager notificationAlertingManager) { + NotificationAlertingManager notificationAlertingManager, + NotificationRowBinderImpl notificationRowBinder) { mContext = context; mNotificationPanel = panel; mHeadsUpManager = headsUp; @@ -217,7 +217,7 @@ public class StatusBarNotificationPresenter implements NotificationPresenter, mEntryManager.addNotificationLifetimeExtender(mGutsManager); mEntryManager.addNotificationLifetimeExtenders( remoteInputManager.getLifetimeExtenders()); - mNotificationRowBinder.setUpWithPresenter(this, notifListContainer, mHeadsUpManager, + notificationRowBinder.setUpWithPresenter(this, notifListContainer, mHeadsUpManager, mEntryManager, this); mNotificationInterruptionStateProvider.setUpWithPresenter( this, mHeadsUpManager, this::canHeadsUp); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java index e1a77b00f3ca..ce69a489737a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java @@ -246,7 +246,7 @@ public class StatusBarWindowController implements Callback, Dumpable, Configurat private boolean isExpanded(State state) { return !state.forceCollapsed && (state.isKeyguardShowingAndNotOccluded() || state.panelVisible || state.keyguardFadingAway || state.bouncerShowing - || state.headsUpShowing || state.bubblesShowing + || state.headsUpShowing || state.bubblesShowing || state.assistShowing || state.scrimsVisibility != ScrimController.VISIBILITY_FULLY_TRANSPARENT); } @@ -490,6 +490,21 @@ public class StatusBarWindowController implements Callback, Dumpable, Configurat } /** + * Sets whether assist UI is showing on the screen. + */ + public void setAssistShowing(boolean assistShowing) { + mCurrentState.assistShowing = assistShowing; + apply(mCurrentState); + } + + /** + * The assist UI showing state for the status bar. + */ + public boolean getAssistShowing() { + return mCurrentState.assistShowing; + } + + /** * Sets if there is a bubble being expanded on the screen. */ public void setBubbleExpanded(boolean bubbleExpanded) { @@ -558,6 +573,7 @@ public class StatusBarWindowController implements Callback, Dumpable, Configurat boolean notTouchable; boolean bubblesShowing; boolean bubbleExpanded; + boolean assistShowing; /** * The {@link StatusBar} state from the status bar. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java index ad4ba75d6569..50c4fac31bbf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java @@ -29,14 +29,18 @@ import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.hardware.display.AmbientDisplayConfiguration; import android.media.AudioManager; import android.media.session.MediaSessionLegacyHelper; import android.net.Uri; import android.os.Bundle; import android.os.SystemClock; +import android.os.UserHandle; +import android.provider.Settings; import android.util.AttributeSet; import android.view.ActionMode; import android.view.DisplayCutout; +import android.view.GestureDetector; import android.view.InputDevice; import android.view.InputQueue; import android.view.KeyEvent; @@ -63,16 +67,23 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.DragDownHelper; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; +import com.android.systemui.tuner.TunerService; import java.io.FileDescriptor; import java.io.PrintWriter; +/** + * Combined status bar and notification panel view. Also holding backdrop and scrims. + */ public class StatusBarWindowView extends FrameLayout { public static final String TAG = "StatusBarWindowView"; public static final boolean DEBUG = StatusBar.DEBUG; + private final GestureDetector mGestureDetector; + private final StatusBarStateController mStatusBarStateController; + private boolean mDoubleTapEnabled; + private boolean mSingleTapEnabled; private DragDownHelper mDragDownHelper; - private DoubleTapHelper mDoubleTapHelper; private NotificationStackScrollLayout mStackScrollLayout; private NotificationPanelView mNotificationPanel; private View mBrightnessMirror; @@ -95,8 +106,37 @@ public class StatusBarWindowView extends FrameLayout { private boolean mTouchActive; private boolean mExpandAnimationRunning; private boolean mExpandAnimationPending; - private final StatusBarStateController - mStatusBarStateController = Dependency.get(StatusBarStateController.class); + + private final GestureDetector.SimpleOnGestureListener mGestureListener = + new GestureDetector.SimpleOnGestureListener() { + @Override + public boolean onSingleTapConfirmed(MotionEvent e) { + if (mSingleTapEnabled) { + mService.wakeUpIfDozing(SystemClock.uptimeMillis(), StatusBarWindowView.this, + "SINGLE_TAP"); + } + return mSingleTapEnabled; + } + + @Override + public boolean onDoubleTap(MotionEvent e) { + if (mDoubleTapEnabled) { + mService.wakeUpIfDozing(SystemClock.uptimeMillis(), StatusBarWindowView.this, + "DOUBLE_TAP"); + } + return mDoubleTapEnabled; + } + }; + private final TunerService.Tunable mTunable = (key, newValue) -> { + AmbientDisplayConfiguration configuration = new AmbientDisplayConfiguration(mContext); + switch (key) { + case Settings.Secure.DOZE_DOUBLE_TAP_GESTURE: + mDoubleTapEnabled = configuration.doubleTapGestureEnabled(UserHandle.USER_CURRENT); + break; + case Settings.Secure.DOZE_TAP_SCREEN_GESTURE: + mSingleTapEnabled = configuration.tapGestureEnabled(UserHandle.USER_CURRENT); + } + }; /** * If set to true, the current gesture started below the notch and we need to dispatch touch @@ -110,10 +150,11 @@ public class StatusBarWindowView extends FrameLayout { mTransparentSrcPaint.setColor(0); mTransparentSrcPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC)); mFalsingManager = FalsingManager.getInstance(context); - mDoubleTapHelper = new DoubleTapHelper(this, active -> {}, () -> { - mService.wakeUpIfDozing(SystemClock.uptimeMillis(), this, "DOUBLE_TAP"); - return true; - }, null, null); + mGestureDetector = new GestureDetector(context, mGestureListener); + mStatusBarStateController = Dependency.get(StatusBarStateController.class); + Dependency.get(TunerService.class).addTunable(mTunable, + Settings.Secure.DOZE_DOUBLE_TAP_GESTURE, + Settings.Secure.DOZE_TAP_SCREEN_GESTURE); } @Override @@ -306,6 +347,7 @@ public class StatusBarWindowView extends FrameLayout { return false; } mFalsingManager.onTouchEvent(ev, getWidth(), getHeight()); + mGestureDetector.onTouchEvent(ev); if (mBrightnessMirror != null && mBrightnessMirror.getVisibility() == VISIBLE) { // Disallow new pointers while the brightness mirror is visible. This is so that you // can't touch anything other than the brightness slider while the mirror is showing @@ -366,7 +408,6 @@ public class StatusBarWindowView extends FrameLayout { public boolean onTouchEvent(MotionEvent ev) { boolean handled = false; if (mService.isDozing()) { - mDoubleTapHelper.onTouchEvent(ev); handled = !mService.isPulsing(); } if ((mStatusBarStateController.getState() == StatusBarState.KEYGUARD && !handled) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java new file mode 100644 index 000000000000..d8ea1f6eef5f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplies.java @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.Notification; +import android.app.RemoteInput; +import android.content.Context; +import android.os.Build; +import android.util.Log; +import android.util.Pair; +import android.widget.Button; + +import com.android.internal.util.ArrayUtils; +import com.android.systemui.statusbar.SmartReplyController; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; + +import java.util.ArrayList; +import java.util.List; + +/** + * Holder for inflated smart replies and actions. These objects should be inflated on a background + * thread, to later be accessed and modified on the (performance critical) UI thread. + */ +public class InflatedSmartReplies { + private static final String TAG = "InflatedSmartReplies"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + @Nullable private final SmartReplyView mSmartReplyView; + @Nullable private final List<Button> mSmartSuggestionButtons; + @NonNull private final SmartRepliesAndActions mSmartRepliesAndActions; + + private InflatedSmartReplies( + @Nullable SmartReplyView smartReplyView, + @Nullable List<Button> smartSuggestionButtons, + @NonNull SmartRepliesAndActions smartRepliesAndActions) { + mSmartReplyView = smartReplyView; + mSmartSuggestionButtons = smartSuggestionButtons; + mSmartRepliesAndActions = smartRepliesAndActions; + } + + @Nullable public SmartReplyView getSmartReplyView() { + return mSmartReplyView; + } + + @Nullable public List<Button> getSmartSuggestionButtons() { + return mSmartSuggestionButtons; + } + + @NonNull public SmartRepliesAndActions getSmartRepliesAndActions() { + return mSmartRepliesAndActions; + } + + /** + * Inflate a SmartReplyView and its smart suggestions. + */ + public static InflatedSmartReplies inflate( + Context context, + NotificationEntry entry, + SmartReplyConstants smartReplyConstants, + SmartReplyController smartReplyController, + HeadsUpManager headsUpManager) { + SmartRepliesAndActions smartRepliesAndActions = + chooseSmartRepliesAndActions(smartReplyConstants, entry); + if (!shouldShowSmartReplyView(entry, smartRepliesAndActions)) { + return new InflatedSmartReplies(null /* smartReplyView */, + null /* smartSuggestionButtons */, smartRepliesAndActions); + } + + SmartReplyView smartReplyView = SmartReplyView.inflate(context); + + List<Button> suggestionButtons = new ArrayList<>(); + if (smartRepliesAndActions.smartReplies != null) { + suggestionButtons.addAll(smartReplyView.inflateRepliesFromRemoteInput( + smartRepliesAndActions.smartReplies, smartReplyController, entry)); + } + if (smartRepliesAndActions.smartActions != null) { + suggestionButtons.addAll( + smartReplyView.inflateSmartActions(smartRepliesAndActions.smartActions, + smartReplyController, entry, headsUpManager)); + } + + return new InflatedSmartReplies(smartReplyView, suggestionButtons, + smartRepliesAndActions); + } + + /** + * Returns whether we should show the smart reply view and its smart suggestions. + */ + public static boolean shouldShowSmartReplyView( + NotificationEntry entry, + SmartRepliesAndActions smartRepliesAndActions) { + if (smartRepliesAndActions.smartReplies == null + && smartRepliesAndActions.smartActions == null) { + // There are no smart replies and no smart actions. + return false; + } + // If we are showing the spinner we don't want to add the buttons. + boolean showingSpinner = entry.notification.getNotification() + .extras.getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false); + if (showingSpinner) { + return false; + } + // If we are keeping the notification around while sending we don't want to add the buttons. + boolean hideSmartReplies = entry.notification.getNotification() + .extras.getBoolean(Notification.EXTRA_HIDE_SMART_REPLIES, false); + if (hideSmartReplies) { + return false; + } + return true; + } + + /** + * Chose what smart replies and smart actions to display. App generated suggestions take + * precedence. So if the app provides any smart replies, we don't show any + * replies or actions generated by the NotificationAssistantService (NAS), and if the app + * provides any smart actions we also don't show any NAS-generated replies or actions. + */ + @NonNull + public static SmartRepliesAndActions chooseSmartRepliesAndActions( + SmartReplyConstants smartReplyConstants, + final NotificationEntry entry) { + Notification notification = entry.notification.getNotification(); + Pair<RemoteInput, Notification.Action> remoteInputActionPair = + notification.findRemoteInputActionPair(false /* freeform */); + Pair<RemoteInput, Notification.Action> freeformRemoteInputActionPair = + notification.findRemoteInputActionPair(true /* freeform */); + + if (!smartReplyConstants.isEnabled()) { + if (DEBUG) { + Log.d(TAG, "Smart suggestions not enabled, not adding suggestions for " + + entry.notification.getKey()); + } + return new SmartRepliesAndActions(null, null); + } + // Only use smart replies from the app if they target P or above. We have this check because + // the smart reply API has been used for other things (Wearables) in the past. The API to + // add smart actions is new in Q so it doesn't require a target-sdk check. + boolean enableAppGeneratedSmartReplies = (!smartReplyConstants.requiresTargetingP() + || entry.targetSdk >= Build.VERSION_CODES.P); + + boolean appGeneratedSmartRepliesExist = + enableAppGeneratedSmartReplies + && remoteInputActionPair != null + && !ArrayUtils.isEmpty(remoteInputActionPair.first.getChoices()) + && remoteInputActionPair.second.actionIntent != null; + + List<Notification.Action> appGeneratedSmartActions = notification.getContextualActions(); + boolean appGeneratedSmartActionsExist = !appGeneratedSmartActions.isEmpty(); + + SmartReplyView.SmartReplies smartReplies = null; + SmartReplyView.SmartActions smartActions = null; + if (appGeneratedSmartRepliesExist) { + smartReplies = new SmartReplyView.SmartReplies( + remoteInputActionPair.first.getChoices(), + remoteInputActionPair.first, + remoteInputActionPair.second.actionIntent, + false /* fromAssistant */); + } + if (appGeneratedSmartActionsExist) { + smartActions = new SmartReplyView.SmartActions(appGeneratedSmartActions, + false /* fromAssistant */); + } + // Apps didn't provide any smart replies / actions, use those from NAS (if any). + if (!appGeneratedSmartRepliesExist && !appGeneratedSmartActionsExist) { + boolean useGeneratedReplies = !ArrayUtils.isEmpty(entry.systemGeneratedSmartReplies) + && freeformRemoteInputActionPair != null + && freeformRemoteInputActionPair.second.getAllowGeneratedReplies() + && freeformRemoteInputActionPair.second.actionIntent != null; + if (useGeneratedReplies) { + smartReplies = new SmartReplyView.SmartReplies( + entry.systemGeneratedSmartReplies, + freeformRemoteInputActionPair.first, + freeformRemoteInputActionPair.second.actionIntent, + true /* fromAssistant */); + } + boolean useSmartActions = !ArrayUtils.isEmpty(entry.systemGeneratedSmartActions) + && notification.getAllowSystemGeneratedContextualActions(); + if (useSmartActions) { + smartActions = new SmartReplyView.SmartActions( + entry.systemGeneratedSmartActions, true /* fromAssistant */); + } + } + return new SmartRepliesAndActions(smartReplies, smartActions); + } + + /** + * Returns whether the {@link Notification} represented by entry has a free-form remote input. + * Such an input can be used e.g. to implement smart reply buttons - by passing the replies + * through the remote input. + */ + public static boolean hasFreeformRemoteInput(NotificationEntry entry) { + Notification notification = entry.notification.getNotification(); + return null != notification.findRemoteInputActionPair(true /* freeform */); + } + + /** + * A storage for smart replies and smart action. + */ + public static class SmartRepliesAndActions { + @Nullable public final SmartReplyView.SmartReplies smartReplies; + @Nullable public final SmartReplyView.SmartActions smartActions; + + SmartRepliesAndActions( + @Nullable SmartReplyView.SmartReplies smartReplies, + @Nullable SmartReplyView.SmartActions smartActions) { + this.smartReplies = smartReplies; + this.smartActions = smartActions; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java index b22150b2f507..72f4fc7820a5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java @@ -21,13 +21,14 @@ import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; import android.app.RemoteInput; import android.content.Context; import android.content.res.Resources; -import android.database.ContentObserver; -import android.net.Uri; import android.os.Handler; -import android.provider.Settings; +import android.provider.DeviceConfig; +import android.text.TextUtils; import android.util.KeyValueListParser; import android.util.Log; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.systemui.R; import javax.inject.Inject; @@ -35,20 +36,10 @@ import javax.inject.Named; import javax.inject.Singleton; @Singleton -public final class SmartReplyConstants extends ContentObserver { +public final class SmartReplyConstants { private static final String TAG = "SmartReplyConstants"; - private static final String KEY_ENABLED = "enabled"; - private static final String KEY_REQUIRES_TARGETING_P = "requires_targeting_p"; - private static final String KEY_MAX_SQUEEZE_REMEASURE_ATTEMPTS = - "max_squeeze_remeasure_attempts"; - private static final String KEY_EDIT_CHOICES_BEFORE_SENDING = - "edit_choices_before_sending"; - private static final String KEY_SHOW_IN_HEADS_UP = "show_in_heads_up"; - private static final String KEY_MIN_NUM_REPLIES = "min_num_system_generated_replies"; - private static final String KEY_MAX_NUM_ACTIONS = "max_num_actions"; - private final boolean mDefaultEnabled; private final boolean mDefaultRequiresP; private final int mDefaultMaxSqueezeRemeasureAttempts; @@ -57,21 +48,25 @@ public final class SmartReplyConstants extends ContentObserver { private final int mDefaultMinNumSystemGeneratedReplies; private final int mDefaultMaxNumActions; - private boolean mEnabled; - private boolean mRequiresTargetingP; - private int mMaxSqueezeRemeasureAttempts; - private boolean mEditChoicesBeforeSending; - private boolean mShowInHeadsUp; - private int mMinNumSystemGeneratedReplies; - private int mMaxNumActions; - + // These fields are updated on the UI thread but can be accessed on both the UI thread and + // background threads. We use the volatile keyword here instead of synchronization blocks since + // we only care about variable updates here being visible to other threads (and not for example + // whether the variables we are reading were updated in the same go). + private volatile boolean mEnabled; + private volatile boolean mRequiresTargetingP; + private volatile int mMaxSqueezeRemeasureAttempts; + private volatile boolean mEditChoicesBeforeSending; + private volatile boolean mShowInHeadsUp; + private volatile int mMinNumSystemGeneratedReplies; + private volatile int mMaxNumActions; + + private final Handler mHandler; private final Context mContext; private final KeyValueListParser mParser = new KeyValueListParser(','); @Inject public SmartReplyConstants(@Named(MAIN_HANDLER_NAME) Handler handler, Context context) { - super(handler); - + mHandler = handler; mContext = context; final Resources resources = mContext.getResources(); mDefaultEnabled = resources.getBoolean( @@ -89,35 +84,86 @@ public final class SmartReplyConstants extends ContentObserver { mDefaultMaxNumActions = resources.getInteger( R.integer.config_smart_replies_in_notifications_max_num_actions); - mContext.getContentResolver().registerContentObserver( - Settings.Global.getUriFor(Settings.Global.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS), - false, this); + registerDeviceConfigListener(); updateConstants(); } - @Override - public void onChange(boolean selfChange, Uri uri) { + private void registerDeviceConfigListener() { + DeviceConfig.addOnPropertyChangedListener( + DeviceConfig.NAMESPACE_SYSTEMUI, + this::postToHandler, + this::onDeviceConfigPropertyChanged); + } + + private void postToHandler(Runnable r) { + this.mHandler.post(r); + } + + @VisibleForTesting + void onDeviceConfigPropertyChanged(String namespace, String name, String value) { + if (!DeviceConfig.NAMESPACE_SYSTEMUI.equals(namespace)) { + Log.e(TAG, "Received update from DeviceConfig for unrelated namespace: " + + namespace + " " + name + "=" + value); + return; + } + updateConstants(); } private void updateConstants() { synchronized (SmartReplyConstants.this) { - try { - mParser.setString(Settings.Global.getString(mContext.getContentResolver(), - Settings.Global.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS)); - } catch (IllegalArgumentException e) { - Log.e(TAG, "Bad smart reply constants", e); - } - mEnabled = mParser.getBoolean(KEY_ENABLED, mDefaultEnabled); - mRequiresTargetingP = mParser.getBoolean(KEY_REQUIRES_TARGETING_P, mDefaultRequiresP); - mMaxSqueezeRemeasureAttempts = mParser.getInt( - KEY_MAX_SQUEEZE_REMEASURE_ATTEMPTS, mDefaultMaxSqueezeRemeasureAttempts); - mEditChoicesBeforeSending = mParser.getBoolean( - KEY_EDIT_CHOICES_BEFORE_SENDING, mDefaultEditChoicesBeforeSending); - mShowInHeadsUp = mParser.getBoolean(KEY_SHOW_IN_HEADS_UP, mDefaultShowInHeadsUp); - mMinNumSystemGeneratedReplies = - mParser.getInt(KEY_MIN_NUM_REPLIES, mDefaultMinNumSystemGeneratedReplies); - mMaxNumActions = mParser.getInt(KEY_MAX_NUM_ACTIONS, mDefaultMaxNumActions); + mEnabled = readDeviceConfigBooleanOrDefaultIfEmpty( + SystemUiDeviceConfigFlags.SSIN_ENABLED, + mDefaultEnabled); + mRequiresTargetingP = readDeviceConfigBooleanOrDefaultIfEmpty( + SystemUiDeviceConfigFlags.SSIN_REQUIRES_TARGETING_P, + mDefaultRequiresP); + mMaxSqueezeRemeasureAttempts = readDeviceConfigIntegerOrDefaultIfEmpty( + SystemUiDeviceConfigFlags.SSIN_MAX_SQUEEZE_REMEASURE_ATTEMPTS, + mDefaultMaxSqueezeRemeasureAttempts); + mEditChoicesBeforeSending = readDeviceConfigBooleanOrDefaultIfEmpty( + SystemUiDeviceConfigFlags.SSIN_EDIT_CHOICES_BEFORE_SENDING, + mDefaultEditChoicesBeforeSending); + mShowInHeadsUp = readDeviceConfigBooleanOrDefaultIfEmpty( + SystemUiDeviceConfigFlags.SSIN_SHOW_IN_HEADS_UP, + mDefaultShowInHeadsUp); + mMinNumSystemGeneratedReplies = readDeviceConfigIntegerOrDefaultIfEmpty( + SystemUiDeviceConfigFlags.SSIN_MIN_NUM_SYSTEM_GENERATED_REPLIES, + mDefaultMinNumSystemGeneratedReplies); + mMaxNumActions = readDeviceConfigIntegerOrDefaultIfEmpty( + SystemUiDeviceConfigFlags.SSIN_MAX_NUM_ACTIONS, + mDefaultMaxNumActions); + } + } + + private static boolean readDeviceConfigBooleanOrDefaultIfEmpty(String propertyName, + boolean defaultValue) { + String value = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_SYSTEMUI, propertyName); + if (TextUtils.isEmpty(value)) { + return defaultValue; + } + if ("true".equals(value)) { + return true; + } + if ("false".equals(value)) { + return false; + } + // For invalid configs we return the default value. + return defaultValue; + } + + private static int readDeviceConfigIntegerOrDefaultIfEmpty(String propertyName, + int defaultValue) { + String value = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_SYSTEMUI, propertyName); + if (TextUtils.isEmpty(value)) { + return defaultValue; + } + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + Log.e(TAG, "Tried to read an integer flag, property name=" + + propertyName + ", value=" + value); + return defaultValue; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java index 45d215ef309c..ed5487f74356 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyView.java @@ -196,66 +196,81 @@ public class SmartReplyView extends ViewGroup { } /** + * Add buttons to the {@link SmartReplyView} - these buttons must have been preinflated using + * one of the methods in this class. + */ + public void addPreInflatedButtons(List<Button> smartSuggestionButtons) { + for (Button button : smartSuggestionButtons) { + addView(button); + } + reallocateCandidateButtonQueueForSqueezing(); + } + + /** * Add smart replies to this view, using the provided {@link RemoteInput} and * {@link PendingIntent} to respond when the user taps a smart reply. Only the replies that fit * into the notification are shown. */ - public void addRepliesFromRemoteInput( - SmartReplies smartReplies, + public List<Button> inflateRepliesFromRemoteInput( + @NonNull SmartReplies smartReplies, SmartReplyController smartReplyController, NotificationEntry entry) { + List<Button> buttons = new ArrayList<>(); + if (smartReplies.remoteInput != null && smartReplies.pendingIntent != null) { if (smartReplies.choices != null) { for (int i = 0; i < smartReplies.choices.length; ++i) { - Button replyButton = inflateReplyButton( - getContext(), this, i, smartReplies, smartReplyController, entry); - addView(replyButton); + buttons.add(inflateReplyButton( + this, getContext(), i, smartReplies, smartReplyController, entry)); } this.mSmartRepliesGeneratedByAssistant = smartReplies.fromAssistant; } } - reallocateCandidateButtonQueueForSqueezing(); + return buttons; } /** * Add smart actions to be shown next to smart replies. Only the actions that fit into the * notification are shown. */ - public void addSmartActions(SmartActions smartActions, + public List<Button> inflateSmartActions(@NonNull SmartActions smartActions, SmartReplyController smartReplyController, NotificationEntry entry, HeadsUpManager headsUpManager) { + List<Button> buttons = new ArrayList<>(); int numSmartActions = smartActions.actions.size(); for (int n = 0; n < numSmartActions; n++) { Notification.Action action = smartActions.actions.get(n); if (action.actionIntent != null) { - Button actionButton = inflateActionButton( - getContext(), this, n, smartActions, smartReplyController, entry, - headsUpManager); - addView(actionButton); + buttons.add(inflateActionButton( + this, getContext(), n, smartActions, smartReplyController, entry, + headsUpManager)); } } - reallocateCandidateButtonQueueForSqueezing(); + return buttons; } - public static SmartReplyView inflate(Context context, ViewGroup root) { - return (SmartReplyView) - LayoutInflater.from(context).inflate(R.layout.smart_reply_view, root, false); + /** + * Inflate an instance of this class. + */ + public static SmartReplyView inflate(Context context) { + return (SmartReplyView) LayoutInflater.from(context).inflate( + R.layout.smart_reply_view, null /* root */); } @VisibleForTesting - Button inflateReplyButton(Context context, ViewGroup root, int replyIndex, - SmartReplies smartReplies, SmartReplyController smartReplyController, + static Button inflateReplyButton(SmartReplyView smartReplyView, Context context, + int replyIndex, SmartReplies smartReplies, SmartReplyController smartReplyController, NotificationEntry entry) { Button b = (Button) LayoutInflater.from(context).inflate( - R.layout.smart_reply_button, root, false); + R.layout.smart_reply_button, smartReplyView, false); CharSequence choice = smartReplies.choices[replyIndex]; b.setText(choice); OnDismissAction action = () -> { - if (mConstants.getEffectiveEditChoicesBeforeSending( + if (smartReplyView.mConstants.getEffectiveEditChoicesBeforeSending( smartReplies.remoteInput.getEditChoicesBeforeSending())) { EditedSuggestionInfo editedSuggestionInfo = new EditedSuggestionInfo(choice, replyIndex); - mRemoteInputManager.activateRemoteInput(b, + smartReplyView.mRemoteInputManager.activateRemoteInput(b, new RemoteInput[] { smartReplies.remoteInput }, smartReplies.remoteInput, smartReplies.pendingIntent, editedSuggestionInfo); return false; @@ -276,33 +291,41 @@ public class SmartReplyView extends ViewGroup { } catch (PendingIntent.CanceledException e) { Log.w(TAG, "Unable to send smart reply", e); } - mSmartReplyContainer.setVisibility(View.GONE); + // Note that as inflateReplyButton is called mSmartReplyContainer is null, but when the + // reply Button is added to the SmartReplyView mSmartReplyContainer will be set. So, it + // will not be possible for a user to trigger this on-click-listener without + // mSmartReplyContainer being set. + smartReplyView.mSmartReplyContainer.setVisibility(View.GONE); return false; // do not defer }; b.setOnClickListener(view -> { - mKeyguardDismissUtil.executeWhenUnlocked(action); + smartReplyView.mKeyguardDismissUtil.executeWhenUnlocked(action); }); b.setAccessibilityDelegate(new AccessibilityDelegate() { public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(host, info); - String label = getResources().getString(R.string.accessibility_send_smart_reply); + String label = smartReplyView.getResources().getString( + R.string.accessibility_send_smart_reply); info.addAction(new AccessibilityAction(AccessibilityNodeInfo.ACTION_CLICK, label)); } }); - setColors(b, mCurrentBackgroundColor, mDefaultStrokeColor, mDefaultTextColor, mRippleColor); + SmartReplyView.setButtonColors(b, smartReplyView.mCurrentBackgroundColor, + smartReplyView.mDefaultStrokeColor, smartReplyView.mDefaultTextColor, + smartReplyView.mRippleColor, smartReplyView.mStrokeWidth); return b; } @VisibleForTesting - Button inflateActionButton(Context context, ViewGroup root, int actionIndex, - SmartActions smartActions, SmartReplyController smartReplyController, - NotificationEntry entry, HeadsUpManager headsUpManager) { + static Button inflateActionButton(SmartReplyView smartReplyView, Context context, + int actionIndex, SmartActions smartActions, + SmartReplyController smartReplyController, NotificationEntry entry, + HeadsUpManager headsUpManager) { Notification.Action action = smartActions.actions.get(actionIndex); Button button = (Button) LayoutInflater.from(context).inflate( - R.layout.smart_action_button, root, false); + R.layout.smart_action_button, smartReplyView, false); button.setText(action.title); Drawable iconDrawable = action.getIcon().loadDrawable(context); @@ -313,7 +336,7 @@ public class SmartReplyView extends ViewGroup { button.setCompoundDrawables(iconDrawable, null, null, null); button.setOnClickListener(view -> - getActivityStarter().startPendingIntentDismissingKeyguard( + smartReplyView.getActivityStarter().startPendingIntentDismissingKeyguard( action.actionIntent, () -> { smartReplyController.smartActionClicked( @@ -803,12 +826,13 @@ public class SmartReplyView extends ViewGroup { int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { final Button child = (Button) getChildAt(i); - setColors(child, backgroundColor, strokeColor, textColor, rippleColor); + setButtonColors(child, backgroundColor, strokeColor, textColor, rippleColor, + mStrokeWidth); } } - private void setColors(Button button, int backgroundColor, int strokeColor, int textColor, - int rippleColor) { + private static void setButtonColors(Button button, int backgroundColor, int strokeColor, + int textColor, int rippleColor, int strokeWidth) { Drawable drawable = button.getBackground(); if (drawable instanceof RippleDrawable) { // Mutate in case other notifications are using this drawable. @@ -821,7 +845,7 @@ public class SmartReplyView extends ViewGroup { if (background instanceof GradientDrawable) { GradientDrawable gradientDrawable = (GradientDrawable) background; gradientDrawable.setColor(backgroundColor); - gradientDrawable.setStroke(mStrokeWidth, strokeColor); + gradientDrawable.setStroke(strokeWidth, strokeColor); } } button.setBackground(drawable); diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java index ecf1784e66c4..0070dcf9a604 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java @@ -19,6 +19,7 @@ import android.app.AlertDialog; import android.app.Dialog; import android.app.DialogFragment; import android.content.DialogInterface; +import android.hardware.display.AmbientDisplayConfiguration; import android.os.Build; import android.os.Bundle; import android.provider.Settings; @@ -29,7 +30,6 @@ import android.view.MenuItem; import androidx.preference.Preference; import androidx.preference.PreferenceFragment; -import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.R; diff --git a/packages/SystemUI/src/com/android/systemui/volume/Events.java b/packages/SystemUI/src/com/android/systemui/volume/Events.java index ca55e1f23d20..1596ddbafda0 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/Events.java +++ b/packages/SystemUI/src/com/android/systemui/volume/Events.java @@ -53,6 +53,8 @@ public class Events { public static final int EVENT_TOUCH_LEVEL_DONE = 16; // (stream|int) (level|bool) public static final int EVENT_ZEN_CONFIG_CHANGED = 17; // (allow/disallow|string) public static final int EVENT_RINGER_TOGGLE = 18; // (ringer_mode) + public static final int EVENT_SHOW_USB_OVERHEAT_ALARM = 19; // (reason|int) (keyguard|bool) + public static final int EVENT_DISMISS_USB_OVERHEAT_ALARM = 20; // (reason|int) (keyguard|bool) private static final String[] EVENT_TAGS = { "show_dialog", @@ -73,7 +75,9 @@ public class Events { "mute_changed", "touch_level_done", "zen_mode_config_changed", - "ringer_toggle" + "ringer_toggle", + "show_usb_overheat_alarm", + "dismiss_usb_overheat_alarm" }; public static final int DISMISS_REASON_UNKNOWN = 0; @@ -85,6 +89,7 @@ public class Events { public static final int DISMISS_REASON_DONE_CLICKED = 6; public static final int DISMISS_STREAM_GONE = 7; public static final int DISMISS_REASON_OUTPUT_CHOOSER = 8; + public static final int DISMISS_REASON_USB_OVERHEAD_ALARM_CHANGED = 9; public static final String[] DISMISS_REASONS = { "unknown", "touch_outside", @@ -94,16 +99,19 @@ public class Events { "settings_clicked", "done_clicked", "a11y_stream_changed", - "output_chooser" + "output_chooser", + "usb_temperature_below_threshold" }; public static final int SHOW_REASON_UNKNOWN = 0; public static final int SHOW_REASON_VOLUME_CHANGED = 1; public static final int SHOW_REASON_REMOTE_VOLUME_CHANGED = 2; + public static final int SHOW_REASON_USB_OVERHEAD_ALARM_CHANGED = 3; public static final String[] SHOW_REASONS = { "unknown", "volume_changed", - "remote_volume_changed" + "remote_volume_changed", + "usb_temperature_above_threshold" }; public static final int ICON_STATE_UNKNOWN = 0; @@ -181,6 +189,19 @@ public class Events { case EVENT_SUPPRESSOR_CHANGED: sb.append(list[0]).append(' ').append(list[1]); break; + case EVENT_SHOW_USB_OVERHEAT_ALARM: + MetricsLogger.visible(context, MetricsEvent.POWER_OVERHEAT_ALARM); + MetricsLogger.histogram(context, "show_usb_overheat_alarm", + (Boolean) list[1] ? 1 : 0); + sb.append(SHOW_REASONS[(Integer) list[0]]).append(" keyguard=").append(list[1]); + break; + case EVENT_DISMISS_USB_OVERHEAT_ALARM: + MetricsLogger.hidden(context, MetricsEvent.POWER_OVERHEAT_ALARM); + MetricsLogger.histogram(context, "dismiss_usb_overheat_alarm", + (Boolean) list[1] ? 1 : 0); + sb.append(DISMISS_REASONS[(Integer) list[0]]) + .append(" keyguard=").append(list[1]); + break; default: sb.append(Arrays.asList(list)); break; diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index c903ab500f62..ffd8206ca887 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -25,6 +25,7 @@ import static android.media.AudioManager.STREAM_ALARM; import static android.media.AudioManager.STREAM_MUSIC; import static android.media.AudioManager.STREAM_RING; import static android.media.AudioManager.STREAM_VOICE_CALL; +import static android.view.View.ACCESSIBILITY_LIVE_REGION_POLITE; import static android.view.View.GONE; import static android.view.View.VISIBLE; @@ -431,8 +432,7 @@ public class VolumeDialogImpl implements VolumeDialog { if (mSettingsIcon != null) { mSettingsIcon.setOnClickListener(v -> { Events.writeEvent(mContext, Events.EVENT_SETTINGS_CLICK); - Intent intent = new Intent(Settings.ACTION_SOUND_SETTINGS); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + Intent intent = new Intent(Settings.Panel.ACTION_VOLUME); dismissH(DISMISS_REASON_SETTINGS_CLICKED); Dependency.get(ActivityStarter.class).startActivity(intent, true /* dismissShade */); @@ -442,6 +442,7 @@ public class VolumeDialogImpl implements VolumeDialog { public void initRingerH() { if (mRingerIcon != null) { + mRingerIcon.setAccessibilityLiveRegion(ACCESSIBILITY_LIVE_REGION_POLITE); mRingerIcon.setOnClickListener(v -> { Prefs.putBoolean(mContext, Prefs.Key.TOUCHED_RINGER_TOGGLE, true); final StreamState ss = mState.states.get(AudioManager.STREAM_RING); @@ -587,8 +588,11 @@ public class VolumeDialogImpl implements VolumeDialog { mHandler.removeMessages(H.DISMISS); mHandler.removeMessages(H.SHOW); mDialogView.animate().cancel(); - mShowing = false; - + if (mShowing) { + mShowing = false; + // Only logs when the volume dialog visibility is changed. + Events.writeEvent(mContext, Events.EVENT_DISMISS_DIALOG, reason); + } mDialogView.setTranslationX(0); mDialogView.setAlpha(1); ViewPropertyAnimator animator = mDialogView.animate() @@ -601,8 +605,6 @@ public class VolumeDialogImpl implements VolumeDialog { }, 50)); if (!isLandscape()) animator.translationX(mDialogView.getWidth() / 2); animator.start(); - - Events.writeEvent(mContext, Events.EVENT_DISMISS_DIALOG, reason); mController.notifyVisible(false); synchronized (mSafetyWarningLock) { if (mSafetyWarning != null) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java index e91a7e9bfcb6..0114075b1e65 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java @@ -29,6 +29,7 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -82,6 +83,9 @@ public class ScreenDecorationsTest extends SysuiTestCase { mTestableLooper = TestableLooper.get(this); mDependency.injectTestDependency(Dependency.MAIN_HANDLER, new Handler(mTestableLooper.getLooper())); + mTunablePaddingService = mDependency.injectMockDependency(TunablePaddingService.class); + mTunerService = mDependency.injectMockDependency(TunerService.class); + mFragmentService = mDependency.injectMockDependency(FragmentService.class); mStatusBar = mock(StatusBar.class); mWindowManager = mock(WindowManager.class); @@ -93,12 +97,9 @@ public class ScreenDecorationsTest extends SysuiTestCase { when(mWindowManager.getDefaultDisplay()).thenReturn(display); mContext.addMockSystemService(WindowManager.class, mWindowManager); - mFragmentService = mDependency.injectMockDependency(FragmentService.class); mFragmentHostManager = mock(FragmentHostManager.class); when(mFragmentService.getFragmentHostManager(any())).thenReturn(mFragmentHostManager); - mTunerService = mDependency.injectMockDependency(TunerService.class); - mScreenDecorations = new ScreenDecorations() { @Override @@ -126,8 +127,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { }; mScreenDecorations.mContext = mContext; mScreenDecorations.mComponents = mContext.getComponents(); - - mTunablePaddingService = mDependency.injectMockDependency(TunablePaddingService.class); + reset(mTunerService); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java index 3bd582f955af..b4059c5db3b6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java @@ -19,6 +19,7 @@ package com.android.systemui.bubbles.animation; import static org.junit.Assert.assertEquals; import android.content.res.Resources; +import android.graphics.Point; import android.graphics.PointF; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; @@ -40,7 +41,8 @@ import org.mockito.Spy; public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestCase { @Spy - private ExpandedAnimationController mExpandedController = new ExpandedAnimationController(); + private ExpandedAnimationController mExpandedController = + new ExpandedAnimationController(new Point(500, 1000) /* displaySize */); private int mStackOffset; private float mBubblePadding; @@ -167,7 +169,7 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC assertEquals(mBubblePadding + (i * (mBubbleSize + mBubblePadding)), mLayout.getChildAt(i).getTranslationX(), 2f); - assertEquals(mBubblePadding + mCutoutInsetSize, + assertEquals(mExpandedController.getExpandedY(), mLayout.getChildAt(i).getTranslationY(), 2f); if (i < mMaxRenderedBubbles) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationTest.java index 45342d4bc3a9..6807c22175ab 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationTest.java @@ -18,12 +18,12 @@ package com.android.systemui.doze; import static junit.framework.TestCase.assertEquals; +import android.hardware.display.AmbientDisplayConfiguration; import android.os.UserHandle; import android.provider.Settings; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; -import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.systemui.SysuiTestCase; import org.junit.Before; diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java index e4558df962e3..9438cbb6ebcd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationUtil.java @@ -16,12 +16,13 @@ package com.android.systemui.doze; +import android.hardware.display.AmbientDisplayConfiguration; + import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.mockito.Mockito.withSettings; -import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.systemui.statusbar.phone.DozeParameters; import org.mockito.Answers; diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java index 0fc0953f333e..23ff3d483599 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java @@ -27,6 +27,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.Instrumentation; +import android.hardware.display.AmbientDisplayConfiguration; import android.os.Handler; import android.os.Looper; import android.support.test.InstrumentationRegistry; @@ -35,7 +36,6 @@ import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; -import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.systemui.SysuiTestCase; import com.android.systemui.dock.DockManager; import com.android.systemui.dock.DockManagerFake; diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java index 5a6200f5f80a..f972508c0375 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java @@ -38,11 +38,11 @@ import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.hardware.display.AmbientDisplayConfiguration; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.UiThreadTest; -import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.systemui.SysuiTestCase; import com.android.systemui.util.wakelock.WakeLockFake; diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java new file mode 100644 index 000000000000..463a6e63835e --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.doze; + +import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_LOCK_SCREEN; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyFloat; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.AlarmManager; +import android.hardware.display.AmbientDisplayConfiguration; +import android.support.test.filters.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.testing.TestableLooper.RunWithLooper; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.SensorManagerPlugin; +import com.android.systemui.statusbar.phone.DozeParameters; +import com.android.systemui.util.AsyncSensorManager; +import com.android.systemui.util.wakelock.WakeLock; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.function.Consumer; + +@RunWith(AndroidTestingRunner.class) +@RunWithLooper +@SmallTest +public class DozeSensorsTest extends SysuiTestCase { + + @Mock + private AlarmManager mAlarmManager; + @Mock + private AsyncSensorManager mSensorManager; + @Mock + private DozeParameters mDozeParameters; + @Mock + private AmbientDisplayConfiguration mAmbientDisplayConfiguration; + @Mock + private WakeLock mWakeLock; + @Mock + private DozeSensors.Callback mCallback; + @Mock + private Consumer<Boolean> mProxCallback; + @Mock + private AlwaysOnDisplayPolicy mAlwaysOnDisplayPolicy; + private SensorManagerPlugin.SensorEventListener mWakeLockScreenListener; + private TestableLooper mTestableLooper; + private DozeSensors mDozeSensors; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mTestableLooper = TestableLooper.get(this); + when(mAmbientDisplayConfiguration.getWakeLockScreenDebounce()).thenReturn(5000L); + when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true); + doAnswer(invocation -> { + ((Runnable) invocation.getArgument(0)).run(); + return null; + }).when(mWakeLock).wrap(any(Runnable.class)); + mDozeSensors = new TestableDozeSensors(); + } + + @Test + public void testSensorDebounce() { + mDozeSensors.setListening(true); + + mWakeLockScreenListener.onSensorChanged(mock(SensorManagerPlugin.SensorEvent.class)); + mTestableLooper.processAllMessages(); + verify(mCallback).onSensorPulse(eq(DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN), + anyBoolean(), anyFloat(), anyFloat(), eq(null)); + + mDozeSensors.requestTemporaryDisable(); + reset(mCallback); + mWakeLockScreenListener.onSensorChanged(mock(SensorManagerPlugin.SensorEvent.class)); + mTestableLooper.processAllMessages(); + verify(mCallback, never()).onSensorPulse(eq(DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN), + anyBoolean(), anyFloat(), anyFloat(), eq(null)); + } + + private class TestableDozeSensors extends DozeSensors { + + TestableDozeSensors() { + super(getContext(), mAlarmManager, mSensorManager, mDozeParameters, + mAmbientDisplayConfiguration, mWakeLock, mCallback, mProxCallback, + mAlwaysOnDisplayPolicy); + for (TriggerSensor sensor : mSensors) { + if (sensor instanceof PluginSensor + && ((PluginSensor) sensor).mPluginSensor.getType() + == TYPE_WAKE_LOCK_SCREEN) { + mWakeLockScreenListener = (PluginSensor) sensor; + } + } + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java index cdac7c979c55..ca373478f33c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java @@ -27,6 +27,7 @@ import static org.mockito.Mockito.when; import android.app.AlarmManager; import android.app.Instrumentation; +import android.hardware.display.AmbientDisplayConfiguration; import android.os.Handler; import android.os.Looper; import android.support.test.InstrumentationRegistry; @@ -34,7 +35,6 @@ import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; -import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.systemui.SysuiTestCase; import com.android.systemui.dock.DockManager; import com.android.systemui.dock.DockManagerFake; diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java index bf6cc53aa5e9..cd500b4c38f9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java @@ -16,9 +16,8 @@ package com.android.systemui.power; -import static android.test.MoreAsserts.assertNotEqual; +import static com.google.common.truth.Truth.assertThat; -import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; @@ -38,7 +37,6 @@ import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.systemui.SysuiTestCase; import com.android.systemui.util.NotificationChannels; -import java.util.concurrent.TimeUnit; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -151,4 +149,13 @@ public class PowerNotificationWarningsTest extends SysuiTestCase { verify(mMockNotificationManager, times(1)).cancelAsUser(anyString(), eq(SystemMessage.NOTE_THERMAL_SHUTDOWN), any()); } + + @Test + public void testShowUsbHighTemperatureAlarm() { + mPowerNotificationWarnings.showUsbHighTemperatureAlarm(); + waitForIdleSync(mContext.getMainThreadHandler()); + assertThat(mPowerNotificationWarnings.mUsbHighTempDialog).isNotNull(); + + mPowerNotificationWarnings.mUsbHighTempDialog.dismiss(); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java index c28e74e7c6a1..0aed63d25112 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java @@ -14,14 +14,14 @@ package com.android.systemui.power; -import static android.os.HardwarePropertiesManager.DEVICE_TEMPERATURE_SKIN; -import static android.os.HardwarePropertiesManager.TEMPERATURE_CURRENT; -import static android.os.HardwarePropertiesManager.TEMPERATURE_SHUTDOWN; import static android.provider.Settings.Global.SHOW_TEMPERATURE_WARNING; +import static android.provider.Settings.Global.SHOW_USB_TEMPERATURE_ALARM; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.anyObject; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -31,9 +31,10 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.Intent; import android.os.BatteryManager; -import android.os.HardwarePropertiesManager; +import android.os.IThermalEventListener; import android.os.IThermalService; import android.os.PowerManager; +import android.os.Temperature; import android.provider.Settings; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; @@ -74,97 +75,104 @@ public class PowerUITest extends SysuiTestCase { private static final int OLD_BATTERY_LEVEL_NINE = 9; private static final int OLD_BATTERY_LEVEL_10 = 10; private static final long VERY_BELOW_SEVERE_HYBRID_THRESHOLD = TimeUnit.MINUTES.toMillis(15); - private HardwarePropertiesManager mHardProps; private WarningsUI mMockWarnings; private PowerUI mPowerUI; private EnhancedEstimates mEnhancedEstimates; @Mock private PowerManager mPowerManager; @Mock private IThermalService mThermalServiceMock; + private IThermalEventListener mThermalEventUsbListener; + private IThermalEventListener mThermalEventSkinListener; @Before public void setup() { MockitoAnnotations.initMocks(this); mMockWarnings = mDependency.injectMockDependency(WarningsUI.class); mEnhancedEstimates = mDependency.injectMockDependency(EnhancedEstimates.class); - mHardProps = mock(HardwarePropertiesManager.class); mContext.putComponent(StatusBar.class, mock(StatusBar.class)); - mContext.addMockSystemService(Context.HARDWARE_PROPERTIES_SERVICE, mHardProps); mContext.addMockSystemService(Context.POWER_SERVICE, mPowerManager); createPowerUi(); + mThermalEventSkinListener = mPowerUI.new ThermalEventSkinListener(); + mThermalEventUsbListener = mPowerUI.new ThermalEventUsbListener(); } @Test - public void testNoConfig_NoWarnings() { - setOverThreshold(); - Settings.Global.putString(mContext.getContentResolver(), SHOW_TEMPERATURE_WARNING, null); - TestableResources resources = mContext.getOrCreateTestableResources(); - resources.addOverride(R.integer.config_showTemperatureWarning, 0); - resources.addOverride(R.integer.config_warningTemperature, 55); - + public void testSkinWarning_throttlingCritical() throws Exception { mPowerUI.start(); + + final Temperature temp = getCriticalStatusTemp(Temperature.TYPE_SKIN, "skin1"); + mThermalEventSkinListener.notifyThrottling(temp); + + // dismiss skin high temperature warning when throttling status is critical + TestableLooper.get(this).processAllMessages(); verify(mMockWarnings, never()).showHighTemperatureWarning(); + verify(mMockWarnings, times(1)).dismissHighTemperatureWarning(); } @Test - public void testConfig_NoWarnings() { - setUnderThreshold(); - Settings.Global.putString(mContext.getContentResolver(), SHOW_TEMPERATURE_WARNING, null); - TestableResources resources = mContext.getOrCreateTestableResources(); - resources.addOverride(R.integer.config_showTemperatureWarning, 1); - resources.addOverride(R.integer.config_warningTemperature, 55); - + public void testSkinWarning_throttlingEmergency() throws Exception { mPowerUI.start(); - verify(mMockWarnings, never()).showHighTemperatureWarning(); + + final Temperature temp = getEmergencyStatusTemp(Temperature.TYPE_SKIN, "skin2"); + mThermalEventSkinListener.notifyThrottling(temp); + + // show skin high temperature warning when throttling status is emergency + TestableLooper.get(this).processAllMessages(); + verify(mMockWarnings, times(1)).showHighTemperatureWarning(); + verify(mMockWarnings, never()).dismissHighTemperatureWarning(); } @Test - public void testConfig_Warnings() { - setOverThreshold(); - Settings.Global.putString(mContext.getContentResolver(), SHOW_TEMPERATURE_WARNING, null); - TestableResources resources = mContext.getOrCreateTestableResources(); - resources.addOverride(R.integer.config_showTemperatureWarning, 1); - resources.addOverride(R.integer.config_warningTemperature, 55); + public void testUsbAlarm_throttlingCritical() throws Exception { + mPowerUI.start(); + + final Temperature temp = getCriticalStatusTemp(Temperature.TYPE_USB_PORT, "usb1"); + mThermalEventUsbListener.notifyThrottling(temp); + // not show usb high temperature alarm when throttling status is critical + TestableLooper.get(this).processAllMessages(); + verify(mMockWarnings, never()).showUsbHighTemperatureAlarm(); + } + + @Test + public void testUsbAlarm_throttlingEmergency() throws Exception { mPowerUI.start(); - // Guarantees mHandler has processed all messages. + + final Temperature temp = getEmergencyStatusTemp(Temperature.TYPE_USB_PORT, "usb2"); + mThermalEventUsbListener.notifyThrottling(temp); + + // show usb high temperature alarm when throttling status is emergency TestableLooper.get(this).processAllMessages(); - verify(mMockWarnings).showHighTemperatureWarning(); + verify(mMockWarnings, times(1)).showUsbHighTemperatureAlarm(); } @Test - public void testSettingOverrideConfig() { - setOverThreshold(); + public void testSettingOverrideConfig_enableSkinTemperatureWarning() throws Exception { Settings.Global.putInt(mContext.getContentResolver(), SHOW_TEMPERATURE_WARNING, 1); TestableResources resources = mContext.getOrCreateTestableResources(); resources.addOverride(R.integer.config_showTemperatureWarning, 0); - resources.addOverride(R.integer.config_warningTemperature, 55); mPowerUI.start(); - // Guarantees mHandler has processed all messages. + mPowerUI.registerThermalEventListener(); + TestableLooper.get(this).processAllMessages(); - verify(mMockWarnings).showHighTemperatureWarning(); + verify(mThermalServiceMock, times(1)) + .registerThermalEventListenerWithType(anyObject(), eq(Temperature.TYPE_SKIN)); } @Test - public void testShutdownBasedThreshold() { - int tolerance = 2; - Settings.Global.putString(mContext.getContentResolver(), SHOW_TEMPERATURE_WARNING, null); + public void testSettingOverrideConfig_enableUsbTemperatureAlarm() throws Exception { + Settings.Global.putInt(mContext.getContentResolver(), SHOW_USB_TEMPERATURE_ALARM, 1); TestableResources resources = mContext.getOrCreateTestableResources(); - resources.addOverride(R.integer.config_showTemperatureWarning, 1); - resources.addOverride(R.integer.config_warningTemperature, -1); - resources.addOverride(R.integer.config_warningTemperatureTolerance, tolerance); - when(mHardProps.getDeviceTemperatures(DEVICE_TEMPERATURE_SKIN, TEMPERATURE_SHUTDOWN)) - .thenReturn(new float[] { 55 + tolerance }); + resources.addOverride(R.integer.config_showUsbPortAlarm, 0); - setCurrentTemp(54); // Below threshold. mPowerUI.start(); - verify(mMockWarnings, never()).showHighTemperatureWarning(); + mPowerUI.registerThermalEventListener(); - setCurrentTemp(56); // Above threshold. - mPowerUI.updateTemperatureWarning(); - verify(mMockWarnings).showHighTemperatureWarning(); + TestableLooper.get(this).processAllMessages(); + verify(mThermalServiceMock, times(1)) + .registerThermalEventListenerWithType(anyObject(), eq(Temperature.TYPE_USB_PORT)); } @Test @@ -532,17 +540,14 @@ public class PowerUITest extends SysuiTestCase { verify(mEnhancedEstimates, times(2)).getEstimate(); } - private void setCurrentTemp(float temp) { - when(mHardProps.getDeviceTemperatures(DEVICE_TEMPERATURE_SKIN, TEMPERATURE_CURRENT)) - .thenReturn(new float[] { temp }); - } - - private void setOverThreshold() { - setCurrentTemp(50000); + private Temperature getEmergencyStatusTemp(int type, String name) { + final float value = 65; + return new Temperature(value, type, name, Temperature.THROTTLING_EMERGENCY); } - private void setUnderThreshold() { - setCurrentTemp(5); + private Temperature getCriticalStatusTemp(int type, String name) { + final float value = 60; + return new Temperature(value, type, name, Temperature.THROTTLING_CRITICAL); } private void createPowerUi() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java index 04e7cab406ea..0d4328f94a39 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java @@ -67,6 +67,8 @@ import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.notification.collection.NotificationData; import com.android.systemui.statusbar.notification.collection.NotificationData.KeyguardEnvironment; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.NotificationRowBinder; +import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; @@ -102,10 +104,8 @@ public class NotificationEntryManagerTest extends SysuiTestCase { @Mock private KeyguardEnvironment mEnvironment; @Mock private ExpandableNotificationRow mRow; @Mock private NotificationListContainer mListContainer; - @Mock - private NotificationEntryListener mEntryListener; - @Mock - private NotificationRowBinder.BindRowCallback mBindCallback; + @Mock private NotificationEntryListener mEntryListener; + @Mock private NotificationRowBinderImpl.BindRowCallback mBindCallback; @Mock private HeadsUpManager mHeadsUpManager; @Mock private NotificationListenerService.RankingMap mRankingMap; @Mock private RemoteInputController mRemoteInputController; @@ -235,10 +235,12 @@ public class NotificationEntryManagerTest extends SysuiTestCase { mEntryManager.setUpWithPresenter(mPresenter, mListContainer, mHeadsUpManager); mEntryManager.addNotificationEntryListener(mEntryListener); - NotificationRowBinder notificationRowBinder = Dependency.get(NotificationRowBinder.class); + NotificationRowBinderImpl notificationRowBinder = + new NotificationRowBinderImpl(mContext, true /* allowLongPress */); notificationRowBinder.setUpWithPresenter( mPresenter, mListContainer, mHeadsUpManager, mEntryManager, mBindCallback); notificationRowBinder.setNotificationClicker(mock(NotificationClicker.class)); + mEntryManager.setRowBinder(notificationRowBinder); setUserSentiment(mEntry.key, NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java index c80396d8292a..d756b0970346 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.java @@ -16,8 +16,6 @@ package com.android.systemui.statusbar.notification.row; -import static com.google.common.truth.Truth.assertThat; - import static org.junit.Assert.assertFalse; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyFloat; @@ -31,62 +29,31 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.AppOpsManager; -import android.app.Notification; -import android.app.PendingIntent; -import android.app.RemoteInput; -import android.content.Intent; import android.graphics.drawable.Icon; -import android.service.notification.StatusBarNotification; import android.support.test.annotation.UiThreadTest; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.util.ArraySet; -import android.util.Pair; import android.view.NotificationHeaderView; import android.view.View; -import com.android.systemui.R; import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.policy.SmartReplyConstants; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.ArrayList; -import java.util.List; @SmallTest @RunWith(AndroidJUnit4.class) public class NotificationContentViewTest extends SysuiTestCase { - private static final String TEST_ACTION = "com.android.SMART_REPLY_VIEW_ACTION"; - NotificationContentView mView; - @Mock - SmartReplyConstants mSmartReplyConstants; - @Mock - StatusBarNotification mStatusBarNotification; - @Mock - Notification mNotification; - NotificationEntry mEntry; - @Mock - RemoteInput mRemoteInput; - @Mock - RemoteInput mFreeFormRemoteInput; - private Icon mActionIcon; - @Before @UiThreadTest public void setup() { - MockitoAnnotations.initMocks(this); - mView = new NotificationContentView(mContext, null); ExpandableNotificationRow row = new ExpandableNotificationRow(mContext, null); ExpandableNotificationRow mockRow = spy(row); @@ -103,13 +70,6 @@ public class NotificationContentViewTest extends SysuiTestCase { mView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight()); - - // Smart replies and actions - when(mNotification.getAllowSystemGeneratedContextualActions()).thenReturn(true); - when(mStatusBarNotification.getNotification()).thenReturn(mNotification); - mEntry = new NotificationEntry(mStatusBarNotification); - when(mSmartReplyConstants.isEnabled()).thenReturn(true); - mActionIcon = Icon.createWithResource(mContext, R.drawable.ic_person); } private View createViewWithHeight(int height) { @@ -158,204 +118,4 @@ public class NotificationContentViewTest extends SysuiTestCase { verify(mockAmbient, never()).showAppOpsIcons(ops); verify(mockHeadsUp, times(1)).showAppOpsIcons(any()); } - - private void setupAppGeneratedReplies(CharSequence[] smartReplies) { - setupAppGeneratedReplies(smartReplies, true /* allowSystemGeneratedReplies */); - } - - private void setupAppGeneratedReplies( - CharSequence[] smartReplies, boolean allowSystemGeneratedReplies) { - PendingIntent pendingIntent = - PendingIntent.getBroadcast(mContext, 0, new Intent(TEST_ACTION), 0); - Notification.Action action = - new Notification.Action.Builder(null, "Test Action", pendingIntent).build(); - when(mRemoteInput.getChoices()).thenReturn(smartReplies); - Pair<RemoteInput, Notification.Action> remoteInputActionPair = - Pair.create(mRemoteInput, action); - when(mNotification.findRemoteInputActionPair(false)).thenReturn(remoteInputActionPair); - - Notification.Action freeFormRemoteInputAction = - createActionBuilder("Freeform Test Action") - .setAllowGeneratedReplies(allowSystemGeneratedReplies) - .build(); - Pair<RemoteInput, Notification.Action> freeFormRemoteInputActionPair = - Pair.create(mFreeFormRemoteInput, freeFormRemoteInputAction); - when(mNotification.findRemoteInputActionPair(true)).thenReturn( - freeFormRemoteInputActionPair); - - when(mSmartReplyConstants.requiresTargetingP()).thenReturn(false); - } - - private void setupAppGeneratedSuggestions( - CharSequence[] smartReplies, List<Notification.Action> smartActions) { - setupAppGeneratedReplies(smartReplies); - when(mNotification.getContextualActions()).thenReturn(smartActions); - } - - @Test - public void chooseSmartRepliesAndActions_smartRepliesOff_noAppGeneratedSmartSuggestions() { - CharSequence[] smartReplies = new String[] {"Reply1", "Reply2"}; - List<Notification.Action> smartActions = - createActions(new String[] {"Test Action 1", "Test Action 2"}); - setupAppGeneratedSuggestions(smartReplies, smartActions); - when(mSmartReplyConstants.isEnabled()).thenReturn(false); - - NotificationContentView.SmartRepliesAndActions repliesAndActions = - NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry); - - assertThat(repliesAndActions.smartReplies).isNull(); - assertThat(repliesAndActions.smartActions).isNull(); - } - - @Test - public void chooseSmartRepliesAndActions_smartRepliesOff_noSystemGeneratedSmartSuggestions() { - mEntry.systemGeneratedSmartReplies = - new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"}; - mEntry.systemGeneratedSmartActions = - createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"}); - when(mSmartReplyConstants.isEnabled()).thenReturn(false); - - NotificationContentView.SmartRepliesAndActions repliesAndActions = - NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry); - - assertThat(repliesAndActions.smartReplies).isNull(); - assertThat(repliesAndActions.smartActions).isNull(); - } - - @Test - public void chooseSmartRepliesAndActions_appGeneratedSmartReplies() { - CharSequence[] smartReplies = new String[] {"Reply1", "Reply2"}; - setupAppGeneratedReplies(smartReplies); - - NotificationContentView.SmartRepliesAndActions repliesAndActions = - NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry); - - assertThat(repliesAndActions.smartReplies.choices).isEqualTo(smartReplies); - assertThat(repliesAndActions.smartReplies.fromAssistant).isFalse(); - assertThat(repliesAndActions.smartActions).isNull(); - } - - @Test - public void chooseSmartRepliesAndActions_appGeneratedSmartRepliesAndActions() { - CharSequence[] smartReplies = new String[] {"Reply1", "Reply2"}; - List<Notification.Action> smartActions = - createActions(new String[] {"Test Action 1", "Test Action 2"}); - setupAppGeneratedSuggestions(smartReplies, smartActions); - - NotificationContentView.SmartRepliesAndActions repliesAndActions = - NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry); - - assertThat(repliesAndActions.smartReplies.choices).isEqualTo(smartReplies); - assertThat(repliesAndActions.smartReplies.fromAssistant).isFalse(); - assertThat(repliesAndActions.smartActions.actions).isEqualTo(smartActions); - assertThat(repliesAndActions.smartActions.fromAssistant).isFalse(); - } - - @Test - public void chooseSmartRepliesAndActions_sysGeneratedSmartReplies() { - // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart - // replies. - setupAppGeneratedReplies(null /* smartReplies */); - - mEntry.systemGeneratedSmartReplies = - new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"}; - NotificationContentView.SmartRepliesAndActions repliesAndActions = - NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry); - - assertThat(repliesAndActions.smartReplies.choices).isEqualTo( - mEntry.systemGeneratedSmartReplies); - assertThat(repliesAndActions.smartReplies.fromAssistant).isTrue(); - assertThat(repliesAndActions.smartActions).isNull(); - } - - @Test - public void chooseSmartRepliesAndActions_noSysGeneratedSmartRepliesIfNotAllowed() { - // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart - // replies. - setupAppGeneratedReplies(null /* smartReplies */, false /* allowSystemGeneratedReplies */); - - mEntry.systemGeneratedSmartReplies = - new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"}; - NotificationContentView.SmartRepliesAndActions repliesAndActions = - NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry); - - assertThat(repliesAndActions.smartReplies).isNull(); - assertThat(repliesAndActions.smartActions).isNull(); - } - - @Test - public void chooseSmartRepliesAndActions_sysGeneratedSmartActions() { - // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart - // actions. - setupAppGeneratedReplies(null /* smartReplies */); - - mEntry.systemGeneratedSmartActions = - createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"}); - NotificationContentView.SmartRepliesAndActions repliesAndActions = - NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry); - - assertThat(repliesAndActions.smartReplies).isNull(); - assertThat(repliesAndActions.smartActions.actions) - .isEqualTo(mEntry.systemGeneratedSmartActions); - assertThat(repliesAndActions.smartActions.fromAssistant).isTrue(); - } - - @Test - public void chooseSmartRepliesAndActions_appGenPreferredOverSysGen() { - CharSequence[] appGenSmartReplies = new String[] {"Reply1", "Reply2"}; - // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart - // replies. - List<Notification.Action> appGenSmartActions = - createActions(new String[] {"Test Action 1", "Test Action 2"}); - setupAppGeneratedSuggestions(appGenSmartReplies, appGenSmartActions); - - mEntry.systemGeneratedSmartReplies = - new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"}; - mEntry.systemGeneratedSmartActions = - createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"}); - - NotificationContentView.SmartRepliesAndActions repliesAndActions = - NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry); - - assertThat(repliesAndActions.smartReplies.choices).isEqualTo(appGenSmartReplies); - assertThat(repliesAndActions.smartReplies.fromAssistant).isFalse(); - assertThat(repliesAndActions.smartActions.actions).isEqualTo(appGenSmartActions); - assertThat(repliesAndActions.smartActions.fromAssistant).isFalse(); - } - - @Test - public void chooseSmartRepliesAndActions_disallowSysGenSmartActions() { - // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart - // actions. - setupAppGeneratedReplies(null /* smartReplies */, false /* allowSystemGeneratedReplies */); - when(mNotification.getAllowSystemGeneratedContextualActions()).thenReturn(false); - mEntry.systemGeneratedSmartReplies = - new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"}; - mEntry.systemGeneratedSmartActions = - createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"}); - - NotificationContentView.SmartRepliesAndActions repliesAndActions = - NotificationContentView.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry); - - assertThat(repliesAndActions.smartActions).isNull(); - assertThat(repliesAndActions.smartReplies).isNull(); - } - - private Notification.Action.Builder createActionBuilder(String actionTitle) { - PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, - new Intent(TEST_ACTION), 0); - return new Notification.Action.Builder(mActionIcon, actionTitle, pendingIntent); - } - - private Notification.Action createAction(String actionTitle) { - return createActionBuilder(actionTitle).build(); - } - - private List<Notification.Action> createActions(String[] actionTitles) { - List<Notification.Action> actions = new ArrayList<>(); - for (String title : actionTitles) { - actions.add(createAction(title)); - } - return actions; - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java index f3740c4d51d0..87699b816c07 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.phone; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -31,6 +32,8 @@ import android.testing.TestableLooper.RunWithLooper; import com.android.systemui.SysuiTestCase; import com.android.systemui.qs.AutoAddTracker; import com.android.systemui.qs.QSTileHost; +import com.android.systemui.statusbar.policy.CastController; +import com.android.systemui.statusbar.policy.CastController.CastDevice; import com.android.systemui.statusbar.policy.DataSaverController; import com.android.systemui.statusbar.policy.HotspotController; @@ -40,6 +43,9 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.Collections; +import java.util.Set; + @RunWith(AndroidTestingRunner.class) @RunWithLooper @SmallTest @@ -47,6 +53,7 @@ public class AutoTileManagerTest extends SysuiTestCase { @Mock private QSTileHost mQsTileHost; @Mock private AutoAddTracker mAutoAddTracker; + @Mock private CastController mCastController; private AutoTileManager mAutoTileManager; @@ -58,7 +65,8 @@ public class AutoTileManagerTest extends SysuiTestCase { mock(HotspotController.class), mock(DataSaverController.class), mock(ManagedProfileController.class), - mock(NightDisplayListener.class)); + mock(NightDisplayListener.class), + mCastController); } @Test @@ -108,4 +116,24 @@ public class AutoTileManagerTest extends SysuiTestCase { ColorDisplayManager.AUTO_MODE_DISABLED); verify(mQsTileHost, never()).addTile("night"); } + + private static Set<CastDevice> buildFakeCastDevice(boolean isCasting) { + CastDevice cd = new CastDevice(); + cd.state = isCasting ? CastDevice.STATE_CONNECTED : CastDevice.STATE_DISCONNECTED; + return Collections.singleton(cd); + } + + @Test + public void castTileAdded_whenDeviceIsCasting() { + doReturn(buildFakeCastDevice(true)).when(mCastController).getCastDevices(); + mAutoTileManager.mCastCallback.onCastDevicesChanged(); + verify(mQsTileHost).addTile("cast"); + } + + @Test + public void castTileNotAdded_whenDeviceIsNotCasting() { + doReturn(buildFakeCastDevice(false)).when(mCastController).getCastDevices(); + mAutoTileManager.mCastCallback.onCastDevicesChanged(); + verify(mQsTileHost, never()).addTile("cast"); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarInflaterViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarInflaterViewTest.java new file mode 100644 index 000000000000..093749adf1c3 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NavigationBarInflaterViewTest.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; + +import android.support.test.filters.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper.RunWithLooper; +import android.util.SparseArray; +import android.view.View; +import android.widget.FrameLayout; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.CommandQueue; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** atest NavigationBarInflaterViewTest */ +@RunWith(AndroidTestingRunner.class) +@RunWithLooper +@SmallTest +public class NavigationBarInflaterViewTest extends SysuiTestCase { + + private NavigationBarInflaterView mNavBarInflaterView; + + private static final int BUTTON_ID = 0; + + @Before + public void setUp() { + mContext.putComponent(CommandQueue.class, mock(CommandQueue.class)); + + mNavBarInflaterView = spy(new NavigationBarInflaterView(mContext, null)); + doNothing().when(mNavBarInflaterView).createInflaters(); + + mNavBarInflaterView.mButtonDispatchers = new SparseArray<>(1); + mNavBarInflaterView.mButtonDispatchers.put(BUTTON_ID, new ButtonDispatcher(BUTTON_ID)); + + initializeViews(); + } + + private void initializeViews() { + mNavBarInflaterView.mVertical = mock(FrameLayout.class); + mNavBarInflaterView.mHorizontal = mock(FrameLayout.class); + initializeLayout(mNavBarInflaterView.mVertical); + initializeLayout(mNavBarInflaterView.mHorizontal); + } + + private void initializeLayout(FrameLayout layout) { + View verticalChildView = mock(View.class); + verticalChildView.setId(BUTTON_ID); + doReturn(layout).when(verticalChildView).getParent(); + doReturn(verticalChildView).when(layout).findViewById(BUTTON_ID); + } + + @After + public void tearDown() { + mNavBarInflaterView = null; + } + + @Test + public void testUpdateButtonDispatchersCurrentView_isVerticalTrue() { + mNavBarInflaterView.setVertical(true); + + mNavBarInflaterView.updateButtonDispatchersCurrentView(); + + ButtonDispatcher button = mNavBarInflaterView.mButtonDispatchers.get(BUTTON_ID); + assertEquals("Buttons need to be set to vertical layout", + mNavBarInflaterView.mVertical.getId(), + ((View) button.getCurrentView().getParent()).getId()); + } + + @Test + public void testUpdateButtonDispatchersCurrentView_isVerticalFalse() { + mNavBarInflaterView.setVertical(false); + + mNavBarInflaterView.updateButtonDispatchersCurrentView(); + + ButtonDispatcher button = mNavBarInflaterView.mButtonDispatchers.get(BUTTON_ID); + assertEquals("Buttons need to be set to horizon layout", + mNavBarInflaterView.mHorizontal.getId(), + ((View) button.getCurrentView().getParent()).getId()); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index e4da859946af..f72d4119b50c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -38,6 +38,7 @@ import android.app.AlarmManager; import android.graphics.Color; import android.os.Handler; import android.os.Looper; +import android.support.test.filters.FlakyTest; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -251,6 +252,7 @@ public class ScrimControllerTest extends SysuiTestCase { assertScrimTint(mScrimBehind, false /* tinted */); } + @FlakyTest(bugId = 124858892) @Test public void transitionToUnlocked() { mScrimController.setPanelExpansion(0f); @@ -295,6 +297,7 @@ public class ScrimControllerTest extends SysuiTestCase { Assert.assertEquals(mScrimState, ScrimState.BOUNCER_SCRIMMED); } + @FlakyTest(bugId = 124858892) @Test public void panelExpansion() { mScrimController.setPanelExpansion(0f); @@ -317,6 +320,7 @@ public class ScrimControllerTest extends SysuiTestCase { mScrimBehindAlpha, mScrimBehind.getViewAlpha(), 0.01f); } + @FlakyTest(bugId = 124858892) @Test public void panelExpansionAffectsAlpha() { mScrimController.setPanelExpansion(0f); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java index 12cb99575fb6..d2b0f7f95967 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java @@ -41,6 +41,7 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.notification.ActivityLaunchAnimator; import com.android.systemui.statusbar.notification.NotificationAlertingManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl; import com.android.systemui.statusbar.notification.row.ActivatableNotificationView; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; @@ -74,7 +75,8 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { statusBarWindowView, mock(NotificationListContainerViewGroup.class), mock(DozeScrimController.class), mock(ScrimController.class), mock(ActivityLaunchAnimator.class), mock(StatusBarKeyguardViewManager.class), - mock(NotificationAlertingManager.class)); + mock(NotificationAlertingManager.class), + mock(NotificationRowBinderImpl.class)); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java new file mode 100644 index 000000000000..3382a906d057 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.policy; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.when; + +import android.app.Notification; +import android.app.PendingIntent; +import android.app.RemoteInput; +import android.content.Intent; +import android.graphics.drawable.Icon; +import android.service.notification.StatusBarNotification; +import android.support.test.annotation.UiThreadTest; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; +import android.util.Pair; + +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.policy.InflatedSmartReplies.SmartRepliesAndActions; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.List; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class InflatedSmartRepliesTest extends SysuiTestCase { + + private static final String TEST_ACTION = "com.android.SMART_REPLY_VIEW_ACTION"; + + @Mock + SmartReplyConstants mSmartReplyConstants; + @Mock + StatusBarNotification mStatusBarNotification; + @Mock + Notification mNotification; + NotificationEntry mEntry; + @Mock + RemoteInput mRemoteInput; + @Mock + RemoteInput mFreeFormRemoteInput; + + private Icon mActionIcon; + + @Before + @UiThreadTest + public void setUp() { + MockitoAnnotations.initMocks(this); + + when(mNotification.getAllowSystemGeneratedContextualActions()).thenReturn(true); + when(mStatusBarNotification.getNotification()).thenReturn(mNotification); + mEntry = new NotificationEntry(mStatusBarNotification); + when(mSmartReplyConstants.isEnabled()).thenReturn(true); + mActionIcon = Icon.createWithResource(mContext, R.drawable.ic_person); + } + + @Test + public void chooseSmartRepliesAndActions_smartRepliesOff_noAppGeneratedSmartSuggestions() { + CharSequence[] smartReplies = new String[] {"Reply1", "Reply2"}; + List<Notification.Action> smartActions = + createActions(new String[] {"Test Action 1", "Test Action 2"}); + setupAppGeneratedSuggestions(smartReplies, smartActions); + when(mSmartReplyConstants.isEnabled()).thenReturn(false); + + SmartRepliesAndActions repliesAndActions = + InflatedSmartReplies.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry); + + assertThat(repliesAndActions.smartReplies).isNull(); + assertThat(repliesAndActions.smartActions).isNull(); + } + + @Test + public void chooseSmartRepliesAndActions_smartRepliesOff_noSystemGeneratedSmartSuggestions() { + mEntry.systemGeneratedSmartReplies = + new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"}; + mEntry.systemGeneratedSmartActions = + createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"}); + when(mSmartReplyConstants.isEnabled()).thenReturn(false); + + SmartRepliesAndActions repliesAndActions = + InflatedSmartReplies.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry); + + assertThat(repliesAndActions.smartReplies).isNull(); + assertThat(repliesAndActions.smartActions).isNull(); + } + + @Test + public void chooseSmartRepliesAndActions_appGeneratedSmartReplies() { + CharSequence[] smartReplies = new String[] {"Reply1", "Reply2"}; + setupAppGeneratedReplies(smartReplies); + + SmartRepliesAndActions repliesAndActions = + InflatedSmartReplies.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry); + + assertThat(repliesAndActions.smartReplies.choices).isEqualTo(smartReplies); + assertThat(repliesAndActions.smartReplies.fromAssistant).isFalse(); + assertThat(repliesAndActions.smartActions).isNull(); + } + + @Test + public void chooseSmartRepliesAndActions_appGeneratedSmartRepliesAndActions() { + CharSequence[] smartReplies = new String[] {"Reply1", "Reply2"}; + List<Notification.Action> smartActions = + createActions(new String[] {"Test Action 1", "Test Action 2"}); + setupAppGeneratedSuggestions(smartReplies, smartActions); + + SmartRepliesAndActions repliesAndActions = + InflatedSmartReplies.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry); + + assertThat(repliesAndActions.smartReplies.choices).isEqualTo(smartReplies); + assertThat(repliesAndActions.smartReplies.fromAssistant).isFalse(); + assertThat(repliesAndActions.smartActions.actions).isEqualTo(smartActions); + assertThat(repliesAndActions.smartActions.fromAssistant).isFalse(); + } + + @Test + public void chooseSmartRepliesAndActions_sysGeneratedSmartReplies() { + // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart + // replies. + setupAppGeneratedReplies(null /* smartReplies */); + + mEntry.systemGeneratedSmartReplies = + new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"}; + SmartRepliesAndActions repliesAndActions = + InflatedSmartReplies.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry); + + assertThat(repliesAndActions.smartReplies.choices).isEqualTo( + mEntry.systemGeneratedSmartReplies); + assertThat(repliesAndActions.smartReplies.fromAssistant).isTrue(); + assertThat(repliesAndActions.smartActions).isNull(); + } + + @Test + public void chooseSmartRepliesAndActions_noSysGeneratedSmartRepliesIfNotAllowed() { + // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart + // replies. + setupAppGeneratedReplies(null /* smartReplies */, false /* allowSystemGeneratedReplies */); + + mEntry.systemGeneratedSmartReplies = + new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"}; + SmartRepliesAndActions repliesAndActions = + InflatedSmartReplies.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry); + + assertThat(repliesAndActions.smartReplies).isNull(); + assertThat(repliesAndActions.smartActions).isNull(); + } + + @Test + public void chooseSmartRepliesAndActions_sysGeneratedSmartActions() { + // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart + // actions. + setupAppGeneratedReplies(null /* smartReplies */); + + mEntry.systemGeneratedSmartActions = + createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"}); + SmartRepliesAndActions repliesAndActions = + InflatedSmartReplies.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry); + + assertThat(repliesAndActions.smartReplies).isNull(); + assertThat(repliesAndActions.smartActions.actions) + .isEqualTo(mEntry.systemGeneratedSmartActions); + assertThat(repliesAndActions.smartActions.fromAssistant).isTrue(); + } + + @Test + public void chooseSmartRepliesAndActions_appGenPreferredOverSysGen() { + CharSequence[] appGenSmartReplies = new String[] {"Reply1", "Reply2"}; + // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart + // replies. + List<Notification.Action> appGenSmartActions = + createActions(new String[] {"Test Action 1", "Test Action 2"}); + setupAppGeneratedSuggestions(appGenSmartReplies, appGenSmartActions); + + mEntry.systemGeneratedSmartReplies = + new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"}; + mEntry.systemGeneratedSmartActions = + createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"}); + + SmartRepliesAndActions repliesAndActions = + InflatedSmartReplies.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry); + + assertThat(repliesAndActions.smartReplies.choices).isEqualTo(appGenSmartReplies); + assertThat(repliesAndActions.smartReplies.fromAssistant).isFalse(); + assertThat(repliesAndActions.smartActions.actions).isEqualTo(appGenSmartActions); + assertThat(repliesAndActions.smartActions.fromAssistant).isFalse(); + } + + @Test + public void chooseSmartRepliesAndActions_disallowSysGenSmartActions() { + // Pass a null-array as app-generated smart replies, so that we use NAS-generated smart + // actions. + setupAppGeneratedReplies(null /* smartReplies */, false /* allowSystemGeneratedReplies */); + when(mNotification.getAllowSystemGeneratedContextualActions()).thenReturn(false); + mEntry.systemGeneratedSmartReplies = + new String[] {"Sys Smart Reply 1", "Sys Smart Reply 2"}; + mEntry.systemGeneratedSmartActions = + createActions(new String[] {"Sys Smart Action 1", "Sys Smart Action 2"}); + + SmartRepliesAndActions repliesAndActions = + InflatedSmartReplies.chooseSmartRepliesAndActions(mSmartReplyConstants, mEntry); + + assertThat(repliesAndActions.smartActions).isNull(); + assertThat(repliesAndActions.smartReplies).isNull(); + } + + private void setupAppGeneratedReplies(CharSequence[] smartReplies) { + setupAppGeneratedReplies(smartReplies, true /* allowSystemGeneratedReplies */); + } + + private void setupAppGeneratedReplies( + CharSequence[] smartReplies, boolean allowSystemGeneratedReplies) { + PendingIntent pendingIntent = + PendingIntent.getBroadcast(mContext, 0, new Intent(TEST_ACTION), 0); + Notification.Action action = + new Notification.Action.Builder(null, "Test Action", pendingIntent).build(); + when(mRemoteInput.getChoices()).thenReturn(smartReplies); + Pair<RemoteInput, Notification.Action> remoteInputActionPair = + Pair.create(mRemoteInput, action); + when(mNotification.findRemoteInputActionPair(false)).thenReturn(remoteInputActionPair); + + Notification.Action freeFormRemoteInputAction = + createActionBuilder("Freeform Test Action") + .setAllowGeneratedReplies(allowSystemGeneratedReplies) + .build(); + Pair<RemoteInput, Notification.Action> freeFormRemoteInputActionPair = + Pair.create(mFreeFormRemoteInput, freeFormRemoteInputAction); + when(mNotification.findRemoteInputActionPair(true)).thenReturn( + freeFormRemoteInputActionPair); + + when(mSmartReplyConstants.requiresTargetingP()).thenReturn(false); + } + + private void setupAppGeneratedSuggestions( + CharSequence[] smartReplies, List<Notification.Action> smartActions) { + setupAppGeneratedReplies(smartReplies); + when(mNotification.getContextualActions()).thenReturn(smartActions); + } + + private Notification.Action.Builder createActionBuilder(String actionTitle) { + PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, + new Intent(TEST_ACTION), 0); + return new Notification.Action.Builder(mActionIcon, actionTitle, pendingIntent); + } + + private Notification.Action createAction(String actionTitle) { + return createActionBuilder(actionTitle).build(); + } + + private List<Notification.Action> createActions(String[] actionTitles) { + List<Notification.Action> actions = new ArrayList<>(); + for (String title : actionTitles) { + actions.add(createAction(title)); + } + return actions; + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java index bc0f7423eb9f..cd9069aa1233 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyConstantsTest.java @@ -23,15 +23,17 @@ import static junit.framework.Assert.assertTrue; import android.app.RemoteInput; import android.os.Handler; import android.os.Looper; -import android.provider.Settings; +import android.provider.DeviceConfig; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableResources; +import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -47,7 +49,7 @@ public class SmartReplyConstantsTest extends SysuiTestCase { @Before public void setUp() { - overrideSetting(null); // No config. + resetAllDeviceConfigFlags(); TestableResources resources = mContext.getOrCreateTestableResources(); resources.addOverride(R.bool.config_smart_replies_in_notifications_enabled, true); resources.addOverride( @@ -58,9 +60,16 @@ public class SmartReplyConstantsTest extends SysuiTestCase { resources.addOverride( R.integer.config_smart_replies_in_notifications_min_num_system_generated_replies, 2); + resources.addOverride( + R.integer.config_smart_replies_in_notifications_max_num_actions, -1); mConstants = new SmartReplyConstants(Handler.createAsync(Looper.myLooper()), mContext); } + @After + public void tearDown() { + resetAllDeviceConfigFlags(); + } + @Test public void testIsEnabledWithNoConfig() { assertTrue(mConstants.isEnabled()); @@ -68,25 +77,25 @@ public class SmartReplyConstantsTest extends SysuiTestCase { @Test public void testIsEnabledWithInvalidConfig() { - overrideSetting("invalid config"); + overrideSetting(SystemUiDeviceConfigFlags.SSIN_ENABLED, "invalid config"); triggerConstantsOnChange(); assertTrue(mConstants.isEnabled()); } @Test public void testIsEnabledWithValidConfig() { - overrideSetting("enabled=false,max_squeeze_remeasure_attempts=5"); + overrideSetting(SystemUiDeviceConfigFlags.SSIN_ENABLED, "false"); triggerConstantsOnChange(); assertFalse(mConstants.isEnabled()); } @Test public void testRequiresTargetingPConfig() { - overrideSetting("enabled=true,requires_targeting_p=false"); + overrideSetting(SystemUiDeviceConfigFlags.SSIN_REQUIRES_TARGETING_P, "false"); triggerConstantsOnChange(); assertEquals(false, mConstants.requiresTargetingP()); - overrideSetting("enabled=true"); + overrideSetting(SystemUiDeviceConfigFlags.SSIN_REQUIRES_TARGETING_P, ""); triggerConstantsOnChange(); assertEquals(true, mConstants.requiresTargetingP()); } @@ -99,21 +108,21 @@ public class SmartReplyConstantsTest extends SysuiTestCase { @Test public void testGetMaxSqueezeRemeasureAttemptsWithInvalidConfig() { - overrideSetting("invalid config"); + overrideSetting(SystemUiDeviceConfigFlags.SSIN_MAX_SQUEEZE_REMEASURE_ATTEMPTS, + "invalid config"); triggerConstantsOnChange(); assertEquals(7, mConstants.getMaxSqueezeRemeasureAttempts()); } @Test public void testGetMaxSqueezeRemeasureAttemptsWithValidConfig() { - overrideSetting("enabled=false,max_squeeze_remeasure_attempts=5"); + overrideSetting(SystemUiDeviceConfigFlags.SSIN_MAX_SQUEEZE_REMEASURE_ATTEMPTS, "5"); triggerConstantsOnChange(); assertEquals(5, mConstants.getMaxSqueezeRemeasureAttempts()); } @Test public void testGetEffectiveEditChoicesBeforeSendingWithNoConfig() { - overrideSetting("enabled=true"); triggerConstantsOnChange(); assertFalse( mConstants.getEffectiveEditChoicesBeforeSending( @@ -128,7 +137,7 @@ public class SmartReplyConstantsTest extends SysuiTestCase { @Test public void testGetEffectiveEditChoicesBeforeSendingWithEnabledConfig() { - overrideSetting("enabled=true,edit_choices_before_sending=true"); + overrideSetting(SystemUiDeviceConfigFlags.SSIN_EDIT_CHOICES_BEFORE_SENDING, "true"); triggerConstantsOnChange(); assertTrue( mConstants.getEffectiveEditChoicesBeforeSending( @@ -143,7 +152,7 @@ public class SmartReplyConstantsTest extends SysuiTestCase { @Test public void testGetEffectiveEditChoicesBeforeSendingWithDisabledConfig() { - overrideSetting("enabled=true,edit_choices_before_sending=false"); + overrideSetting(SystemUiDeviceConfigFlags.SSIN_EDIT_CHOICES_BEFORE_SENDING, "false"); triggerConstantsOnChange(); assertFalse( mConstants.getEffectiveEditChoicesBeforeSending( @@ -164,53 +173,71 @@ public class SmartReplyConstantsTest extends SysuiTestCase { @Test public void testShowInHeadsUpEnabled() { - overrideSetting("enabled=true,show_in_heads_up=true"); + overrideSetting(SystemUiDeviceConfigFlags.SSIN_SHOW_IN_HEADS_UP, "true"); triggerConstantsOnChange(); assertTrue(mConstants.getShowInHeadsUp()); } @Test public void testShowInHeadsUpDisabled() { - overrideSetting("enabled=true,show_in_heads_up=false"); + overrideSetting(SystemUiDeviceConfigFlags.SSIN_SHOW_IN_HEADS_UP, "false"); triggerConstantsOnChange(); assertFalse(mConstants.getShowInHeadsUp()); } @Test - public void testMaxNumActionsWithNoConfig() { + public void testGetMinNumSystemGeneratedRepliesWithNoConfig() { assertTrue(mConstants.isEnabled()); - assertEquals(-1, mConstants.getMaxNumActions()); + assertEquals(2, mConstants.getMinNumSystemGeneratedReplies()); } @Test - public void testMaxNumActionsSet() { - overrideSetting("enabled=true,max_num_actions=10"); + public void testGetMinNumSystemGeneratedRepliesWithValidConfig() { + overrideSetting(SystemUiDeviceConfigFlags.SSIN_MIN_NUM_SYSTEM_GENERATED_REPLIES, "5"); triggerConstantsOnChange(); - assertEquals(10, mConstants.getMaxNumActions()); - } - - private void overrideSetting(String flags) { - Settings.Global.putString(mContext.getContentResolver(), - Settings.Global.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS, flags); + assertEquals(5, mConstants.getMinNumSystemGeneratedReplies()); } @Test - public void testGetMinNumSystemGeneratedRepliesWithNoConfig() { + public void testMaxNumActionsWithNoConfig() { assertTrue(mConstants.isEnabled()); - assertEquals(2, mConstants.getMinNumSystemGeneratedReplies()); + assertEquals(-1, mConstants.getMaxNumActions()); } @Test - public void testGetMinNumSystemGeneratedRepliesWithValidConfig() { - overrideSetting("enabled=true,min_num_system_generated_replies=5"); + public void testMaxNumActionsSet() { + overrideSetting(SystemUiDeviceConfigFlags.SSIN_MAX_NUM_ACTIONS, "10"); triggerConstantsOnChange(); - assertEquals(5, mConstants.getMinNumSystemGeneratedReplies()); + assertEquals(10, mConstants.getMaxNumActions()); + } + + private void overrideSetting(String propertyName, String value) { + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, + propertyName, value, false /* makeDefault */); } private void triggerConstantsOnChange() { - // Since Settings.Global is mocked in TestableContext, we need to manually trigger the - // content observer. - mConstants.onChange(false, - Settings.Global.getUriFor(Settings.Global.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS)); + mConstants.onDeviceConfigPropertyChanged(DeviceConfig.NAMESPACE_SYSTEMUI, + "" /* name */, "" /* value */); + } + + private void resetAllDeviceConfigFlags() { + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.SSIN_ENABLED, "", false /* makeDefault */); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.SSIN_REQUIRES_TARGETING_P, "", false /* makeDefault */); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.SSIN_MAX_SQUEEZE_REMEASURE_ATTEMPTS, "", + false /* makeDefault */); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.SSIN_EDIT_CHOICES_BEFORE_SENDING, "", + false /* makeDefault */); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.SSIN_SHOW_IN_HEADS_UP, "", false /* makeDefault */); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.SSIN_MIN_NUM_SYSTEM_GENERATED_REPLIES, "", + false /* makeDefault */); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI, + SystemUiDeviceConfigFlags.SSIN_MAX_NUM_ACTIONS, "", false /* makeDefault */); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java index 6793ecaabdd7..60b0d61558e8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java @@ -113,7 +113,7 @@ public class SmartReplyViewTest extends SysuiTestCase { mDependency.injectTestDependency(SmartReplyConstants.class, mConstants); mContainer = new View(mContext, null); - mView = SmartReplyView.inflate(mContext, null); + mView = SmartReplyView.inflate(mContext); // Any number of replies are fine. when(mConstants.getMinNumSystemGeneratedReplies()).thenReturn(0); @@ -402,17 +402,18 @@ public class SmartReplyViewTest extends SysuiTestCase { } private void setSmartReplies(CharSequence[] choices) { - setSmartReplies(choices, false /* fromAssistant */); + mView.resetSmartSuggestions(mContainer); + List<Button> replyButtons = inflateSmartReplies(choices, false /* fromAssistant */); + mView.addPreInflatedButtons(replyButtons); } - private void setSmartReplies(CharSequence[] choices, boolean fromAssistant) { + private List<Button> inflateSmartReplies(CharSequence[] choices, boolean fromAssistant) { PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(TEST_ACTION), 0); RemoteInput input = new RemoteInput.Builder(TEST_RESULT_KEY).setChoices(choices).build(); SmartReplyView.SmartReplies smartReplies = new SmartReplyView.SmartReplies(choices, input, pendingIntent, fromAssistant); - mView.resetSmartSuggestions(mContainer); - mView.addRepliesFromRemoteInput(smartReplies, mLogger, mEntry); + return mView.inflateRepliesFromRemoteInput(smartReplies, mLogger, mEntry); } private Notification.Action createAction(String actionTitle) { @@ -431,11 +432,12 @@ public class SmartReplyViewTest extends SysuiTestCase { private void setSmartActions(String[] actionTitles) { mView.resetSmartSuggestions(mContainer); - mView.addSmartActions( + List<Button> actions = mView.inflateSmartActions( new SmartReplyView.SmartActions(createActions(actionTitles), false), mLogger, mEntry, mHeadsUpManager); + mView.addPreInflatedButtons(actions); } private void setSmartRepliesAndActions(CharSequence[] choices, String[] actionTitles) { @@ -444,12 +446,14 @@ public class SmartReplyViewTest extends SysuiTestCase { private void setSmartRepliesAndActions( CharSequence[] choices, String[] actionTitles, boolean fromAssistant) { - setSmartReplies(choices, fromAssistant); - mView.addSmartActions( + mView.resetSmartSuggestions(mContainer); + List<Button> smartSuggestions = inflateSmartReplies(choices, fromAssistant); + smartSuggestions.addAll(mView.inflateSmartActions( new SmartReplyView.SmartActions(createActions(actionTitles), fromAssistant), mLogger, mEntry, - mHeadsUpManager); + mHeadsUpManager)); + mView.addPreInflatedButtons(smartSuggestions); } private ViewGroup buildExpectedView(CharSequence[] choices, int lineCount) { @@ -485,8 +489,8 @@ public class SmartReplyViewTest extends SysuiTestCase { SmartReplyView.SmartReplies smartReplies = new SmartReplyView.SmartReplies(choices, null, null, false); for (int i = 0; i < choices.length; ++i) { - Button current = mView.inflateReplyButton(mContext, mView, i, smartReplies, - null, null); + Button current = SmartReplyView.inflateReplyButton(mView, mContext, i, smartReplies, + null /* SmartReplyController */, null /* NotificationEntry */); current.setPadding(paddingHorizontal, current.getPaddingTop(), paddingHorizontal, current.getPaddingBottom()); if (previous != null) { @@ -752,7 +756,7 @@ public class SmartReplyViewTest extends SysuiTestCase { } private Button inflateActionButton(Notification.Action action) { - return mView.inflateActionButton(getContext(), mView, 0, + return SmartReplyView.inflateActionButton(mView, getContext(), 0, new SmartReplyView.SmartActions(Collections.singletonList(action), false), mLogger, mEntry, mHeadsUpManager); } diff --git a/proto/Android.bp b/proto/Android.bp index 817a54d93268..7b119a771ba4 100644 --- a/proto/Android.bp +++ b/proto/Android.bp @@ -6,6 +6,7 @@ java_library_static { }, srcs: ["src/**/*.proto"], no_framework_libs: true, + sdk_version: "9", // Pin java_version until jarjar is certified to support later versions. http://b/72703434 java_version: "1.8", target: { @@ -27,14 +28,4 @@ java_library_static { srcs: ["src/metrics_constants/metrics_constants.proto"], no_framework_libs: true, sdk_version: "system_current", - // Pin java_version until jarjar is certified to support later versions. http://b/72703434 - java_version: "1.8", - target: { - android: { - jarjar_rules: "jarjar-rules.txt", - }, - host: { - static_libs: ["libprotobuf-java-nano"], - }, - }, } diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto index 530d1156a5e4..365c6b4d16ee 100644 --- a/proto/src/metrics_constants/metrics_constants.proto +++ b/proto/src/metrics_constants/metrics_constants.proto @@ -247,6 +247,12 @@ message MetricsEvent { LOCATION_GONE = 6; // the view isn't laid out at all } + // Subtypes for profile logging + enum ActiveUserProfile { + PARENT_PROFILE = 1; + MANAGED_PROFILE = 2; + } + // Known visual elements: views or controls. enum View { // Unknown view @@ -3589,7 +3595,7 @@ message MetricsEvent { // OPEN: Settings > Apps > Default Apps > Default sms DEFAULT_SMS_PICKER = 789; - // OPEN: Settings > Apps > Default Apps > Default notification assistant + // OPEN: Settings > Apps > Notification > Notification Assistant DEFAULT_NOTIFICATION_ASSISTANT = 790; // OPEN: Settings > Apps > Default Apps > Warning dialog to confirm selection @@ -5428,7 +5434,7 @@ message MetricsEvent { FIELD_END_BATTERY_PERCENT = 1308; // ACTION: Settings > Display > Night Light - // SUBTYPE: com.android.server.display.ColorDisplayService.AutoMode value + // SUBTYPE: com.android.server.display.color.ColorDisplayService.AutoMode value // CATEGORY: SETTINGS // OS: P ACTION_NIGHT_DISPLAY_AUTO_MODE_CHANGED = 1309; @@ -6162,6 +6168,11 @@ message MetricsEvent { // OS: P FIELD_AUTOFILL_SESSION_ID = 1456; + // FIELD: Device USB overheat alarm trigger. + // CATEGORY: GLOBAL_SYSTEM_UI + // OS: P + POWER_OVERHEAT_ALARM = 1457; + // ---- End P Constants, all P constants go above this line ---- // Time since this notification last interrupted (visibly or audible) the user @@ -7002,6 +7013,58 @@ message MetricsEvent { // Different display can have different orientations, so need to log display id FIELD_DISPLAY_ID = 1660; + // ACTION: Changing from work to parent profile or vice versa + // OS: Q + ACTION_SWITCH_SHARE_PROFILE = 1661; + + // ACTION: Show Contextual homepage, log latency in loading cards + ACTION_CONTEXTUAL_HOME_SHOW = 1662; + + // ACTION: Contextual card displays + ACTION_CONTEXTUAL_CARD_SHOW = 1663; + + // ACTION: Contextual cards are eligible to be shown, but don't rank high + ACTION_CONTEXTUAL_CARD_NOT_SHOW = 1664; + + // ACTION: Settings > long press a card, and click dismiss + // Contextual card is dismissed + ACTION_CONTEXTUAL_CARD_DISMISS = 1665; + + // ACTION: Settings > click a card + // Contextual card is clicked + ACTION_CONTEXTUAL_CARD_CLICK = 1666; + + // Mapping: go/at-mapping + PAGE_ATSSI = 1667; + + PAGE_ATSII = 1668; + + PAGE_ATUS = 1669; + + PAGE_ATSSP = 1670; + + PAGE_ATSAP = 1671; + + PAGE_ATSCP = 1672; + + PAGE_ATHNP = 1673; + + ACTION_ATSG = 1674; + + ACTION_ATPG = 1675; + + ACTION_ATCLPB = 1676; + + ACTION_ATCGIB = 1677; + + ACTION_ATCPAB = 1678; + + ACTION_ATCSAUC = 1679; + + ACTION_ATCSCUC = 1680; + + ACTION_ATCHNUC = 1681; + // ---- End Q Constants, all Q constants go above this line ---- // Add new aosp constants above this line. // END OF AOSP CONSTANTS diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto index ad0ed8beb7c5..d68442fcda35 100644 --- a/proto/src/wifi.proto +++ b/proto/src/wifi.proto @@ -521,6 +521,18 @@ message WifiLog { // Total number of saved networks with mac randomization enabled. optional int32 num_saved_networks_with_mac_randomization = 138; + + // Link Probe metrics + optional LinkProbeStats link_probe_stats = 139; + + // List of NetworkSelectionExperimentDecisions stats for each experiment + repeated NetworkSelectionExperimentDecisions network_selection_experiment_decisions_list = 140; + + // Network Request API surface metrics. + optional WifiNetworkRequestApiLog wifi_network_request_api_log = 141; + + // Network Suggestion API surface metrics. + optional WifiNetworkSuggestionApiLog wifi_network_suggestion_api_log = 142; } // Information that gets logged for every WiFi connection. @@ -648,6 +660,39 @@ message ConnectionEvent { HLF_UNWANTED = 4; } + // Entity that recommended connecting to this network. + enum ConnectionNominator { + // Unknown nominator + NOMINATOR_UNKNOWN = 0; + + // User selected network manually + NOMINATOR_MANUAL = 1; + + // Saved network + NOMINATOR_SAVED = 2; + + // Suggestion API + NOMINATOR_SUGGESTION = 3; + + // Passpoint + NOMINATOR_PASSPOINT = 4; + + // Carrier suggestion + NOMINATOR_CARRIER = 5; + + // External scorer + NOMINATOR_EXTERNAL_SCORED = 6; + + // Netrec + NOMINATOR_NETREC = 7; + + // User connected choice override + NOMINATOR_SAVED_USER_CONNECT_CHOICE = 8; + + // Open Network Available Pop-up + NOMINATOR_OPEN_NETWORK_AVAILABLE = 9; + } + // Start time of the connection. optional int64 start_time_millis = 1;// [(datapol.semantic_type) = ST_TIMESTAMP]; @@ -677,6 +722,12 @@ message ConnectionEvent { // Connection is using locally generated random MAC address. optional bool use_randomized_mac = 10 [default = false]; + + // Who chose to connect. + optional ConnectionNominator connection_nominator = 11; + + // The currently running network selector when this connection event occurred. + optional int32 network_selector_experiment_id = 12; } // Number of occurrences of a specific RSSI poll rssi value @@ -1424,6 +1475,9 @@ message WifiPowerStats { // Amount of time wifi is scanning (ms) optional int64 scan_time_ms = 12; + + // Actual monitored rail energy consumed by wifi (mAh) + optional double monitored_rail_energy_consumed_mah = 13; } // Metrics for Wifi Wake @@ -1956,7 +2010,6 @@ message DeviceMobilityStatePnoScanStats { // The total duration elapsed while in this mobility state, in ms optional int64 total_duration_ms = 3; - // the total duration elapsed while in this mobility state with PNO scans running, in ms optional int64 pno_duration_ms = 4; } @@ -2095,6 +2148,8 @@ message GroupEvent { // Easy Connect (DPP) message WifiDppLog { + reserved 6; + // Number of Configurator-Initiator requests optional int32 num_dpp_configurator_initiator_requests = 1; @@ -2111,7 +2166,7 @@ message WifiDppLog { repeated DppFailureStatusHistogramBucket dpp_failure_code = 5; // Easy Connect (DPP) operation time bucket - repeated HistogramBucket dpp_operation_time = 6; + repeated HistogramBucketInt32 dpp_operation_time = 7; // Histogram bucket for Wi-Fi DPP configurator success message DppConfiguratorSuccessStatusHistogramBucket { @@ -2131,18 +2186,6 @@ message WifiDppLog { optional int32 count = 2; } - // Histogram bucket for Wi-Fi DPP logs. Range is [start, end) - message HistogramBucket { - // lower range of the bucket (inclusive) - optional int32 start = 1; - - // upper range of the bucket (exclusive) - optional int32 end = 2; - - // number of samples in the bucket - optional int32 count = 3; - } - enum DppConfiguratorSuccessCode { // Unknown success code EASY_CONNECT_EVENT_SUCCESS_UNKNOWN = 0; @@ -2208,3 +2251,135 @@ message WifiConfigStoreIO { optional int32 count = 3; } } + +// Histogram bucket counting with int32. Range is [start, end) +message HistogramBucketInt32 { + // lower range of the bucket (inclusive) + optional int32 start = 1; + + // upper range of the bucket (exclusive) + optional int32 end = 2; + + // number of samples in the bucket + optional int32 count = 3; +} + +// Single entry in a map from int32 => int32 +message MapEntryInt32Int32 { + // the key + optional int32 key = 1; + + // the value + optional int32 value = 2; +} + +message LinkProbeStats { + enum LinkProbeFailureReason { + // unknown reason + LINK_PROBE_FAILURE_REASON_UNKNOWN = 0; + + // Specified MCS rate when it is unsupported by the driver. + LINK_PROBE_FAILURE_REASON_MCS_UNSUPPORTED = 1; + + // Driver reported that no ACK was received for the transmitted probe. + LINK_PROBE_FAILURE_REASON_NO_ACK = 2; + + // Driver failed to report on the status of the transmitted probe within the timeout. + LINK_PROBE_FAILURE_REASON_TIMEOUT = 3; + + // An existing link probe is in progress. + LINK_PROBE_FAILURE_REASON_ALREADY_STARTED = 4; + } + + // Counts the number of failures for each failure reason. + message LinkProbeFailureReasonCount { + // The failure reason. + optional LinkProbeFailureReason failure_reason = 1; + + // The number of occurrences for this failure reason. + optional int32 count = 2; + } + + // Counts the occurrences of RSSI values when a link probe succeeds. + repeated MapEntryInt32Int32 success_rssi_counts = 1; + + // Counts the occurrences of RSSI values when a link probe fails. + repeated MapEntryInt32Int32 failure_rssi_counts = 2; + + // Counts the occurrences of Link Speed values when a link probe succeeds. + repeated MapEntryInt32Int32 success_link_speed_counts = 3; + + // Counts the occurrences of Link Speed values when a link probe fails. + repeated MapEntryInt32Int32 failure_link_speed_counts = 4; + + // Histogram for the number of seconds since the last TX success when a link probe succeeds. + repeated HistogramBucketInt32 success_seconds_since_last_tx_success_histogram = 5; + + // Histogram for the number of seconds since the last TX success when a link probe fails. + repeated HistogramBucketInt32 failure_seconds_since_last_tx_success_histogram = 6; + + // Histogram for the elapsed time of successful link probes, in ms. + repeated HistogramBucketInt32 success_elapsed_time_ms_histogram = 7; + + // Counts the occurrences of error codes for failed link probes. + repeated LinkProbeFailureReasonCount failure_reason_counts = 8; +} + +// Stores the decisions that were made by a experiment when compared against another experiment +message NetworkSelectionExperimentDecisions { + // the id of one experiment + optional int32 experiment1_id = 1; + + // the id of the other experiment + optional int32 experiment2_id = 2; + + // Counts occurrences of the number of network choices there were when experiment1 makes the + // same network selection as experiment2. + // The keys are the number of network choices, and the values are the number of occurrences of + // this number of network choices when exp1 and exp2 make the same network selection. + repeated MapEntryInt32Int32 same_selection_num_choices_counter = 3; + + // Counts occurrences of the number of network choices there were when experiment1 makes the + // same network selection as experiment2. + // The keys are the number of network choices, and the values are the number of occurrences of + // this number of network choices when exp1 and exp2 make different network selections. + // Note that it is possible for the network selection to be different even when there only exists + // a single network choice, since choosing not to connect to that network is a valid choice. + repeated MapEntryInt32Int32 different_selection_num_choices_counter = 4; +} + +// NetworkRequest API metrics. +message WifiNetworkRequestApiLog { + // Number of requests via this API surface. + optional int32 num_request = 1; + + // Histogram of requests via this API surface to number of networks matched in scan results. + optional HistogramBucketInt32 network_match_size_histogram = 2; + + // Number of successful network connection from this API. + optional int32 num_connect_success = 3; + + // Number of requests via this API surface that bypassed user approval. + optional int32 num_user_approval_bypass = 4; + + // Number of requests via this API surface that was rejected by the user. + optional int32 num_user_reject = 5; + + // Number of unique apps using this API surface. + optional int32 num_apps = 6; +} + +// NetworkSuggestion API metrics. +message WifiNetworkSuggestionApiLog { + // Number of modifications to their suggestions by apps. + optional int32 num_modification = 1; + + // Number of successful network connection from app suggestions. + optional int32 num_connect_success = 2; + + // Number of network connection failures from app suggestions. + optional int32 num_connect_failure = 3; + + // Histogram for size of the network lists provided by various apps on the device. + repeated HistogramBucketInt32 network_list_size_histogram = 4; +} diff --git a/services/art-profile b/services/art-profile index e7503806edfe..6de96e861388 100644 --- a/services/art-profile +++ b/services/art-profile @@ -1013,7 +1013,7 @@ HPLcom/android/server/display/AutomaticBrightnessController;->updateAmbientLux() HPLcom/android/server/display/AutomaticBrightnessController;->weightIntegral(J)F HPLcom/android/server/display/BrightnessTracker$Injector;->currentTimeMillis()J HPLcom/android/server/display/BrightnessTracker$Injector;->elapsedRealtimeNanos()J -HPLcom/android/server/display/ColorDisplayService$ColorMatrixEvaluator;->evaluate(F[F[F)[F +HPLcom/android/server/display/color/ColorDisplayService$ColorMatrixEvaluator;->evaluate(F[F[F)[F HPLcom/android/server/display/ColorFade;->draw(F)Z HPLcom/android/server/display/ColorFade;->drawFaded(FF)V HPLcom/android/server/display/DisplayManagerService$BinderService;->getDisplayIds()[I @@ -2942,7 +2942,7 @@ HSPLcom/android/server/policy/WindowManagerPolicy;->onConfigurationChanged()V HSPLcom/android/server/policy/WindowManagerPolicy;->onKeyguardOccludedChangedLw(Z)V HSPLcom/android/server/policy/WindowManagerPolicy;->onLockTaskStateChangedLw(I)V HSPLcom/android/server/policy/WindowManagerPolicy;->onSystemUiStarted()V -HSPLcom/android/server/policy/WindowManagerPolicy;->performHapticFeedbackLw(Lcom/android/server/policy/WindowManagerPolicy$WindowState;IZ)Z +HSPLcom/android/server/policy/WindowManagerPolicy;->performHapticFeedback(ILjava/lang/String;IZ)Z HSPLcom/android/server/policy/WindowManagerPolicy;->prepareAddWindowLw(Lcom/android/server/policy/WindowManagerPolicy$WindowState;Landroid/view/WindowManager$LayoutParams;)I HSPLcom/android/server/policy/WindowManagerPolicy;->registerShortcutKey(JLcom/android/internal/policy/IShortcutService;)V HSPLcom/android/server/policy/WindowManagerPolicy;->removeWindowLw(Lcom/android/server/policy/WindowManagerPolicy$WindowState;)V @@ -3413,7 +3413,7 @@ Lcom/android/server/devicepolicy/CryptoTestHelper; Lcom/android/server/devicepolicy/DevicePolicyManagerService$Lifecycle; Lcom/android/server/display/-$$Lambda$VirtualDisplayAdapter$PFyqe-aYIEBicSVtuy5lL_bT8B0; Lcom/android/server/display/AutomaticBrightnessController$Callbacks; -Lcom/android/server/display/ColorDisplayService; +Lcom/android/server/display/color/ColorDisplayService; Lcom/android/server/display/DisplayAdapter$1; Lcom/android/server/display/DisplayAdapter$2; Lcom/android/server/display/DisplayAdapter$Listener; @@ -3433,7 +3433,7 @@ Lcom/android/server/display/DisplayManagerService$SyncRoot; Lcom/android/server/display/DisplayManagerService; Lcom/android/server/display/DisplayManagerShellCommand; Lcom/android/server/display/DisplayPowerController; -Lcom/android/server/display/DisplayTransformManager; +Lcom/android/server/display/color/DisplayTransformManager; Lcom/android/server/display/LocalDisplayAdapter$DisplayModeRecord; Lcom/android/server/display/LocalDisplayAdapter$HotplugDisplayEventReceiver; Lcom/android/server/display/LocalDisplayAdapter$LocalDisplayDevice$1; @@ -10616,39 +10616,39 @@ PLcom/android/server/display/BrightnessTracker;->stopSensorListener()V PLcom/android/server/display/BrightnessTracker;->writeAmbientBrightnessStats()V PLcom/android/server/display/BrightnessTracker;->writeEvents()V PLcom/android/server/display/BrightnessTracker;->writeEventsLocked(Ljava/io/OutputStream;)V -PLcom/android/server/display/ColorDisplayService$2;-><init>(Lcom/android/server/display/ColorDisplayService;Lcom/android/server/display/DisplayTransformManager;)V -PLcom/android/server/display/ColorDisplayService$2;->onAnimationUpdate(Landroid/animation/ValueAnimator;)V -PLcom/android/server/display/ColorDisplayService$3;-><init>(Lcom/android/server/display/ColorDisplayService;Lcom/android/server/display/DisplayTransformManager;[F)V -PLcom/android/server/display/ColorDisplayService$3;->onAnimationEnd(Landroid/animation/Animator;)V -PLcom/android/server/display/ColorDisplayService$AutoMode;-><init>(Lcom/android/server/display/ColorDisplayService;)V -PLcom/android/server/display/ColorDisplayService$AutoMode;-><init>(Lcom/android/server/display/ColorDisplayService;Lcom/android/server/display/ColorDisplayService$1;)V -PLcom/android/server/display/ColorDisplayService$ColorMatrixEvaluator;-><init>()V -PLcom/android/server/display/ColorDisplayService$ColorMatrixEvaluator;-><init>(Lcom/android/server/display/ColorDisplayService$1;)V -PLcom/android/server/display/ColorDisplayService$ColorMatrixEvaluator;->evaluate(FLjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; -PLcom/android/server/display/ColorDisplayService$CustomAutoMode$1;-><init>(Lcom/android/server/display/ColorDisplayService$CustomAutoMode;Lcom/android/server/display/ColorDisplayService;)V -PLcom/android/server/display/ColorDisplayService$CustomAutoMode;-><init>(Lcom/android/server/display/ColorDisplayService;)V -PLcom/android/server/display/ColorDisplayService$CustomAutoMode;->onActivated(Z)V -PLcom/android/server/display/ColorDisplayService$CustomAutoMode;->onAlarm()V -PLcom/android/server/display/ColorDisplayService$CustomAutoMode;->onStart()V -PLcom/android/server/display/ColorDisplayService$CustomAutoMode;->updateActivated()V -PLcom/android/server/display/ColorDisplayService$CustomAutoMode;->updateNextAlarm(Ljava/lang/Boolean;Ljava/time/LocalDateTime;)V -PLcom/android/server/display/ColorDisplayService;-><init>(Landroid/content/Context;)V -PLcom/android/server/display/ColorDisplayService;->access$1000(Lcom/android/server/display/ColorDisplayService;)Ljava/lang/Boolean; -PLcom/android/server/display/ColorDisplayService;->access$602(Lcom/android/server/display/ColorDisplayService;Landroid/animation/ValueAnimator;)Landroid/animation/ValueAnimator; -PLcom/android/server/display/ColorDisplayService;->applyTint(Z)V -PLcom/android/server/display/ColorDisplayService;->getDateTimeAfter(Ljava/time/LocalTime;Ljava/time/LocalDateTime;)Ljava/time/LocalDateTime; -PLcom/android/server/display/ColorDisplayService;->getDateTimeBefore(Ljava/time/LocalTime;Ljava/time/LocalDateTime;)Ljava/time/LocalDateTime; -PLcom/android/server/display/ColorDisplayService;->isUserSetupCompleted(Landroid/content/ContentResolver;I)Z -PLcom/android/server/display/ColorDisplayService;->onActivated(Z)V -PLcom/android/server/display/ColorDisplayService;->onAutoModeChanged(I)V -PLcom/android/server/display/ColorDisplayService;->onBootPhase(I)V -PLcom/android/server/display/ColorDisplayService;->onDisplayColorModeChanged(I)V -PLcom/android/server/display/ColorDisplayService;->onStart()V -PLcom/android/server/display/ColorDisplayService;->onStartUser(I)V -PLcom/android/server/display/ColorDisplayService;->onUserChanged(I)V -PLcom/android/server/display/ColorDisplayService;->setCoefficientMatrix(Landroid/content/Context;Z)V -PLcom/android/server/display/ColorDisplayService;->setMatrix(I[F)V -PLcom/android/server/display/ColorDisplayService;->setUp()V +PLcom/android/server/display/color/ColorDisplayService$2;-><init>(Lcom/android/server/display/color/ColorDisplayService;Lcom/android/server/display/color/DisplayTransformManager;)V +PLcom/android/server/display/color/ColorDisplayService$2;->onAnimationUpdate(Landroid/animation/ValueAnimator;)V +PLcom/android/server/display/color/ColorDisplayService$3;-><init>(Lcom/android/server/display/color/ColorDisplayService;Lcom/android/server/display/color/DisplayTransformManager;[F)V +PLcom/android/server/display/color/ColorDisplayService$3;->onAnimationEnd(Landroid/animation/Animator;)V +PLcom/android/server/display/color/ColorDisplayService$AutoMode;-><init>(Lcom/android/server/display/color/ColorDisplayService;)V +PLcom/android/server/display/color/ColorDisplayService$AutoMode;-><init>(Lcom/android/server/display/color/ColorDisplayService;Lcom/android/server/display/color/ColorDisplayService$1;)V +PLcom/android/server/display/color/ColorDisplayService$ColorMatrixEvaluator;-><init>()V +PLcom/android/server/display/color/ColorDisplayService$ColorMatrixEvaluator;-><init>(Lcom/android/server/display/color/ColorDisplayService$1;)V +PLcom/android/server/display/color/ColorDisplayService$ColorMatrixEvaluator;->evaluate(FLjava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; +PLcom/android/server/display/color/ColorDisplayService$CustomAutoMode$1;-><init>(Lcom/android/server/display/color/ColorDisplayService$CustomAutoMode;Lcom/android/server/display/color/ColorDisplayService;)V +PLcom/android/server/display/color/ColorDisplayService$CustomAutoMode;-><init>(Lcom/android/server/display/color/ColorDisplayService;)V +PLcom/android/server/display/color/ColorDisplayService$CustomAutoMode;->onActivated(Z)V +PLcom/android/server/display/color/ColorDisplayService$CustomAutoMode;->onAlarm()V +PLcom/android/server/display/color/ColorDisplayService$CustomAutoMode;->onStart()V +PLcom/android/server/display/color/ColorDisplayService$CustomAutoMode;->updateActivated()V +PLcom/android/server/display/color/ColorDisplayService$CustomAutoMode;->updateNextAlarm(Ljava/lang/Boolean;Ljava/time/LocalDateTime;)V +PLcom/android/server/display/color/ColorDisplayService;-><init>(Landroid/content/Context;)V +PLcom/android/server/display/color/ColorDisplayService;->access$1000(Lcom/android/server/display/color/ColorDisplayService;)Ljava/lang/Boolean; +PLcom/android/server/display/color/ColorDisplayService;->access$602(Lcom/android/server/display/color/ColorDisplayService;Landroid/animation/ValueAnimator;)Landroid/animation/ValueAnimator; +PLcom/android/server/display/color/ColorDisplayService;->applyTint(Z)V +PLcom/android/server/display/color/ColorDisplayService;->getDateTimeAfter(Ljava/time/LocalTime;Ljava/time/LocalDateTime;)Ljava/time/LocalDateTime; +PLcom/android/server/display/color/ColorDisplayService;->getDateTimeBefore(Ljava/time/LocalTime;Ljava/time/LocalDateTime;)Ljava/time/LocalDateTime; +PLcom/android/server/display/color/ColorDisplayService;->isUserSetupCompleted(Landroid/content/ContentResolver;I)Z +PLcom/android/server/display/color/ColorDisplayService;->onActivated(Z)V +PLcom/android/server/display/color/ColorDisplayService;->onAutoModeChanged(I)V +PLcom/android/server/display/color/ColorDisplayService;->onBootPhase(I)V +PLcom/android/server/display/color/ColorDisplayService;->onDisplayColorModeChanged(I)V +PLcom/android/server/display/color/ColorDisplayService;->onStart()V +PLcom/android/server/display/color/ColorDisplayService;->onStartUser(I)V +PLcom/android/server/display/color/ColorDisplayService;->onUserChanged(I)V +PLcom/android/server/display/color/ColorDisplayService;->setCoefficientMatrix(Landroid/content/Context;Z)V +PLcom/android/server/display/color/ColorDisplayService;->setMatrix(I[F)V +PLcom/android/server/display/color/ColorDisplayService;->setUp()V PLcom/android/server/display/ColorFade$NaturalSurfaceLayout;-><init>(Landroid/hardware/display/DisplayManagerInternal;ILandroid/view/SurfaceControl;)V PLcom/android/server/display/ColorFade$NaturalSurfaceLayout;->dispose()V PLcom/android/server/display/ColorFade$NaturalSurfaceLayout;->onDisplayTransaction()V @@ -10878,17 +10878,17 @@ PLcom/android/server/display/DisplayPowerState;->setColorFadeLevel(F)V PLcom/android/server/display/DisplayPowerState;->setScreenBrightness(I)V PLcom/android/server/display/DisplayPowerState;->setScreenState(I)V PLcom/android/server/display/DisplayPowerState;->waitUntilClean(Ljava/lang/Runnable;)Z -PLcom/android/server/display/DisplayTransformManager;->applyColorMatrix([F)V -PLcom/android/server/display/DisplayTransformManager;->applySaturation(F)V -PLcom/android/server/display/DisplayTransformManager;->computeColorMatrixLocked()[F -PLcom/android/server/display/DisplayTransformManager;->getColorMatrix(I)[F -PLcom/android/server/display/DisplayTransformManager;->needsLinearColorMatrix()Z -PLcom/android/server/display/DisplayTransformManager;->needsLinearColorMatrix(I)Z -PLcom/android/server/display/DisplayTransformManager;->setColorMatrix(I[F)V -PLcom/android/server/display/DisplayTransformManager;->setColorMode(I[F)Z -PLcom/android/server/display/DisplayTransformManager;->setDaltonizerMode(I)V -PLcom/android/server/display/DisplayTransformManager;->setDisplayColor(I)V -PLcom/android/server/display/DisplayTransformManager;->updateConfiguration()V +PLcom/android/server/display/color/DisplayTransformManager;->applyColorMatrix([F)V +PLcom/android/server/display/color/DisplayTransformManager;->applySaturation(F)V +PLcom/android/server/display/color/DisplayTransformManager;->computeColorMatrixLocked()[F +PLcom/android/server/display/color/DisplayTransformManager;->getColorMatrix(I)[F +PLcom/android/server/display/color/DisplayTransformManager;->needsLinearColorMatrix()Z +PLcom/android/server/display/color/DisplayTransformManager;->needsLinearColorMatrix(I)Z +PLcom/android/server/display/color/DisplayTransformManager;->setColorMatrix(I[F)V +PLcom/android/server/display/color/DisplayTransformManager;->setColorMode(I[F)Z +PLcom/android/server/display/color/DisplayTransformManager;->setDaltonizerMode(I)V +PLcom/android/server/display/color/DisplayTransformManager;->setDisplayColor(I)V +PLcom/android/server/display/color/DisplayTransformManager;->updateConfiguration()V PLcom/android/server/display/HysteresisLevels;-><init>([I[I[I)V PLcom/android/server/display/HysteresisLevels;->getBrighteningThreshold(F)F PLcom/android/server/display/HysteresisLevels;->getDarkeningThreshold(F)F @@ -15736,7 +15736,7 @@ PLcom/android/server/policy/PhoneWindowManager;->onConfigurationChanged()V PLcom/android/server/policy/PhoneWindowManager;->onKeyguardOccludedChangedLw(Z)V PLcom/android/server/policy/PhoneWindowManager;->onOverlayChangedLw()V PLcom/android/server/policy/PhoneWindowManager;->onSystemUiStarted()V -PLcom/android/server/policy/PhoneWindowManager;->performHapticFeedbackLw(Lcom/android/server/policy/WindowManagerPolicy$WindowState;IZ)Z +PLcom/android/server/policy/PhoneWindowManager;->performHapticFeedback(ILjava/lang/String;IZ)Z PLcom/android/server/policy/PhoneWindowManager;->powerPress(JZI)V PLcom/android/server/policy/PhoneWindowManager;->prepareAddWindowLw(Lcom/android/server/policy/WindowManagerPolicy$WindowState;Landroid/view/WindowManager$LayoutParams;)I PLcom/android/server/policy/PhoneWindowManager;->readCameraLensCoverState()V @@ -18331,7 +18331,7 @@ PLcom/android/server/wm/Session;->getWindowId(Landroid/os/IBinder;)Landroid/view PLcom/android/server/wm/Session;->killSessionLocked()V PLcom/android/server/wm/Session;->onRectangleOnScreenRequested(Landroid/os/IBinder;Landroid/graphics/Rect;)V PLcom/android/server/wm/Session;->onWindowSurfaceVisibilityChanged(Lcom/android/server/wm/WindowSurfaceController;ZI)V -PLcom/android/server/wm/Session;->performHapticFeedback(Landroid/view/IWindow;IZ)Z +PLcom/android/server/wm/Session;->performHapticFeedback(IZ)Z PLcom/android/server/wm/Session;->relayout(Landroid/view/IWindow;ILandroid/view/WindowManager$LayoutParams;IIIIJLandroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/graphics/Rect;Landroid/view/DisplayCutout$ParcelableWrapper;Landroid/util/MergedConfiguration;Landroid/view/Surface;)I PLcom/android/server/wm/Session;->remove(Landroid/view/IWindow;)V PLcom/android/server/wm/Session;->sendWallpaperCommand(Landroid/os/IBinder;Ljava/lang/String;IIILandroid/os/Bundle;Z)Landroid/os/Bundle; @@ -19436,7 +19436,7 @@ SPLcom/android/server/display/DisplayManagerService;->scheduleTraversalLocked(Z) SPLcom/android/server/display/DisplayManagerService;->sendDisplayEventLocked(II)V SPLcom/android/server/display/DisplayManagerService;->updateDisplayStateLocked(Lcom/android/server/display/DisplayDevice;)Ljava/lang/Runnable; SPLcom/android/server/display/DisplayManagerService;->updateLogicalDisplaysLocked()Z -SPLcom/android/server/display/DisplayTransformManager;-><init>()V +SPLcom/android/server/display/color/DisplayTransformManager;-><init>()V SPLcom/android/server/display/LocalDisplayAdapter$DisplayModeRecord;-><init>(Landroid/view/SurfaceControl$PhysicalDisplayInfo;)V SPLcom/android/server/display/LocalDisplayAdapter$DisplayModeRecord;->hasMatchingMode(Landroid/view/SurfaceControl$PhysicalDisplayInfo;)Z SPLcom/android/server/display/LocalDisplayAdapter$HotplugDisplayEventReceiver;-><init>(Lcom/android/server/display/LocalDisplayAdapter;Landroid/os/Looper;)V diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index e0fb33799ff7..245e2c990ee3 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -30,6 +30,7 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManagerInternal; import android.app.ActivityThread; +import android.content.AutofillOptions; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; @@ -69,6 +70,7 @@ import android.view.autofill.IAutoFillManagerClient; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.infra.AbstractRemoteService; import com.android.internal.os.IResultReceiver; import com.android.internal.util.DumpUtils; import com.android.internal.util.Preconditions; @@ -108,6 +110,8 @@ public final class AutofillManagerService private static final char COMPAT_PACKAGE_URL_IDS_BLOCK_BEGIN = '['; private static final char COMPAT_PACKAGE_URL_IDS_BLOCK_END = ']'; + private static final int DEFAULT_AUGMENTED_AUTOFILL_REQUEST_TIMEOUT_MILLIS = 5_000; + /** * Maximum number of partitions that can be allowed in a session. * @@ -161,6 +165,11 @@ public final class AutofillManagerService @GuardedBy("mLock") private int mSupportedSmartSuggestionModes; + @GuardedBy("mLock") + int mAugmentedServiceIdleUnbindTimeoutMs; + @GuardedBy("mLock") + int mAugmentedServiceRequestTimeoutMs; + public AutofillManagerService(Context context) { super(context, new SecureSettingsServiceNameResolver(context, Settings.Secure.AUTOFILL_SERVICE), @@ -170,12 +179,12 @@ public final class AutofillManagerService DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_AUTOFILL, ActivityThread.currentApplication().getMainExecutor(), - (namespace, name, value) -> setSmartSuggestionModesFromDeviceConfig(value)); + (namespace, key, value) -> onDeviceConfigChange(key, value)); setLogLevelFromSettings(); setMaxPartitionsFromSettings(); setMaxVisibleDatasetsFromSettings(); - setSmartSuggestionModesFromDeviceConfig(); + setDeviceConfigProperties(); final IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); @@ -226,6 +235,18 @@ public final class AutofillManagerService } } + private void onDeviceConfigChange(@NonNull String key, @Nullable String value) { + switch (key) { + case AutofillManager.DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES: + case AutofillManager.DEVICE_CONFIG_AUGMENTED_SERVICE_IDLE_UNBIND_TIMEOUT: + case AutofillManager.DEVICE_CONFIG_AUGMENTED_SERVICE_REQUEST_TIMEOUT: + setDeviceConfigProperties(); + break; + default: + Slog.i(mTag, "Ignoring change on " + key); + } + } + @Override // from AbstractMasterSystemService protected AutofillManagerServiceImpl newServiceLocked(@UserIdInt int resolvedUserId, boolean disabled) { @@ -456,27 +477,24 @@ public final class AutofillManagerService } } - private void setSmartSuggestionModesFromDeviceConfig() { - final String value = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_AUTOFILL, - AutofillManager.DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES); - setSmartSuggestionModesFromDeviceConfig(value); - } - - private void setSmartSuggestionModesFromDeviceConfig(@Nullable String value) { - if (sDebug) Slog.d(TAG, "setSmartSuggestionEmulationFromDeviceConfig(): value=" + value); - final int flags; - if (value == null) { - flags = AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM; - } else { - try { - flags = Integer.parseInt(value); - } catch (Exception e) { - Slog.w(TAG, "setSmartSuggestionEmulationFromDeviceConfig(): NAN:" + value); - return; - } - } + private void setDeviceConfigProperties() { synchronized (mLock) { - mSupportedSmartSuggestionModes = flags; + mAugmentedServiceIdleUnbindTimeoutMs = Helper.getIntDeviceConfigProperty( + AutofillManager.DEVICE_CONFIG_AUGMENTED_SERVICE_IDLE_UNBIND_TIMEOUT, + (int) AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS); + mAugmentedServiceRequestTimeoutMs = Helper.getIntDeviceConfigProperty( + AutofillManager.DEVICE_CONFIG_AUGMENTED_SERVICE_REQUEST_TIMEOUT, + DEFAULT_AUGMENTED_AUTOFILL_REQUEST_TIMEOUT_MILLIS); + mSupportedSmartSuggestionModes = Helper.getIntDeviceConfigProperty( + AutofillManager.DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES, + AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM); + if (verbose) { + Slog.v(mTag, "setDeviceConfigProperties(): " + + "augmentedIdleTimeout=" + mAugmentedServiceIdleUnbindTimeoutMs + + ", augmentedRequestTimeout=" + mAugmentedServiceRequestTimeoutMs + + ", smartSuggestionMode=" + + getSmartSuggestionModeToString(mSupportedSmartSuggestionModes)); + } } } @@ -699,12 +717,31 @@ public final class AutofillManagerService } @Override - public boolean isCompatibilityModeRequested(@NonNull String packageName, + public AutofillOptions getAutofillOptions(@NonNull String packageName, long versionCode, @UserIdInt int userId) { - return mAutofillCompatState.isCompatibilityModeRequested( + final int loggingLevel; + if (verbose) { + loggingLevel = AutofillManager.FLAG_ADD_CLIENT_VERBOSE + | AutofillManager.FLAG_ADD_CLIENT_DEBUG; + } else if (debug) { + loggingLevel = AutofillManager.FLAG_ADD_CLIENT_DEBUG; + } else { + loggingLevel = AutofillManager.NO_LOGGING; + } + final boolean compatModeEnabled = mAutofillCompatState.isCompatibilityModeRequested( packageName, versionCode, userId); - } + final AutofillOptions options = new AutofillOptions(loggingLevel, compatModeEnabled); + synchronized (mLock) { + final AutofillManagerServiceImpl service = + getServiceForUserLocked(UserHandle.getCallingUserId()); + if (service != null) { + service.setAugmentedAutofillWhitelistLocked(options, packageName); + } + } + + return options; + } } /** @@ -1260,6 +1297,10 @@ public final class AutofillManagerService pw.print("Smart Suggestion modes: "); pw.println(getSmartSuggestionModeToString(mSupportedSmartSuggestionModes)); } + pw.print("Augmented Service Idle Unbind Timeout: "); + pw.println(mAugmentedServiceIdleUnbindTimeoutMs); + pw.print("Augmented Service Request Timeout: "); + pw.println(mAugmentedServiceRequestTimeoutMs); if (showHistory) { pw.println(); pw.println("Requests history:"); pw.println(); mRequestsHistory.reverseDump(fd, pw, args); diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index 1a6dabb49908..3f33813ff4e7 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -28,6 +28,7 @@ import android.annotation.Nullable; import android.app.ActivityManagerInternal; import android.app.ActivityTaskManager; import android.app.IActivityTaskManager; +import android.content.AutofillOptions; import android.content.ComponentName; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; @@ -97,7 +98,7 @@ final class AutofillManagerServiceImpl private static final int MAX_SESSION_ID_CREATE_TRIES = 2048; /** Minimum interval to prune abandoned sessions */ - private static final int MAX_ABANDONED_SESSION_MILLIS = 30000; + private static final int MAX_ABANDONED_SESSION_MILLIS = 30_000; private final AutoFillUI mUi; private final MetricsLogger mMetricsLogger = new MetricsLogger(); @@ -1084,8 +1085,11 @@ final class AutofillManagerServiceImpl if (remoteService != null) { remoteService.destroy(); } + mRemoteAugmentedAutofillService = null; } - }, mMaster.isInstantServiceAllowed(), mMaster.verbose); + }, mMaster.isInstantServiceAllowed(), mMaster.verbose, + mMaster.mAugmentedServiceIdleUnbindTimeoutMs, + mMaster.mAugmentedServiceRequestTimeoutMs); } return mRemoteAugmentedAutofillService; @@ -1095,14 +1099,17 @@ final class AutofillManagerServiceImpl * Called when the {@link #mAugmentedAutofillResolver} changed (among other places). */ private void updateRemoteAugmentedAutofillService(@Nullable String serviceName) { - if (serviceName == null) { - if (sVerbose) Slog.v(TAG, "updateRemoteAugmentedAutofillService(): time's up!"); - synchronized (mLock) { - if (mRemoteAugmentedAutofillService != null) { - mRemoteAugmentedAutofillService.destroy(); - mRemoteAugmentedAutofillService = null; + synchronized (mLock) { + if (mRemoteAugmentedAutofillService != null) { + if (sVerbose) { + Slog.v(TAG, "updateRemoteAugmentedAutofillService(): " + + "destroying old remote service"); } + mRemoteAugmentedAutofillService.destroy(); + mRemoteAugmentedAutofillService = null; } + + mRemoteAugmentedAutofillService = getRemoteAugmentedAutofillServiceLocked(); } } @@ -1169,6 +1176,13 @@ final class AutofillManagerServiceImpl return mWhitelistedAugmentAutofillPackages.contains(packageName); } + @GuardedBy("mLock") + void setAugmentedAutofillWhitelistLocked(@NonNull AutofillOptions options, + @NonNull String packageName) { + // TODO(b/122595322): need to setwhitelisted activities as well. + options.augmentedEnabled = mWhitelistedAugmentAutofillPackages.contains(packageName); + } + private void whitelistForAugmentedAutofillPackages(@NonNull List<String> packages) { // TODO(b/123100824): add CTS test for when it's null synchronized (mLock) { @@ -1179,6 +1193,7 @@ final class AutofillManagerServiceImpl if (mMaster.verbose) Slog.v(TAG, "whitelisting augmented packages: " + packages); mWhitelistedAugmentAutofillPackages.addAll(packages); } + mRemoteAugmentedAutofillService = getRemoteAugmentedAutofillServiceLocked(); } } diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java index 3c0da7d2d388..d300bf210e8a 100644 --- a/services/autofill/java/com/android/server/autofill/Helper.java +++ b/services/autofill/java/com/android/server/autofill/Helper.java @@ -22,9 +22,11 @@ import android.app.assist.AssistStructure; import android.app.assist.AssistStructure.ViewNode; import android.content.ComponentName; import android.metrics.LogMaker; +import android.provider.DeviceConfig; import android.service.autofill.Dataset; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.Log; import android.util.Slog; import android.view.WindowManager; import android.view.autofill.AutofillId; @@ -205,6 +207,21 @@ public final class Helper { } } + /** + * Gets the value of a device config property from the Autofill namespace. + */ + static int getIntDeviceConfigProperty(@NonNull String key, int defaultValue) { + final String value = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_AUTOFILL, key); + if (value == null) return defaultValue; + + try { + return Integer.parseInt(value); + } catch (Exception e) { + Log.w(TAG, "error parsing value (" + value + ") of property " + key + ": " + e); + return defaultValue; + } + } + private interface ViewNodeFilter { boolean matches(ViewNode node); } diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java index 9947aed8f0b4..a38c3cf9975e 100644 --- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java +++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java @@ -31,7 +31,6 @@ import android.os.SystemClock; import android.service.autofill.augmented.AugmentedAutofillService; import android.service.autofill.augmented.IAugmentedAutofillService; import android.service.autofill.augmented.IFillCallback; -import android.text.format.DateUtils; import android.util.Pair; import android.util.Slog; import android.view.autofill.AutofillId; @@ -48,13 +47,20 @@ final class RemoteAugmentedAutofillService private static final String TAG = RemoteAugmentedAutofillService.class.getSimpleName(); - private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS; + private final int mIdleUnbindTimeoutMs; + private final int mRequestTimeoutMs; RemoteAugmentedAutofillService(Context context, ComponentName serviceName, int userId, RemoteAugmentedAutofillServiceCallbacks callbacks, - boolean bindInstantServiceAllowed, boolean verbose) { + boolean bindInstantServiceAllowed, boolean verbose, int idleUnbindTimeoutMs, + int requestTimeoutMs) { super(context, AugmentedAutofillService.SERVICE_INTERFACE, serviceName, userId, callbacks, bindInstantServiceAllowed, verbose); + mIdleUnbindTimeoutMs = idleUnbindTimeoutMs; + mRequestTimeoutMs = requestTimeoutMs; + + // Bind right away. + scheduleBind(); } @Nullable @@ -82,6 +88,22 @@ final class RemoteAugmentedAutofillService return new Pair<>(serviceInfo, serviceComponent); } + @Override // from RemoteService + protected void handleOnConnectedStateChanged(boolean state) { + if (state && getTimeoutIdleBindMillis() != PERMANENT_BOUND_TIMEOUT_MS) { + scheduleUnbind(); + } + try { + if (state) { + mService.onConnected(); + } else { + mService.onDisconnected(); + } + } catch (Exception e) { + Slog.w(mTag, "Exception calling onConnectedStateChanged(" + state + "): " + e); + } + } + @Override // from AbstractRemoteService protected IAugmentedAutofillService getServiceInterface(IBinder service) { return IAugmentedAutofillService.Stub.asInterface(service); @@ -89,12 +111,12 @@ final class RemoteAugmentedAutofillService @Override // from AbstractRemoteService protected long getTimeoutIdleBindMillis() { - return PERMANENT_BOUND_TIMEOUT_MS; + return mIdleUnbindTimeoutMs; } @Override // from AbstractRemoteService protected long getRemoteRequestMillis() { - return TIMEOUT_REMOTE_REQUEST_MILLIS; + return mRequestTimeoutMs; } /** @@ -190,7 +212,7 @@ final class RemoteAugmentedAutofillService protected void onTimeout(RemoteAugmentedAutofillService remoteService) { // TODO(b/122858578): must update the logged AUTOFILL_AUGMENTED_REQUEST with the // timeout - Slog.w(TAG, "PendingAutofillRequest timed out (" + TIMEOUT_REMOTE_REQUEST_MILLIS + Slog.w(TAG, "PendingAutofillRequest timed out (" + remoteService.mRequestTimeoutMs + "ms) for " + remoteService); // NOTE: so far we don't need notify RemoteAugmentedAutofillServiceCallbacks finish(); diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 848f249d9a39..2b94d10d6dac 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -17,6 +17,7 @@ package com.android.server.autofill; import static android.service.autofill.AutofillFieldClassificationService.EXTRA_SCORES; +import static android.service.autofill.FillRequest.FLAG_AUGMENTED_AUTOFILL_REQUEST; import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST; import static android.service.autofill.FillRequest.INVALID_REQUEST_ID; import static android.view.autofill.AutofillManager.ACTION_START_SESSION; @@ -527,6 +528,16 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState */ @GuardedBy("mLock") private void requestNewFillResponseLocked(int flags) { + + if ((flags & FLAG_AUGMENTED_AUTOFILL_REQUEST) != 0) { + // TODO(b/122858578): log metrics + if (sVerbose) { + Slog.v(TAG, "requestNewFillResponse(): triggering augmented autofill instead"); + } + triggerAugmentedAutofillLocked(); + return; + } + int requestId; do { diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index 0dd1ded40ead..ffda5819927b 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -464,15 +464,25 @@ public class BackupManagerService { */ @Nullable public UserHandle getUserForAncestralSerialNumber(long ancestralSerialNumber) { - for (UserHandle handle : mContext.getSystemService(UserManager.class).getUserProfiles()) { - UserBackupManagerService userBackupManagerService = getServiceUsers().get( - handle.getIdentifier()); + int callingUserId = Binder.getCallingUserHandle().getIdentifier(); + long oldId = Binder.clearCallingIdentity(); + int[] userIds; + try { + userIds = mContext.getSystemService(UserManager.class).getProfileIds(callingUserId, + false); + } finally { + Binder.restoreCallingIdentity(oldId); + } + + for (int userId : userIds) { + UserBackupManagerService userBackupManagerService = getServiceUsers().get(userId); if (userBackupManagerService != null) { if (userBackupManagerService.getAncestralSerialNumber() == ancestralSerialNumber) { - return handle; + return UserHandle.of(userId); } } } + return null; } diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java index 45ceeda689f7..9f7a940c1d16 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java @@ -25,6 +25,7 @@ import android.annotation.UserIdInt; import android.app.ActivityManagerInternal; import android.app.ActivityThread; import android.content.ComponentName; +import android.content.ContentCaptureOptions; import android.content.ContentResolver; import android.content.Context; import android.content.pm.ActivityPresentationInfo; @@ -51,6 +52,7 @@ import android.view.contentcapture.IContentCaptureManager; import android.view.contentcapture.UserDataRemovalRequest; import com.android.internal.annotations.GuardedBy; +import com.android.internal.infra.AbstractRemoteService; import com.android.internal.os.IResultReceiver; import com.android.internal.util.DumpUtils; import com.android.internal.util.Preconditions; @@ -101,6 +103,14 @@ public final class ContentCaptureManagerService extends @Nullable private boolean mDisabledByDeviceConfig; + // Device-config settings that are cached and passed back to apps + @GuardedBy("mLock") int mDevCfgLoggingLevel; + @GuardedBy("mLock") int mDevCfgMaxBufferSize; + @GuardedBy("mLock") int mDevCfgIdleFlushingFrequencyMs; + @GuardedBy("mLock") int mDevCfgTextChangeFlushingFrequencyMs; + @GuardedBy("mLock") int mDevCfgLogHistorySize; + @GuardedBy("mLock") int mDevCfgIdleUnbindTimeoutMs; + public ContentCaptureManagerService(@NonNull Context context) { super(context, new FrameworkResourcesServiceNameResolver(context, com.android.internal.R.string.config_defaultContentCaptureService), @@ -108,16 +118,15 @@ public final class ContentCaptureManagerService extends DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_CONTENT_CAPTURE, ActivityThread.currentApplication().getMainExecutor(), (namespace, key, value) -> onDeviceConfigChange(key, value)); - setLoggingLevelFromDeviceConfig(); - setDisabledFromDeviceConfig(); + setDeviceConfigProperties(); - final int loggingSize = ContentCaptureHelper.getIntDeviceConfigProperty( - ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE, 20); - if (loggingSize > 0) { - if (debug) Slog.d(mTag, "log history size: " + loggingSize); - mRequestsHistory = new LocalLog(loggingSize); + if (mDevCfgLogHistorySize > 0) { + if (debug) Slog.d(mTag, "log history size: " + mDevCfgLogHistorySize); + mRequestsHistory = new LocalLog(mDevCfgLogHistorySize); } else { - if (debug) Slog.d(mTag, "disabled log history because size is " + loggingSize); + if (debug) { + Slog.d(mTag, "disabled log history because size is " + mDevCfgLogHistorySize); + } mRequestsHistory = null; } @@ -231,34 +240,72 @@ public final class ContentCaptureManagerService extends case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY: case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE: case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_TEXT_CHANGE_FLUSH_FREQUENCY: - // TODO(b/123096662): implement it - Slog.d(mTag, "changes on " + key + " not supported yet"); + case ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_UNBIND_TIMEOUT: + setFineTuneParamsFromDeviceConfig(); return; default: Slog.i(mTag, "Ignoring change on " + key); } } + private void setFineTuneParamsFromDeviceConfig() { + synchronized (mLock) { + mDevCfgMaxBufferSize = ContentCaptureHelper.getIntDeviceConfigProperty( + ContentCaptureManager.DEVICE_CONFIG_PROPERTY_MAX_BUFFER_SIZE, + ContentCaptureManager.DEFAULT_MAX_BUFFER_SIZE); + mDevCfgIdleFlushingFrequencyMs = ContentCaptureHelper.getIntDeviceConfigProperty( + ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY, + ContentCaptureManager.DEFAULT_IDLE_FLUSHING_FREQUENCY_MS); + mDevCfgTextChangeFlushingFrequencyMs = ContentCaptureHelper.getIntDeviceConfigProperty( + ContentCaptureManager.DEVICE_CONFIG_PROPERTY_TEXT_CHANGE_FLUSH_FREQUENCY, + ContentCaptureManager.DEFAULT_TEXT_CHANGE_FLUSHING_FREQUENCY_MS); + mDevCfgLogHistorySize = ContentCaptureHelper.getIntDeviceConfigProperty( + ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE, 20); + mDevCfgIdleUnbindTimeoutMs = ContentCaptureHelper.getIntDeviceConfigProperty( + ContentCaptureManager.DEVICE_CONFIG_PROPERTY_IDLE_UNBIND_TIMEOUT, + (int) AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS); + if (verbose) { + Slog.v(mTag, "setFineTuneParamsFromDeviceConfig(): " + + "bufferSize=" + mDevCfgMaxBufferSize + + ", idleFlush=" + mDevCfgIdleFlushingFrequencyMs + + ", textFluxh=" + mDevCfgTextChangeFlushingFrequencyMs + + ", logHistory=" + mDevCfgLogHistorySize + + ", idleUnbindTimeoutMs=" + mDevCfgIdleUnbindTimeoutMs); + } + } + } + private void setLoggingLevelFromDeviceConfig() { - ContentCaptureHelper.setLoggingLevel(); + mDevCfgLoggingLevel = ContentCaptureHelper.getIntDeviceConfigProperty( + ContentCaptureManager.DEVICE_CONFIG_PROPERTY_LOGGING_LEVEL, + ContentCaptureHelper.getDefaultLoggingLevel()); + ContentCaptureHelper.setLoggingLevel(mDevCfgLoggingLevel); verbose = ContentCaptureHelper.sVerbose; debug = ContentCaptureHelper.sDebug; + if (verbose) { + Slog.v(mTag, "setLoggingLevelFromDeviceConfig(): level=" + mDevCfgLoggingLevel + + ", debug=" + debug + ", verbose=" + verbose); + } } - private void setDisabledFromDeviceConfig() { - final String value = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_CONTENT_CAPTURE, + private void setDeviceConfigProperties() { + setLoggingLevelFromDeviceConfig(); + setFineTuneParamsFromDeviceConfig(); + final String enabled = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_CONTENT_CAPTURE, ContentCaptureManager.DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED); - setDisabledByDeviceConfig(value); + setDisabledByDeviceConfig(enabled); } - private void setDisabledByDeviceConfig(@Nullable String value) { - if (verbose) Slog.v(mTag, "setDisabledByDeviceConfig(): value=" + value); + private void setDisabledByDeviceConfig(@Nullable String explicitlyEnabled) { + if (verbose) { + Slog.v(mTag, "setDisabledByDeviceConfig(): explicitlyEnabled=" + explicitlyEnabled); + } final UserManager um = getContext().getSystemService(UserManager.class); final List<UserInfo> users = um.getUsers(); final boolean newDisabledValue; - if (value != null && value.equalsIgnoreCase("false")) { + if (explicitlyEnabled != null && explicitlyEnabled.equalsIgnoreCase("false")) { newDisabledValue = true; } else { newDisabledValue = false; @@ -423,9 +470,20 @@ public final class ContentCaptureManagerService extends protected void dumpLocked(String prefix, PrintWriter pw) { super.dumpLocked(prefix, pw); + final String prefix2 = prefix + " "; + pw.print(prefix); pw.print("Disabled users: "); pw.println(mDisabledUsers); - pw.print(prefix); pw.print("Disabled by DeviceConfig: "); - pw.println(mDisabledByDeviceConfig); + pw.print(prefix); pw.println("DeviceConfig Settings: "); + pw.print(prefix2); pw.print("disabled: "); pw.println(mDisabledByDeviceConfig); + pw.print(prefix2); pw.print("loggingLevel: "); pw.println(mDevCfgLoggingLevel); + pw.print(prefix2); pw.print("maxBufferSize: "); pw.println(mDevCfgMaxBufferSize); + pw.print(prefix2); pw.print("idleFlushingFrequencyMs: "); + pw.println(mDevCfgIdleFlushingFrequencyMs); + pw.print(prefix2); pw.print("textChangeFlushingFrequencyMs: "); + pw.println(mDevCfgTextChangeFlushingFrequencyMs); + pw.print(prefix2); pw.print("logHistorySize: "); pw.println(mDevCfgLogHistorySize); + pw.print(prefix2); pw.print("idleUnbindTimeoutMs: "); + pw.println(mDevCfgIdleUnbindTimeoutMs); } final class ContentCaptureManagerServiceStub extends IContentCaptureManager.Stub { @@ -534,6 +592,8 @@ public final class ContentCaptureManagerService extends pw.println(); mRequestsHistory.reverseDump(fd, pw, args); pw.println(); + } else { + pw.println(); } } @@ -570,5 +630,16 @@ public final class ContentCaptureManagerService extends } return false; } + + @Override + public ContentCaptureOptions getOptionsForPackage(int userId, String packageName) { + synchronized (mLock) { + final ContentCapturePerUserService service = peekServiceForUserLocked(userId); + if (service != null) { + return service.getOptionsForPackageLocked(packageName); + } + } + return null; + } } } diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java index 9e159600e42c..b33259d369a0 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java @@ -35,6 +35,7 @@ import android.app.AppGlobals; import android.app.assist.AssistContent; import android.app.assist.AssistStructure; import android.content.ComponentName; +import android.content.ContentCaptureOptions; import android.content.pm.ActivityPresentationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; @@ -125,7 +126,7 @@ final class ContentCapturePerUserService mRemoteService = new RemoteContentCaptureService(mMaster.getContext(), ContentCaptureService.SERVICE_INTERFACE, serviceComponentName, mRemoteServiceCallback, mUserId, this, mMaster.isBindInstantServiceAllowed(), - mMaster.verbose); + mMaster.verbose, mMaster.mDevCfgIdleUnbindTimeoutMs); } } @@ -408,6 +409,27 @@ final class ContentCapturePerUserService } } + @GuardedBy("mLock") + ContentCaptureOptions getOptionsForPackageLocked(@NonNull String packageName) { + if (!mWhitelistedPackages.contains(packageName)) { + if (mMaster.verbose) { + Slog.v(mTag, "getOptionsForPackage(" + packageName + "): not whitelisted"); + } + return null; + } + + // TODO(b/122595322): need to check whitelisted activities as well. + final ArraySet<ComponentName> whitelistedComponents = null; + ContentCaptureOptions options = new ContentCaptureOptions(mMaster.mDevCfgLoggingLevel, + mMaster.mDevCfgMaxBufferSize, mMaster.mDevCfgIdleFlushingFrequencyMs, + mMaster.mDevCfgTextChangeFlushingFrequencyMs, mMaster.mDevCfgLogHistorySize, + whitelistedComponents); + if (mMaster.verbose) { + Slog.v(mTag, "getOptionsForPackage(" + packageName + "): " + options); + } + return options; + } + @Override protected void dumpLocked(String prefix, PrintWriter pw) { super.dumpLocked(prefix, pw); diff --git a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java index 54eea5d8591c..dc07c0ab0c76 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java @@ -15,6 +15,9 @@ */ package com.android.server.contentcapture; +import static android.view.contentcapture.ContentCaptureHelper.sDebug; +import static android.view.contentcapture.ContentCaptureHelper.sVerbose; + import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ComponentName; @@ -23,7 +26,6 @@ import android.os.IBinder; import android.service.contentcapture.IContentCaptureService; import android.service.contentcapture.IContentCaptureServiceCallback; import android.service.contentcapture.SnapshotData; -import android.text.format.DateUtils; import android.util.Slog; import android.view.contentcapture.ContentCaptureContext; import android.view.contentcapture.UserDataRemovalRequest; @@ -35,17 +37,17 @@ final class RemoteContentCaptureService extends AbstractMultiplePendingRequestsRemoteService<RemoteContentCaptureService, IContentCaptureService> { - private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS; - private final IBinder mServerCallback; + private final int mIdleUnbindTimeoutMs; RemoteContentCaptureService(Context context, String serviceInterface, ComponentName serviceComponentName, IContentCaptureServiceCallback callback, int userId, ContentCaptureServiceCallbacks callbacks, boolean bindInstantServiceAllowed, - boolean verbose) { + boolean verbose, int idleUnbindTimeoutMs) { super(context, serviceInterface, serviceComponentName, userId, callbacks, bindInstantServiceAllowed, verbose, /* initialCapacity= */ 2); mServerCallback = callback.asBinder(); + mIdleUnbindTimeoutMs = idleUnbindTimeoutMs; // Bind right away, which will trigger a onConnected() on service's scheduleBind(); @@ -58,14 +60,7 @@ final class RemoteContentCaptureService @Override // from AbstractRemoteService protected long getTimeoutIdleBindMillis() { - // TODO(b/111276913): read from Settings so it can be changed in the field - return PERMANENT_BOUND_TIMEOUT_MS; - } - - @Override // from AbstractRemoteService - protected long getRemoteRequestMillis() { - // TODO(b/111276913): read from Settings so it can be changed in the field - return TIMEOUT_REMOTE_REQUEST_MILLIS; + return mIdleUnbindTimeoutMs; } @Override // from RemoteService @@ -75,7 +70,7 @@ final class RemoteContentCaptureService } try { if (state) { - mService.onConnected(mServerCallback); + mService.onConnected(mServerCallback, sVerbose, sDebug); } else { mService.onDisconnected(); } diff --git a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java index 385bc6cf3932..a18686da653e 100644 --- a/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java +++ b/services/contentsuggestions/java/com/android/server/contentsuggestions/ContentSuggestionsPerUserService.java @@ -80,12 +80,7 @@ public final class ContentSuggestionsPerUserService extends @Override // from PerUserSystemService protected boolean updateLocked(boolean disabled) { final boolean enabledChanged = super.updateLocked(disabled); - if (enabledChanged) { - if (!isEnabledLocked()) { - // Clear the remote service for the next call - mRemoteService = null; - } - } + updateRemoteServiceLocked(); return enabledChanged; } @@ -133,6 +128,15 @@ public final class ContentSuggestionsPerUserService extends } @GuardedBy("mLock") + private void updateRemoteServiceLocked() { + if (mRemoteService != null) { + mRemoteService.destroy(); + mRemoteService = null; + } + } + + + @GuardedBy("mLock") @Nullable private RemoteContentSuggestionsService getRemoteServiceLocked() { if (mRemoteService == null) { diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 915c131ff0fb..0d4fbb8ea6e2 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -71,6 +71,8 @@ import android.net.INetworkMonitorCallbacks; import android.net.INetworkPolicyListener; import android.net.INetworkPolicyManager; import android.net.INetworkStatsService; +import android.net.InetAddresses; +import android.net.IpPrefix; import android.net.LinkProperties; import android.net.LinkProperties.CompareResult; import android.net.MatchAllNetworkSpecifier; @@ -1060,7 +1062,8 @@ public class ConnectivityService extends IConnectivityManager.Stub handleRegisterNetworkRequest(new NetworkRequestInfo( null, networkRequest, new Binder())); } else { - handleReleaseNetworkRequest(networkRequest, Process.SYSTEM_UID); + handleReleaseNetworkRequest(networkRequest, Process.SYSTEM_UID, + /* callOnUnavailable */ false); } } @@ -1741,6 +1744,12 @@ public class ConnectivityService extends IConnectivityManager.Stub } } } + + @Override + public void onNat64PrefixEvent(int netId, boolean added, + String prefixString, int prefixLength) { + mHandler.post(() -> handleNat64PrefixEvent(netId, added, prefixString, prefixLength)); + } }; @VisibleForTesting @@ -2374,6 +2383,11 @@ public class ConnectivityService extends IConnectivityManager.Stub pw.decreaseIndent(); } + + pw.println(); + pw.println("NetworkStackClient logs:"); + pw.increaseIndent(); + NetworkStackClient.getInstance().dump(pw); } private void dumpNetworks(IndentingPrintWriter pw) { @@ -2641,11 +2655,25 @@ public class ConnectivityService extends IConnectivityManager.Stub return true; } + private boolean maybeHandleNetworkFactoryMessage(Message msg) { + switch (msg.what) { + default: + return false; + case NetworkFactory.EVENT_UNFULFILLABLE_REQUEST: { + handleReleaseNetworkRequest((NetworkRequest) msg.obj, msg.sendingUid, + /* callOnUnavailable */ true); + break; + } + } + return true; + } + @Override public void handleMessage(Message msg) { - if (!maybeHandleAsyncChannelMessage(msg) && - !maybeHandleNetworkMonitorMessage(msg) && - !maybeHandleNetworkAgentInfoMessage(msg)) { + if (!maybeHandleAsyncChannelMessage(msg) + && !maybeHandleNetworkMonitorMessage(msg) + && !maybeHandleNetworkAgentInfoMessage(msg) + && !maybeHandleNetworkFactoryMessage(msg)) { maybeHandleNetworkAgentMessage(msg); } } @@ -2763,6 +2791,29 @@ public class ConnectivityService extends IConnectivityManager.Stub handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties)); } + private void handleNat64PrefixEvent(int netId, boolean added, String prefixString, + int prefixLength) { + NetworkAgentInfo nai = mNetworkForNetId.get(netId); + if (nai == null) return; + + log(String.format("NAT64 prefix %s on netId %d: %s/%d", + (added ? "added" : "removed"), netId, prefixString, prefixLength)); + + IpPrefix prefix = null; + if (added) { + try { + prefix = new IpPrefix(InetAddresses.parseNumericAddress(prefixString), + prefixLength); + } catch (IllegalArgumentException e) { + loge("Invalid NAT64 prefix " + prefixString + "/" + prefixLength); + return; + } + } + + nai.clatd.setNat64Prefix(prefix); + handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties)); + } + private void updateLingerState(NetworkAgentInfo nai, long now) { // 1. Update the linger timer. If it's changed, reschedule or cancel the alarm. // 2. If the network was lingering and there are now requests, unlinger it. @@ -2787,6 +2838,9 @@ public class ConnectivityService extends IConnectivityManager.Stub if (mNetworkFactoryInfos.containsKey(msg.replyTo)) { if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { if (VDBG) log("NetworkFactory connected"); + // Finish setting up the full connection + mNetworkFactoryInfos.get(msg.replyTo).asyncChannel.sendMessage( + AsyncChannel.CMD_CHANNEL_FULL_CONNECTION); // A network factory has connected. Send it all current NetworkRequests. for (NetworkRequestInfo nri : mNetworkRequests.values()) { if (nri.request.isListen()) continue; @@ -2887,7 +2941,7 @@ public class ConnectivityService extends IConnectivityManager.Stub e.rethrowFromSystemServer(); } mNetworkAgentInfos.remove(nai.messenger); - nai.maybeStopClat(); + nai.clatd.update(); synchronized (mNetworkForNetId) { // Remove the NetworkAgent, but don't mark the netId as // available until we've told netd to delete it below. @@ -2957,7 +3011,8 @@ public class ConnectivityService extends IConnectivityManager.Stub if (existingRequest != null) { // remove the existing request. if (DBG) log("Replacing " + existingRequest.request + " with " + nri.request + " because their intents matched."); - handleReleaseNetworkRequest(existingRequest.request, getCallingUid()); + handleReleaseNetworkRequest(existingRequest.request, getCallingUid(), + /* callOnUnavailable */ false); } handleRegisterNetworkRequest(nri); } @@ -2983,7 +3038,7 @@ public class ConnectivityService extends IConnectivityManager.Stub int callingUid) { NetworkRequestInfo nri = findExistingNetworkRequestInfo(pendingIntent); if (nri != null) { - handleReleaseNetworkRequest(nri.request, callingUid); + handleReleaseNetworkRequest(nri.request, callingUid, /* callOnUnavailable */ false); } } @@ -3066,7 +3121,8 @@ public class ConnectivityService extends IConnectivityManager.Stub callCallbackForRequest(nri, null, ConnectivityManager.CALLBACK_UNAVAIL, 0); } - private void handleReleaseNetworkRequest(NetworkRequest request, int callingUid) { + private void handleReleaseNetworkRequest(NetworkRequest request, int callingUid, + boolean callOnUnavailable) { final NetworkRequestInfo nri = getNriForAppRequest(request, callingUid, "release NetworkRequest"); if (nri == null) { @@ -3076,6 +3132,9 @@ public class ConnectivityService extends IConnectivityManager.Stub log("releasing " + nri.request + " (release request)"); } handleRemoveNetworkRequest(nri); + if (callOnUnavailable) { + callCallbackForRequest(nri, null, ConnectivityManager.CALLBACK_UNAVAIL, 0); + } } private void handleRemoveNetworkRequest(final NetworkRequestInfo nri) { @@ -3507,7 +3566,8 @@ public class ConnectivityService extends IConnectivityManager.Stub break; } case EVENT_RELEASE_NETWORK_REQUEST: { - handleReleaseNetworkRequest((NetworkRequest) msg.obj, msg.arg1); + handleReleaseNetworkRequest((NetworkRequest) msg.obj, msg.arg1, + /* callOnUnavailable */ false); break; } case EVENT_SET_ACCEPT_UNVALIDATED: { @@ -5227,11 +5287,10 @@ public class ConnectivityService extends IConnectivityManager.Stub LinkProperties oldLp) { int netId = networkAgent.network.netId; - // The NetworkAgentInfo does not know whether clatd is running on its network or not. Before - // we do anything else, make sure its LinkProperties are accurate. - if (networkAgent.clatd != null) { - networkAgent.clatd.fixupLinkProperties(oldLp, newLp); - } + // The NetworkAgentInfo does not know whether clatd is running on its network or not, or + // whether there is a NAT64 prefix. Before we do anything else, make sure its LinkProperties + // are accurate. + networkAgent.clatd.fixupLinkProperties(oldLp, newLp); updateInterfaces(newLp, oldLp, netId, networkAgent.networkCapabilities); updateMtu(newLp, oldLp); @@ -5261,8 +5320,8 @@ public class ConnectivityService extends IConnectivityManager.Stub synchronized (networkAgent) { networkAgent.linkProperties = newLp; } - // Start or stop clat accordingly to network state. - networkAgent.updateClat(mNMS); + // Start or stop DNS64 detection and 464xlat according to network state. + networkAgent.clatd.update(); notifyIfacesChangedForNetworkStats(); if (networkAgent.everConnected) { try { diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags index 0ed5beb57f82..4d39f9ab8d78 100644 --- a/services/core/java/com/android/server/EventLogTags.logtags +++ b/services/core/java/com/android/server/EventLogTags.logtags @@ -145,7 +145,7 @@ option java_package com.android.server # --------------------------- # SystemServer.run() starts: 3010 boot_progress_system_run (time|2|3) - +3011 system_server_start (start_count|1),(uptime|2|3),(elapse_time|2|3) # --------------------------- # PackageManagerService.java diff --git a/services/core/java/com/android/server/ExtconUEventObserver.java b/services/core/java/com/android/server/ExtconUEventObserver.java index 775e4c8cf4ed..6b42b3d06d4b 100644 --- a/services/core/java/com/android/server/ExtconUEventObserver.java +++ b/services/core/java/com/android/server/ExtconUEventObserver.java @@ -162,15 +162,6 @@ public abstract class ExtconUEventObserver extends UEventObserver { /** Does the {@link /sys/class/extcon} directory exist */ public static boolean extconExists() { File extconDir = new File("/sys/class/extcon"); - boolean retVal = extconDir.exists() && extconDir.isDirectory(); - // TODO(b/124364409): return the correct value after selinux policy is updated. - if (retVal) { - Slog.w(TAG, extconDir + " exists " + extconDir.exists() + " isDir " - + extconDir.isDirectory() - + " but reporting it does not exist until selinux policies are updated." - + " see b/124364409" - ); - } - return false; + return extconDir.exists() && extconDir.isDirectory(); } } diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index 5989a46c5a0a..d7cc19b6dbd4 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -337,7 +337,7 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void onChange(boolean selfChange) { synchronized (mLock) { - onProviderAllowedChangedLocked(true); + onProviderAllowedChangedLocked(); } } }, UserHandle.USER_ALL); @@ -436,6 +436,10 @@ public class LocationManagerService extends ILocationManager.Stub { @GuardedBy("mLock") private void onLocationModeChangedLocked(boolean broadcast) { + if (D) { + Log.d(TAG, "location enabled is now " + isLocationEnabled()); + } + for (LocationProvider p : mProviders) { p.onLocationModeChangedLocked(); } @@ -448,16 +452,10 @@ public class LocationManagerService extends ILocationManager.Stub { } @GuardedBy("mLock") - private void onProviderAllowedChangedLocked(boolean broadcast) { + private void onProviderAllowedChangedLocked() { for (LocationProvider p : mProviders) { p.onAllowedChangedLocked(); } - - if (broadcast) { - mContext.sendBroadcastAsUser( - new Intent(LocationManager.PROVIDERS_CHANGED_ACTION), - UserHandle.ALL); - } } @GuardedBy("mLock") @@ -827,6 +825,10 @@ public class LocationManagerService extends ILocationManager.Stub { return; } + if (D) { + Log.d(TAG, "foreground user is changing to " + userId); + } + // let providers know the current user is on the way out before changing the user for (LocationProvider p : mProviders) { p.onUserChangingLocked(); @@ -839,7 +841,12 @@ public class LocationManagerService extends ILocationManager.Stub { // if the user changes, per-user settings may also have changed onLocationModeChangedLocked(false); - onProviderAllowedChangedLocked(false); + onProviderAllowedChangedLocked(); + + // always force useability to be rechecked, even if no per-user settings have changed + for (LocationProvider p : mProviders) { + p.onUseableChangedLocked(false); + } } private class LocationProvider implements AbstractLocationProvider.LocationProviderManager { @@ -891,9 +898,13 @@ public class LocationManagerService extends ILocationManager.Stub { public void attachLocked(AbstractLocationProvider provider) { checkNotNull(provider); checkState(mProvider == null); - mProvider = provider; - onUseableChangedLocked(); + if (D) { + Log.d(TAG, mName + " provider attached"); + } + + mProvider = provider; + onUseableChangedLocked(false); } public String getName() { @@ -1054,26 +1065,12 @@ public class LocationManagerService extends ILocationManager.Stub { return; } - mEnabled = enabled; - - // update provider allowed settings to reflect enabled status - if (mIsManagedBySettings) { - if (mEnabled && !mAllowed) { - Settings.Secure.putStringForUser( - mContext.getContentResolver(), - Settings.Secure.LOCATION_PROVIDERS_ALLOWED, - "+" + mName, - mCurrentUserId); - } else if (!mEnabled && mAllowed) { - Settings.Secure.putStringForUser( - mContext.getContentResolver(), - Settings.Secure.LOCATION_PROVIDERS_ALLOWED, - "-" + mName, - mCurrentUserId); - } + if (D) { + Log.d(TAG, mName + " provider enabled is now " + mEnabled); } - onUseableChangedLocked(); + mEnabled = enabled; + onUseableChangedLocked(false); } }); } @@ -1091,41 +1088,28 @@ public class LocationManagerService extends ILocationManager.Stub { @GuardedBy("mLock") public void onLocationModeChangedLocked() { - onUseableChangedLocked(); - } - - private boolean isAllowed() { - return isAllowedForUser(mCurrentUserId); - } - - private boolean isAllowedForUser(int userId) { - String allowedProviders = Settings.Secure.getStringForUser( - mContext.getContentResolver(), - Settings.Secure.LOCATION_PROVIDERS_ALLOWED, - userId); - return TextUtils.delimitedStringContains(allowedProviders, ',', mName); + onUseableChangedLocked(false); } @GuardedBy("mLock") public void onAllowedChangedLocked() { if (mIsManagedBySettings) { - boolean allowed = isAllowed(); + String allowedProviders = Settings.Secure.getStringForUser( + mContext.getContentResolver(), + Settings.Secure.LOCATION_PROVIDERS_ALLOWED, + mCurrentUserId); + boolean allowed = TextUtils.delimitedStringContains(allowedProviders, ',', mName); + if (allowed == mAllowed) { return; } - mAllowed = allowed; - // make a best effort to keep the setting matching the real enabled state of the - // provider so that legacy applications aren't broken. - if (mAllowed && !mEnabled) { - Settings.Secure.putStringForUser( - mContext.getContentResolver(), - Settings.Secure.LOCATION_PROVIDERS_ALLOWED, - "-" + mName, - mCurrentUserId); + if (D) { + Log.d(TAG, mName + " provider allowed is now " + mAllowed); } - onUseableChangedLocked(); + mAllowed = allowed; + onUseableChangedLocked(true); } } @@ -1140,17 +1124,49 @@ public class LocationManagerService extends ILocationManager.Stub { } @GuardedBy("mLock") - public void onUseableChangedLocked() { + private boolean isUseableIgnoringAllowedLocked() { + return mProvider != null && mProviders.contains(this) && isLocationEnabled() + && mEnabled; + } + + @GuardedBy("mLock") + public void onUseableChangedLocked(boolean isAllowedChanged) { // if any property that contributes to "useability" here changes state, it MUST result // in a direct or indrect call to onUseableChangedLocked. this allows the provider to // guarantee that it will always eventually reach the correct state. - boolean useable = mProvider != null - && mProviders.contains(this) && isLocationEnabled() && mAllowed && mEnabled; + boolean useableIgnoringAllowed = isUseableIgnoringAllowedLocked(); + boolean useable = useableIgnoringAllowed && mAllowed; + + // update deprecated provider allowed settings for backwards compatibility, and do this + // even if there is no change in overall useability state. this may result in trying to + // overwrite the same value, but Settings handles deduping this. + if (mIsManagedBySettings) { + // a "-" change derived from the allowed setting should not be overwritten, but a + // "+" change should be corrected if necessary + if (useableIgnoringAllowed && !isAllowedChanged) { + Settings.Secure.putStringForUser( + mContext.getContentResolver(), + Settings.Secure.LOCATION_PROVIDERS_ALLOWED, + "+" + mName, + mCurrentUserId); + } else if (!useableIgnoringAllowed) { + Settings.Secure.putStringForUser( + mContext.getContentResolver(), + Settings.Secure.LOCATION_PROVIDERS_ALLOWED, + "-" + mName, + mCurrentUserId); + } + } + if (useable == mUseable) { return; } mUseable = useable; + if (D) { + Log.d(TAG, mName + " provider useable is now " + mUseable); + } + if (!mUseable) { // If any provider has been disabled, clear all last locations for all // providers. This is to be on the safe side in case a provider has location @@ -1160,6 +1176,10 @@ public class LocationManagerService extends ILocationManager.Stub { } updateProviderUseableLocked(this); + + mContext.sendBroadcastAsUser( + new Intent(LocationManager.PROVIDERS_CHANGED_ACTION), + UserHandle.ALL); } @GuardedBy("mLock") @@ -1720,7 +1740,7 @@ public class LocationManagerService extends ILocationManager.Stub { mProviders.add(provider); provider.onAllowedChangedLocked(); // allowed state may change while provider was inactive - provider.onUseableChangedLocked(); + provider.onUseableChangedLocked(false); } @GuardedBy("mLock") @@ -1728,7 +1748,7 @@ public class LocationManagerService extends ILocationManager.Stub { if (mProviders.remove(provider)) { long identity = Binder.clearCallingIdentity(); try { - provider.onUseableChangedLocked(); + provider.onUseableChangedLocked(false); } finally { Binder.restoreCallingIdentity(identity); } @@ -1972,7 +1992,7 @@ public class LocationManagerService extends ILocationManager.Stub { continue; } - // requests that ignore location settings will never provider notifications + // requests that ignore location settings will never provide notifications if (isSettingsExemptLocked(record)) { continue; } @@ -2010,19 +2030,22 @@ public class LocationManagerService extends ILocationManager.Stub { WorkSource worksource = new WorkSource(); ProviderRequest providerRequest = new ProviderRequest(); - long backgroundThrottleInterval; + if (records != null && !records.isEmpty()) { + long backgroundThrottleInterval; - long identity = Binder.clearCallingIdentity(); - try { - backgroundThrottleInterval = Settings.Global.getLong( - mContext.getContentResolver(), - Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS, - DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS); - } finally { - Binder.restoreCallingIdentity(identity); - } + long identity = Binder.clearCallingIdentity(); + try { + backgroundThrottleInterval = Settings.Global.getLong( + mContext.getContentResolver(), + Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS, + DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS); + } finally { + Binder.restoreCallingIdentity(identity); + } - if (records != null && !records.isEmpty()) { + final boolean isForegroundOnlyMode = + mPowerManager.getLocationPowerSaveMode() + == PowerManager.LOCATION_MODE_FOREGROUND_ONLY; // initialize the low power mode to true and set to false if any of the records requires providerRequest.lowPowerMode = true; for (UpdateRecord record : records) { @@ -2037,7 +2060,9 @@ public class LocationManagerService extends ILocationManager.Stub { record.mReceiver.mAllowedResolutionLevel)) { continue; } - if (!provider.isUseableLocked()) { + final boolean isBatterySaverDisablingLocation = + isForegroundOnlyMode && !record.mIsForegroundUid; + if (!provider.isUseableLocked() || isBatterySaverDisablingLocation) { if (isSettingsExemptLocked(record)) { providerRequest.locationSettingsIgnored = true; providerRequest.lowPowerMode = false; @@ -2108,7 +2133,6 @@ public class LocationManagerService extends ILocationManager.Stub { } } - if (D) Log.d(TAG, "provider request: " + provider + " " + providerRequest); provider.setRequestLocked(providerRequest, worksource); } @@ -2502,7 +2526,6 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public Location getLastLocation(LocationRequest r, String packageName) { - if (D) Log.d(TAG, "getLastLocation: " + r); synchronized (mLock) { LocationRequest request = r != null ? r : DEFAULT_LOCATION_REQUEST; int allowedResolutionLevel = getCallerAllowedResolutionLevel(); @@ -2638,33 +2661,33 @@ public class LocationManagerService extends ILocationManager.Stub { synchronized (mLock) { checkResolutionLevelIsSufficientForProviderUseLocked(allowedResolutionLevel, request.getProvider()); - // Require that caller can manage given document - boolean callerHasLocationHardwarePermission = - mContext.checkCallingPermission(android.Manifest.permission.LOCATION_HARDWARE) - == PERMISSION_GRANTED; - LocationRequest sanitizedRequest = createSanitizedRequest(request, - allowedResolutionLevel, - callerHasLocationHardwarePermission); + } + // Require that caller can manage given document + boolean callerHasLocationHardwarePermission = + mContext.checkCallingPermission(android.Manifest.permission.LOCATION_HARDWARE) + == PERMISSION_GRANTED; + LocationRequest sanitizedRequest = createSanitizedRequest(request, + allowedResolutionLevel, + callerHasLocationHardwarePermission); - if (D) { - Log.d(TAG, "requestGeofence: " + sanitizedRequest + " " + geofence + " " + intent); - } + if (D) { + Log.d(TAG, "requestGeofence: " + sanitizedRequest + " " + geofence + " " + intent); + } - // geo-fence manager uses the public location API, need to clear identity - int uid = Binder.getCallingUid(); - if (UserHandle.getUserId(uid) != UserHandle.USER_SYSTEM) { - // temporary measure until geofences work for secondary users - Log.w(TAG, "proximity alerts are currently available only to the primary user"); - return; - } - long identity = Binder.clearCallingIdentity(); - try { - mGeofenceManager.addFence(sanitizedRequest, geofence, intent, - allowedResolutionLevel, - uid, packageName); - } finally { - Binder.restoreCallingIdentity(identity); - } + // geo-fence manager uses the public location API, need to clear identity + int uid = Binder.getCallingUid(); + if (UserHandle.getUserId(uid) != UserHandle.USER_SYSTEM) { + // temporary measure until geofences work for secondary users + Log.w(TAG, "proximity alerts are currently available only to the primary user"); + return; + } + long identity = Binder.clearCallingIdentity(); + try { + mGeofenceManager.addFence(sanitizedRequest, geofence, intent, + allowedResolutionLevel, + uid, packageName); + } finally { + Binder.restoreCallingIdentity(identity); } } diff --git a/services/core/java/com/android/server/LooperStatsService.java b/services/core/java/com/android/server/LooperStatsService.java index c334540f48cc..b423f62e9b6c 100644 --- a/services/core/java/com/android/server/LooperStatsService.java +++ b/services/core/java/com/android/server/LooperStatsService.java @@ -54,7 +54,7 @@ public class LooperStatsService extends Binder { private static final String SETTINGS_TRACK_SCREEN_INTERACTIVE_KEY = "track_screen_state"; private static final String DEBUG_SYS_LOOPER_STATS_ENABLED = "debug.sys.looper_stats_enabled"; - private static final int DEFAULT_SAMPLING_INTERVAL = 100; + private static final int DEFAULT_SAMPLING_INTERVAL = 1000; private static final int DEFAULT_ENTRIES_SIZE_CAP = 2000; private static final boolean DEFAULT_ENABLED = true; private static final boolean DEFAULT_TRACK_SCREEN_INTERACTIVE = false; diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java index 526aebe2e3e3..097a7d614ab6 100644 --- a/services/core/java/com/android/server/PinnerService.java +++ b/services/core/java/com/android/server/PinnerService.java @@ -42,6 +42,7 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.RemoteException; +import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.provider.MediaStore; @@ -235,9 +236,17 @@ public final class PinnerService extends SystemService { * Handler for on start pinning message */ private void handlePinOnStart() { - // Files to pin come from the overlay and can be specified per-device config - String[] filesToPin = mContext.getResources().getStringArray( - com.android.internal.R.array.config_defaultPinnerServiceFiles); + final String bootImage = SystemProperties.get("dalvik.vm.boot-image", ""); + String[] filesToPin = null; + if (bootImage.endsWith("apex.art")) { + // Use the files listed for that specific boot image + filesToPin = mContext.getResources().getStringArray( + com.android.internal.R.array.config_apexBootImagePinnerServiceFiles); + } else { + // Files to pin come from the overlay and can be specified per-device config + filesToPin = mContext.getResources().getStringArray( + com.android.internal.R.array.config_defaultPinnerServiceFiles); + } // Continue trying to pin each file even if we fail to pin some of them for (String fileToPin : filesToPin) { PinnedFile pf = pinFile(fileToPin, diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index cd9d84cca250..98f260ce7a68 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -205,6 +205,9 @@ class StorageManagerService extends IStorageManager.Stub private static final boolean ENABLE_ISOLATED_STORAGE = StorageManager.hasIsolatedStorage(); + private static final boolean ENABLE_LEGACY_GREYLIST = SystemProperties + .getBoolean(StorageManager.PROP_LEGACY_GREYLIST, true); + public static class Lifecycle extends SystemService { private StorageManagerService mStorageManagerService; @@ -347,12 +350,6 @@ class StorageManagerService extends IStorageManager.Stub @GuardedBy("mPackagesLock") private final SparseArray<ArraySet<String>> mPackages = new SparseArray<>(); - /** - * List of volumes visible to any user. - * TODO: may be have a map of userId -> volumes? - */ - private final CopyOnWriteArrayList<VolumeInfo> mVisibleVols = new CopyOnWriteArrayList<>(); - private volatile int mCurrentUserId = UserHandle.USER_SYSTEM; /** Holding lock for AppFuse business */ @@ -956,8 +953,6 @@ class StorageManagerService extends IStorageManager.Stub addInternalVolumeLocked(); } - mVisibleVols.clear(); - try { mVold.reset(); @@ -1895,9 +1890,6 @@ class StorageManagerService extends IStorageManager.Stub private void mount(VolumeInfo vol) { try { mVold.mount(vol.id, vol.mountFlags, vol.mountUserId); - if ((vol.mountFlags & VolumeInfo.MOUNT_FLAG_VISIBLE) != 0) { - mVisibleVols.add(vol); - } } catch (Exception e) { Slog.wtf(TAG, e); } @@ -1914,9 +1906,6 @@ class StorageManagerService extends IStorageManager.Stub private void unmount(VolumeInfo vol) { try { mVold.unmount(vol.id); - if ((vol.mountFlags & VolumeInfo.MOUNT_FLAG_VISIBLE) != 0) { - mVisibleVols.remove(vol); - } } catch (Exception e) { Slog.wtf(TAG, e); } @@ -2303,7 +2292,26 @@ class StorageManagerService extends IStorageManager.Stub refreshIsolatedStorageSettings(); // Perform hard reboot to kick policy into place - mContext.getSystemService(PowerManager.class).reboot(null); + mHandler.post(() -> { + mContext.getSystemService(PowerManager.class).reboot(null); + }); + } finally { + Binder.restoreCallingIdentity(token); + } + } + + if ((mask & StorageManager.DEBUG_LEGACY_GREYLIST) != 0) { + final boolean enabled = (flags & StorageManager.DEBUG_LEGACY_GREYLIST) != 0; + + final long token = Binder.clearCallingIdentity(); + try { + SystemProperties.set(StorageManager.PROP_LEGACY_GREYLIST, + Boolean.toString(enabled)); + + // Perform hard reboot to kick policy into place + mHandler.post(() -> { + mContext.getSystemService(PowerManager.class).reboot(null); + }); } finally { Binder.restoreCallingIdentity(token); } @@ -2653,8 +2661,8 @@ class StorageManagerService extends IStorageManager.Stub mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, "no permission to access the crypt keeper"); - if (StorageManager.isFileEncryptedNativeOnly()) { - // Not supported on FBE devices + if (!StorageManager.isBlockEncrypted()) { + // Only supported on FDE devices return; } @@ -2677,8 +2685,8 @@ class StorageManagerService extends IStorageManager.Stub mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, "no permission to access the crypt keeper"); - if (StorageManager.isFileEncryptedNativeOnly()) { - // Not supported on FBE devices + if (!StorageManager.isBlockEncrypted()) { + // Only supported on FDE devices return null; } @@ -2811,6 +2819,7 @@ class StorageManagerService extends IStorageManager.Stub @Override public void unlockUserKey(int userId, int serialNumber, byte[] token, byte[] secret) { + Slog.d(TAG, "unlockUserKey: " + userId); enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); if (StorageManager.isFileEncryptedNativeOrEmulated()) { @@ -3688,16 +3697,17 @@ class StorageManagerService extends IStorageManager.Stub } else if (mPmInternal.isInstantApp(packageName, UserHandle.getUserId(uid))) { return Zygote.MOUNT_EXTERNAL_NONE; } else { - // STOPSHIP: remove this temporary workaround once developers - // fix bugs where they're opening _data paths in native code - switch (packageName) { - case "com.facebook.katana": // b/123996076 - case "jp.naver.line.android": // b/124767356 - case "com.mxtech.videoplayer.ad": // b/124531483 - return Zygote.MOUNT_EXTERNAL_LEGACY; - default: - return Zygote.MOUNT_EXTERNAL_WRITE; + if (ENABLE_LEGACY_GREYLIST) { + // STOPSHIP: remove this temporary workaround once developers + // fix bugs where they're opening _data paths in native code + switch (packageName) { + case "com.facebook.katana": // b/123996076 + case "jp.naver.line.android": // b/124767356 + case "com.mxtech.videoplayer.ad": // b/124531483 + return Zygote.MOUNT_EXTERNAL_LEGACY; + } } + return Zygote.MOUNT_EXTERNAL_WRITE; } } catch (RemoteException e) { // Should not happen @@ -3850,14 +3860,6 @@ class StorageManagerService extends IStorageManager.Stub pw.decreaseIndent(); pw.println(); - pw.println("mVisibleVols:"); - pw.increaseIndent(); - for (int i = 0; i < mVisibleVols.size(); i++) { - mVisibleVols.get(i).dump(pw); - } - pw.decreaseIndent(); - - pw.println(); pw.println("Primary storage UUID: " + mPrimaryStorageUuid); pw.println(); @@ -4055,33 +4057,9 @@ class StorageManagerService extends IStorageManager.Stub } @Override - public String[] getVisibleVolumesForUser(int userId) { - final ArrayList<String> visibleVolsForUser = new ArrayList<>(); - for (int i = mVisibleVols.size() - 1; i >= 0; --i) { - final VolumeInfo vol = mVisibleVols.get(i); - if (vol.isVisibleForUser(userId)) { - visibleVolsForUser.add(getVolumeLabel(vol)); - } - } - return visibleVolsForUser.toArray(new String[visibleVolsForUser.size()]); - } - - @Override public String getSandboxId(String packageName) { return StorageManagerService.this.getSandboxId(packageName, mPmInternal.getSharedUserIdForPackage(packageName)); } - - private String getVolumeLabel(VolumeInfo vol) { - // STOPSHIP: Label needs to part of VolumeInfo and need to be passed on from vold - switch (vol.getType()) { - case VolumeInfo.TYPE_EMULATED: - return "emulated"; - case VolumeInfo.TYPE_PUBLIC: - return vol.fsUuid == null ? vol.id : vol.fsUuid; - default: - return null; - } - } } } diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index fd946cdfa48a..5633082ce4b3 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -177,8 +177,6 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private ServiceState[] mServiceState; - private int[] mNetworkType; - private int[] mVoiceActivationState; private int[] mDataActivationState; @@ -213,6 +211,9 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private CallAttributes mCallAttributes = new CallAttributes(new PreciseCallState(), TelephonyManager.NETWORK_TYPE_UNKNOWN, new CallQuality()); + // network type of the call associated with the mCallAttributes and mCallQuality + private int mCallNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN; + private int[] mSrvccState; private int mDefaultSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; @@ -375,7 +376,6 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mDataConnectionNetworkType = new int[numPhones]; mCallIncomingNumber = new String[numPhones]; mServiceState = new ServiceState[numPhones]; - mNetworkType = new int[numPhones]; mVoiceActivationState = new int[numPhones]; mDataActivationState = new int[numPhones]; mUserMobileDataState = new boolean[numPhones]; @@ -396,7 +396,6 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mDataActivationState[i] = TelephonyManager.SIM_ACTIVATION_STATE_UNKNOWN; mCallIncomingNumber[i] = ""; mServiceState[i] = new ServiceState(); - mNetworkType[i] = mServiceState[i].getVoiceNetworkType(); mSignalStrength[i] = new SignalStrength(); mUserMobileDataState[i] = false; mMessageWaiting[i] = false; @@ -998,21 +997,6 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (validatePhoneId(phoneId)) { mServiceState[phoneId] = state; - boolean notifyCallAttributes = true; - if (mNetworkType[phoneId] != mServiceState[phoneId].getVoiceNetworkType()) { - mNetworkType[phoneId] = state.getVoiceNetworkType(); - mCallAttributes = new CallAttributes(mPreciseCallState, mNetworkType[phoneId], - mCallQuality); - } else { - // No change to network type, so no need to notify call attributes - notifyCallAttributes = false; - } - - if (mCallQuality == null) { - // No call quality reported yet, so no need to notify call attributes - notifyCallAttributes = false; - } - for (Record r : mRecords) { if (VDBG) { log("notifyServiceStateForSubscriber: r=" + r + " subId=" + subId @@ -1040,14 +1024,6 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mRemoveList.add(r.binder); } } - if (notifyCallAttributes && r.matchPhoneStateListenerEvent( - PhoneStateListener.LISTEN_CALL_ATTRIBUTES_CHANGED)) { - try { - r.callback.onCallAttributesChanged(mCallAttributes); - } catch (RemoteException ex) { - mRemoveList.add(r.binder); - } - } } } else { log("notifyServiceStateForSubscriber: INVALID phoneId=" + phoneId); @@ -1574,7 +1550,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { log("notifyPreciseCallState: mCallQuality is null, skipping call attributes"); notifyCallAttributes = false; } else { - mCallAttributes = new CallAttributes(mPreciseCallState, mNetworkType[phoneId], + mCallAttributes = new CallAttributes(mPreciseCallState, mCallNetworkType, mCallQuality); } @@ -1840,16 +1816,16 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } @Override - public void notifyCallQualityChanged(CallQuality callQuality, int phoneId) { + public void notifyCallQualityChanged(CallQuality callQuality, int phoneId, + int callNetworkType) { if (!checkNotifyPermission("notifyCallQualityChanged()")) { return; } // merge CallQuality with PreciseCallState and network type mCallQuality = callQuality; - mCallAttributes = new CallAttributes(mPreciseCallState, - mNetworkType[phoneId], - callQuality); + mCallNetworkType = callNetworkType; + mCallAttributes = new CallAttributes(mPreciseCallState, callNetworkType, callQuality); synchronized (mRecords) { TelephonyManager tm = (TelephonyManager) mContext.getSystemService( @@ -1886,7 +1862,6 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { pw.println("mCallState=" + mCallState[i]); pw.println("mCallIncomingNumber=" + mCallIncomingNumber[i]); pw.println("mServiceState=" + mServiceState[i]); - pw.println("mNetworkType=" + mNetworkType[i]); pw.println("mVoiceActivationState= " + mVoiceActivationState[i]); pw.println("mDataActivationState= " + mDataActivationState[i]); pw.println("mUserMobileDataState= " + mUserMobileDataState[i]); @@ -1900,6 +1875,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { pw.println("mImsCallDisconnectCause=" + mImsReasonInfo.get(i).toString()); pw.decreaseIndent(); } + pw.println("mCallNetworkType=" + mCallNetworkType); pw.println("mPreciseDataConnectionState=" + mPreciseDataConnectionState); pw.println("mPreciseCallState=" + mPreciseCallState); pw.println("mCallDisconnectCause=" + mCallDisconnectCause); diff --git a/services/core/java/com/android/server/ZramWriteback.java b/services/core/java/com/android/server/ZramWriteback.java index 3a4aff2ea9aa..49bf29bff284 100644 --- a/services/core/java/com/android/server/ZramWriteback.java +++ b/services/core/java/com/android/server/ZramWriteback.java @@ -174,6 +174,7 @@ public final class ZramWriteback extends JobService { // back at later point if they remain untouched. js.schedule(new JobInfo.Builder(MARK_IDLE_JOB_ID, sZramWriteback) .setMinimumLatency(TimeUnit.MINUTES.toMillis(markIdleDelay)) + .setOverrideDeadline(TimeUnit.MINUTES.toMillis(markIdleDelay)) .build()); // Schedule a one time job to flush idle pages to disk. diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index fb541e00f588..346492fccc21 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -933,6 +933,27 @@ public final class ActiveServices { } } + /** + * Return the current foregroundServiceType of the ServiceRecord. + * @param className ComponentName of the Service class. + * @param token IBinder token. + * @return current foreground service type. + */ + public int getForegroundServiceTypeLocked(ComponentName className, IBinder token) { + final int userId = UserHandle.getCallingUserId(); + final long origId = Binder.clearCallingIdentity(); + int ret = ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE; + try { + ServiceRecord r = findServiceLocked(className, token, userId); + if (r != null) { + ret = r.foregroundServiceType; + } + } finally { + Binder.restoreCallingIdentity(origId); + } + return ret; + } + boolean foregroundAppShownEnoughLocked(ActiveForegroundApp aa, long nowElapsed) { if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "Shown enough: pkg=" + aa.mPackageName + ", uid=" + aa.mUid); diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index d025d739055b..415a8927295d 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -16,8 +16,6 @@ package com.android.server.am; -import static android.provider.DeviceConfig.ActivityManager.KEY_MAX_CACHED_PROCESSES; - import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER_QUICK; import android.app.ActivityThread; @@ -105,6 +103,12 @@ final class ActivityManagerConstants extends ContentObserver { private static final long DEFAULT_MEMORY_INFO_THROTTLE_TIME = 5*60*1000; private static final long DEFAULT_TOP_TO_FGS_GRACE_DURATION = 15 * 1000; + // Flag stored in the DeviceConfig API. + /** + * Maximum number of cached processes. + */ + private static final String KEY_MAX_CACHED_PROCESSES = "max_cached_processes"; + // Maximum number of cached processes we will allow. public int MAX_CACHED_PROCESSES = DEFAULT_MAX_CACHED_PROCESSES; @@ -292,7 +296,7 @@ final class ActivityManagerConstants extends ContentObserver { updateConstants(); updateActivityStartsLoggingEnabled(); updateBackgroundActivityStartsEnabled(); - DeviceConfig.addOnPropertyChangedListener(DeviceConfig.ActivityManager.NAMESPACE, + DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, ActivityThread.currentApplication().getMainExecutor(), mOnDeviceConfigChangedListener); updateMaxCachedProcesses(); @@ -402,7 +406,7 @@ final class ActivityManagerConstants extends ContentObserver { private void updateActivityStartsLoggingEnabled() { mFlagActivityStartsLoggingEnabled = Settings.Global.getInt(mResolver, - Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED, 0) == 1; + Settings.Global.ACTIVITY_STARTS_LOGGING_ENABLED, 1) == 1; } private void updateBackgroundActivityStartsEnabled() { @@ -412,7 +416,7 @@ final class ActivityManagerConstants extends ContentObserver { private void updateMaxCachedProcesses() { String maxCachedProcessesFlag = DeviceConfig.getProperty( - DeviceConfig.ActivityManager.NAMESPACE, KEY_MAX_CACHED_PROCESSES); + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_MAX_CACHED_PROCESSES); try { CUR_MAX_CACHED_PROCESSES = mOverrideMaxCachedProcesses < 0 ? (TextUtils.isEmpty(maxCachedProcessesFlag) diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 96cd79bfad13..fe85d231b903 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -187,9 +187,11 @@ import android.app.backup.IBackupManager; import android.app.usage.UsageEvents; import android.app.usage.UsageStatsManagerInternal; import android.appwidget.AppWidgetManager; +import android.content.AutofillOptions; import android.content.BroadcastReceiver; import android.content.ComponentCallbacks2; import android.content.ComponentName; +import android.content.ContentCaptureOptions; import android.content.ContentProvider; import android.content.ContentResolver; import android.content.Context; @@ -341,6 +343,7 @@ import com.android.server.Watchdog; import com.android.server.am.ActivityManagerServiceDumpProcessesProto.UidObserverRegistrationProto; import com.android.server.am.MemoryStatUtil.MemoryStat; import com.android.server.appop.AppOpsService; +import com.android.server.contentcapture.ContentCaptureManagerInternal; import com.android.server.firewall.IntentFirewall; import com.android.server.job.JobSchedulerInternal; import com.android.server.pm.Installer; @@ -1484,6 +1487,8 @@ public class ActivityManagerService extends IActivityManager.Stub private static String sTheRealBuildSerial = Build.UNKNOWN; + private ParcelFileDescriptor[] mLifeMonitorFds; + final class UiHandler extends Handler { public UiHandler() { super(com.android.server.UiThread.get().getLooper(), null, true); @@ -1856,7 +1861,7 @@ public class ActivityManagerService extends IActivityManager.Stub ProcessRecord proc; int procState; int statType; - int pid; + int pid = -1; long lastPssTime; synchronized (ActivityManagerService.this) { if (mPendingPssProcesses.size() <= 0) { @@ -4739,15 +4744,24 @@ public class ActivityManagerService extends IActivityManager.Stub // Figure out whether the app needs to run in autofill compat mode. - boolean isAutofillCompatEnabled = false; + AutofillOptions autofillOptions = null; if (UserHandle.getAppId(app.info.uid) >= Process.FIRST_APPLICATION_UID) { final AutofillManagerInternal afm = LocalServices.getService( AutofillManagerInternal.class); if (afm != null) { - isAutofillCompatEnabled = afm.isCompatibilityModeRequested( + autofillOptions = afm.getAutofillOptions( app.info.packageName, app.info.versionCode, app.userId); } } + ContentCaptureOptions contentCaptureOptions = null; + if (UserHandle.getAppId(app.info.uid) >= Process.FIRST_APPLICATION_UID) { + final ContentCaptureManagerInternal ccm = + LocalServices.getService(ContentCaptureManagerInternal.class); + if (ccm != null) { + contentCaptureOptions = ccm.getOptionsForPackage(app.userId, + app.info.packageName); + } + } checkTime(startTime, "attachApplicationLocked: immediately before bindApplication"); bindApplicationTimeMillis = SystemClock.elapsedRealtime(); @@ -4768,7 +4782,7 @@ public class ActivityManagerService extends IActivityManager.Stub new Configuration(app.getWindowProcessController().getConfiguration()), app.compat, getCommonServicesLocked(app.isolated), mCoreSettingsObserver.getCoreSettingsLocked(), - buildSerial, isAutofillCompatEnabled); + buildSerial, autofillOptions, contentCaptureOptions); } else { thread.bindApplication(processName, appInfo, providers, null, profilerInfo, null, null, null, testMode, @@ -4777,7 +4791,7 @@ public class ActivityManagerService extends IActivityManager.Stub new Configuration(app.getWindowProcessController().getConfiguration()), app.compat, getCommonServicesLocked(app.isolated), mCoreSettingsObserver.getCoreSettingsLocked(), - buildSerial, isAutofillCompatEnabled); + buildSerial, autofillOptions, contentCaptureOptions); } if (profilerInfo != null) { profilerInfo.closeFd(); @@ -13572,6 +13586,13 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override + public int getForegroundServiceType(ComponentName className, IBinder token) { + synchronized (this) { + return mServices.getForegroundServiceTypeLocked(className, token); + } + } + + @Override public int handleIncomingUser(int callingPid, int callingUid, int userId, boolean allowAll, boolean requireFull, String name, String callerPackage) { return mUserController.handleIncomingUser(callingPid, callingUid, userId, allowAll, @@ -15025,7 +15046,7 @@ public class ActivityManagerService extends IActivityManager.Stub oldQueue.performReceiveLocked(oldRecord.callerApp, oldRecord.resultTo, oldRecord.intent, Activity.RESULT_CANCELED, null, null, - false, false, oldRecord.userId, oldRecord); + false, false, oldRecord.userId); } catch (RemoteException e) { Slog.w(TAG, "Failure [" + queue.mQueueName + "] sending broadcast result of " @@ -17096,6 +17117,13 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override + public boolean startUserInForegroundWithListener(final int userId, + @Nullable IProgressListener unlockListener) { + // Permission check done inside UserController. + return mUserController.startUser(userId, /* foreground */ true, unlockListener); + } + + @Override public boolean unlockUser(int userId, byte[] token, byte[] secret, IProgressListener listener) { return mUserController.unlockUser(userId, token, secret, listener); } @@ -18489,4 +18517,24 @@ public class ActivityManagerService extends IActivityManager.Stub private boolean isOnOffloadQueue(int flags) { return (mEnableOffloadQueue && ((flags & Intent.FLAG_RECEIVER_OFFLOAD) != 0)); } + + @Override + public ParcelFileDescriptor getLifeMonitor() { + if (!isCallerShell()) { + throw new SecurityException("Only shell can call it"); + } + synchronized (this) { + try { + if (mLifeMonitorFds == null) { + mLifeMonitorFds = ParcelFileDescriptor.createPipe(); + } + // The returned FD will be closed, but we want to keep our reader open, + // so return a dup instead. + return mLifeMonitorFds[0].dup(); + } catch (IOException e) { + Slog.w(TAG, "Unable to create pipe", e); + return null; + } + } + } } diff --git a/services/core/java/com/android/server/am/AppCompactor.java b/services/core/java/com/android/server/am/AppCompactor.java index 17ffd9c5e093..1f2116065bc1 100644 --- a/services/core/java/com/android/server/am/AppCompactor.java +++ b/services/core/java/com/android/server/am/AppCompactor.java @@ -17,14 +17,6 @@ package com.android.server.am; import static android.os.Process.THREAD_PRIORITY_FOREGROUND; -import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_ACTION_1; -import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_ACTION_2; -import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_STATSD_SAMPLE_RATE; -import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_1; -import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_2; -import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_3; -import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_4; -import static android.provider.DeviceConfig.ActivityManager.KEY_USE_COMPACTION; import android.app.ActivityManager; import android.app.ActivityThread; @@ -51,6 +43,17 @@ import java.util.Random; public final class AppCompactor { + // Flags stored in the DeviceConfig API. + @VisibleForTesting static final String KEY_USE_COMPACTION = "use_compaction"; + @VisibleForTesting static final String KEY_COMPACT_ACTION_1 = "compact_action_1"; + @VisibleForTesting static final String KEY_COMPACT_ACTION_2 = "compact_action_2"; + @VisibleForTesting static final String KEY_COMPACT_THROTTLE_1 = "compact_throttle_1"; + @VisibleForTesting static final String KEY_COMPACT_THROTTLE_2 = "compact_throttle_2"; + @VisibleForTesting static final String KEY_COMPACT_THROTTLE_3 = "compact_throttle_3"; + @VisibleForTesting static final String KEY_COMPACT_THROTTLE_4 = "compact_throttle_4"; + @VisibleForTesting static final String KEY_COMPACT_STATSD_SAMPLE_RATE = + "compact_statsd_sample_rate"; + // Phenotype sends int configurations and we map them to the strings we'll use on device, // preventing a weird string value entering the kernel. private static final int COMPACT_ACTION_FILE_FLAG = 1; @@ -165,7 +168,7 @@ public final class AppCompactor { * starts the background thread if necessary. */ public void init() { - DeviceConfig.addOnPropertyChangedListener(DeviceConfig.ActivityManager.NAMESPACE, + DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, ActivityThread.currentApplication().getMainExecutor(), mOnFlagsChangedListener); synchronized (mPhenotypeFlagLock) { updateUseCompaction(); @@ -228,7 +231,7 @@ public final class AppCompactor { @GuardedBy("mPhenotypeFlagLock") private void updateUseCompaction() { String useCompactionFlag = - DeviceConfig.getProperty(DeviceConfig.ActivityManager.NAMESPACE, + DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_USE_COMPACTION); mUseCompaction = TextUtils.isEmpty(useCompactionFlag) ? DEFAULT_USE_COMPACTION : Boolean.parseBoolean(useCompactionFlag); @@ -241,10 +244,10 @@ public final class AppCompactor { @GuardedBy("mPhenotypeFlagLock") private void updateCompactionActions() { String compactAction1Flag = - DeviceConfig.getProperty(DeviceConfig.ActivityManager.NAMESPACE, + DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_COMPACT_ACTION_1); String compactAction2Flag = - DeviceConfig.getProperty(DeviceConfig.ActivityManager.NAMESPACE, + DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_COMPACT_ACTION_2); int compactAction1 = DEFAULT_COMPACT_ACTION_1; @@ -271,16 +274,16 @@ public final class AppCompactor { private void updateCompactionThrottles() { boolean useThrottleDefaults = false; String throttleSomeSomeFlag = - DeviceConfig.getProperty(DeviceConfig.ActivityManager.NAMESPACE, + DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_COMPACT_THROTTLE_1); String throttleSomeFullFlag = - DeviceConfig.getProperty(DeviceConfig.ActivityManager.NAMESPACE, + DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_COMPACT_THROTTLE_2); String throttleFullSomeFlag = - DeviceConfig.getProperty(DeviceConfig.ActivityManager.NAMESPACE, + DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_COMPACT_THROTTLE_3); String throttleFullFullFlag = - DeviceConfig.getProperty(DeviceConfig.ActivityManager.NAMESPACE, + DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_COMPACT_THROTTLE_4); if (TextUtils.isEmpty(throttleSomeSomeFlag) || TextUtils.isEmpty(throttleSomeFullFlag) @@ -309,7 +312,7 @@ public final class AppCompactor { @GuardedBy("mPhenotypeFlagLock") private void updateStatsdSampleRate() { - String sampleRateFlag = DeviceConfig.getProperty(DeviceConfig.ActivityManager.NAMESPACE, + String sampleRateFlag = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_COMPACT_STATSD_SAMPLE_RATE); try { mStatsdSampleRate = TextUtils.isEmpty(sampleRateFlag) diff --git a/services/core/java/com/android/server/am/BroadcastConstants.java b/services/core/java/com/android/server/am/BroadcastConstants.java index 820caf12ac84..be17b1bc600c 100644 --- a/services/core/java/com/android/server/am/BroadcastConstants.java +++ b/services/core/java/com/android/server/am/BroadcastConstants.java @@ -38,6 +38,8 @@ public class BroadcastConstants { static final String KEY_DEFERRAL = "bcast_deferral"; static final String KEY_DEFERRAL_DECAY_FACTOR = "bcast_deferral_decay_factor"; static final String KEY_DEFERRAL_FLOOR = "bcast_deferral_floor"; + static final String KEY_ALLOW_BG_ACTIVITY_START_TIMEOUT = + "bcast_allow_bg_activity_start_timeout"; // All time intervals are in milliseconds private static final long DEFAULT_TIMEOUT = 10_000; @@ -45,6 +47,7 @@ public class BroadcastConstants { private static final long DEFAULT_DEFERRAL = 5_000; private static final float DEFAULT_DEFERRAL_DECAY_FACTOR = 0.75f; private static final long DEFAULT_DEFERRAL_FLOOR = 0; + private static final long DEFAULT_ALLOW_BG_ACTIVITY_START_TIMEOUT = 10_000; // All time constants are in milliseconds @@ -59,6 +62,8 @@ public class BroadcastConstants { public float DEFERRAL_DECAY_FACTOR = DEFAULT_DEFERRAL_DECAY_FACTOR; // Minimum that the deferral time can decay to until the backlog fully clears public long DEFERRAL_FLOOR = DEFAULT_DEFERRAL_FLOOR; + // For how long after a whitelisted receiver's start its process can start a background activity + public long ALLOW_BG_ACTIVITY_START_TIMEOUT = DEFAULT_ALLOW_BG_ACTIVITY_START_TIMEOUT; // Settings override tracking for this instance private String mSettingsKey; @@ -113,6 +118,8 @@ public class BroadcastConstants { DEFERRAL_DECAY_FACTOR = mParser.getFloat(KEY_DEFERRAL_DECAY_FACTOR, DEFERRAL_DECAY_FACTOR); DEFERRAL_FLOOR = mParser.getLong(KEY_DEFERRAL_FLOOR, DEFERRAL_FLOOR); + ALLOW_BG_ACTIVITY_START_TIMEOUT = mParser.getLong(KEY_ALLOW_BG_ACTIVITY_START_TIMEOUT, + ALLOW_BG_ACTIVITY_START_TIMEOUT); } } @@ -145,6 +152,9 @@ public class BroadcastConstants { pw.print(" "); pw.print(KEY_DEFERRAL_FLOOR); pw.print(" = "); TimeUtils.formatDuration(DEFERRAL_FLOOR, pw); + + pw.print(" "); pw.print(KEY_ALLOW_BG_ACTIVITY_START_TIMEOUT); pw.print(" = "); + TimeUtils.formatDuration(ALLOW_BG_ACTIVITY_START_TIMEOUT, pw); pw.println(); } } diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index d9ea1da79f56..efb1c445925f 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -74,9 +74,6 @@ public final class BroadcastQueue { static final int MAX_BROADCAST_SUMMARY_HISTORY = ActivityManager.isLowRamDeviceStatic() ? 25 : 300; - // For how long after a whitelisted receiver's start its process can start a background activity - private static final int RECEIVER_BG_ACTIVITY_START_TIMEOUT_MS = 10_000; - final ActivityManagerService mService; /** @@ -310,9 +307,6 @@ public final class BroadcastQueue { r.curApp = app; app.curReceivers.add(r); app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER); - if (r.allowBackgroundActivityStarts) { - app.addAllowBackgroundActivityStartsToken(r); - } mService.mProcessList.updateLruProcessLocked(app, false, null); if (!skipOomAdj) { mService.updateOomAdjLocked(); @@ -454,8 +448,25 @@ public final class BroadcastQueue { Slog.w(TAG, "finishReceiver [" + mQueueName + "] called but state is IDLE"); } if (r.allowBackgroundActivityStarts && r.curApp != null) { - r.curApp.removeAllowBackgroundActivityStartsToken(r); - } + if (elapsed > mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT) { + // if the receiver has run for more than allowed bg activity start timeout, + // just remove the token for this process now and we're done + r.curApp.removeAllowBackgroundActivityStartsToken(r); + } else { + // the receiver had run for less than allowed bg activity start timeout, + // so allow the process to still start activities from bg for some more time + String msgToken = (r.curApp.toShortString() + r.toString()).intern(); + // first, if there exists a past scheduled request to remove this token, drop + // that request - we don't want the token to be swept from under our feet... + mHandler.removeCallbacksAndMessages(msgToken); + // ...then schedule the removal of the token after the extended timeout + mHandler.postAtTime(() -> { + if (r.curApp != null) { + r.curApp.removeAllowBackgroundActivityStartsToken(r); + } + }, msgToken, (r.receiverTime + mConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT)); + } + } // If we're abandoning this broadcast before any receivers were actually spun up, // nextReceiver is zero; in which case time-to-process bookkeeping doesn't apply. if (r.nextReceiver > 0) { @@ -554,7 +565,7 @@ public final class BroadcastQueue { void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver, Intent intent, int resultCode, String data, Bundle extras, - boolean ordered, boolean sticky, int sendingUser, BroadcastRecord br) + boolean ordered, boolean sticky, int sendingUser) throws RemoteException { // Send the intent to the receiver asynchronously using one-way binder calls. if (app != null) { @@ -562,15 +573,6 @@ public final class BroadcastQueue { // If we have an app thread, do the call through that so it is // correctly ordered with other one-way calls. try { - if (br.allowBackgroundActivityStarts) { - app.addAllowBackgroundActivityStartsToken(br); - // schedule removal of the whitelisting token after the timeout - mHandler.postDelayed(() -> { - if (app != null) { - app.removeAllowBackgroundActivityStartsToken(br); - } - }, RECEIVER_BG_ACTIVITY_START_TIMEOUT_MS); - } app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode, data, extras, ordered, sticky, sendingUser, app.getReportedProcState()); // TODO: Uncomment this when (b/28322359) is fixed and we aren't getting @@ -794,9 +796,13 @@ public final class BroadcastQueue { skipReceiverLocked(r); } } else { + if (r.receiverTime == 0) { + r.receiverTime = SystemClock.uptimeMillis(); + } + maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r); performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver, new Intent(r.intent), r.resultCode, r.resultData, - r.resultExtras, r.ordered, r.initialSticky, r.userId, r); + r.resultExtras, r.ordered, r.initialSticky, r.userId); } if (ordered) { r.state = BroadcastRecord.CALL_DONE_RECEIVE; @@ -1100,7 +1106,7 @@ public final class BroadcastQueue { } performReceiveLocked(r.callerApp, r.resultTo, new Intent(r.intent), r.resultCode, - r.resultData, r.resultExtras, false, false, r.userId, r); + r.resultData, r.resultExtras, false, false, r.userId); // Set this to null so that the reference // (local and remote) isn't kept in the mBroadcastHistory. r.resultTo = null; @@ -1255,6 +1261,9 @@ public final class BroadcastQueue { r.state = BroadcastRecord.IDLE; scheduleBroadcastsLocked(); } else { + if (filter.receiverList != null) { + maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r); + } if (brOptions != null && brOptions.getTemporaryAppWhitelistDuration() > 0) { scheduleTempWhitelistLocked(filter.owningUid, brOptions.getTemporaryAppWhitelistDuration(), r); @@ -1561,6 +1570,7 @@ public final class BroadcastQueue { try { app.addPackage(info.activityInfo.packageName, info.activityInfo.applicationInfo.versionCode, mService.mProcessStats); + maybeAddAllowBackgroundActivityStartsToken(app, r); processCurBroadcastLocked(r, app, skipOomAdj); return; } catch (RemoteException e) { @@ -1611,10 +1621,23 @@ public final class BroadcastQueue { return; } + maybeAddAllowBackgroundActivityStartsToken(r.curApp, r); mPendingBroadcast = r; mPendingBroadcastRecvIndex = recIdx; } + private void maybeAddAllowBackgroundActivityStartsToken(ProcessRecord proc, BroadcastRecord r) { + if (r == null || proc == null || !r.allowBackgroundActivityStarts) { + return; + } + String msgToken = (proc.toShortString() + r.toString()).intern(); + // first, if there exists a past scheduled request to remove this token, drop + // that request - we don't want the token to be swept from under our feet... + mHandler.removeCallbacksAndMessages(msgToken); + // ...then add the token + proc.addAllowBackgroundActivityStartsToken(r); + } + final void setBroadcastTimeoutLocked(long timeoutTime) { if (! mPendingBroadcastTimeoutMessage) { Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG, this); diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 3a61dd987cf5..30798a87a060 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -1764,8 +1764,6 @@ public final class ProcessList { .getPackagesForUid(uid); final StorageManagerInternal storageManagerInternal = LocalServices.getService(StorageManagerInternal.class); - final String[] visibleVolIds = storageManagerInternal - .getVisibleVolumesForUser(UserHandle.getUserId(uid)); final String sandboxId = storageManagerInternal.getSandboxId(app.info.packageName); Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " + app.processName); @@ -1776,7 +1774,7 @@ public final class ProcessList { app.processName, uid, uid, gids, runtimeFlags, mountExternal, app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet, app.info.dataDir, null, app.info.packageName, - packageNames, visibleVolIds, sandboxId, + packageNames, sandboxId, new String[] {PROC_START_SEQ_IDENT + app.startSeq}); } else if (hostingType.equals("app_zygote")) { final AppZygote appZygote = createAppZygoteForProcessIfNeeded(app); @@ -1785,14 +1783,14 @@ public final class ProcessList { app.processName, uid, uid, gids, runtimeFlags, mountExternal, app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet, app.info.dataDir, null, app.info.packageName, - packageNames, visibleVolIds, sandboxId, /*useBlastulaPool=*/ false, + packageNames, sandboxId, /*useBlastulaPool=*/ false, 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.packageName, - packageNames, visibleVolIds, sandboxId, + packageNames, sandboxId, new String[] {PROC_START_SEQ_IDENT + app.startSeq}); } checkSlow(startTime, "startProcess: returned from zygote!"); diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java index cbbbe4743108..894a704c5bba 100644 --- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java @@ -38,8 +38,9 @@ import java.util.HashSet; /** * Maps system settings to system properties. * <p>The properties are dynamically updated when settings change. + * @hide */ -class SettingsToPropertiesMapper { +public class SettingsToPropertiesMapper { private static final String TAG = "SettingsToPropertiesMapper"; @@ -156,8 +157,8 @@ class SettingsToPropertiesMapper { * during current device booting. * @return */ - public boolean isNativeFlagsResetPerformed() { - String value = systemPropertiesGet(RESET_PERFORMED_PROPERTY); + public static boolean isNativeFlagsResetPerformed() { + String value = SystemProperties.get(RESET_PERFORMED_PROPERTY); return "true".equals(value); } @@ -166,7 +167,7 @@ class SettingsToPropertiesMapper { * booting. * @return */ - public String[] getResetNativeCategories() { + public static String[] getResetNativeCategories() { if (!isNativeFlagsResetPerformed()) { return new String[0]; } @@ -214,7 +215,7 @@ class SettingsToPropertiesMapper { if (value == null) { // It's impossible to remove system property, therefore we check previous value to // avoid setting an empty string if the property wasn't set. - if (TextUtils.isEmpty(systemPropertiesGet(key))) { + if (TextUtils.isEmpty(SystemProperties.get(key))) { return; } value = ""; @@ -224,7 +225,7 @@ class SettingsToPropertiesMapper { } try { - systemPropertiesSet(key, value); + SystemProperties.set(key, value); } catch (Exception e) { // Failure to set a property can be caused by SELinux denial. This usually indicates // that the property wasn't whitelisted in sepolicy. @@ -250,17 +251,7 @@ class SettingsToPropertiesMapper { } @VisibleForTesting - protected String systemPropertiesGet(String key) { - return SystemProperties.get(key); - } - - @VisibleForTesting - protected void systemPropertiesSet(String key, String value) { - SystemProperties.set(key, value); - } - - @VisibleForTesting - protected String getResetFlagsFileContent() { + static String getResetFlagsFileContent() { String content = null; try { File reset_flag_file = new File(RESET_RECORD_FILE_PATH); @@ -279,4 +270,4 @@ class SettingsToPropertiesMapper { String settingValue = Settings.Global.getString(mContentResolver, settingName); setProperty(propName, settingValue); } -} +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index ac20f6c7eaaf..1e406c01f255 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -2214,7 +2214,11 @@ class UserController implements Handler.Callback { void startUserWidgets(int userId) { AppWidgetManagerInternal awm = LocalServices.getService(AppWidgetManagerInternal.class); if (awm != null) { - awm.unlockUser(userId); + // Out of band, because this is called during a sequence with + // sensitive cross-service lock management + FgThread.getHandler().post(() -> { + awm.unlockUser(userId); + }); } } diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java index 708de73f8eb2..4485a54b5cdf 100644 --- a/services/core/java/com/android/server/appop/HistoricalRegistry.java +++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java @@ -144,7 +144,7 @@ final class HistoricalRegistry { * Whether history is enabled. */ @GuardedBy("mInMemoryLock") - private int mMode = AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE; + private int mMode = AppOpsManager.HISTORICAL_MODE_DISABLED; /** * This granularity has been chosen to allow clean delineation for intervals diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index d902201df212..6bd412bcd536 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -88,7 +88,12 @@ import android.media.audiofx.AudioEffect; import android.media.audiopolicy.AudioMix; import android.media.audiopolicy.AudioPolicy; import android.media.audiopolicy.AudioPolicyConfig; +import android.media.audiopolicy.AudioProductStrategies; +import android.media.audiopolicy.AudioVolumeGroup; +import android.media.audiopolicy.AudioVolumeGroups; import android.media.audiopolicy.IAudioPolicyCallback; +import android.media.projection.IMediaProjection; +import android.media.projection.IMediaProjectionManager; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -99,6 +104,7 @@ import android.os.Looper; import android.os.Message; import android.os.PowerManager; import android.os.RemoteException; +import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; @@ -124,6 +130,7 @@ import android.widget.Toast; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.DumpUtils; +import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; import com.android.server.EventLogTags; import com.android.server.LocalServices; @@ -271,6 +278,11 @@ public class AudioService extends IAudioService.Stub private SettingsObserver mSettingsObserver; + /** @see AudioProductStrategies */ + private static AudioProductStrategies sAudioProductStrategies; + /** @see AudioVolumeGroups */ + private static AudioVolumeGroups sAudioVolumeGroups; + private int mMode = AudioSystem.MODE_NORMAL; // protects mRingerMode private final Object mSettingsLock = new Object(); @@ -453,6 +465,8 @@ public class AudioService extends IAudioService.Stub // Broadcast receiver for device connections intent broadcasts private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver(); + private IMediaProjectionManager mProjectionService; // to validate projection token + /** Interface for UserManagerService. */ private final UserManagerInternal mUserManagerInternal; private final ActivityManagerInternal mActivityManagerInternal; @@ -619,6 +633,9 @@ public class AudioService extends IAudioService.Stub mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); mHasVibrator = mVibrator == null ? false : mVibrator.hasVibrator(); + sAudioProductStrategies = new AudioProductStrategies(); + sAudioVolumeGroups = new AudioVolumeGroups(); + // Initialize volume int maxCallVolume = SystemProperties.getInt("ro.config.vc_call_vol_steps", -1); if (maxCallVolume != -1) { @@ -983,6 +1000,22 @@ public class AudioService extends IAudioService.Stub } } + /** + * @return the {@link android.media.audiopolicy.AudioProductStrategies} discovered from the + * platform configuration file. + */ + public @NonNull AudioProductStrategies getAudioProductStrategies() { + return sAudioProductStrategies; + } + + /** + * @return the {@link android.media.audiopolicy.AudioVolumeGroups} discovered from the + * platform configuration file. + */ + public @NonNull AudioVolumeGroups listAudioVolumeGroups() { + return sAudioVolumeGroups; + } + private void checkAllAliasStreamVolumes() { synchronized (mSettingsLock) { synchronized (VolumeStreamState.class) { @@ -1873,6 +1906,62 @@ public class AudioService extends IAudioService.Stub mStreamStates[stream].mute(index == 0); } + private void enforceModifyAudioRoutingPermission() { + if (mContext.checkCallingPermission( + android.Manifest.permission.MODIFY_AUDIO_ROUTING) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Missing MODIFY_AUDIO_ROUTING permission"); + } + } + + /** @see AudioManager#setVolumeIndexForAttributes(attr, int, int) */ + public void setVolumeIndexForAttributes(@NonNull AudioAttributes attr, int index, int flags, + String callingPackage) { + enforceModifyAudioRoutingPermission(); + Preconditions.checkNotNull(attr, "attr must not be null"); + // @todo not hold the caller context, post message + int stream = sAudioProductStrategies.getLegacyStreamTypeForAudioAttributes(attr); + final int device = getDeviceForStream(stream); + + int oldIndex = AudioSystem.getVolumeIndexForAttributes(attr, device); + + AudioSystem.setVolumeIndexForAttributes(attr, index, device); + + final int volumeGroup = sAudioProductStrategies.getVolumeGroupIdForAttributes(attr); + final AudioVolumeGroup avg = sAudioVolumeGroups.getById(volumeGroup); + if (avg == null) { + return; + } + for (final int groupedStream : avg.getLegacyStreamTypes()) { + setStreamVolume(stream, index, flags, callingPackage, callingPackage, + Binder.getCallingUid()); + } + } + + /** @see AudioManager#getVolumeIndexForAttributes(attr) */ + public int getVolumeIndexForAttributes(@NonNull AudioAttributes attr) { + enforceModifyAudioRoutingPermission(); + Preconditions.checkNotNull(attr, "attr must not be null"); + int stream = sAudioProductStrategies.getLegacyStreamTypeForAudioAttributes(attr); + final int device = getDeviceForStream(stream); + + return AudioSystem.getVolumeIndexForAttributes(attr, device); + } + + /** @see AudioManager#getMaxVolumeIndexForAttributes(attr) */ + public int getMaxVolumeIndexForAttributes(@NonNull AudioAttributes attr) { + enforceModifyAudioRoutingPermission(); + Preconditions.checkNotNull(attr, "attr must not be null"); + return AudioSystem.getMaxVolumeIndexForAttributes(attr); + } + + /** @see AudioManager#getMinVolumeIndexForAttributes(attr) */ + public int getMinVolumeIndexForAttributes(@NonNull AudioAttributes attr) { + enforceModifyAudioRoutingPermission(); + Preconditions.checkNotNull(attr, "attr must not be null"); + return AudioSystem.getMinVolumeIndexForAttributes(attr); + } + /** @see AudioManager#setStreamVolume(int, int, int) */ public void setStreamVolume(int streamType, int index, int flags, String callingPackage) { if ((streamType == AudioManager.STREAM_ACCESSIBILITY) && !canChangeAccessibilityVolume()) { @@ -5782,7 +5871,7 @@ public class AudioService extends IAudioService.Stub // - wired: logged before onSetWiredDeviceConnectionState() is executed // - A2DP: logged at reception of method call /*package*/ static final AudioEventLogger sDeviceLogger = new AudioEventLogger( - LOG_NB_EVENTS_DEVICE_CONNECTION, "wired/A2DP/hearing aid device connection BLABLI"); + LOG_NB_EVENTS_DEVICE_CONNECTION, "wired/A2DP/hearing aid device connection"); static final AudioEventLogger sForceUseLogger = new AudioEventLogger( LOG_NB_EVENTS_FORCE_USE, @@ -6186,22 +6275,21 @@ public class AudioService extends IAudioService.Stub // Audio policy management //========================================================================================== public String registerAudioPolicy(AudioPolicyConfig policyConfig, IAudioPolicyCallback pcb, - boolean hasFocusListener, boolean isFocusPolicy, boolean isVolumeController) { + boolean hasFocusListener, boolean isFocusPolicy, boolean isVolumeController, + IMediaProjection projection) { AudioSystem.setDynamicPolicyCallback(mDynPolicyCallback); - String regId = null; - // error handling - boolean hasPermissionForPolicy = - (PackageManager.PERMISSION_GRANTED == mContext.checkCallingPermission( - android.Manifest.permission.MODIFY_AUDIO_ROUTING)); - if (!hasPermissionForPolicy) { - Slog.w(TAG, "Can't register audio policy for pid " + Binder.getCallingPid() + " / uid " - + Binder.getCallingUid() + ", need MODIFY_AUDIO_ROUTING"); + if (!isPolicyRegisterAllowed(policyConfig, projection)) { + Slog.w(TAG, "Permission denied to register audio policy for pid " + + Binder.getCallingPid() + " / uid " + Binder.getCallingUid() + + ", need MODIFY_AUDIO_ROUTING or MediaProjection that can project audio"); return null; } mDynPolicyLogger.log((new AudioEventLogger.StringEvent("registerAudioPolicy for " + pcb.asBinder() + " with config:" + policyConfig)).printLog(TAG)); + + String regId = null; synchronized (mAudioPolicies) { try { if (mAudioPolicies.containsKey(pcb.asBinder())) { @@ -6223,6 +6311,76 @@ public class AudioService extends IAudioService.Stub return regId; } + /** + * Apps with MODIFY_AUDIO_ROUTING can register any policy. + * Apps with an audio capable MediaProjection are allowed to register a RENDER|LOOPBACK policy + * as those policy do not modify the audio routing. + */ + private boolean isPolicyRegisterAllowed(AudioPolicyConfig policyConfig, + IMediaProjection projection) { + + boolean isLoopbackRenderPolicy = policyConfig.getMixes().stream().allMatch( + mix -> mix.getRouteFlags() == (mix.ROUTE_FLAG_RENDER | mix.ROUTE_FLAG_LOOP_BACK)); + + // Policy that do not modify the audio routing only need an audio projection + if (isLoopbackRenderPolicy && canProjectAudio(projection)) { + return true; + } + + boolean hasPermissionModifyAudioRouting = + (PackageManager.PERMISSION_GRANTED == mContext.checkCallingPermission( + android.Manifest.permission.MODIFY_AUDIO_ROUTING)); + if (hasPermissionModifyAudioRouting) { + return true; + } + return false; + } + + /** @return true if projection is a valid MediaProjection that can project audio. */ + private boolean canProjectAudio(IMediaProjection projection) { + if (projection == null) { + return false; + } + + IMediaProjectionManager projectionService = getProjectionService(); + if (projectionService == null) { + Log.e(TAG, "Can't get service IMediaProjectionManager"); + return false; + } + + try { + if (!projectionService.isValidMediaProjection(projection)) { + Log.w(TAG, "App passed invalid MediaProjection token"); + return false; + } + } catch (RemoteException e) { + Log.e(TAG, "Can't call .isValidMediaProjection() on IMediaProjectionManager" + + projectionService.asBinder(), e); + return false; + } + + try { + if (!projection.canProjectAudio()) { + Log.w(TAG, "App passed MediaProjection that can not project audio"); + return false; + } + } catch (RemoteException e) { + Log.e(TAG, "Can't call .canProjectAudio() on valid IMediaProjection" + + projection.asBinder(), e); + return false; + } + + return true; + } + + private IMediaProjectionManager getProjectionService() { + if (mProjectionService == null) { + IBinder b = ServiceManager.getService(Context.MEDIA_PROJECTION_SERVICE); + mProjectionService = IMediaProjectionManager.Stub.asInterface(b); + } + return mProjectionService; + } + public void unregisterAudioPolicyAsync(IAudioPolicyCallback pcb) { mDynPolicyLogger.log((new AudioEventLogger.StringEvent("unregisterAudioPolicyAsync for " + pcb.asBinder()).printLog(TAG))); diff --git a/services/core/java/com/android/server/biometrics/ClientMonitor.java b/services/core/java/com/android/server/biometrics/ClientMonitor.java index e80b39ba9d1f..89fa2de92c1c 100644 --- a/services/core/java/com/android/server/biometrics/ClientMonitor.java +++ b/services/core/java/com/android/server/biometrics/ClientMonitor.java @@ -209,11 +209,7 @@ public abstract class ClientMonitor extends LoggableMonitor implements IBinder.D public void binderDied() { // If the current client dies we should cancel the current operation. Slog.e(getLogTag(), "Binder died, cancelling client"); - try { - getDaemonWrapper().cancel(); - } catch (RemoteException e) { - Slog.e(getLogTag(), "Remote exception", e); - } + stop(false /* initiatedByClient */); mToken = null; mListener = null; } diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java index 9d9b1cfdf6e2..2646d7669d79 100644 --- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java +++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java @@ -18,19 +18,24 @@ package com.android.server.connectivity; import android.net.ConnectivityManager; import android.net.INetd; +import android.net.InetAddresses; import android.net.InterfaceConfiguration; +import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.NetworkInfo; import android.net.RouteInfo; import android.os.INetworkManagementService; import android.os.RemoteException; +import android.os.ServiceSpecificException; import android.util.Slog; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.server.net.BaseNetworkObserver; import java.net.Inet4Address; +import java.net.Inet6Address; import java.util.Objects; /** @@ -67,15 +72,16 @@ public class Nat464Xlat extends BaseNetworkObserver { private final NetworkAgentInfo mNetwork; private enum State { - IDLE, // start() not called. Base iface and stacked iface names are null. - STARTING, // start() called. Base iface and stacked iface names are known. - RUNNING, // start() called, and the stacked iface is known to be up. - STOPPING; // stop() called, this Nat464Xlat is still registered as a network observer for - // the stacked interface. + IDLE, // start() not called. Base iface and stacked iface names are null. + DISCOVERING, // same as IDLE, except prefix discovery in progress. + STARTING, // start() called. Base iface and stacked iface names are known. + RUNNING, // start() called, and the stacked iface is known to be up. } + private IpPrefix mNat64Prefix; private String mBaseIface; private String mIface; + private Inet6Address mIPv6Address; private State mState = State.IDLE; public Nat464Xlat(NetworkAgentInfo nai, INetd netd, INetworkManagementService nmService) { @@ -85,20 +91,51 @@ public class Nat464Xlat extends BaseNetworkObserver { } /** - * Determines whether a network requires clat. + * Whether to attempt 464xlat on this network. This is true for an IPv6-only network that is + * currently connected and where the NetworkAgent has not disabled 464xlat. It is the signal to + * enable NAT64 prefix discovery. + * * @param network the NetworkAgentInfo corresponding to the network. * @return true if the network requires clat, false otherwise. */ - public static boolean requiresClat(NetworkAgentInfo nai) { + @VisibleForTesting + protected static boolean requiresClat(NetworkAgentInfo nai) { // TODO: migrate to NetworkCapabilities.TRANSPORT_*. final boolean supported = ArrayUtils.contains(NETWORK_TYPES, nai.networkInfo.getType()); final boolean connected = ArrayUtils.contains(NETWORK_STATES, nai.networkInfo.getState()); - // We only run clat on networks that don't have a native IPv4 address. - final boolean hasIPv4Address = - (nai.linkProperties != null) && nai.linkProperties.hasIPv4Address(); - final boolean skip464xlat = - (nai.netMisc() != null) && nai.netMisc().skip464xlat; - return supported && connected && !hasIPv4Address && !skip464xlat; + + // Only run clat on networks that have a global IPv6 address and don't have a native IPv4 + // address. + LinkProperties lp = nai.linkProperties; + final boolean isIpv6OnlyNetwork = (lp != null) && lp.hasGlobalIPv6Address() + && !lp.hasIPv4Address(); + + // If the network tells us it doesn't use clat, respect that. + final boolean skip464xlat = (nai.netMisc() != null) && nai.netMisc().skip464xlat; + + return supported && connected && isIpv6OnlyNetwork && !skip464xlat; + } + + /** + * Whether the clat demon should be started on this network now. This is true if requiresClat is + * true and a NAT64 prefix has been discovered. + * + * @param nai the NetworkAgentInfo corresponding to the network. + * @return true if the network should start clat, false otherwise. + */ + @VisibleForTesting + protected static boolean shouldStartClat(NetworkAgentInfo nai) { + LinkProperties lp = nai.linkProperties; + return requiresClat(nai) && lp != null && lp.getNat64Prefix() != null; + } + + /** + * @return true if we have started prefix discovery and not yet stopped it (regardless of + * whether it is still running or has succeeded). + * A true result corresponds to internal states DISCOVERING, STARTING and RUNNING. + */ + public boolean isPrefixDiscoveryStarted() { + return mState == State.DISCOVERING || isStarted(); } /** @@ -106,7 +143,7 @@ public class Nat464Xlat extends BaseNetworkObserver { * A true result corresponds to internal states STARTING and RUNNING. */ public boolean isStarted() { - return mState != State.IDLE; + return (mState == State.STARTING || mState == State.RUNNING); } /** @@ -124,32 +161,31 @@ public class Nat464Xlat extends BaseNetworkObserver { } /** - * @return true if clatd has been stopped. - */ - public boolean isStopping() { - return mState == State.STOPPING; - } - - /** * Start clatd, register this Nat464Xlat as a network observer for the stacked interface, * and set internal state. */ private void enterStartingState(String baseIface) { try { mNMService.registerObserver(this); - } catch(RemoteException e) { - Slog.e(TAG, - "startClat: Can't register interface observer for clat on " + mNetwork.name()); + } catch (RemoteException e) { + Slog.e(TAG, "Can't register interface observer for clat on " + mNetwork.name()); return; } + + String addrStr = null; try { - mNetd.clatdStart(baseIface); - } catch(RemoteException|IllegalStateException e) { - Slog.e(TAG, "Error starting clatd on " + baseIface, e); + addrStr = mNetd.clatdStart(baseIface, mNat64Prefix.toString()); + } catch (RemoteException | ServiceSpecificException e) { + Slog.e(TAG, "Error starting clatd on " + baseIface + ": " + e); } mIface = CLAT_PREFIX + baseIface; mBaseIface = baseIface; mState = State.STARTING; + try { + mIPv6Address = (Inet6Address) InetAddresses.parseNumericAddress(addrStr); + } catch (ClassCastException | IllegalArgumentException | NullPointerException e) { + Slog.e(TAG, "Invalid IPv6 address " + addrStr); + } } /** @@ -161,37 +197,27 @@ public class Nat464Xlat extends BaseNetworkObserver { } /** - * Stop clatd, and turn ND offload on if it had been turned off. - */ - private void enterStoppingState() { - try { - mNetd.clatdStop(mBaseIface); - } catch(RemoteException|IllegalStateException e) { - Slog.e(TAG, "Error stopping clatd on " + mBaseIface, e); - } - - mState = State.STOPPING; - } - - /** * Unregister as a base observer for the stacked interface, and clear internal state. */ - private void enterIdleState() { + private void leaveStartedState() { try { mNMService.unregisterObserver(this); - } catch(RemoteException|IllegalStateException e) { - Slog.e(TAG, "Error unregistering clatd observer on " + mBaseIface, e); + } catch (RemoteException | IllegalStateException e) { + Slog.e(TAG, "Error unregistering clatd observer on " + mBaseIface + ": " + e); } - mIface = null; mBaseIface = null; mState = State.IDLE; + if (requiresClat(mNetwork)) { + mState = State.DISCOVERING; + } else { + stopPrefixDiscovery(); + mState = State.IDLE; + } } - /** - * Starts the clat daemon. - */ - public void start() { + @VisibleForTesting + protected void start() { if (isStarted()) { Slog.e(TAG, "startClat: already started"); return; @@ -212,28 +238,92 @@ public class Nat464Xlat extends BaseNetworkObserver { enterStartingState(baseIface); } - /** - * Stops the clat daemon. - */ - public void stop() { + @VisibleForTesting + protected void stop() { if (!isStarted()) { + Slog.e(TAG, "stopClat: already stopped"); return; } + Slog.i(TAG, "Stopping clatd on " + mBaseIface); + try { + mNetd.clatdStop(mBaseIface); + } catch (RemoteException | ServiceSpecificException e) { + Slog.e(TAG, "Error stopping clatd on " + mBaseIface + ": " + e); + } + + String iface = mIface; + boolean wasRunning = isRunning(); + + // Change state before updating LinkProperties. handleUpdateLinkProperties ends up calling + // fixupLinkProperties, and if at that time the state is still RUNNING, fixupLinkProperties + // would wrongly inform ConnectivityService that there is still a stacked interface. + leaveStartedState(); + + if (wasRunning) { + LinkProperties lp = new LinkProperties(mNetwork.linkProperties); + lp.removeStackedLink(iface); + mNetwork.connService().handleUpdateLinkProperties(mNetwork, lp); + } + } + + private void startPrefixDiscovery() { + try { + mNetd.resolverStartPrefix64Discovery(getNetId()); + mState = State.DISCOVERING; + } catch (RemoteException | ServiceSpecificException e) { + Slog.e(TAG, "Error starting prefix discovery on netId " + getNetId() + ": " + e); + } + } - boolean wasStarting = isStarting(); - enterStoppingState(); - if (wasStarting) { - enterIdleState(); + private void stopPrefixDiscovery() { + try { + mNetd.resolverStopPrefix64Discovery(getNetId()); + } catch (RemoteException | ServiceSpecificException e) { + Slog.e(TAG, "Error stopping prefix discovery on netId " + getNetId() + ": " + e); } } /** + * Starts/stops NAT64 prefix discovery and clatd as necessary. + */ + public void update() { + // TODO: turn this class into a proper StateMachine. // http://b/126113090 + if (requiresClat(mNetwork)) { + if (!isPrefixDiscoveryStarted()) { + startPrefixDiscovery(); + } else if (shouldStartClat(mNetwork)) { + // NAT64 prefix detected. Start clatd. + // TODO: support the NAT64 prefix changing after it's been discovered. There is no + // need to support this at the moment because it cannot happen without changes to + // the Dns64Configuration code in netd. + start(); + } else { + // NAT64 prefix removed. Stop clatd and go back into DISCOVERING state. + stop(); + } + } else { + // Network no longer requires clat. Stop clat and prefix discovery. + if (isStarted()) { + stop(); + } else if (isPrefixDiscoveryStarted()) { + leaveStartedState(); + } + } + } + + public void setNat64Prefix(IpPrefix nat64Prefix) { + mNat64Prefix = nat64Prefix; + } + + /** * Copies the stacked clat link in oldLp, if any, to the passed LinkProperties. * This is necessary because the LinkProperties in mNetwork come from the transport layer, which * has no idea that 464xlat is running on top of it. */ public void fixupLinkProperties(LinkProperties oldLp, LinkProperties lp) { + lp.setNat64Prefix(mNat64Prefix); + if (!isRunning()) { return; } @@ -272,7 +362,7 @@ public class Nat464Xlat extends BaseNetworkObserver { try { InterfaceConfiguration config = mNMService.getInterfaceConfig(iface); return config.getLinkAddress(); - } catch(RemoteException|IllegalStateException e) { + } catch (RemoteException | IllegalStateException e) { Slog.e(TAG, "Error getting link properties: " + e); return null; } @@ -282,6 +372,20 @@ public class Nat464Xlat extends BaseNetworkObserver { * Adds stacked link on base link and transitions to RUNNING state. */ private void handleInterfaceLinkStateChanged(String iface, boolean up) { + // TODO: if we call start(), then stop(), then start() again, and the + // interfaceLinkStateChanged notification for the first start is delayed past the first + // stop, then the code becomes out of sync with system state and will behave incorrectly. + // + // This is not trivial to fix because: + // 1. It is not guaranteed that start() will eventually result in the interface coming up, + // because there could be an error starting clat (e.g., if the interface goes down before + // the packet socket can be bound). + // 2. If start is called multiple times, there is nothing in the interfaceLinkStateChanged + // notification that says which start() call the interface was created by. + // + // Once this code is converted to StateMachine, it will be possible to use deferMessage to + // ensure it stays in STARTING state until the interfaceLinkStateChanged notification fires, + // and possibly use a timeout (or provide some guarantees at the lower layer) to address #1. if (!isStarting() || !up || !Objects.equals(mIface, iface)) { return; } @@ -307,20 +411,16 @@ public class Nat464Xlat extends BaseNetworkObserver { if (!Objects.equals(mIface, iface)) { return; } - if (!isRunning() && !isStopping()) { + if (!isRunning()) { return; } Slog.i(TAG, "interface " + iface + " removed"); - if (!isStopping()) { - // Ensure clatd is stopped if stop() has not been called: this likely means that clatd - // has crashed. - enterStoppingState(); - } - enterIdleState(); - LinkProperties lp = new LinkProperties(mNetwork.linkProperties); - lp.removeStackedLink(iface); - mNetwork.connService().handleUpdateLinkProperties(mNetwork, lp); + // If we're running, and the interface was removed, then we didn't call stop(), and it's + // likely that clatd crashed. Ensure we call stop() so we can start clatd again. Calling + // stop() will also update LinkProperties, and if clatd crashed, the LinkProperties update + // will cause ConnectivityService to call start() again. + stop(); } @Override @@ -337,4 +437,9 @@ public class Nat464Xlat extends BaseNetworkObserver { public String toString() { return "mBaseIface: " + mBaseIface + ", mIface: " + mIface + ", mState: " + mState; } + + @VisibleForTesting + protected int getNetId() { + return mNetwork.network.netId; + } } diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index cd4ce2d142bb..6ef9fbbf0da8 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -238,7 +238,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { public final int factorySerialNumber; // Used by ConnectivityService to keep track of 464xlat. - public Nat464Xlat clatd; + public final Nat464Xlat clatd; // Set after asynchronous creation of the NetworkMonitor. private volatile INetworkMonitor mNetworkMonitor; @@ -246,8 +246,6 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { private static final String TAG = ConnectivityService.class.getSimpleName(); private static final boolean VDBG = false; private final ConnectivityService mConnService; - private final INetd mNetd; - private final INetworkManagementService mNMS; private final Context mContext; private final Handler mHandler; @@ -262,9 +260,8 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { linkProperties = lp; networkCapabilities = nc; currentScore = score; + clatd = new Nat464Xlat(this, netd, nms); mConnService = connService; - mNetd = netd; - mNMS = nms; mContext = context; mHandler = handler; networkMisc = misc; @@ -598,32 +595,6 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { for (LingerTimer timer : mLingerTimers) { pw.println(timer); } } - public void updateClat(INetworkManagementService netd) { - if (Nat464Xlat.requiresClat(this)) { - maybeStartClat(); - } else { - maybeStopClat(); - } - } - - /** Ensure clat has started for this network. */ - public void maybeStartClat() { - if (clatd != null && clatd.isStarted()) { - return; - } - clatd = new Nat464Xlat(this, mNetd, mNMS); - clatd.start(); - } - - /** Ensure clat has stopped for this network. */ - public void maybeStopClat() { - if (clatd == null) { - return; - } - clatd.stop(); - clatd = null; - } - public String toString() { return "NetworkAgentInfo{ ni{" + networkInfo + "} " + "network{" + network + "} nethandle{" + network.getNetworkHandle() + "} " diff --git a/services/core/java/com/android/server/connectivity/PermissionMonitor.java b/services/core/java/com/android/server/connectivity/PermissionMonitor.java index 420b23e6a39f..d84a4d2db993 100644 --- a/services/core/java/com/android/server/connectivity/PermissionMonitor.java +++ b/services/core/java/com/android/server/connectivity/PermissionMonitor.java @@ -19,10 +19,11 @@ package com.android.server.connectivity; import static android.Manifest.permission.CHANGE_NETWORK_STATE; import static android.Manifest.permission.CONNECTIVITY_INTERNAL; import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS; +import static android.Manifest.permission.INTERNET; import static android.Manifest.permission.NETWORK_STACK; -import static android.content.pm.ApplicationInfo.FLAG_SYSTEM; -import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; +import static android.Manifest.permission.UPDATE_DEVICE_STATS; import static android.content.pm.PackageManager.GET_PERMISSIONS; +import static android.content.pm.PackageManager.MATCH_ANY_USER; import static android.os.Process.INVALID_UID; import static android.os.Process.SYSTEM_UID; @@ -32,23 +33,31 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.PackageManagerInternal; import android.content.pm.UserInfo; -import android.net.Uri; +import android.net.INetd; +import android.net.util.NetdService; import android.os.Build; import android.os.INetworkManagementService; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.util.Log; +import android.util.Slog; +import android.util.SparseIntArray; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.ArrayUtils; +import com.android.server.LocalServices; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Map.Entry; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; /** @@ -75,6 +84,59 @@ public class PermissionMonitor { // Keys are App IDs. Values are true for SYSTEM permission and false for NETWORK permission. private final Map<Integer, Boolean> mApps = new HashMap<>(); + // Keys are App packageNames, Values are app uids. . We need to keep track of this information + // because PackageListObserver#onPackageRemoved does not pass the UID. + @GuardedBy("mPackageNameUidMap") + private final Map<String, Integer> mPackageNameUidMap = new HashMap<>(); + + private class PackageListObserver implements PackageManagerInternal.PackageListObserver { + @Override + public void onPackageAdded(String packageName) { + final PackageInfo app = getPackageInfo(packageName); + if (app == null) { + Slog.wtf(TAG, "Failed to get information of installed package: " + packageName); + return; + } + int uid = (app.applicationInfo != null) ? app.applicationInfo.uid : INVALID_UID; + if (uid == INVALID_UID) { + Slog.wtf(TAG, "Failed to get the uid of installed package: " + packageName + + "uid: " + uid); + return; + } + if (app.requestedPermissions == null) { + return; + } + sendPackagePermissionsForUid(uid, + filterPermission(Arrays.asList(app.requestedPermissions))); + synchronized (mPackageNameUidMap) { + mPackageNameUidMap.put(packageName, uid); + } + } + + @Override + public void onPackageRemoved(String packageName) { + int uid; + synchronized (mPackageNameUidMap) { + if (!mPackageNameUidMap.containsKey(packageName)) { + return; + } + uid = mPackageNameUidMap.get(packageName); + mPackageNameUidMap.remove(packageName); + } + int permission = 0; + String[] packages = mPackageManager.getPackagesForUid(uid); + if (packages != null && packages.length > 0) { + for (String name : packages) { + final PackageInfo app = getPackageInfo(name); + if (app != null && app.requestedPermissions != null) { + permission |= filterPermission(Arrays.asList(app.requestedPermissions)); + } + } + } + sendPackagePermissionsForUid(uid, permission); + } + } + public PermissionMonitor(Context context, INetworkManagementService netd) { mContext = context; mPackageManager = context.getPackageManager(); @@ -87,12 +149,21 @@ public class PermissionMonitor { public synchronized void startMonitoring() { log("Monitoring"); - List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS); + PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class); + if (pmi != null) { + pmi.getPackageList(new PackageListObserver()); + } else { + loge("failed to get the PackageManagerInternal service"); + } + List<PackageInfo> apps = mPackageManager.getInstalledPackages(GET_PERMISSIONS + | MATCH_ANY_USER); if (apps == null) { loge("No apps"); return; } + SparseIntArray netdPermsUids = new SparseIntArray(); + for (PackageInfo app : apps) { int uid = app.applicationInfo != null ? app.applicationInfo.uid : INVALID_UID; if (uid < 0) { @@ -110,6 +181,17 @@ public class PermissionMonitor { mApps.put(uid, hasRestrictedPermission); } } + + //TODO: unify the management of the permissions into one codepath. + if (app.requestedPermissions != null) { + int otherNetdPerms = filterPermission(Arrays.asList(app.requestedPermissions)); + if (otherNetdPerms != 0) { + netdPermsUids.put(uid, netdPermsUids.get(uid) | otherNetdPerms); + synchronized (mPackageNameUidMap) { + mPackageNameUidMap.put(app.applicationInfo.packageName, uid); + } + } + } } List<UserInfo> users = mUserManager.getUsers(true); // exclude dying users @@ -121,6 +203,7 @@ public class PermissionMonitor { log("Users: " + mUsers.size() + ", Apps: " + mApps.size()); update(mUsers, mApps, true); + sendPackagePermissionsToNetd(netdPermsUids); } @VisibleForTesting @@ -339,6 +422,107 @@ public class PermissionMonitor { } } + private static int filterPermission(List<String> requestedPermissions) { + int permissions = 0; + if (requestedPermissions.contains(INTERNET)) { + permissions |= INetd.PERMISSION_INTERNET; + } + if (requestedPermissions.contains(UPDATE_DEVICE_STATS)) { + permissions |= INetd.PERMISSION_UPDATE_DEVICE_STATS; + } + return permissions; + } + + private PackageInfo getPackageInfo(String packageName) { + try { + PackageInfo app = mPackageManager.getPackageInfo(packageName, GET_PERMISSIONS + | MATCH_ANY_USER); + return app; + } catch (NameNotFoundException e) { + // App not found. + loge("NameNotFoundException " + packageName); + return null; + } + } + + /** + * Called by PackageListObserver when a package is installed/uninstalled. Send the updated + * permission information to netd. + * + * @param uid the app uid of the package installed + * @param permissions the permissions the app requested and netd cares about. + * + * @hide + */ + private void sendPackagePermissionsForUid(int uid, int permissions) { + SparseIntArray netdPermissionsAppIds = new SparseIntArray(); + netdPermissionsAppIds.put(uid, permissions); + sendPackagePermissionsToNetd(netdPermissionsAppIds); + } + + /** + * Called by packageManagerService to send IPC to netd. Grant or revoke the INTERNET + * and/or UPDATE_DEVICE_STATS permission of the uids in array. + * + * @param netdPermissionsAppIds integer pairs of uids and the permission granted to it. If the + * permission is 0, revoke all permissions of that uid. + * + * @hide + */ + private void sendPackagePermissionsToNetd(SparseIntArray netdPermissionsAppIds) { + INetd netdService = NetdService.getInstance(); + if (netdService == null) { + Log.e(TAG, "Failed to get the netd service"); + return; + } + ArrayList<Integer> allPermissionAppIds = new ArrayList<>(); + ArrayList<Integer> internetPermissionAppIds = new ArrayList<>(); + ArrayList<Integer> updateStatsPermissionAppIds = new ArrayList<>(); + ArrayList<Integer> uninstalledAppIds = new ArrayList<>(); + for (int i = 0; i < netdPermissionsAppIds.size(); i++) { + int permissions = netdPermissionsAppIds.valueAt(i); + switch(permissions) { + case (INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS): + allPermissionAppIds.add(netdPermissionsAppIds.keyAt(i)); + break; + case INetd.PERMISSION_INTERNET: + internetPermissionAppIds.add(netdPermissionsAppIds.keyAt(i)); + break; + case INetd.PERMISSION_UPDATE_DEVICE_STATS: + updateStatsPermissionAppIds.add(netdPermissionsAppIds.keyAt(i)); + break; + case INetd.NO_PERMISSIONS: + uninstalledAppIds.add(netdPermissionsAppIds.keyAt(i)); + break; + default: + Log.e(TAG, "unknown permission type: " + permissions + "for uid: " + + netdPermissionsAppIds.keyAt(i)); + } + } + try { + // TODO: add a lock inside netd to protect IPC trafficSetNetPermForUids() + if (allPermissionAppIds.size() != 0) { + netdService.trafficSetNetPermForUids( + INetd.PERMISSION_INTERNET | INetd.PERMISSION_UPDATE_DEVICE_STATS, + ArrayUtils.convertToIntArray(allPermissionAppIds)); + } + if (internetPermissionAppIds.size() != 0) { + netdService.trafficSetNetPermForUids(INetd.PERMISSION_INTERNET, + ArrayUtils.convertToIntArray(internetPermissionAppIds)); + } + if (updateStatsPermissionAppIds.size() != 0) { + netdService.trafficSetNetPermForUids(INetd.PERMISSION_UPDATE_DEVICE_STATS, + ArrayUtils.convertToIntArray(updateStatsPermissionAppIds)); + } + if (uninstalledAppIds.size() != 0) { + netdService.trafficSetNetPermForUids(INetd.NO_PERMISSIONS, + ArrayUtils.convertToIntArray(uninstalledAppIds)); + } + } catch (RemoteException e) { + Log.e(TAG, "Pass appId list of special permission failed." + e); + } + } + private static void log(String s) { if (DBG) { Log.d(TAG, s); diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java index bfa7f9d439e3..4e4b15fc5b9b 100644 --- a/services/core/java/com/android/server/content/ContentService.java +++ b/services/core/java/com/android/server/content/ContentService.java @@ -19,6 +19,7 @@ package com.android.server.content; import android.Manifest; import android.accounts.Account; import android.annotation.Nullable; +import android.annotation.RequiresPermission; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.AppOpsManager; @@ -1174,6 +1175,7 @@ public final class ContentService extends IContentService.Stub { } @Override + @RequiresPermission(android.Manifest.permission.CACHE_CONTENT) public void putCache(String packageName, Uri key, Bundle value, int userId) { Bundle.setDefusable(value, true); enforceCrossUserPermission(userId, TAG); @@ -1196,6 +1198,7 @@ public final class ContentService extends IContentService.Stub { } @Override + @RequiresPermission(android.Manifest.permission.CACHE_CONTENT) public Bundle getCache(String packageName, Uri key, int userId) { enforceCrossUserPermission(userId, TAG); mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CACHE_CONTENT, TAG); diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index d8956c20051a..9f80a83038af 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -916,15 +916,17 @@ public class SyncManager { extras = new Bundle(); } extras.size(); // Force unpacel. - mLogger.log("scheduleSync: account=", requestedAccount, - " u", userId, - " authority=", requestedAuthority, - " reason=", reason, - " extras=", extras, - " cuid=", callingUid, " cpid=", callingPid, " cpkg=", callingPackage, - " mdm=", minDelayMillis, - " ciar=", checkIfAccountReady, - " sef=", syncExemptionFlag); + if (Log.isLoggable(TAG, Log.VERBOSE)) { + mLogger.log("scheduleSync: account=", requestedAccount, + " u", userId, + " authority=", requestedAuthority, + " reason=", reason, + " extras=", extras, + " cuid=", callingUid, " cpid=", callingPid, " cpkg=", callingPackage, + " mdm=", minDelayMillis, + " ciar=", checkIfAccountReady, + " sef=", syncExemptionFlag); + } AccountAndUser[] accounts = null; if (requestedAccount != null) { diff --git a/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java b/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java index 726362a7df36..d04f92039f4c 100644 --- a/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java +++ b/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java @@ -16,7 +16,9 @@ package com.android.server.contentcapture; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.content.ContentCaptureOptions; import android.os.Bundle; import android.os.IBinder; @@ -41,4 +43,12 @@ public abstract class ContentCaptureManagerInternal { */ public abstract boolean sendActivityAssistData(@UserIdInt int userId, @NonNull IBinder activityToken, @NonNull Bundle data); + + /** + * Gets the content capture options for the given user and package, or {@code null} if the + * package is not whitelisted by the service. + */ + @Nullable + public abstract ContentCaptureOptions getOptionsForPackage(@UserIdInt int userId, + @NonNull String packageName); } diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java index 31b497d001eb..36d9c0e4478e 100644 --- a/services/core/java/com/android/server/display/ColorFade.java +++ b/services/core/java/com/android/server/display/ColorFade.java @@ -582,7 +582,7 @@ final class ColorFade { final SurfaceControl.Builder builder = new SurfaceControl.Builder(mSurfaceSession).setName("ColorFade"); if (mMode == MODE_FADE) { - builder.setColorLayer(true); + builder.setColorLayer(); } else { builder.setBufferSize(mDisplayWidth, mDisplayHeight); } diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index b2509e908493..1aaaf41679f7 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -37,7 +37,9 @@ import android.content.pm.ParceledListSlice; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.ColorSpace; +import android.graphics.GraphicBuffer; import android.graphics.Point; +import android.graphics.Rect; import android.hardware.SensorManager; import android.hardware.display.AmbientBrightnessDayStats; import android.hardware.display.BrightnessChangeEvent; @@ -94,7 +96,6 @@ import com.android.server.DisplayThread; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.UiThread; -import com.android.server.display.ColorDisplayService.ColorDisplayServiceInternal; import com.android.server.wm.SurfaceAnimationThread; import com.android.server.wm.WindowManagerInternal; @@ -355,7 +356,6 @@ public final class DisplayManagerService extends SystemService { publishBinderService(Context.DISPLAY_SERVICE, new BinderService(), true /*allowIsolated*/); publishLocalService(DisplayManagerInternal.class, new LocalService()); - publishLocalService(DisplayTransformManager.class, new DisplayTransformManager()); } @Override @@ -1277,7 +1277,14 @@ public final class DisplayManagerService extends SystemService { if (token == null) { return false; } - SurfaceControl.screenshot(token, outSurface); + final GraphicBuffer gb = SurfaceControl.screenshotToBufferWithSecureLayersUnsafe( + token, new Rect(), 0 /* width */, 0 /* height */, false /* useIdentityTransform */, + 0 /* rotation */); + try { + outSurface.attachAndQueueBuffer(gb); + } catch (RuntimeException e) { + Slog.w(TAG, "Failed to take screenshot - " + e.getMessage()); + } return true; } @@ -1482,7 +1489,7 @@ public final class DisplayManagerService extends SystemService { pw.println(" mSingleDisplayDemoMode=" + mSingleDisplayDemoMode); pw.println(" mWifiDisplayScanRequestCount=" + mWifiDisplayScanRequestCount); pw.println(" mStableDisplaySize=" + mStableDisplaySize); - + pw.println(" mMinimumBrightnessCurve=" + mMinimumBrightnessCurve); IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); ipw.increaseIndent(); @@ -1526,13 +1533,6 @@ public final class DisplayManagerService extends SystemService { pw.println(); mPersistentDataStore.dump(pw); - - final ColorDisplayServiceInternal cds = LocalServices.getService( - ColorDisplayServiceInternal.class); - if (cds != null) { - pw.println(); - cds.dump(pw); - } } } diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 489194726c5a..77df10bf536a 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -65,7 +65,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { new LongSparseArray<LocalDisplayDevice>(); @SuppressWarnings("unused") // Becomes active at instantiation time. - private HotplugDisplayEventReceiver mHotplugReceiver; + private PhysicalDisplayEventReceiver mPhysicalDisplayEventReceiver; + // Called with SyncRoot lock held. public LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, @@ -77,7 +78,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { public void registerLocked() { super.registerLocked(); - mHotplugReceiver = new HotplugDisplayEventReceiver(getHandler().getLooper()); + mPhysicalDisplayEventReceiver = new PhysicalDisplayEventReceiver(getHandler().getLooper()); for (long physicalDisplayId : SurfaceControl.getPhysicalDisplayIds()) { tryConnectDisplayLocked(physicalDisplayId); @@ -727,8 +728,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { } } - private final class HotplugDisplayEventReceiver extends DisplayEventReceiver { - public HotplugDisplayEventReceiver(Looper looper) { + private final class PhysicalDisplayEventReceiver extends DisplayEventReceiver { + PhysicalDisplayEventReceiver(Looper looper) { super(looper, VSYNC_SOURCE_APP); } @@ -742,5 +743,15 @@ final class LocalDisplayAdapter extends DisplayAdapter { } } } + + @Override + public void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId) { + if (DEBUG) { + Slog.d(TAG, "onConfigChanged(" + + "timestampNanos=" + timestampNanos + + ", builtInDisplayId=" + physicalDisplayId + + ", configId=" + configId + ")"); + } + } } } diff --git a/services/core/java/com/android/server/display/OWNERS b/services/core/java/com/android/server/display/OWNERS index 0d64dbd83a34..25cb5ae0891a 100644 --- a/services/core/java/com/android/server/display/OWNERS +++ b/services/core/java/com/android/server/display/OWNERS @@ -2,5 +2,3 @@ michaelwr@google.com dangittik@google.com hackbod@google.com ogunwale@google.com - -per-file ColorDisplayService.java=christyfranks@google.com diff --git a/services/core/java/com/android/server/display/AppSaturationController.java b/services/core/java/com/android/server/display/color/AppSaturationController.java index 5d5e4f70ea7f..e42be02d4a9e 100644 --- a/services/core/java/com/android/server/display/AppSaturationController.java +++ b/services/core/java/com/android/server/display/color/AppSaturationController.java @@ -14,14 +14,14 @@ * limitations under the License. */ -package com.android.server.display; +package com.android.server.display.color; import android.annotation.UserIdInt; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.server.display.ColorDisplayService.ColorTransformController; +import com.android.server.display.color.ColorDisplayService.ColorTransformController; import java.io.PrintWriter; import java.lang.ref.WeakReference; diff --git a/services/core/java/com/android/server/display/ColorDisplayService.java b/services/core/java/com/android/server/display/color/ColorDisplayService.java index 7dd3b363810d..b1e5510b3adc 100644 --- a/services/core/java/com/android/server/display/ColorDisplayService.java +++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.display; +package com.android.server.display.color; import static android.hardware.display.ColorDisplayManager.AUTO_MODE_CUSTOM_TIME; import static android.hardware.display.ColorDisplayManager.AUTO_MODE_DISABLED; @@ -24,9 +24,9 @@ import static android.hardware.display.ColorDisplayManager.COLOR_MODE_BOOSTED; import static android.hardware.display.ColorDisplayManager.COLOR_MODE_NATURAL; import static android.hardware.display.ColorDisplayManager.COLOR_MODE_SATURATED; -import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE; -import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY; -import static com.android.server.display.DisplayTransformManager.LEVEL_COLOR_MATRIX_SATURATION; +import static com.android.server.display.color.DisplayTransformManager.LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE; +import static com.android.server.display.color.DisplayTransformManager.LEVEL_COLOR_MATRIX_NIGHT_DISPLAY; +import static com.android.server.display.color.DisplayTransformManager.LEVEL_COLOR_MATRIX_SATURATION; import android.Manifest; import android.animation.Animator; @@ -233,6 +233,7 @@ public final class ColorDisplayService extends SystemService { public void onStart() { publishBinderService(Context.COLOR_DISPLAY_SERVICE, new BinderService()); publishLocalService(ColorDisplayServiceInternal.class, new ColorDisplayServiceInternal()); + publishLocalService(DisplayTransformManager.class, new DisplayTransformManager()); } @Override @@ -632,9 +633,9 @@ public final class ColorDisplayService extends SystemService { @VisibleForTesting void updateDisplayWhiteBalanceStatus() { boolean oldActivated = mDisplayWhiteBalanceTintController.isActivated(); - mDisplayWhiteBalanceTintController.setActivated(isDisplayWhiteBalanceSettingEnabled() && - !mNightDisplayTintController.isActivated() && - DisplayTransformManager.needsLinearColorMatrix()); + mDisplayWhiteBalanceTintController.setActivated(isDisplayWhiteBalanceSettingEnabled() + && !mNightDisplayTintController.isActivated() + && DisplayTransformManager.needsLinearColorMatrix()); boolean activated = mDisplayWhiteBalanceTintController.isActivated(); if (mDisplayWhiteBalanceListener != null && oldActivated != activated) { @@ -819,7 +820,7 @@ public final class ColorDisplayService extends SystemService { /** * Get the current color mode from system properties, or return -1 if invalid. * - * See {@link com.android.server.display.DisplayTransformManager} + * See {@link DisplayTransformManager} */ private @ColorMode int getCurrentColorModeFromSystemProperties() { final int displayColorSetting = SystemProperties.getInt("persist.sys.sf.native_mode", 0); @@ -851,7 +852,7 @@ public final class ColorDisplayService extends SystemService { private void dumpInternal(PrintWriter pw) { pw.println("COLOR DISPLAY MANAGER dumpsys (color_display)"); - pw.println("Night Display:"); + pw.println("Night display:"); if (mNightDisplayTintController.isAvailable(getContext())) { pw.println(" Activated: " + mNightDisplayTintController.isActivated()); pw.println(" Color temp: " + mNightDisplayTintController.getColorTemperature()); @@ -923,14 +924,15 @@ public final class ColorDisplayService extends SystemService { if (mLastActivatedTime != null) { // Maintain the existing activated state if within the current period. - if (mLastActivatedTime.isBefore(now) && mLastActivatedTime.isAfter(start) + if (mLastActivatedTime.isBefore(now) + && mLastActivatedTime.isAfter(start) && (mLastActivatedTime.isAfter(end) || now.isBefore(end))) { activate = mNightDisplayTintController.isActivatedSetting(); } } - if (mNightDisplayTintController.isActivatedStateNotSet() || ( - mNightDisplayTintController.isActivated() != activate)) { + if (mNightDisplayTintController.isActivatedStateNotSet() + || (mNightDisplayTintController.isActivated() != activate)) { mNightDisplayTintController.setActivated(activate); } @@ -1305,10 +1307,11 @@ public final class ColorDisplayService extends SystemService { } final class DisplayWhiteBalanceTintController extends TintController { + // Three chromaticity coordinates per color: X, Y, and Z - private final int NUM_VALUES_PER_PRIMARY = 3; + private static final int NUM_VALUES_PER_PRIMARY = 3; // Four colors: red, green, blue, and white - private final int NUM_DISPLAY_PRIMARIES_VALS = 4 * NUM_VALUES_PER_PRIMARY; + private static final int NUM_DISPLAY_PRIMARIES_VALS = 4 * NUM_VALUES_PER_PRIMARY; private final Object mLock = new Object(); @VisibleForTesting @@ -1478,25 +1481,25 @@ public final class ColorDisplayService extends SystemService { pw.println(" mTemperatureMax = " + mTemperatureMax); pw.println(" mTemperatureDefault = " + mTemperatureDefault); pw.println(" mCurrentColorTemperature = " + mCurrentColorTemperature); - pw.println(" mCurrentColorTemperatureXYZ = " + - matrixToString(mCurrentColorTemperatureXYZ, 3)); - pw.println(" mDisplayColorSpaceRGB RGB-to-XYZ = " + - matrixToString(mDisplayColorSpaceRGB.getTransform(), 3)); - pw.println(" mChromaticAdaptationMatrix = " + - matrixToString(mChromaticAdaptationMatrix, 3)); - pw.println(" mDisplayColorSpaceRGB XYZ-to-RGB = " + - matrixToString(mDisplayColorSpaceRGB.getInverseTransform(), 3)); - pw.println(" mMatrixDisplayWhiteBalance = " + - matrixToString(mMatrixDisplayWhiteBalance, 4)); + pw.println(" mCurrentColorTemperatureXYZ = " + + matrixToString(mCurrentColorTemperatureXYZ, 3)); + pw.println(" mDisplayColorSpaceRGB RGB-to-XYZ = " + + matrixToString(mDisplayColorSpaceRGB.getTransform(), 3)); + pw.println(" mChromaticAdaptationMatrix = " + + matrixToString(mChromaticAdaptationMatrix, 3)); + pw.println(" mDisplayColorSpaceRGB XYZ-to-RGB = " + + matrixToString(mDisplayColorSpaceRGB.getInverseTransform(), 3)); + pw.println(" mMatrixDisplayWhiteBalance = " + + matrixToString(mMatrixDisplayWhiteBalance, 4)); } } private ColorSpace.Rgb makeRgbColorSpaceFromXYZ(float[] redGreenBlueXYZ, float[] whiteXYZ) { return new ColorSpace.Rgb( - "Display Color Space", - redGreenBlueXYZ, - whiteXYZ, - 2.2f // gamma, unused for display white balance + "Display Color Space", + redGreenBlueXYZ, + whiteXYZ, + 2.2f // gamma, unused for display white balance ); } @@ -1507,19 +1510,19 @@ public final class ColorDisplayService extends SystemService { } DisplayPrimaries primaries = SurfaceControl.getDisplayNativePrimaries(displayToken); - if (primaries == null || primaries.red == null || primaries.green == null || - primaries.blue == null || primaries.white == null) { + if (primaries == null || primaries.red == null || primaries.green == null + || primaries.blue == null || primaries.white == null) { return null; } return makeRgbColorSpaceFromXYZ( - new float[] { - primaries.red.X, primaries.red.Y, primaries.red.Z, - primaries.green.X, primaries.green.Y, primaries.green.Z, - primaries.blue.X, primaries.blue.Y, primaries.blue.Z, + new float[]{ + primaries.red.X, primaries.red.Y, primaries.red.Z, + primaries.green.X, primaries.green.Y, primaries.green.Z, + primaries.blue.X, primaries.blue.Y, primaries.blue.Z, }, - new float[] { primaries.white.X, primaries.white.Y, primaries.white.Z } - ); + new float[]{primaries.white.X, primaries.white.Y, primaries.white.Z} + ); } private ColorSpace.Rgb getDisplayColorSpaceFromResources(Resources res) { @@ -1540,7 +1543,7 @@ public final class ColorDisplayService extends SystemService { return makeRgbColorSpaceFromXYZ(displayRedGreenBlueXYZ, displayWhiteXYZ); } - }; + } /** * Local service that allows color transforms to be enabled from other system services. @@ -1572,10 +1575,6 @@ public final class ColorDisplayService extends SystemService { return mDisplayWhiteBalanceTintController.isActivated(); } - public void dump(PrintWriter pw) { - mDisplayWhiteBalanceTintController.dump(pw); - } - /** * Adds a {@link WeakReference<ColorTransformController>} for a newly started activity, and * invokes {@link ColorTransformController#applyAppSaturation(float[], float[])} if needed. diff --git a/services/core/java/com/android/server/display/DisplayTransformManager.java b/services/core/java/com/android/server/display/color/DisplayTransformManager.java index ef92401bf3fd..026837f07356 100644 --- a/services/core/java/com/android/server/display/DisplayTransformManager.java +++ b/services/core/java/com/android/server/display/color/DisplayTransformManager.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.display; +package com.android.server.display.color; import android.app.ActivityTaskManager; import android.hardware.display.ColorDisplayManager; @@ -117,12 +117,12 @@ public class DisplayTransformManager { /** * Sets and applies a current color transform matrix for a given level. * <p> - * Note: all color transforms are first composed to a single matrix in ascending order based - * on level before being applied to the display. + * Note: all color transforms are first composed to a single matrix in ascending order based on + * level before being applied to the display. * * @param level the level used to identify and compose the color transform (low -> high) * @param value the 4x4 color transform matrix (in column-major order), or {@code null} to - * remove the color transform matrix associated with the provided level + * remove the color transform matrix associated with the provided level */ public void setColorMatrix(int level, float[] value) { if (value != null && value.length != 16) { @@ -235,13 +235,15 @@ public class DisplayTransformManager { } /** - * Return true when the specified colorMode requires the color matrix to - * work in linear space. + * Return true when the specified colorMode requires the color matrix to work in linear space. */ public static boolean needsLinearColorMatrix(int colorMode) { return colorMode != ColorDisplayManager.COLOR_MODE_SATURATED; } + /** + * Sets color mode and updates night display transform values. + */ public boolean setColorMode(int colorMode, float[] nightDisplayMatrix) { if (colorMode == ColorDisplayManager.COLOR_MODE_NATURAL) { applySaturation(COLOR_SATURATION_NATURAL); @@ -264,8 +266,8 @@ public class DisplayTransformManager { } /** - * Returns whether the screen is color managed via SurfaceFlinger's - * {@link #SURFACE_FLINGER_TRANSACTION_QUERY_COLOR_MANAGED}. + * Returns whether the screen is color managed via SurfaceFlinger's {@link + * #SURFACE_FLINGER_TRANSACTION_QUERY_COLOR_MANAGED}. */ public boolean isDeviceColorManaged() { final IBinder flinger = ServiceManager.getService(SURFACE_FLINGER); diff --git a/services/core/java/com/android/server/display/color/OWNERS b/services/core/java/com/android/server/display/color/OWNERS new file mode 100644 index 000000000000..27adf127d880 --- /dev/null +++ b/services/core/java/com/android/server/display/color/OWNERS @@ -0,0 +1,4 @@ +christyfranks@google.com +justinklaassen@google.com + +per-file DisplayTransformManager.java=michaelwr@google.com
\ No newline at end of file diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java index d95e92b71357..e7181e23f15d 100644 --- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java +++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java @@ -22,7 +22,7 @@ import android.util.Spline; import com.android.internal.util.Preconditions; import com.android.server.LocalServices; -import com.android.server.display.ColorDisplayService.ColorDisplayServiceInternal; +import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal; import com.android.server.display.utils.History; import java.io.PrintWriter; diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceSettings.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceSettings.java index a53e91c622fb..1b7251c19778 100644 --- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceSettings.java +++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceSettings.java @@ -29,8 +29,8 @@ import android.util.Slog; import com.android.internal.util.Preconditions; import com.android.server.LocalServices; -import com.android.server.display.ColorDisplayService; -import com.android.server.display.ColorDisplayService.ColorDisplayServiceInternal; +import com.android.server.display.color.ColorDisplayService; +import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal; import com.android.server.display.whitebalance.DisplayWhiteBalanceController.Callbacks; import java.io.PrintWriter; diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java index bffa8f486db7..3052e3cdcd21 100644 --- a/services/core/java/com/android/server/dreams/DreamManagerService.java +++ b/services/core/java/com/android/server/dreams/DreamManagerService.java @@ -28,6 +28,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ServiceInfo; import android.database.ContentObserver; +import android.hardware.display.AmbientDisplayConfiguration; import android.hardware.input.InputManagerInternal; import android.os.Binder; import android.os.Build; @@ -46,7 +47,6 @@ import android.service.dreams.IDreamManager; import android.util.Slog; import android.view.Display; -import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.internal.util.DumpUtils; import com.android.server.FgThread; import com.android.server.LocalServices; diff --git a/services/core/java/com/android/server/gpu/GpuService.java b/services/core/java/com/android/server/gpu/GpuService.java index 6899c3ffcbb1..647727f795da 100644 --- a/services/core/java/com/android/server/gpu/GpuService.java +++ b/services/core/java/com/android/server/gpu/GpuService.java @@ -62,6 +62,7 @@ public class GpuService extends SystemService { private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0"; private static final String GAME_DRIVER_WHITELIST_FILENAME = "whitelist.txt"; + private static final String GAME_DRIVER_SPHAL_LIBRARIES_FILENAME = "sphal_libraries.txt"; private static final int BASE64_FLAGS = Base64.NO_PADDING | Base64.NO_WRAP; private final Context mContext; @@ -162,6 +163,25 @@ public class GpuService extends SystemService { } } + private static void assetToSettingsGlobal(Context context, Context driverContext, + String fileName, String settingsGlobal, CharSequence delimiter) { + try { + final BufferedReader reader = new BufferedReader( + new InputStreamReader(driverContext.getAssets().open(fileName))); + final ArrayList<String> assetStrings = new ArrayList<>(); + for (String assetString; (assetString = reader.readLine()) != null; ) { + assetStrings.add(assetString); + } + Settings.Global.putString(context.getContentResolver(), + settingsGlobal, + String.join(delimiter, assetStrings)); + } catch (IOException e) { + if (DEBUG) { + Slog.w(TAG, "Failed to load " + fileName + ", abort."); + } + } + } + private void fetchGameDriverPackageProperties() { final ApplicationInfo driverInfo; try { @@ -186,29 +206,25 @@ public class GpuService extends SystemService { // Reset the whitelist. Settings.Global.putString(mContentResolver, Settings.Global.GAME_DRIVER_WHITELIST, ""); + // Reset the sphal libraries + Settings.Global.putString(mContentResolver, + Settings.Global.GAME_DRIVER_SPHAL_LIBRARIES, ""); mGameDriverVersionCode = driverInfo.longVersionCode; try { final Context driverContext = mContext.createPackageContext(mDriverPackageName, Context.CONTEXT_RESTRICTED); - final BufferedReader reader = new BufferedReader( - new InputStreamReader(driverContext.getAssets() - .open(GAME_DRIVER_WHITELIST_FILENAME))); - final ArrayList<String> whitelistedPackageNames = new ArrayList<>(); - for (String packageName; (packageName = reader.readLine()) != null; ) { - whitelistedPackageNames.add(packageName); - } - Settings.Global.putString(mContentResolver, - Settings.Global.GAME_DRIVER_WHITELIST, - String.join(",", whitelistedPackageNames)); + + assetToSettingsGlobal(mContext, driverContext, GAME_DRIVER_WHITELIST_FILENAME, + Settings.Global.GAME_DRIVER_WHITELIST, ","); + + assetToSettingsGlobal(mContext, driverContext, GAME_DRIVER_SPHAL_LIBRARIES_FILENAME, + Settings.Global.GAME_DRIVER_SPHAL_LIBRARIES, ":"); + } catch (PackageManager.NameNotFoundException e) { if (DEBUG) { Slog.w(TAG, "driver package '" + mDriverPackageName + "' not installed"); } - } catch (IOException e) { - if (DEBUG) { - Slog.w(TAG, "Failed to load whitelist driver package, abort."); - } } } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java index 67109867c074..2026957b6c44 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java @@ -31,6 +31,8 @@ import android.media.AudioFormat; import android.media.AudioManager; import android.media.AudioSystem; import android.media.tv.TvContract; +import android.media.tv.TvInputInfo; +import android.media.tv.TvInputManager.TvInputCallback; import android.os.SystemProperties; import android.provider.Settings.Global; import android.util.Slog; @@ -84,10 +86,13 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { .get(Constants.PROPERTY_SYSTEM_AUDIO_DEVICE_ARC_PORT, "0").contains("tvinput"); // Keeps the mapping (HDMI port ID to TV input URI) to keep track of the TV inputs ready to - // accept input switching request from HDMI devices. Requests for which the corresponding - // input ID is not yet registered by TV input framework need to be buffered for delayed - // processing. - private final HashMap<Integer, String> mTvInputs = new HashMap<>(); + // accept input switching request from HDMI devices. + @GuardedBy("mLock") + private final HashMap<Integer, String> mPortIdToTvInputs = new HashMap<>(); + + // A map from TV input id to HDMI device info. + @GuardedBy("mLock") + private final HashMap<String, HdmiDeviceInfo> mTvInputsToDeviceInfo = new HashMap<>(); // Copy of mDeviceInfos to guarantee thread-safety. @GuardedBy("mLock") @@ -103,14 +108,57 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { mService.readBooleanSetting(Global.HDMI_CEC_SWITCH_ENABLED, false); mSystemAudioControlFeatureEnabled = mService.readBooleanSetting(Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, true); - // TODO(amyjojo): Maintain a portId to TvinputId map. - mTvInputs.put(2, "com.droidlogic.tvinput/.services.Hdmi1InputService/HW5"); - mTvInputs.put(4, "com.droidlogic.tvinput/.services.Hdmi2InputService/HW6"); - mTvInputs.put(1, "com.droidlogic.tvinput/.services.Hdmi3InputService/HW7"); } private static final String SHORT_AUDIO_DESCRIPTOR_CONFIG_PATH = "/vendor/etc/sadConfig.xml"; + private final TvInputCallback mTvInputCallback = new TvInputCallback() { + @Override + public void onInputAdded(String inputId) { + addOrUpdateTvInput(inputId); + } + + @Override + public void onInputRemoved(String inputId) { + removeTvInput(inputId); + } + + @Override + public void onInputUpdated(String inputId) { + addOrUpdateTvInput(inputId); + } + }; + + @ServiceThreadOnly + private void addOrUpdateTvInput(String inputId) { + assertRunOnServiceThread(); + synchronized (mLock) { + TvInputInfo tvInfo = mService.getTvInputManager().getTvInputInfo(inputId); + if (tvInfo == null) { + return; + } + HdmiDeviceInfo info = tvInfo.getHdmiDeviceInfo(); + if (info == null) { + return; + } + mPortIdToTvInputs.put(info.getPortId(), inputId); + mTvInputsToDeviceInfo.put(inputId, info); + } + } + + @ServiceThreadOnly + private void removeTvInput(String inputId) { + assertRunOnServiceThread(); + synchronized (mLock) { + if (mTvInputsToDeviceInfo.get(inputId) == null) { + return; + } + int portId = mTvInputsToDeviceInfo.get(inputId).getPortId(); + mPortIdToTvInputs.remove(portId); + mTvInputsToDeviceInfo.remove(inputId); + } + } + /** * Called when a device is newly added or a new device is detected or * an existing device is updated. @@ -248,17 +296,29 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { } if (mService.getPortInfo(portId).getType() == HdmiPortInfo.PORT_OUTPUT) { mCecMessageCache.flushAll(); - } else { - if (connected) { - launchDeviceDiscovery(); - } else { - // TODO(amyjojo): remove device from mDeviceInfo + } else if (!connected && mPortIdToTvInputs.get(portId) != null) { + String tvInputId = mPortIdToTvInputs.get(portId); + HdmiDeviceInfo info = mTvInputsToDeviceInfo.get(tvInputId); + if (info == null) { + return; } + // Update with TIF on the device removal. TIF callback will update + // mPortIdToTvInputs and mPortIdToTvInputs. + removeCecDevice(info.getLogicalAddress()); } } @Override @ServiceThreadOnly + protected void disableDevice(boolean initiatedByCec, PendingActionClearedCallback callback) { + super.disableDevice(initiatedByCec, callback); + assertRunOnServiceThread(); + mService.unregisterTvInputCallback(mTvInputCallback); + // TODO(amyjojo): check disableDevice and onStandby behaviors per spec + } + + @Override + @ServiceThreadOnly protected void onStandby(boolean initiatedByCec, int standbyAction) { assertRunOnServiceThread(); mTvSystemAudioModeSupport = false; @@ -284,6 +344,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { mAddress, mService.getPhysicalAddress(), mDeviceType)); mService.sendCecCommand( HdmiCecMessageBuilder.buildDeviceVendorIdCommand(mAddress, mService.getVendorId())); + mService.registerTvInputCallback(mTvInputCallback); int systemAudioControlOnPowerOnProp = SystemProperties.getInt( PROPERTY_SYSTEM_AUDIO_CONTROL_ON_POWER_ON, @@ -996,6 +1057,17 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { } // Wake up device mService.wakeUp(); + // If Audio device is the active source or is on the active path, + // enable system audio mode without querying TV's support on sam. + // This is per HDMI spec 1.4b CEC 13.15.4.2. + if (mService.pathToPortId(getActiveSource().physicalAddress) + != Constants.INVALID_PORT_ID) { + setSystemAudioMode(true); + mService.sendCecCommand( + HdmiCecMessageBuilder.buildSetSystemAudioMode( + mAddress, Constants.ADDR_BROADCAST, true)); + return; + } // Check if TV supports System Audio Control. // Handle broadcasting setSystemAudioMode on or aborting message on callback. queryTvSystemAudioModeSupport(new TvSystemAudioModeSupportedCallback() { @@ -1064,9 +1136,9 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { setLocalActivePort(portId); return; } else { - String uri = mTvInputs.get(portId); + String uri = mPortIdToTvInputs.get(portId); if (uri != null) { - switchToTvInput(mTvInputs.get(portId)); + switchToTvInput(uri); } else { HdmiLogger.debug("Port number does not match any Tv Input."); return; @@ -1215,7 +1287,8 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { pw.println("mArcIntentUsed: " + mArcIntentUsed); pw.println("mRoutingPort: " + getRoutingPort()); pw.println("mLocalActivePort: " + getLocalActivePort()); - HdmiUtils.dumpMap(pw, "mTvInputs:", mTvInputs); + HdmiUtils.dumpMap(pw, "mPortIdToTvInputs:", mPortIdToTvInputs); + HdmiUtils.dumpMap(pw, "mTvInputsToDeviceInfo:", mTvInputsToDeviceInfo); HdmiUtils.dumpSparseArray(pw, "mDeviceInfos:", mDeviceInfos); pw.decreaseIndent(); super.dump(pw); diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 072238ee7f55..f5adb0111559 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -576,7 +576,8 @@ public class HdmiControlService extends SystemService { Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED, Global.MHL_INPUT_SWITCHING_ENABLED, Global.MHL_POWER_CHARGE_ENABLED, - Global.HDMI_CEC_SWITCH_ENABLED + Global.HDMI_CEC_SWITCH_ENABLED, + Global.DEVICE_NAME }; for (String s : settings) { resolver.registerContentObserver(Global.getUriFor(s), false, mSettingsObserver, @@ -642,6 +643,10 @@ public class HdmiControlService extends SystemService { case Global.MHL_POWER_CHARGE_ENABLED: mMhlController.setOption(OPTION_MHL_POWER_CHARGE, toInt(enabled)); break; + case Global.DEVICE_NAME: + String deviceName = readStringSetting(option, Build.MODEL); + setDisplayName(deviceName); + break; } } } @@ -670,6 +675,15 @@ public class HdmiControlService extends SystemService { return SystemProperties.getBoolean(key, defVal); } + String readStringSetting(String key, String defVal) { + ContentResolver cr = getContext().getContentResolver(); + String content = Global.getString(cr, key); + if (TextUtils.isEmpty(content)) { + return defVal; + } + return content; + } + private void initializeCec(int initiatedBy) { mAddressAllocated = false; mCecController.setOption(OptionKey.SYSTEM_CEC_CONTROL, true); @@ -1178,13 +1192,29 @@ public class HdmiControlService extends SystemService { } private HdmiDeviceInfo createDeviceInfo(int logicalAddress, int deviceType, int powerStatus) { - // TODO: find better name instead of model name. - String displayName = Build.MODEL; + String displayName = readStringSetting(Global.DEVICE_NAME, Build.MODEL); return new HdmiDeviceInfo(logicalAddress, getPhysicalAddress(), pathToPortId(getPhysicalAddress()), deviceType, getVendorId(), displayName, powerStatus); } + // Set the display name in HdmiDeviceInfo of the current devices to content provided by + // Global.DEVICE_NAME. Only set and broadcast if the new name is different. + private void setDisplayName(String newDisplayName) { + for (HdmiCecLocalDevice device : getAllLocalDevices()) { + HdmiDeviceInfo deviceInfo = device.getDeviceInfo(); + if (deviceInfo.getDisplayName().equals(newDisplayName)) { + continue; + } + device.setDeviceInfo(new HdmiDeviceInfo( + deviceInfo.getLogicalAddress(), deviceInfo.getPhysicalAddress(), + deviceInfo.getPortId(), deviceInfo.getDeviceType(), deviceInfo.getVendorId(), + newDisplayName, deviceInfo.getDevicePowerStatus())); + sendCecCommand(HdmiCecMessageBuilder.buildSetOsdNameCommand( + device.mAddress, Constants.ADDR_TV, newDisplayName)); + } + } + @ServiceThreadOnly void handleMhlHotplugEvent(int portId, boolean connected) { assertRunOnServiceThread(); diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java index 2bfb31f6e74f..d2ccfcc732a9 100644 --- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java +++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java @@ -133,7 +133,8 @@ public abstract class AbstractMasterSystemService<M extends AbstractMasterSystem * {@link com.android.internal.infra.AbstractRemoteService} instances, or * {@code null} when the service doesn't bind to remote services. * @param disallowProperty when not {@code null}, defines a {@link UserManager} restriction that - * disables the service. + * disables the service. <b>NOTE: </b> you'll also need to add it to + * {@code UserRestrictionsUtils.USER_RESTRICTIONS}. */ protected AbstractMasterSystemService(@NonNull Context context, @Nullable ServiceNameResolver serviceNameResolver, diff --git a/services/core/java/com/android/server/job/controllers/StorageController.java b/services/core/java/com/android/server/job/controllers/StorageController.java index c2ae53f69309..51187dff4d59 100644 --- a/services/core/java/com/android/server/job/controllers/StorageController.java +++ b/services/core/java/com/android/server/job/controllers/StorageController.java @@ -81,20 +81,16 @@ public final class StorageController extends StateController { synchronized (mLock) { for (int i = mTrackedTasks.size() - 1; i >= 0; i--) { final JobStatus ts = mTrackedTasks.valueAt(i); - boolean previous = ts.setStorageNotLowConstraintSatisfied(storageNotLow); - if (previous != storageNotLow) { - reportChange = true; - } + reportChange |= ts.setStorageNotLowConstraintSatisfied(storageNotLow); } } - // Let the scheduler know that state has changed. This may or may not result in an - // execution. - if (reportChange) { - mStateChangedListener.onControllerStateChanged(); - } - // Also tell the scheduler that any ready jobs should be flushed. if (storageNotLow) { + // Tell the scheduler that any ready jobs should be flushed. mStateChangedListener.onRunJobNow(null); + } else if (reportChange) { + // Let the scheduler know that state has changed. This may or may not result in an + // execution. + mStateChangedListener.onControllerStateChanged(); } } @@ -143,9 +139,10 @@ public final class StorageController extends StateController { + sElapsedRealtimeClock.millis()); } mStorageLow = true; + maybeReportNewStorageState(); } else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) { if (DEBUG) { - Slog.d(TAG, "Available stoage high enough to do work. @ " + Slog.d(TAG, "Available storage high enough to do work. @ " + sElapsedRealtimeClock.millis()); } mStorageLow = false; diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index 243b6fe7a30e..ae915037947e 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -140,6 +140,9 @@ public class GnssLocationProvider extends AbstractLocationProvider implements private static final int LOCATION_HAS_SPEED_ACCURACY = 64; private static final int LOCATION_HAS_BEARING_ACCURACY = 128; + // these need to match ElapsedRealtimeFlags enum in types.hal + private static final int ELAPSED_REALTIME_HAS_TIMESTAMP_NS = 1; + // IMPORTANT - the GPS_DELETE_* symbols here must match GnssAidingData enum in IGnss.hal private static final int GPS_DELETE_EPHEMERIS = 0x0001; private static final int GPS_DELETE_ALMANAC = 0x0002; @@ -527,6 +530,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements mPowerManager.getPowerSaveState(ServiceType.LOCATION); switch (result.locationMode) { case PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF: + case PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF: // If we are in battery saver mode and the screen is off, disable GPS. disableGps |= result.batterySaverEnabled && !mPowerManager.isInteractive(); break; @@ -683,7 +687,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements } } - private void handleRequestLocation(boolean independentFromGnss) { + private void handleRequestLocation(boolean independentFromGnss, boolean isUserEmergency) { if (isRequestLocationRateLimited()) { if (DEBUG) { Log.d(TAG, "RequestLocation is denied due to too frequent requests."); @@ -719,9 +723,17 @@ public class GnssLocationProvider extends AbstractLocationProvider implements String.format( "GNSS HAL Requesting location updates from %s provider for %d millis.", provider, durationMillis)); + + LocationRequest locationRequest = LocationRequest.createFromDeprecatedProvider(provider, + LOCATION_UPDATE_MIN_TIME_INTERVAL_MILLIS, /* minDistance= */ 0, + /* singleShot= */ false); + + // Ignore location settings if in emergency mode. + if (isUserEmergency && mNIHandler.getInEmergency()) { + locationRequest.setLocationSettingsIgnored(true); + } try { - locationManager.requestLocationUpdates(provider, - LOCATION_UPDATE_MIN_TIME_INTERVAL_MILLIS, /*minDistance=*/ 0, + locationManager.requestLocationUpdates(locationRequest, locationListener, mHandler.getLooper()); locationListener.mNumLocationUpdateRequest++; mHandler.postDelayed(() -> { @@ -756,10 +768,16 @@ public class GnssLocationProvider extends AbstractLocationProvider implements float speedAccuracyMetersPerSecond = location.getSpeedAccuracyMetersPerSecond(); float bearingAccuracyDegrees = location.getBearingAccuracyDegrees(); long timestamp = location.getTime(); - native_inject_best_location(gnssLocationFlags, latitudeDegrees, longitudeDegrees, - altitudeMeters, speedMetersPerSec, bearingDegrees, horizontalAccuracyMeters, - verticalAccuracyMeters, speedAccuracyMetersPerSecond, bearingAccuracyDegrees, - timestamp); + + int elapsedRealtimeFlags = ELAPSED_REALTIME_HAS_TIMESTAMP_NS; + long elapsedRealtimeNanos = location.getElapsedRealtimeNanos(); + + native_inject_best_location( + gnssLocationFlags, latitudeDegrees, longitudeDegrees, + altitudeMeters, speedMetersPerSec, bearingDegrees, + horizontalAccuracyMeters, verticalAccuracyMeters, + speedAccuracyMetersPerSecond, bearingAccuracyDegrees, timestamp, + elapsedRealtimeFlags, elapsedRealtimeNanos); } /** Returns true if the location request is too frequent. */ @@ -1259,9 +1277,6 @@ public class GnssLocationProvider extends AbstractLocationProvider implements if (VERBOSE) Log.v(TAG, "reportLocation " + location.toString()); - // It would be nice to push the elapsed real-time timestamp - // further down the stack, but this is still useful - location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos()); location.setExtras(mLocationExtras.getBundle()); reportLocation(location); @@ -1821,11 +1836,13 @@ public class GnssLocationProvider extends AbstractLocationProvider implements } @NativeEntryPoint - private void requestLocation(boolean independentFromGnss) { + private void requestLocation(boolean independentFromGnss, boolean isUserEmergency) { if (DEBUG) { - Log.d(TAG, "requestLocation. independentFromGnss: " + independentFromGnss); + Log.d(TAG, "requestLocation. independentFromGnss: " + independentFromGnss + + ", isUserEmergency: " + + isUserEmergency); } - sendMessage(REQUEST_LOCATION, 0, independentFromGnss); + sendMessage(REQUEST_LOCATION, independentFromGnss ? 1 : 0, isUserEmergency); } @NativeEntryPoint @@ -1916,7 +1933,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements mNtpTimeHelper.retrieveAndInjectNtpTime(); break; case REQUEST_LOCATION: - handleRequestLocation((boolean) msg.obj); + handleRequestLocation(msg.arg1 == 1, (boolean) msg.obj); break; case DOWNLOAD_XTRA_DATA: handleDownloadXtraData(); @@ -2150,17 +2167,11 @@ public class GnssLocationProvider extends AbstractLocationProvider implements private native int native_read_nmea(byte[] buffer, int bufferSize); private native void native_inject_best_location( - int gnssLocationFlags, - double latitudeDegrees, - double longitudeDegrees, - double altitudeMeters, - float speedMetersPerSec, - float bearingDegrees, - float horizontalAccuracyMeters, - float verticalAccuracyMeters, - float speedAccuracyMetersPerSecond, - float bearingAccuracyDegrees, - long timestamp); + int gnssLocationFlags, double latitudeDegrees, double longitudeDegrees, + double altitudeMeters, float speedMetersPerSec, float bearingDegrees, + float horizontalAccuracyMeters, float verticalAccuracyMeters, + float speedAccuracyMetersPerSecond, float bearingAccuracyDegrees, + long timestamp, int elapsedRealtimeFlags, long elapsedRealtimeNanos); private native void native_inject_location(double latitude, double longitude, float accuracy); @@ -2187,4 +2198,4 @@ public class GnssLocationProvider extends AbstractLocationProvider implements int lac, int cid); private native void native_agps_set_id(int type, String setid); -}
\ No newline at end of file +} diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java index afe34737fdee..00e1ffa7450d 100644 --- a/services/core/java/com/android/server/location/LocationProviderProxy.java +++ b/services/core/java/com/android/server/location/LocationProviderProxy.java @@ -34,13 +34,11 @@ import com.android.internal.location.ILocationProvider; import com.android.internal.location.ILocationProviderManager; import com.android.internal.location.ProviderProperties; import com.android.internal.location.ProviderRequest; -import com.android.internal.os.TransferPipe; import com.android.server.FgThread; import com.android.server.LocationManagerService; import com.android.server.ServiceWatcher; import java.io.FileDescriptor; -import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; @@ -189,14 +187,6 @@ public class LocationProviderProxy extends AbstractLocationProvider { pw.println(" additional packages=" + mProviderPackages); } } - mServiceWatcher.runOnBinderBlocking(binder -> { - try { - TransferPipe.dumpAsync(binder, fd, args); - } catch (IOException | RemoteException e) { - pw.println(" <failed to dump location provider: " + e + ">"); - } - return null; - }, null); } @Override diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 4b4788cd4a16..ee968c87e753 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -123,7 +123,6 @@ import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintWriter; -import java.nio.charset.StandardCharsets; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.KeyStoreException; @@ -194,6 +193,8 @@ public class LockSettingsService extends ILockSettings.Stub { protected IGateKeeperService mGateKeeperService; protected IAuthSecret mAuthSecretService; + private static final String GSI_RUNNING_PROP = "ro.gsid.image_running"; + /** * The UIDs that are used for system credential storage in keystore. */ @@ -275,7 +276,7 @@ public class LockSettingsService extends ILockSettings.Stub { * @param managedUserPassword Managed profile original password (when it has separated lock). * NULL when it does not have a separated lock before. */ - public void tieManagedProfileLockIfNecessary(int managedUserId, String managedUserPassword) { + public void tieManagedProfileLockIfNecessary(int managedUserId, byte[] managedUserPassword) { if (DEBUG) Slog.v(TAG, "Check child profile lock for user: " + managedUserId); // Only for managed profile if (!mUserManager.getUserInfo(managedUserId).isManagedProfile()) { @@ -310,7 +311,12 @@ public class LockSettingsService extends ILockSettings.Stub { byte[] randomLockSeed = new byte[] {}; try { randomLockSeed = SecureRandom.getInstance("SHA1PRNG").generateSeed(40); - String newPassword = String.valueOf(HexEncoding.encode(randomLockSeed)); + char[] newPasswordChars = HexEncoding.encode(randomLockSeed); + byte[] newPassword = new byte[newPasswordChars.length]; + for (int i = 0; i < newPasswordChars.length; i++) { + newPassword[i] = (byte) newPasswordChars[i]; + } + Arrays.fill(newPasswordChars, '\u0000'); final int quality = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC; setLockCredentialInternal(newPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, managedUserPassword, quality, managedUserId); @@ -319,6 +325,7 @@ public class LockSettingsService extends ILockSettings.Stub { // password directly, so we always store a password. setLong(LockPatternUtils.PASSWORD_TYPE_KEY, quality, managedUserId); tieProfileLockToParent(managedUserId, newPassword); + Arrays.fill(newPassword, (byte) 0); } catch (NoSuchAlgorithmException | RemoteException e) { Slog.e(TAG, "Fail to tie managed profile", e); // Nothing client can do to fix this issue, so we do not throw exception out @@ -405,9 +412,17 @@ public class LockSettingsService extends ILockSettings.Stub { return new SyntheticPasswordManager(getContext(), storage, getUserManager()); } + public boolean hasBiometrics() { + return BiometricManager.hasBiometrics(mContext); + } + public int binderGetCallingUid() { return Binder.getCallingUid(); } + + public boolean isGsiRunning() { + return SystemProperties.getInt(GSI_RUNNING_PROP, 0) > 0; + } } public LockSettingsService(Context context) { @@ -608,7 +623,7 @@ public class LockSettingsService extends ILockSettings.Stub { try { final long handle = getSyntheticPasswordHandleLocked(userId); - final String noCredential = null; + final byte[] noCredential = null; AuthenticationResult result = mSpManager.unwrapPasswordBasedSyntheticPassword( getGateKeeperService(), handle, noCredential, userId, null); @@ -946,7 +961,7 @@ public class LockSettingsService extends ILockSettings.Stub { @Override public void setSeparateProfileChallengeEnabled(int userId, boolean enabled, - String managedUserPassword) { + byte[] managedUserPassword) { checkWritePermission(userId); if (!mLockPatternUtils.hasSecureLockScreen()) { throw new UnsupportedOperationException( @@ -959,8 +974,8 @@ public class LockSettingsService extends ILockSettings.Stub { } @GuardedBy("mSeparateChallengeLock") - private void setSeparateProfileChallengeEnabledLocked(@UserIdInt int userId, boolean enabled, - String managedUserPassword) { + private void setSeparateProfileChallengeEnabledLocked(@UserIdInt int userId, + boolean enabled, byte[] managedUserPassword) { final boolean old = getBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, false, userId); setBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, enabled, userId); try { @@ -1103,24 +1118,28 @@ public class LockSettingsService extends ILockSettings.Stub { return mStorage.hasCredential(userId); } - private void setKeystorePassword(String password, int userHandle) { + private void setKeystorePassword(byte[] password, int userHandle) { final KeyStore ks = KeyStore.getInstance(); - ks.onUserPasswordChanged(userHandle, password); + // TODO(b/120484642): Update keystore to accept byte[] passwords + String passwordString = password == null ? null : new String(password); + ks.onUserPasswordChanged(userHandle, passwordString); } - private void unlockKeystore(String password, int userHandle) { + private void unlockKeystore(byte[] password, int userHandle) { if (DEBUG) Slog.v(TAG, "Unlock keystore for user: " + userHandle); + // TODO(b/120484642): Update keystore to accept byte[] passwords + String passwordString = password == null ? null : new String(password); final KeyStore ks = KeyStore.getInstance(); - ks.unlock(userHandle, password); + ks.unlock(userHandle, passwordString); } @VisibleForTesting - protected String getDecryptedPasswordForTiedProfile(int userId) + protected byte[] getDecryptedPasswordForTiedProfile(int userId) throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, CertificateException, IOException { - if (DEBUG) Slog.v(TAG, "Get child profile decrytped key"); + if (DEBUG) Slog.v(TAG, "Get child profile decrypted key"); byte[] storedData = mStorage.readChildProfileLock(userId); if (storedData == null) { throw new FileNotFoundException("Child profile lock file not found"); @@ -1139,7 +1158,7 @@ public class LockSettingsService extends ILockSettings.Stub { cipher.init(Cipher.DECRYPT_MODE, decryptionKey, new GCMParameterSpec(128, iv)); decryptionResult = cipher.doFinal(encryptedPassword); - return new String(decryptionResult, StandardCharsets.UTF_8); + return decryptionResult; } private void unlockChildProfile(int profileHandle, boolean ignoreUserNotAuthenticated) @@ -1216,11 +1235,11 @@ public class LockSettingsService extends ILockSettings.Stub { && mUserManager.isUserRunning(userInfo.id); } - private Map<Integer, String> getDecryptedPasswordsForAllTiedProfiles(int userId) { + private Map<Integer, byte[]> getDecryptedPasswordsForAllTiedProfiles(int userId) { if (mUserManager.getUserInfo(userId).isManagedProfile()) { return null; } - Map<Integer, String> result = new ArrayMap<Integer, String>(); + Map<Integer, byte[]> result = new ArrayMap<Integer, byte[]>(); final List<UserInfo> profiles = mUserManager.getProfiles(userId); final int size = profiles.size(); for (int i = 0; i < size; i++) { @@ -1258,7 +1277,7 @@ public class LockSettingsService extends ILockSettings.Stub { * terminates when the user is a managed profile. */ private void synchronizeUnifiedWorkChallengeForProfiles(int userId, - Map<Integer, String> profilePasswordMap) throws RemoteException { + Map<Integer, byte[]> profilePasswordMap) throws RemoteException { if (mUserManager.getUserInfo(userId).isManagedProfile()) { return; } @@ -1307,9 +1326,10 @@ public class LockSettingsService extends ILockSettings.Stub { // This method should be called by LockPatternUtil only, all internal methods in this class // should call setLockCredentialInternal. @Override - public void setLockCredential(String credential, int type, String savedCredential, - int requestedQuality, int userId) + public void setLockCredential(byte[] credential, int type, + byte[] savedCredential, int requestedQuality, int userId) throws RemoteException { + if (!mLockPatternUtils.hasSecureLockScreen()) { throw new UnsupportedOperationException( "This operation requires secure lock screen feature"); @@ -1323,14 +1343,14 @@ public class LockSettingsService extends ILockSettings.Stub { notifySeparateProfileChallengeChanged(userId); } - private void setLockCredentialInternal(String credential, int credentialType, - String savedCredential, int requestedQuality, int userId) throws RemoteException { + private void setLockCredentialInternal(byte[] credential, int credentialType, + byte[] savedCredential, int requestedQuality, int userId) throws RemoteException { // Normalize savedCredential and credential such that empty string is always represented // as null. - if (TextUtils.isEmpty(savedCredential)) { + if (savedCredential == null || savedCredential.length == 0) { savedCredential = null; } - if (TextUtils.isEmpty(credential)) { + if (credential == null || credential.length == 0) { credential = null; } synchronized (mSpManager) { @@ -1399,7 +1419,7 @@ public class LockSettingsService extends ILockSettings.Stub { mStorage.writeCredentialHash(willStore, userId); // push new secret and auth token to vold GateKeeperResponse gkResponse = getGateKeeperService() - .verifyChallenge(userId, 0, willStore.hash, credential.getBytes()); + .verifyChallenge(userId, 0, willStore.hash, credential); setUserKeyProtection(userId, credential, convertResponse(gkResponse)); fixateNewestUserKeyAuth(userId); // Refresh the auth token @@ -1419,9 +1439,8 @@ public class LockSettingsService extends ILockSettings.Stub { } @VisibleForTesting - protected void tieProfileLockToParent(int userId, String password) { + protected void tieProfileLockToParent(int userId, byte[] password) { if (DEBUG) Slog.v(TAG, "tieProfileLockToParent for user: " + userId); - byte[] randomLockSeed = password.getBytes(StandardCharsets.UTF_8); byte[] encryptionResult; byte[] iv; try { @@ -1455,7 +1474,7 @@ public class LockSettingsService extends ILockSettings.Stub { KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_GCM + "/" + KeyProperties.ENCRYPTION_PADDING_NONE); cipher.init(Cipher.ENCRYPT_MODE, keyStoreEncryptionKey); - encryptionResult = cipher.doFinal(randomLockSeed); + encryptionResult = cipher.doFinal(password); iv = cipher.getIV(); } finally { // The original key can now be discarded. @@ -1480,17 +1499,11 @@ public class LockSettingsService extends ILockSettings.Stub { } private byte[] enrollCredential(byte[] enrolledHandle, - String enrolledCredential, String toEnroll, int userId) + byte[] enrolledCredential, byte[] toEnroll, int userId) throws RemoteException { checkWritePermission(userId); - byte[] enrolledCredentialBytes = enrolledCredential == null - ? null - : enrolledCredential.getBytes(); - byte[] toEnrollBytes = toEnroll == null - ? null - : toEnroll.getBytes(); GateKeeperResponse response = getGateKeeperService().enroll(userId, enrolledHandle, - enrolledCredentialBytes, toEnrollBytes); + enrolledCredential, toEnroll); if (response == null) { return null; @@ -1511,7 +1524,7 @@ public class LockSettingsService extends ILockSettings.Stub { addUserKeyAuth(userId, null, key); } - private void setUserKeyProtection(int userId, String credential, VerifyCredentialResponse vcr) + private void setUserKeyProtection(int userId, byte[] credential, VerifyCredentialResponse vcr) throws RemoteException { if (DEBUG) Slog.d(TAG, "setUserKeyProtection: user=" + userId); if (vcr == null) { @@ -1533,16 +1546,15 @@ public class LockSettingsService extends ILockSettings.Stub { addUserKeyAuth(userId, null, null); } - private static byte[] secretFromCredential(String credential) throws RemoteException { + private static byte[] secretFromCredential(byte[] credential) throws RemoteException { try { MessageDigest digest = MessageDigest.getInstance("SHA-512"); // Personalize the hash - byte[] personalization = "Android FBE credential hash" - .getBytes(StandardCharsets.UTF_8); + byte[] personalization = "Android FBE credential hash".getBytes(); // Pad it to the block size of the hash function personalization = Arrays.copyOf(personalization, 128); digest.update(personalization); - digest.update(credential.getBytes(StandardCharsets.UTF_8)); + digest.update(credential); return digest.digest(); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("NoSuchAlgorithmException for SHA-512"); @@ -1578,7 +1590,7 @@ public class LockSettingsService extends ILockSettings.Stub { checkWritePermission(userId); if (DEBUG) Slog.v(TAG, "Reset keystore for user: " + userId); int managedUserId = -1; - String managedUserDecryptedPassword = null; + byte[] managedUserDecryptedPassword = null; final List<UserInfo> profiles = mUserManager.getProfiles(userId); for (UserInfo pi : profiles) { // Unlock managed profile with unified lock @@ -1615,17 +1627,20 @@ public class LockSettingsService extends ILockSettings.Stub { tieProfileLockToParent(managedUserId, managedUserDecryptedPassword); } } + if (managedUserDecryptedPassword != null && managedUserDecryptedPassword.length > 0) { + Arrays.fill(managedUserDecryptedPassword, (byte) 0); + } } @Override - public VerifyCredentialResponse checkCredential(String credential, int type, int userId, + public VerifyCredentialResponse checkCredential(byte[] credential, int type, int userId, ICheckCredentialProgressCallback progressCallback) throws RemoteException { checkPasswordReadPermission(userId); return doVerifyCredential(credential, type, false, 0, userId, progressCallback); } @Override - public VerifyCredentialResponse verifyCredential(String credential, int type, long challenge, + public VerifyCredentialResponse verifyCredential(byte[] credential, int type, long challenge, int userId) throws RemoteException { checkPasswordReadPermission(userId); return doVerifyCredential(credential, type, true, challenge, userId, @@ -1636,10 +1651,10 @@ public class LockSettingsService extends ILockSettings.Stub { * Verify user credential and unlock the user. Fix pattern bug by deprecating the old base zero * format. */ - private VerifyCredentialResponse doVerifyCredential(String credential, int credentialType, + private VerifyCredentialResponse doVerifyCredential(byte[] credential, int credentialType, boolean hasChallenge, long challenge, int userId, ICheckCredentialProgressCallback progressCallback) throws RemoteException { - if (TextUtils.isEmpty(credential)) { + if (credential == null || credential.length == 0) { throw new IllegalArgumentException("Credential can't be null or empty"); } if (userId == USER_FRP && Settings.Global.getInt(mContext.getContentResolver(), @@ -1674,9 +1689,9 @@ public class LockSettingsService extends ILockSettings.Stub { boolean shouldReEnrollBaseZero = storedHash.type == LockPatternUtils.CREDENTIAL_TYPE_PATTERN && storedHash.isBaseZeroPattern; - String credentialToVerify; + byte[] credentialToVerify; if (shouldReEnrollBaseZero) { - credentialToVerify = LockPatternUtils.patternStringToBaseZero(credential); + credentialToVerify = LockPatternUtils.patternByteArrayToBaseZero(credential); } else { credentialToVerify = credential; } @@ -1696,7 +1711,7 @@ public class LockSettingsService extends ILockSettings.Stub { } @Override - public VerifyCredentialResponse verifyTiedProfileChallenge(String credential, int type, + public VerifyCredentialResponse verifyTiedProfileChallenge(byte[] credential, int type, long challenge, int userId) throws RemoteException { checkPasswordReadPermission(userId); if (!isManagedProfileWithUnifiedLock(userId)) { @@ -1738,14 +1753,15 @@ public class LockSettingsService extends ILockSettings.Stub { * hash to GK. */ private VerifyCredentialResponse verifyCredential(int userId, CredentialHash storedHash, - String credential, boolean hasChallenge, long challenge, + byte[] credential, boolean hasChallenge, long challenge, ICheckCredentialProgressCallback progressCallback) throws RemoteException { - if ((storedHash == null || storedHash.hash.length == 0) && TextUtils.isEmpty(credential)) { + if ((storedHash == null || storedHash.hash.length == 0) + && (credential == null || credential.length == 0)) { // don't need to pass empty credentials to GateKeeper return VerifyCredentialResponse.OK; } - if (storedHash == null || TextUtils.isEmpty(credential)) { + if (storedHash == null || credential == null || credential.length == 0) { return VerifyCredentialResponse.ERROR; } @@ -1756,14 +1772,14 @@ public class LockSettingsService extends ILockSettings.Stub { if (storedHash.version == CredentialHash.VERSION_LEGACY) { final byte[] hash; if (storedHash.type == LockPatternUtils.CREDENTIAL_TYPE_PATTERN) { - hash = LockPatternUtils.patternToHash(LockPatternUtils.stringToPattern(credential)); + hash = LockPatternUtils.patternToHash( + LockPatternUtils.byteArrayToPattern(credential)); } else { - hash = mLockPatternUtils.legacyPasswordToHash(credential, userId) - .getBytes(StandardCharsets.UTF_8); + hash = mLockPatternUtils.legacyPasswordToHash(credential, userId).getBytes(); } if (Arrays.equals(hash, storedHash.hash)) { if (storedHash.type == LockPatternUtils.CREDENTIAL_TYPE_PATTERN) { - unlockKeystore(LockPatternUtils.patternStringToBaseZero(credential), userId); + unlockKeystore(LockPatternUtils.patternByteArrayToBaseZero(credential), userId); } else { unlockKeystore(credential, userId); } @@ -1794,7 +1810,7 @@ public class LockSettingsService extends ILockSettings.Stub { } } GateKeeperResponse gateKeeperResponse = getGateKeeperService() - .verifyChallenge(userId, challenge, storedHash.hash, credential.getBytes()); + .verifyChallenge(userId, challenge, storedHash.hash, credential); VerifyCredentialResponse response = convertResponse(gateKeeperResponse); boolean shouldReEnroll = gateKeeperResponse.getShouldReEnroll(); @@ -1853,7 +1869,7 @@ public class LockSettingsService extends ILockSettings.Stub { * Call this method to notify DPMS regarding the latest password metric. This should be called * when the user is authenticating or when a new password is being set. */ - private void notifyActivePasswordMetricsAvailable(String password, @UserIdInt int userId) { + private void notifyActivePasswordMetricsAvailable(byte[] password, @UserIdInt int userId) { final PasswordMetrics metrics; if (password == null) { metrics = new PasswordMetrics(); @@ -1902,6 +1918,7 @@ public class LockSettingsService extends ILockSettings.Stub { // service can't connect to vold, it restarts, and then the new instance // does successfully connect. final IStorageManager service = mInjector.getStorageManager(); + // TODO(b/120484642): Update vold to return a password as a byte array String password; long identity = Binder.clearCallingIdentity(); try { @@ -1916,8 +1933,8 @@ public class LockSettingsService extends ILockSettings.Stub { try { if (mLockPatternUtils.isLockPatternEnabled(userId)) { - if (checkCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, userId, - null /* progressCallback */) + if (checkCredential(password.getBytes(), LockPatternUtils.CREDENTIAL_TYPE_PATTERN, + userId, null /* progressCallback */) .getResponseCode() == GateKeeperResponse.RESPONSE_OK) { return true; } @@ -1927,8 +1944,8 @@ public class LockSettingsService extends ILockSettings.Stub { try { if (mLockPatternUtils.isLockPasswordEnabled(userId)) { - if (checkCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, userId, - null /* progressCallback */) + if (checkCredential(password.getBytes(), LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, + userId, null /* progressCallback */) .getResponseCode() == GateKeeperResponse.RESPONSE_OK) { return true; } @@ -1956,7 +1973,8 @@ public class LockSettingsService extends ILockSettings.Stub { } catch (RemoteException ex) { Slog.w(TAG, "unable to clear GK secure user id"); } - if (unknownUser || mUserManager.getUserInfo(userId).isManagedProfile()) { + UserInfo userInfo = mUserManager.getUserInfo(userId); + if (unknownUser || userInfo == null || userInfo.isManagedProfile()) { removeKeystoreProfileKey(userId); } } @@ -2217,6 +2235,11 @@ public class LockSettingsService extends ILockSettings.Stub { } tryRemoveUserFromSpCacheLater(userId); + if (mInjector.isGsiRunning()) { + Slog.w(TAG, "AuthSecret disabled in GSI"); + return; + } + // Pass the primary user's auth secret to the HAL if (mAuthSecretService != null && mUserManager.getUserInfo(userId).isPrimary()) { try { @@ -2307,7 +2330,7 @@ public class LockSettingsService extends ILockSettings.Stub { @GuardedBy("mSpManager") @VisibleForTesting protected AuthenticationToken initializeSyntheticPasswordLocked(byte[] credentialHash, - String credential, int credentialType, int requestedQuality, + byte[] credential, int credentialType, int requestedQuality, int userId) throws RemoteException { Slog.i(TAG, "Initialize SyntheticPassword for user: " + userId); final AuthenticationToken auth = mSpManager.newSyntheticPasswordAndSid( @@ -2368,7 +2391,7 @@ public class LockSettingsService extends ILockSettings.Stub { setLong(SYNTHETIC_PASSWORD_ENABLED_KEY, 1, UserHandle.USER_SYSTEM); } - private VerifyCredentialResponse spBasedDoVerifyCredential(String userCredential, int + private VerifyCredentialResponse spBasedDoVerifyCredential(byte[] userCredential, int credentialType, boolean hasChallenge, long challenge, int userId, ICheckCredentialProgressCallback progressCallback) throws RemoteException { if (DEBUG) Slog.d(TAG, "spBasedDoVerifyCredential: user=" + userId); @@ -2423,7 +2446,7 @@ public class LockSettingsService extends ILockSettings.Stub { notifyActivePasswordMetricsAvailable(userCredential, userId); unlockKeystore(authResult.authToken.deriveKeyStorePassword(), userId); // Reset lockout - if (BiometricManager.hasBiometrics(mContext)) { + if (mInjector.hasBiometrics()) { BiometricManager bm = mContext.getSystemService(BiometricManager.class); Slog.i(TAG, "Resetting lockout, length: " + authResult.gkResponse.getPayload().length); @@ -2467,12 +2490,12 @@ public class LockSettingsService extends ILockSettings.Stub { * added back when new password is set in future. */ @GuardedBy("mSpManager") - private long setLockCredentialWithAuthTokenLocked(String credential, int credentialType, + private long setLockCredentialWithAuthTokenLocked(byte[] credential, int credentialType, AuthenticationToken auth, int requestedQuality, int userId) throws RemoteException { if (DEBUG) Slog.d(TAG, "setLockCredentialWithAuthTokenLocked: user=" + userId); long newHandle = mSpManager.createPasswordBasedSyntheticPassword(getGateKeeperService(), credential, credentialType, auth, requestedQuality, userId); - final Map<Integer, String> profilePasswords; + final Map<Integer, byte[]> profilePasswords; if (credential != null) { // // not needed by synchronizeUnifiedWorkChallengeForProfiles() profilePasswords = null; @@ -2511,12 +2534,19 @@ public class LockSettingsService extends ILockSettings.Stub { synchronizeUnifiedWorkChallengeForProfiles(userId, profilePasswords); notifyActivePasswordMetricsAvailable(credential, userId); + + if (profilePasswords != null) { + for (Map.Entry<Integer, byte[]> entry : profilePasswords.entrySet()) { + Arrays.fill(entry.getValue(), (byte) 0); + } + } + return newHandle; } @GuardedBy("mSpManager") - private void spBasedSetLockCredentialInternalLocked(String credential, int credentialType, - String savedCredential, int requestedQuality, int userId) throws RemoteException { + private void spBasedSetLockCredentialInternalLocked(byte[] credential, int credentialType, + byte[] savedCredential, int requestedQuality, int userId) throws RemoteException { if (DEBUG) Slog.d(TAG, "spBasedSetLockCredentialInternalLocked: user=" + userId); if (isManagedProfileWithUnifiedLock(userId)) { // get credential from keystore when managed profile has unified lock @@ -2589,9 +2619,9 @@ public class LockSettingsService extends ILockSettings.Stub { * If user is a managed profile with unified challenge, currentCredential is ignored. */ @Override - public byte[] getHashFactor(String currentCredential, int userId) throws RemoteException { + public byte[] getHashFactor(byte[] currentCredential, int userId) throws RemoteException { checkPasswordReadPermission(userId); - if (TextUtils.isEmpty(currentCredential)) { + if (currentCredential == null || currentCredential.length == 0) { currentCredential = null; } if (isManagedProfileWithUnifiedLock(userId)) { @@ -2685,7 +2715,7 @@ public class LockSettingsService extends ILockSettings.Stub { } } - private boolean setLockCredentialWithToken(String credential, int type, long tokenHandle, + private boolean setLockCredentialWithToken(byte[] credential, int type, long tokenHandle, byte[] token, int requestedQuality, int userId) throws RemoteException { boolean result; synchronized (mSpManager) { @@ -2705,7 +2735,7 @@ public class LockSettingsService extends ILockSettings.Stub { return result; } - private boolean setLockCredentialWithTokenInternal(String credential, int type, + private boolean setLockCredentialWithTokenInternal(byte[] credential, int type, long tokenHandle, byte[] token, int requestedQuality, int userId) throws RemoteException { final AuthenticationResult result; synchronized (mSpManager) { @@ -2932,8 +2962,8 @@ public class LockSettingsService extends ILockSettings.Stub { } @Override - public boolean setLockCredentialWithToken(String credential, int type, long tokenHandle, - byte[] token, int requestedQuality, int userId) { + public boolean setLockCredentialWithToken(byte[] credential, int type, + long tokenHandle, byte[] token, int requestedQuality, int userId) { if (!mLockPatternUtils.hasSecureLockScreen()) { throw new UnsupportedOperationException( "This operation requires secure lock screen feature."); diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java index 6163077e1acf..ee22264112ab 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java @@ -190,22 +190,30 @@ class LockSettingsShellCommand extends ShellCommand { } private void runSetPattern() { - mLockPatternUtils.saveLockPattern(stringToPattern(mNew), mOld, mCurrentUserId); + byte[] oldBytes = mOld != null ? mOld.getBytes() : null; + mLockPatternUtils.saveLockPattern(stringToPattern(mNew), oldBytes, mCurrentUserId); getOutPrintWriter().println("Pattern set to '" + mNew + "'"); } private void runSetPassword() { - mLockPatternUtils.saveLockPassword(mNew, mOld, PASSWORD_QUALITY_ALPHABETIC, mCurrentUserId); + byte[] newBytes = mNew != null ? mNew.getBytes() : null; + byte[] oldBytes = mOld != null ? mOld.getBytes() : null; + mLockPatternUtils.saveLockPassword(newBytes, oldBytes, PASSWORD_QUALITY_ALPHABETIC, + mCurrentUserId); getOutPrintWriter().println("Password set to '" + mNew + "'"); } private void runSetPin() { - mLockPatternUtils.saveLockPassword(mNew, mOld, PASSWORD_QUALITY_NUMERIC, mCurrentUserId); + byte[] newBytes = mNew != null ? mNew.getBytes() : null; + byte[] oldBytes = mOld != null ? mOld.getBytes() : null; + mLockPatternUtils.saveLockPassword(newBytes, oldBytes, PASSWORD_QUALITY_NUMERIC, + mCurrentUserId); getOutPrintWriter().println("Pin set to '" + mNew + "'"); } private void runClear() { - mLockPatternUtils.clearLock(mOld, mCurrentUserId); + byte[] oldBytes = mOld != null ? mOld.getBytes() : null; + mLockPatternUtils.clearLock(oldBytes, mCurrentUserId); getOutPrintWriter().println("Lock credential cleared"); } @@ -232,7 +240,8 @@ class LockSettingsShellCommand extends ShellCommand { try { final boolean result; if (havePassword) { - result = mLockPatternUtils.checkPassword(mOld, mCurrentUserId); + byte[] passwordBytes = mOld != null ? mOld.getBytes() : null; + result = mLockPatternUtils.checkPassword(passwordBytes, mCurrentUserId); } else { result = mLockPatternUtils.checkPattern(stringToPattern(mOld), mCurrentUserId); } diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java index 0e195bcc98e0..ea39dff1acc9 100644 --- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java +++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java @@ -97,7 +97,7 @@ public class SyntheticPasswordManager { private static final String WEAVER_SLOT_NAME = "weaver"; public static final long DEFAULT_HANDLE = 0L; - private static final String DEFAULT_PASSWORD = "default-password"; + private static final byte[] DEFAULT_PASSWORD = "default-password".getBytes(); private static final byte WEAVER_VERSION = 1; private static final int INVALID_WEAVER_SLOT = -1; @@ -165,7 +165,7 @@ public class SyntheticPasswordManager { } } - public String deriveKeyStorePassword() { + public byte[] deriveKeyStorePassword() { return bytesToHex(derivePassword(PERSONALIZATION_KEY_STORE_PASSWORD)); } @@ -454,11 +454,11 @@ public class SyntheticPasswordManager { * */ public AuthenticationToken newSyntheticPasswordAndSid(IGateKeeperService gatekeeper, - byte[] hash, String credential, int userId) throws RemoteException { + byte[] hash, byte[] credential, int userId) throws RemoteException { AuthenticationToken result = AuthenticationToken.create(); GateKeeperResponse response; if (hash != null) { - response = gatekeeper.enroll(userId, hash, credential.getBytes(), + response = gatekeeper.enroll(userId, hash, credential, result.deriveGkPassword()); if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) { Log.w(TAG, "Fail to migrate SID, assuming no SID, user " + userId); @@ -616,7 +616,7 @@ public class SyntheticPasswordManager { * @see #clearSidForUser */ public long createPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper, - String credential, int credentialType, AuthenticationToken authToken, + byte[] credential, int credentialType, AuthenticationToken authToken, int requestedQuality, int userId) throws RemoteException { if (credential == null || credentialType == LockPatternUtils.CREDENTIAL_TYPE_NONE) { @@ -670,7 +670,7 @@ public class SyntheticPasswordManager { } public VerifyCredentialResponse verifyFrpCredential(IGateKeeperService gatekeeper, - String userCredential, int credentialType, + byte[] userCredential, int credentialType, ICheckCredentialProgressCallback progressCallback) throws RemoteException { PersistentData persistentData = mStorage.readPersistentDataBlock(); if (persistentData.type == PersistentData.TYPE_SP) { @@ -839,7 +839,7 @@ public class SyntheticPasswordManager { * unknown. Caller might choose to validate it by examining AuthenticationResult.credentialType */ public AuthenticationResult unwrapPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper, - long handle, String credential, int userId, + long handle, byte[] credential, int userId, ICheckCredentialProgressCallback progressCallback) throws RemoteException { if (credential == null) { credential = DEFAULT_PASSWORD; @@ -1152,7 +1152,7 @@ public class SyntheticPasswordManager { return String.format("%s%x", LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX, handle); } - private byte[] computePasswordToken(String password, PasswordData data) { + private byte[] computePasswordToken(byte[] password, PasswordData data) { return scrypt(password, data.salt, 1 << data.scryptN, 1 << data.scryptR, 1 << data.scryptP, PASSWORD_TOKEN_LENGTH); } @@ -1173,8 +1173,8 @@ public class SyntheticPasswordManager { return nativeSidFromPasswordHandle(handle); } - protected byte[] scrypt(String password, byte[] salt, int N, int r, int p, int outLen) { - return new Scrypt().scrypt(password.getBytes(), salt, N, r, p, outLen); + protected byte[] scrypt(byte[] password, byte[] salt, int n, int r, int p, int outLen) { + return new Scrypt().scrypt(password, salt, n, r, p, outLen); } native long nativeSidFromPasswordHandle(byte[] handle); @@ -1195,17 +1195,17 @@ public class SyntheticPasswordManager { return result; } - final protected static char[] hexArray = "0123456789ABCDEF".toCharArray(); - public static String bytesToHex(byte[] bytes) { + protected static final byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes(); + private static byte[] bytesToHex(byte[] bytes) { if (bytes == null) { - return "null"; + return "null".getBytes(); } - char[] hexChars = new char[bytes.length * 2]; + byte[] hexBytes = new byte[bytes.length * 2]; for ( int j = 0; j < bytes.length; j++ ) { int v = bytes[j] & 0xFF; - hexChars[j * 2] = hexArray[v >>> 4]; - hexChars[j * 2 + 1] = hexArray[v & 0x0F]; + hexBytes[j * 2] = HEX_ARRAY[v >>> 4]; + hexBytes[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; } - return new String(hexChars); + return hexBytes; } } diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java index e7a71b99a213..5676da213dd2 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java @@ -38,7 +38,6 @@ import com.android.server.locksettings.recoverablekeystore.storage.RecoverySnaps import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; @@ -51,6 +50,7 @@ import java.security.UnrecoverableKeyException; import java.security.cert.CertPath; import java.security.cert.CertificateException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; @@ -85,7 +85,7 @@ public class KeySyncTask implements Runnable { private final RecoverableKeyStoreDb mRecoverableKeyStoreDb; private final int mUserId; private final int mCredentialType; - private final String mCredential; + private final byte[] mCredential; private final boolean mCredentialUpdated; private final PlatformKeyManager mPlatformKeyManager; private final RecoverySnapshotStorage mRecoverySnapshotStorage; @@ -100,7 +100,7 @@ public class KeySyncTask implements Runnable { RecoverySnapshotListenersStorage recoverySnapshotListenersStorage, int userId, int credentialType, - String credential, + byte[] credential, boolean credentialUpdated ) throws NoSuchAlgorithmException, KeyStoreException, InsecureUserException { return new KeySyncTask( @@ -134,7 +134,7 @@ public class KeySyncTask implements Runnable { RecoverySnapshotListenersStorage recoverySnapshotListenersStorage, int userId, int credentialType, - String credential, + byte[] credential, boolean credentialUpdated, PlatformKeyManager platformKeyManager, TestOnlyInsecureCertificateHelper testOnlyInsecureCertificateHelper, @@ -450,7 +450,7 @@ public class KeySyncTask implements Runnable { */ @VisibleForTesting @KeyChainProtectionParams.LockScreenUiFormat static int getUiFormat( - int credentialType, String credential) { + int credentialType, byte[] credential) { if (credentialType == LockPatternUtils.CREDENTIAL_TYPE_PATTERN) { return KeyChainProtectionParams.UI_FORMAT_PATTERN; } else if (isPin(credential)) { @@ -475,13 +475,13 @@ public class KeySyncTask implements Runnable { * Returns {@code true} if {@code credential} looks like a pin. */ @VisibleForTesting - static boolean isPin(@Nullable String credential) { + static boolean isPin(@Nullable byte[] credential) { if (credential == null) { return false; } - int length = credential.length(); + int length = credential.length; for (int i = 0; i < length; i++) { - if (!Character.isDigit(credential.charAt(i))) { + if (!Character.isDigit((char) credential[i])) { return false; } } @@ -494,8 +494,7 @@ public class KeySyncTask implements Runnable { * @return The SHA-256 hash. */ @VisibleForTesting - static byte[] hashCredentialsBySaltedSha256(byte[] salt, String credentials) { - byte[] credentialsBytes = credentials.getBytes(StandardCharsets.UTF_8); + static byte[] hashCredentialsBySaltedSha256(byte[] salt, byte[] credentialsBytes) { ByteBuffer byteBuffer = ByteBuffer.allocate( salt.length + credentialsBytes.length + LENGTH_PREFIX_BYTES * 2); byteBuffer.order(ByteOrder.LITTLE_ENDIAN); @@ -506,17 +505,19 @@ public class KeySyncTask implements Runnable { byte[] bytes = byteBuffer.array(); try { - return MessageDigest.getInstance(LOCK_SCREEN_HASH_ALGORITHM).digest(bytes); + byte[] hash = MessageDigest.getInstance(LOCK_SCREEN_HASH_ALGORITHM).digest(bytes); + Arrays.fill(bytes, (byte) 0); + return hash; } catch (NoSuchAlgorithmException e) { // Impossible, SHA-256 must be supported on Android. throw new RuntimeException(e); } } - private byte[] hashCredentialsByScrypt(byte[] salt, String credentials) { + private byte[] hashCredentialsByScrypt(byte[] salt, byte[] credentials) { return mScrypt.scrypt( - credentials.getBytes(StandardCharsets.UTF_8), salt, - SCRYPT_PARAM_N, SCRYPT_PARAM_R, SCRYPT_PARAM_P, SCRYPT_PARAM_OUTLEN_BYTES); + credentials, salt, SCRYPT_PARAM_N, SCRYPT_PARAM_R, SCRYPT_PARAM_P, + SCRYPT_PARAM_OUTLEN_BYTES); } private static SecretKey generateRecoveryKey() throws NoSuchAlgorithmException { diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java index ed864c0221c9..47b9c27284e5 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java @@ -892,13 +892,13 @@ public class RecoverableKeyStoreManager { * This function can only be used inside LockSettingsService. * * @param storedHashType from {@code CredentialHash} - * @param credential - unencrypted String. Password length should be at most 16 symbols {@code - * mPasswordMaxLength} + * @param credential - unencrypted byte array. Password length should be at most 16 symbols + * {@code mPasswordMaxLength} * @param userId for user who just unlocked the device. * @hide */ public void lockScreenSecretAvailable( - int storedHashType, @NonNull String credential, int userId) { + int storedHashType, @NonNull byte[] credential, int userId) { // So as not to block the critical path unlocking the phone, defer to another thread. try { mExecutorService.execute(KeySyncTask.newInstance( @@ -923,13 +923,13 @@ public class RecoverableKeyStoreManager { * This function can only be used inside LockSettingsService. * * @param storedHashType from {@code CredentialHash} - * @param credential - unencrypted String + * @param credential - unencrypted byte array * @param userId for the user whose lock screen credentials were changed. * @hide */ public void lockScreenSecretChanged( int storedHashType, - @Nullable String credential, + @Nullable byte[] credential, int userId) { // So as not to block the critical path unlocking the phone, defer to another thread. try { diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java index 057429c3e7d4..90a36723de4d 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java @@ -87,10 +87,30 @@ public class TestOnlyInsecureCertificateHelper { || isTestOnlyCertificateAlias(rootCertificateAlias); } - public boolean doesCredentialSupportInsecureMode(int credentialType, String credential) { - return (credentialType == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD) - && (credential != null) - && credential.startsWith(TrustedRootCertificates.INSECURE_PASSWORD_PREFIX); + /** + * Checks whether a password is in "Insecure mode" + * @param credentialType the type of credential, e.g. pattern and password + * @param credential the pattern or password + * @return true, if the credential is in "Insecure mode" + */ + public boolean doesCredentialSupportInsecureMode(int credentialType, byte[] credential) { + if (credential == null) { + return false; + } + if (credentialType != LockPatternUtils.CREDENTIAL_TYPE_PASSWORD) { + return false; + } + byte[] insecurePasswordPrefixBytes = + TrustedRootCertificates.INSECURE_PASSWORD_PREFIX.getBytes(); + if (credential.length < insecurePasswordPrefixBytes.length) { + return false; + } + for (int i = 0; i < insecurePasswordPrefixBytes.length; i++) { + if (credential[i] != insecurePasswordPrefixBytes[i]) { + return false; + } + } + return true; } public Map<String, Pair<SecretKey, byte[]>> keepOnlyWhitelistedInsecureKeys( diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index b221241c25e2..152cf7fe7d41 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -77,6 +77,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { private final int mUserId; private final String mPackageName; private final String mTag; + private final Bundle mSessionInfo; private final ControllerLink mController; private final MediaSession.Token mSessionToken; private final SessionLink mSession; @@ -121,13 +122,14 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { private String mMetadataDescription; public MediaSessionRecord(int ownerPid, int ownerUid, int userId, String ownerPackageName, - SessionCallbackLink cb, String tag, MediaSessionService.ServiceImpl service, - Looper handlerLooper) { + SessionCallbackLink cb, String tag, Bundle sessionInfo, + MediaSessionService.ServiceImpl service, Looper handlerLooper) { mOwnerPid = ownerPid; mOwnerUid = ownerUid; mUserId = userId; mPackageName = ownerPackageName; mTag = tag; + mSessionInfo = sessionInfo; mController = new ControllerLink(new ControllerStub()); mSessionToken = new MediaSession.Token(mController); mSession = new SessionLink(new SessionStub()); @@ -1309,6 +1311,11 @@ public class MediaSessionRecord implements IBinder.DeathRecipient { } @Override + public Bundle getSessionInfo() { + return mSessionInfo; + } + + @Override public PendingIntent getLaunchPendingIntent() { return mLaunchIntent; } diff --git a/services/core/java/com/android/server/media/MediaSessionServiceImpl.java b/services/core/java/com/android/server/media/MediaSessionServiceImpl.java index 62d9b20b636d..b86328b92869 100644 --- a/services/core/java/com/android/server/media/MediaSessionServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaSessionServiceImpl.java @@ -549,9 +549,11 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl { } private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId, - String callerPackageName, SessionCallbackLink cb, String tag) throws RemoteException { + String callerPackageName, SessionCallbackLink cb, String tag, Bundle sessionInfo) + throws RemoteException { synchronized (mLock) { - return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag); + return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, + tag, sessionInfo); } } @@ -563,7 +565,7 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl { * 4. It needs to be added to the relevant user record. */ private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId, - String callerPackageName, SessionCallbackLink cb, String tag) { + String callerPackageName, SessionCallbackLink cb, String tag, Bundle sessionInfo) { FullUserRecord user = getFullUserRecordLocked(userId); if (user == null) { Log.w(TAG, "Request from invalid user: " + userId + ", pkg=" + callerPackageName); @@ -571,7 +573,7 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl { } final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId, - callerPackageName, cb, tag, this, mHandler.getLooper()); + callerPackageName, cb, tag, sessionInfo, this, mHandler.getLooper()); try { cb.getBinder().linkToDeath(session, 0); } catch (RemoteException e) { @@ -991,7 +993,7 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl { @Override public SessionLink createSession(String packageName, SessionCallbackLink cb, String tag, - int userId) throws RemoteException { + Bundle sessionInfo, int userId) throws RemoteException { final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); @@ -1002,8 +1004,8 @@ public class MediaSessionServiceImpl extends MediaSessionService.ServiceImpl { if (cb == null) { throw new IllegalArgumentException("Controller callback cannot be null"); } - return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag) - .getSessionBinder(); + return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag, + sessionInfo).getSessionBinder(); } finally { Binder.restoreCallingIdentity(token); } diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java index fccff57e3234..270fbc68e143 100644 --- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java +++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java @@ -16,17 +16,15 @@ package com.android.server.media.projection; -import com.android.server.Watchdog; - import android.Manifest; import android.app.AppOpsManager; import android.content.Context; import android.content.pm.PackageManager; import android.hardware.display.DisplayManager; import android.media.MediaRouter; -import android.media.projection.IMediaProjectionManager; import android.media.projection.IMediaProjection; import android.media.projection.IMediaProjectionCallback; +import android.media.projection.IMediaProjectionManager; import android.media.projection.IMediaProjectionWatcherCallback; import android.media.projection.MediaProjectionInfo; import android.media.projection.MediaProjectionManager; @@ -41,6 +39,7 @@ import android.util.Slog; import com.android.internal.util.DumpUtils; import com.android.server.SystemService; +import com.android.server.Watchdog; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -361,8 +360,9 @@ public final class MediaProjectionManagerService extends SystemService @Override // Binder call public boolean canProjectAudio() { - return mType == MediaProjectionManager.TYPE_MIRRORING || - mType == MediaProjectionManager.TYPE_PRESENTATION; + return mType == MediaProjectionManager.TYPE_MIRRORING + || mType == MediaProjectionManager.TYPE_PRESENTATION + || mType == MediaProjectionManager.TYPE_SCREEN_CAPTURE; } @Override // Binder call diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 3557fcf35dbc..e8c402c770c7 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -594,8 +594,6 @@ public class NotificationManagerService extends SystemService { mConditionProviders.migrateToXml(); handleSavePolicyFile(); } - - mAssistants.ensureAssistant(); } private void loadPolicyFile() { diff --git a/services/core/java/com/android/server/notification/NotificationShellCmd.java b/services/core/java/com/android/server/notification/NotificationShellCmd.java index 2aaa1edcfad9..26cc0c10ccb3 100644 --- a/services/core/java/com/android/server/notification/NotificationShellCmd.java +++ b/services/core/java/com/android/server/notification/NotificationShellCmd.java @@ -16,6 +16,7 @@ package com.android.server.notification; +import android.app.ActivityManager; import android.app.INotificationManager; import android.app.Notification; import android.app.NotificationChannel; @@ -49,12 +50,12 @@ public class NotificationShellCmd extends ShellCommand { private static final String USAGE = "usage: cmd notification SUBCMD [args]\n\n" + "SUBCMDs:\n" - + " allow_listener COMPONENT [user_id]\n" - + " disallow_listener COMPONENT [user_id]\n" - + " allow_assistant COMPONENT\n" - + " remove_assistant COMPONENT\n" - + " allow_dnd PACKAGE\n" - + " disallow_dnd PACKAGE\n" + + " allow_listener COMPONENT [user_id (current user if not specified)]\n" + + " disallow_listener COMPONENT [user_id (current user if not specified)]\n" + + " allow_assistant COMPONENT [user_id (current user if not specified)]\n" + + " remove_assistant COMPONENT [user_id (current user if not specified)]\n" + + " allow_dnd PACKAGE [user_id (current user if not specified)]\n" + + " disallow_dnd PACKAGE [user_id (current user if not specified)]\n" + " suspend_package PACKAGE\n" + " unsuspend_package PACKAGE\n" + " post [--help | flags] TAG TEXT"; @@ -109,14 +110,24 @@ public class NotificationShellCmd extends ShellCommand { try { switch (cmd.replace('-', '_')) { case "allow_dnd": { - mBinderService.setNotificationPolicyAccessGranted( - getNextArgRequired(), true); + String packageName = getNextArgRequired(); + int userId = ActivityManager.getCurrentUser(); + if (peekNextArg() != null) { + userId = Integer.parseInt(getNextArgRequired()); + } + mBinderService.setNotificationPolicyAccessGrantedForUser( + packageName, userId, true); } break; case "disallow_dnd": { - mBinderService.setNotificationPolicyAccessGranted( - getNextArgRequired(), false); + String packageName = getNextArgRequired(); + int userId = ActivityManager.getCurrentUser(); + if (peekNextArg() != null) { + userId = Integer.parseInt(getNextArgRequired()); + } + mBinderService.setNotificationPolicyAccessGrantedForUser( + packageName, userId, false); } break; case "allow_listener": { @@ -125,13 +136,11 @@ public class NotificationShellCmd extends ShellCommand { pw.println("Invalid listener - must be a ComponentName"); return -1; } - String userId = getNextArg(); - if (userId == null) { - mBinderService.setNotificationListenerAccessGranted(cn, true); - } else { - mBinderService.setNotificationListenerAccessGrantedForUser( - cn, Integer.parseInt(userId), true); + int userId = ActivityManager.getCurrentUser(); + if (peekNextArg() != null) { + userId = Integer.parseInt(getNextArgRequired()); } + mBinderService.setNotificationListenerAccessGrantedForUser(cn, userId, true); } break; case "disallow_listener": { @@ -140,13 +149,11 @@ public class NotificationShellCmd extends ShellCommand { pw.println("Invalid listener - must be a ComponentName"); return -1; } - String userId = getNextArg(); - if (userId == null) { - mBinderService.setNotificationListenerAccessGranted(cn, false); - } else { - mBinderService.setNotificationListenerAccessGrantedForUser( - cn, Integer.parseInt(userId), false); + int userId = ActivityManager.getCurrentUser(); + if (peekNextArg() != null) { + userId = Integer.parseInt(getNextArgRequired()); } + mBinderService.setNotificationListenerAccessGrantedForUser(cn, userId, false); } break; case "allow_assistant": { @@ -155,7 +162,11 @@ public class NotificationShellCmd extends ShellCommand { pw.println("Invalid assistant - must be a ComponentName"); return -1; } - mBinderService.setNotificationAssistantAccessGranted(cn, true); + int userId = ActivityManager.getCurrentUser(); + if (peekNextArg() != null) { + userId = Integer.parseInt(getNextArgRequired()); + } + mBinderService.setNotificationAssistantAccessGrantedForUser(cn, userId, true); } break; case "disallow_assistant": { @@ -164,7 +175,11 @@ public class NotificationShellCmd extends ShellCommand { pw.println("Invalid assistant - must be a ComponentName"); return -1; } - mBinderService.setNotificationAssistantAccessGranted(cn, false); + int userId = ActivityManager.getCurrentUser(); + if (peekNextArg() != null) { + userId = Integer.parseInt(getNextArgRequired()); + } + mBinderService.setNotificationAssistantAccessGrantedForUser(cn, userId, false); } break; case "suspend_package": { @@ -176,6 +191,7 @@ public class NotificationShellCmd extends ShellCommand { // only use for testing mDirectService.simulatePackageSuspendBroadcast(false, getNextArgRequired()); } + break; case "distract_package": { // only use for testing // Flag values are in diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index 2c47ec03f9fa..660309cd578d 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -722,7 +722,8 @@ public class PreferencesHelper implements RankingConfig { if (!channel.equals(updatedChannel)) { // only log if there are real changes - MetricsLogger.action(getChannelLog(updatedChannel, pkg)); + MetricsLogger.action(getChannelLog(updatedChannel, pkg) + .setSubtype(fromUser ? 1 : 0)); } if (updatedChannel.canBypassDnd() != mAreChannelsBypassingDnd diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java index 1dada92ab118..f4454ae2a180 100644 --- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java +++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java @@ -146,8 +146,8 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { if (isDumpstateBinderServiceRunningLocked()) { Slog.w(TAG, "'dumpstate' is already running. Cannot start a new bugreport" + " while another one is currently in progress."); - // TODO(b/111441001): Use a new error code; add this to the documentation of the API. - reportError(listener, IDumpstateListener.BUGREPORT_ERROR_RUNTIME_ERROR); + reportError(listener, + IDumpstateListener.BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS); return; } diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java index dc990afd729b..a4c04b2e64aa 100644 --- a/services/core/java/com/android/server/pm/ApexManager.java +++ b/services/core/java/com/android/server/pm/ApexManager.java @@ -22,6 +22,10 @@ import android.apex.ApexInfo; import android.apex.ApexInfoList; import android.apex.ApexSessionInfo; import android.apex.IApexService; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.PackageInfo; import android.content.pm.PackageParser; import android.content.pm.PackageParser.PackageParserException; @@ -30,6 +34,7 @@ import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; import android.util.Slog; +import com.android.internal.annotations.GuardedBy; import com.android.internal.util.IndentingPrintWriter; import java.io.File; @@ -48,40 +53,58 @@ import java.util.stream.Collectors; class ApexManager { static final String TAG = "ApexManager"; private final IApexService mApexService; - private final Map<String, PackageInfo> mActivePackagesCache; + private final Context mContext; + private final Object mLock = new Object(); + @GuardedBy("mLock") + private Map<String, PackageInfo> mActivePackagesCache; - ApexManager() { + ApexManager(Context context) { try { mApexService = IApexService.Stub.asInterface( ServiceManager.getServiceOrThrow("apexservice")); } catch (ServiceNotFoundException e) { throw new IllegalStateException("Required service apexservice not available"); } - mActivePackagesCache = populateActivePackagesCache(); + mContext = context; } - @NonNull - private Map<String, PackageInfo> populateActivePackagesCache() { - try { - List<PackageInfo> list = new ArrayList<>(); - final ApexInfo[] activePkgs = mApexService.getActivePackages(); - for (ApexInfo ai : activePkgs) { - // If the device is using flattened APEX, don't report any APEX - // packages since they won't be managed or updated by PackageManager. - if ((new File(ai.packagePath)).isDirectory()) { - break; - } - try { - list.add(PackageParser.generatePackageInfoFromApex( - new File(ai.packagePath), true /* collect certs */)); - } catch (PackageParserException pe) { - throw new IllegalStateException("Unable to parse: " + ai, pe); + void systemReady() { + mContext.registerReceiver(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + onBootCompleted(); + mContext.unregisterReceiver(this); + } + }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED)); + } + + private void populateActivePackagesCacheIfNeeded() { + synchronized (mLock) { + if (mActivePackagesCache != null) { + return; + } + try { + List<PackageInfo> list = new ArrayList<>(); + final ApexInfo[] activePkgs = mApexService.getActivePackages(); + for (ApexInfo ai : activePkgs) { + // If the device is using flattened APEX, don't report any APEX + // packages since they won't be managed or updated by PackageManager. + if ((new File(ai.packagePath)).isDirectory()) { + break; + } + try { + list.add(PackageParser.generatePackageInfoFromApex( + new File(ai.packagePath), true /* collect certs */)); + } catch (PackageParserException pe) { + throw new IllegalStateException("Unable to parse: " + ai, pe); + } } + mActivePackagesCache = list.stream().collect( + Collectors.toMap(p -> p.packageName, Function.identity())); + } catch (RemoteException re) { + Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString()); + throw new RuntimeException(re); } - return list.stream().collect(Collectors.toMap(p -> p.packageName, Function.identity())); - } catch (RemoteException re) { - Slog.e(TAG, "Unable to retrieve packages from apexservice: " + re.toString()); - throw new RuntimeException(re); } } @@ -96,6 +119,7 @@ class ApexManager { * is not found. */ @Nullable PackageInfo getActivePackage(String packageName) { + populateActivePackagesCacheIfNeeded(); return mActivePackagesCache.get(packageName); } @@ -106,6 +130,7 @@ class ApexManager { * active package. */ Collection<PackageInfo> getActivePackages() { + populateActivePackagesCacheIfNeeded(); return mActivePackagesCache.values(); } @@ -198,6 +223,7 @@ class ApexManager { * @return true if APEX packages can be managed on this device, false otherwise. */ boolean isApexSupported() { + populateActivePackagesCacheIfNeeded(); // There is no system-wide property available to check if APEX are flattened and hence can't // be updated. In absence of such property, we assume that if we didn't index APEX packages // since they were flattened, no APEX management should be possible. @@ -205,6 +231,21 @@ class ApexManager { } /** + * Abandons the (only) active session previously submitted. + * + * @return {@code true} upon success, {@code false} if any remote exception occurs + */ + boolean abortActiveSession() { + try { + mApexService.abortActiveSession(); + return true; + } catch (RemoteException re) { + Slog.e(TAG, "Unable to contact apexservice", re); + return false; + } + } + + /** * Dumps various state information to the provided {@link PrintWriter} object. * * @param pw the {@link PrintWriter} object to send information to. @@ -217,7 +258,7 @@ class ApexManager { ipw.println("Active APEX packages:"); ipw.increaseIndent(); try { - populateActivePackagesCache(); + populateActivePackagesCacheIfNeeded(); for (PackageInfo pi : mActivePackagesCache.values()) { if (packageName != null && !packageName.equals(pi.packageName)) { continue; @@ -246,6 +287,12 @@ class ApexManager { ipw.println("State: ACTIVATED"); } else if (si.isActivationFailed) { ipw.println("State: ACTIVATION FAILED"); + } else if (si.isSuccess) { + ipw.println("State: SUCCESS"); + } else if (si.isRollbackInProgress) { + ipw.println("State: ROLLBACK IN PROGRESS"); + } else if (si.isRolledBack) { + ipw.println("State: ROLLED BACK"); } ipw.decreaseIndent(); } @@ -254,4 +301,8 @@ class ApexManager { ipw.println("Couldn't communicate with apexd."); } } + + public void onBootCompleted() { + populateActivePackagesCacheIfNeeded(); + } } diff --git a/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java b/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java index d53d81cf0860..a1ff76fc8c09 100644 --- a/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java +++ b/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java @@ -61,7 +61,7 @@ public class DynamicCodeLoggingService extends JobService { private static final Pattern EXECUTE_NATIVE_AUDIT_PATTERN = Pattern.compile(".*\\bavc: granted \\{ execute(?:_no_trans|) \\} .*" + "\\bpath=(?:\"([^\" ]*)\"|([0-9A-F]+)) .*" - + "\\bscontext=u:r:untrusted_app_2(?:5|7):.*" + + "\\bscontext=u:r:untrusted_app(?:_25|_27)?:.*" + "\\btcontext=u:object_r:app_data_file:.*" + "\\btclass=file\\b.*"); diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index c2a75aba28b9..bab612d3c092 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -616,6 +616,7 @@ public class Installer extends SystemService { * * @param pkg name of the package to snapshot user data for. * @param userId id of the user whose data to snapshot. + * @param snapshotId id of this snapshot. * @param storageFlags flags controlling which data (CE or DE) to snapshot. * * @return inode of the snapshot of users CE package data, or {@code 0} if a remote calls @@ -623,12 +624,12 @@ public class Installer extends SystemService { * * @throws InstallerException if failed to snapshot user data. */ - public long snapshotAppData(String pkg, @UserIdInt int userId, int storageFlags) + public long snapshotAppData(String pkg, @UserIdInt int userId, int snapshotId, int storageFlags) throws InstallerException { if (!checkBeforeRemote()) return 0; try { - return mInstalld.snapshotAppData(null, pkg, userId, storageFlags); + return mInstalld.snapshotAppData(null, pkg, userId, snapshotId, storageFlags); } catch (Exception e) { throw InstallerException.from(e); } @@ -639,8 +640,8 @@ public class Installer extends SystemService { * * @param pkg name of the package to restore user data for. * @param appId id of the package to restore user data for. - * @param ceDataInode inode of CE user data folder of this app. * @param userId id of the user whose data to restore. + * @param snapshotId id of the snapshot to restore. * @param storageFlags flags controlling which data (CE or DE) to restore. * * @return {@code true} if user data restore was successful, or {@code false} if a remote call @@ -648,12 +649,12 @@ public class Installer extends SystemService { * * @throws InstallerException if failed to restore user data. */ - public boolean restoreAppDataSnapshot(String pkg, @AppIdInt int appId, long ceDataInode, - String seInfo, @UserIdInt int userId, int storageFlags) throws InstallerException { + public boolean restoreAppDataSnapshot(String pkg, @AppIdInt int appId, String seInfo, + @UserIdInt int userId, int snapshotId, int storageFlags) throws InstallerException { if (!checkBeforeRemote()) return false; try { - mInstalld.restoreAppDataSnapshot(null, pkg, appId, ceDataInode, seInfo, userId, + mInstalld.restoreAppDataSnapshot(null, pkg, appId, seInfo, userId, snapshotId, storageFlags); return true; } catch (Exception e) { @@ -667,6 +668,7 @@ public class Installer extends SystemService { * @param pkg name of the package to delete user data snapshot for. * @param userId id of the user whose user data snapshot to delete. * @param ceSnapshotInode inode of CE user data snapshot. + * @param snapshotId id of the snapshot to delete. * @param storageFlags flags controlling which user data snapshot (CE or DE) to delete. * * @return {@code true} if user data snapshot was successfully deleted, or {@code false} if a @@ -675,11 +677,12 @@ public class Installer extends SystemService { * @throws InstallerException if failed to delete user data snapshot. */ public boolean destroyAppDataSnapshot(String pkg, @UserIdInt int userId, long ceSnapshotInode, - int storageFlags) throws InstallerException { + int snapshotId, int storageFlags) throws InstallerException { if (!checkBeforeRemote()) return false; try { - mInstalld.destroyAppDataSnapshot(null, pkg, userId, ceSnapshotInode, storageFlags); + mInstalld.destroyAppDataSnapshot(null, pkg, userId, ceSnapshotInode, snapshotId, + storageFlags); return true; } catch (Exception e) { throw InstallerException.from(e); diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index 62c4815a7de0..9e9128430e01 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -20,7 +20,6 @@ import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.ActivityManagerInternal; -import android.app.AppDetailsActivity; import android.app.AppGlobals; import android.app.IApplicationThread; import android.app.PendingIntent; @@ -367,7 +366,8 @@ public class LauncherAppsService extends SystemService { private ResolveInfo getHiddenAppActivityInfo(String packageName, int callingUid, UserHandle user) { Intent intent = new Intent(); - intent.setComponent(new ComponentName(packageName, AppDetailsActivity.class.getName())); + intent.setComponent(new ComponentName(packageName, + PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME)); final PackageManagerInternal pmInt = LocalServices.getService(PackageManagerInternal.class); List<ResolveInfo> apps = pmInt.queryIntentActivities(intent, diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java index b52c02100faf..1c9028d10ca2 100644 --- a/services/core/java/com/android/server/pm/OtaDexoptService.java +++ b/services/core/java/com/android/server/pm/OtaDexoptService.java @@ -333,7 +333,7 @@ public class OtaDexoptService extends IOtaDexopt.Stub { PackageDexOptimizer optimizer = new OTADexoptPackageDexOptimizer( collectingInstaller, mPackageManagerService.mInstallLock, mContext); - optimizer.performDexOpt(pkg, pkg.usesLibraryInfos, + optimizer.performDexOpt(pkg, null /* ISAs */, null /* CompilerStats.PackageStats */, mPackageManagerService.getDexManager().getPackageUseInfoOrDefault(pkg.packageName), diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index 5b3820815f92..580137db1630 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -125,7 +125,7 @@ public class PackageDexOptimizer { * <p>Calls to {@link com.android.server.pm.Installer#dexopt} on {@link #mInstaller} are * synchronized on {@link #mInstallLock}. */ - int performDexOpt(PackageParser.Package pkg, List<SharedLibraryInfo> sharedLibraries, + int performDexOpt(PackageParser.Package pkg, String[] instructionSets, CompilerStats.PackageStats packageStats, PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) { if (pkg.applicationInfo.uid == -1) { @@ -138,7 +138,7 @@ public class PackageDexOptimizer { synchronized (mInstallLock) { final long acquireTime = acquireWakeLockLI(pkg.applicationInfo.uid); try { - return performDexOptLI(pkg, sharedLibraries, instructionSets, + return performDexOptLI(pkg, instructionSets, packageStats, packageUseInfo, options); } finally { releaseWakeLockLI(acquireTime); @@ -152,9 +152,9 @@ public class PackageDexOptimizer { */ @GuardedBy("mInstallLock") private int performDexOptLI(PackageParser.Package pkg, - List<SharedLibraryInfo> sharedLibraries, String[] targetInstructionSets, CompilerStats.PackageStats packageStats, PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) { + final List<SharedLibraryInfo> sharedLibraries = pkg.usesLibraryInfos; final String[] instructionSets = targetInstructionSets != null ? targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo); final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets); diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index f06da49f5b94..b72e83692e8a 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -28,10 +28,8 @@ import android.app.NotificationManager; import android.app.PackageDeleteObserver; import android.app.PackageInstallObserver; import android.app.admin.DevicePolicyManagerInternal; -import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.content.IntentSender; import android.content.IntentSender.SendIntentException; import android.content.pm.ApplicationInfo; @@ -141,7 +139,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements private final Callbacks mCallbacks; - private volatile boolean mBootCompleted = false; + private volatile boolean mOkToSendBroadcasts = false; /** * File storing persisted {@link #mSessions} metadata. @@ -206,27 +204,17 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements mSessionsDir.mkdirs(); mApexManager = am; - mStagingManager = new StagingManager(pm, this, am); - } - private void setBootCompleted() { - mBootCompleted = true; + mStagingManager = new StagingManager(pm, this, am, context); } - boolean isBootCompleted() { - return mBootCompleted; + boolean okToSendBroadcasts() { + return mOkToSendBroadcasts; } public void systemReady() { mAppOps = mContext.getSystemService(AppOpsManager.class); - mContext.registerReceiver(new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - setBootCompleted(); - mContext.unregisterReceiver(this); - } - }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED)); synchronized (mSessions) { readSessionsLocked(); @@ -269,6 +257,16 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements for (PackageInstallerSession session : stagedSessionsToRestore) { mStagingManager.restoreSession(session); } + // Broadcasts are not sent while we restore sessions on boot, since no processes would be + // ready to listen to them. From now on, we greedily assume that broadcasts requests are + // safe to send out. The worst that can happen is that a broadcast is attempted before + // ActivityManagerService completes its own systemReady(), in which case it will be rejected + // with an otherwise harmless exception. + // A more appropriate way to do this would be to wait until the correct boot phase is + // reached, but since we are not a SystemService we can't override onBootPhase. + // Waiting on the BOOT_COMPLETED broadcast can take several minutes, so that's not a viable + // way either. + mOkToSendBroadcasts = true; } @GuardedBy("mSessions") @@ -483,11 +481,12 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } } - if (params.isStaged) { + boolean isApex = (params.installFlags & PackageManager.INSTALL_APEX) != 0; + if (params.isStaged || isApex) { mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, TAG); } - if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) { + if (isApex) { if (!mApexManager.isApexSupported()) { throw new IllegalArgumentException( "This device doesn't support the installation of APEX files"); @@ -821,6 +820,13 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } @Override + public void installExistingPackage(String packageName, int installFlags, int installReason, + IntentSender statusReceiver, int userId) { + mPm.installExistingPackageAsUser(packageName, userId, installFlags, installReason, + statusReceiver); + } + + @Override public void setPermissionsResult(int sessionId, boolean accepted) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, TAG); @@ -1179,7 +1185,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements public void onStagedSessionChanged(PackageInstallerSession session) { writeSessionsAsync(); - if (mBootCompleted) { + if (mOkToSendBroadcasts) { mPm.sendSessionUpdatedBroadcast(session.generateInfo(false), session.userId); } diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 1b719048bc89..45b3b5b1e18e 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -322,18 +322,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { public boolean handleMessage(Message msg) { switch (msg.what) { case MSG_COMMIT: - synchronized (mLock) { - try { - commitLocked(); - } catch (PackageManagerException e) { - final String completeMsg = ExceptionUtils.getCompleteMessage(e); - Slog.e(TAG, - "Commit of session " + sessionId + " failed: " + completeMsg); - destroyInternal(); - dispatchSessionFinished(e.error, completeMsg, null); - } - } - + handleCommit(); break; case MSG_ON_PACKAGE_INSTALLED: final SomeArgs args = (SomeArgs) msg.obj; @@ -501,9 +490,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { if (info.childSessionIds == null) { info.childSessionIds = EMPTY_CHILD_SESSION_ARRAY; } - info.isSessionApplied = mStagedSessionApplied; - info.isSessionReady = mStagedSessionReady; - info.isSessionFailed = mStagedSessionFailed; + info.isStagedSessionApplied = mStagedSessionApplied; + info.isStagedSessionReady = mStagedSessionReady; + info.isStagedSessionFailed = mStagedSessionFailed; info.setStagedSessionErrorCode(mStagedSessionErrorCode, mStagedSessionErrorMessage); } return info; @@ -1073,38 +1062,66 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mCallback.onSessionSealedBlocking(this); } - @GuardedBy("mLock") - private void commitLocked() - throws PackageManagerException { + private void handleCommit() { if (params.isStaged) { mStagingManager.commitSession(this); destroyInternal(); dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "Session staged", null); return; } + if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) { - throw new PackageManagerException( - PackageManager.INSTALL_FAILED_INTERNAL_ERROR, - "APEX packages can only be installed using staged sessions."); + destroyInternal(); + dispatchSessionFinished(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, + "APEX packages can only be installed using staged sessions.", null); + return; + } + + // For a multiPackage session, read the child sessions + // outside of the lock, because reading the child + // sessions with the lock held could lead to deadlock + // (b/123391593). + List<PackageInstallerSession> childSessions = null; + if (isMultiPackage()) { + final int[] childSessionIds = getChildSessionIds(); + childSessions = new ArrayList<>(childSessionIds.length); + for (int childSessionId : childSessionIds) { + childSessions.add(mSessionProvider.getSession(childSessionId)); + } } + + try { + synchronized (mLock) { + commitNonStagedLocked(childSessions); + } + } catch (PackageManagerException e) { + final String completeMsg = ExceptionUtils.getCompleteMessage(e); + Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg); + destroyInternal(); + dispatchSessionFinished(e.error, completeMsg, null); + } + } + + @GuardedBy("mLock") + private void commitNonStagedLocked(List<PackageInstallerSession> childSessions) + throws PackageManagerException { final PackageManagerService.ActiveInstallSession committingSession = makeSessionActiveLocked(); if (committingSession == null) { return; } if (isMultiPackage()) { - final int[] childSessionIds = getChildSessionIds(); - List<PackageManagerService.ActiveInstallSession> childSessions = - new ArrayList<>(childSessionIds.length); + List<PackageManagerService.ActiveInstallSession> activeChildSessions = + new ArrayList<>(childSessions.size()); boolean success = true; PackageManagerException failure = null; - for (int childSessionId : getChildSessionIds()) { - final PackageInstallerSession session = mSessionProvider.getSession(childSessionId); + for (int i = 0; i < childSessions.size(); ++i) { + final PackageInstallerSession session = childSessions.get(i); try { final PackageManagerService.ActiveInstallSession activeSession = session.makeSessionActiveLocked(); if (activeSession != null) { - childSessions.add(activeSession); + activeChildSessions.add(activeSession); } } catch (PackageManagerException e) { failure = e; @@ -1119,7 +1136,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } return; } - mPm.installStage(childSessions); + mPm.installStage(activeChildSessions); } else { mPm.installStage(committingSession); } @@ -1474,12 +1491,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // Inherit base if not overridden if (mResolvedBaseFile == null) { mResolvedBaseFile = new File(appInfo.getBaseCodePath()); - mResolvedInheritedFiles.add(mResolvedBaseFile); + resolveInheritedFile(mResolvedBaseFile); // Inherit the dex metadata if present. final File baseDexMetadataFile = DexMetadataHelper.findDexMetadataForFile(mResolvedBaseFile); if (baseDexMetadataFile != null) { - mResolvedInheritedFiles.add(baseDexMetadataFile); + resolveInheritedFile(baseDexMetadataFile); } baseApk = existingBase; } @@ -1491,12 +1508,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { final File splitFile = new File(existing.splitCodePaths[i]); final boolean splitRemoved = removeSplitList.contains(splitName); if (!stagedSplits.contains(splitName) && !splitRemoved) { - mResolvedInheritedFiles.add(splitFile); + resolveInheritedFile(splitFile); // Inherit the dex metadata if present. final File splitDexMetadataFile = DexMetadataHelper.findDexMetadataForFile(splitFile); if (splitDexMetadataFile != null) { - mResolvedInheritedFiles.add(splitDexMetadataFile); + resolveInheritedFile(splitDexMetadataFile); } } } @@ -1610,6 +1627,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mResolvedStagedFiles.add(stagedSignature); } + private void resolveInheritedFile(File origFile) { + mResolvedInheritedFiles.add(origFile); + + // Inherit the fsverity signature file if present. + final File fsveritySignatureFile = new File( + VerityUtils.getFsveritySignatureFilePath(origFile.getPath())); + if (fsveritySignatureFile.exists()) { + mResolvedInheritedFiles.add(fsveritySignatureFile); + } + } + @GuardedBy("mLock") private void assertApkConsistentLocked(String tag, ApkLite apk) throws PackageManagerException { @@ -1839,6 +1867,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { synchronized (mLock) { assertCallerIsOwnerOrRootLocked(); + if (mCommitted && params.isStaged) { + synchronized (mLock) { + mDestroyed = true; + } + mStagingManager.abortCommittedSession(this); + + cleanStageDir(); + } + if (mRelinquished) { Slog.d(TAG, "Ignoring abandon after commit relinquished control"); return; @@ -1869,7 +1906,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } @Override - public void addChildSessionId(int childSessionId) { + public void addChildSessionId(int childSessionId) throws RemoteException { final PackageInstallerSession childSession = mSessionProvider.getSession(childSessionId); if (childSession == null) { throw new RemoteException("Unable to add child.", @@ -1884,9 +1921,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { new PackageManagerException("Child session " + childSessionId + " and parent session " + this.sessionId + " do not have consistent" + " staging session settings."), - false, true).rethrowAsRuntimeException(); + false, true); } synchronized (mLock) { + assertCallerIsOwnerOrRootLocked(); + assertPreparedAndNotSealedLocked("addChildSessionId"); + final int indexOfSession = mChildSessionIds.indexOfKey(childSessionId); if (indexOfSession >= 0) { return; @@ -1968,7 +2008,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // Send broadcast to default launcher only if it's a new install final boolean isNewInstall = extras == null || !extras.getBoolean(Intent.EXTRA_REPLACING); - if (success && isNewInstall && mPm.mInstallerService.isBootCompleted()) { + if (success && isNewInstall && mPm.mInstallerService.okToSendBroadcasts()) { mPm.sendSessionCommitBroadcast(generateInfo(), userId); } @@ -1998,6 +2038,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mStagedSessionErrorMessage = errorMessage; Slog.d(TAG, "Marking session " + sessionId + " as failed: " + errorMessage); } + cleanStageDir(); mCallback.onStagedSessionChanged(this); } @@ -2009,7 +2050,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mStagedSessionFailed = false; mStagedSessionErrorCode = SessionInfo.STAGED_SESSION_NO_ERROR; mStagedSessionErrorMessage = ""; + Slog.d(TAG, "Marking session " + sessionId + " as applied"); } + cleanStageDir(); mCallback.onStagedSessionChanged(this); } @@ -2053,9 +2096,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } // For staged sessions, we don't delete the directory where the packages have been copied, - // since these packages are supposed to be read on reboot. StagingManager is in charge of - // deleting these dirs when the staged session has reached a final state. - // TODO(b/118865310): Implement packageDir deletion in StagingManager. + // since these packages are supposed to be read on reboot. + // Those dirs are deleted when the staged session has reached a final state. if (stageDir != null && !params.isStaged) { try { mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath()); @@ -2064,6 +2106,19 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } + private void cleanStageDir() { + if (isMultiPackage()) { + for (int childSessionId : getChildSessionIds()) { + mSessionProvider.getSession(childSessionId).cleanStageDir(); + } + } else { + try { + mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath()); + } catch (InstallerException ignored) { + } + } + } + void dump(IndentingPrintWriter pw) { synchronized (mLock) { dumpLocked(pw); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 9ff2f442593e..b3b7f2556aaf 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -121,7 +121,6 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.ActivityManagerInternal; -import android.app.AppDetailsActivity; import android.app.AppOpsManager; import android.app.BroadcastOptions; import android.app.IActivityManager; @@ -630,6 +629,7 @@ public class PackageManagerService extends IPackageManager.Stub final boolean mIsUpgrade; final boolean mIsPreNUpgrade; final boolean mIsPreNMR1Upgrade; + final boolean mIsPreQUpgrade; @GuardedBy("mPackages") private boolean mDexOptDialogShown; @@ -1303,12 +1303,14 @@ public class PackageManagerService extends IPackageManager.Stub // Recordkeeping of restore-after-install operations that are currently in flight // between the Package Manager and the Backup Manager static class PostInstallData { - public InstallArgs args; - public PackageInstalledInfo res; + public final InstallArgs args; + public final PackageInstalledInfo res; + public final Runnable mPostInstallRunnable; - PostInstallData(InstallArgs _a, PackageInstalledInfo _r) { + PostInstallData(InstallArgs _a, PackageInstalledInfo _r, Runnable postInstallRunnable) { args = _a; res = _r; + mPostInstallRunnable = postInstallRunnable; } } @@ -1440,7 +1442,9 @@ public class PackageManagerService extends IPackageManager.Stub final boolean didRestore = (msg.arg2 != 0); mRunningInstalls.delete(msg.arg1); - if (data != null) { + if (data != null && data.mPostInstallRunnable != null) { + data.mPostInstallRunnable.run(); + } else if (data != null) { InstallArgs args = data.args; PackageInstalledInfo parentRes = data.res; @@ -2396,6 +2400,7 @@ public class PackageManagerService extends IPackageManager.Stub mIsPreNUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N; mIsPreNMR1Upgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N_MR1; + mIsPreQUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.Q; int preUpgradeSdkVersion = ver.sdkVersion; @@ -3022,6 +3027,21 @@ public class PackageManagerService extends IPackageManager.Stub ver.fingerprint = Build.FINGERPRINT; } + // Grandfather existing (installed before Q) non-system apps to hide + // their icons in launcher. + if (!onlyCore && mIsPreQUpgrade) { + Slog.i(TAG, "Whitelisting all existing apps to hide their icons"); + int size = mSettings.mPackages.size(); + for (int i = 0; i < size; i++) { + final PackageSetting ps = mSettings.mPackages.valueAt(i); + if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) { + continue; + } + ps.disableComponentLPw(PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME, + UserHandle.USER_SYSTEM); + } + } + // clear only after permissions and other defaults have been updated mExistingSystemPackages.clear(); mPromoteSystemApps = false; @@ -3081,7 +3101,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - mApexManager = new ApexManager(); + mApexManager = new ApexManager(context); mInstallerService = new PackageInstallerService(context, this, mApexManager); final Pair<ComponentName, String> instantAppResolverComponent = getInstantAppResolverLPr(); @@ -4212,6 +4232,55 @@ public class PackageManagerService extends IPackageManager.Stub return -1; } + /** + * Check if any package sharing/holding a uid has a low enough target SDK. + * + * @param uid The uid of the packages + * @param higherTargetSDK The target SDK that might be higher than the searched package + * + * @return {@code true} if there is a package sharing/holding the uid with + * {@code package.targetSDK < higherTargetSDK} + */ + private boolean hasTargetSdkInUidLowerThan(int uid, int higherTargetSDK) { + int userId = UserHandle.getUserId(uid); + + synchronized (mPackages) { + Object obj = mSettings.getSettingLPr(UserHandle.getAppId(uid)); + if (obj == null) { + return false; + } + + if (obj instanceof PackageSetting) { + final PackageSetting ps = (PackageSetting) obj; + + if (!ps.getInstalled(userId)) { + return false; + } + + return ps.pkg.applicationInfo.targetSdkVersion < higherTargetSDK; + } else if (obj instanceof SharedUserSetting) { + final SharedUserSetting sus = (SharedUserSetting) obj; + + final int numPkgs = sus.packages.size(); + for (int i = 0; i < numPkgs; i++) { + final PackageSetting ps = sus.packages.valueAt(i); + + if (!ps.getInstalled(userId)) { + continue; + } + + if (ps.pkg.applicationInfo.targetSdkVersion < higherTargetSDK) { + return true; + } + } + + return false; + } else { + return false; + } + } + } + @Override public int[] getPackageGids(String packageName, int flags, int userId) { if (!sUserManager.exists(userId)) return null; @@ -5280,13 +5349,21 @@ public class PackageManagerService extends IPackageManager.Stub @Override public void grantRuntimePermission(String packageName, String permName, final int userId) { - mPermissionManager.grantRuntimePermission(permName, packageName, false /*overridePolicy*/, + boolean overridePolicy = (checkUidPermission( + Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, Binder.getCallingUid()) + == PackageManager.PERMISSION_GRANTED); + + mPermissionManager.grantRuntimePermission(permName, packageName, overridePolicy, getCallingUid(), userId, mPermissionCallback); } @Override public void revokeRuntimePermission(String packageName, String permName, int userId) { - mPermissionManager.revokeRuntimePermission(permName, packageName, false /*overridePolicy*/, + boolean overridePolicy = (checkUidPermission( + Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, Binder.getCallingUid()) + == PackageManager.PERMISSION_GRANTED); + + mPermissionManager.revokeRuntimePermission(permName, packageName, overridePolicy, getCallingUid(), userId, mPermissionCallback); } @@ -5329,10 +5406,37 @@ public class PackageManagerService extends IPackageManager.Stub @Override public void updatePermissionFlags(String permName, String packageName, int flagMask, - int flagValues, int userId) { + int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) { + int callingUid = getCallingUid(); + boolean overridePolicy = false; + + if (callingUid != Process.SYSTEM_UID && callingUid != Process.ROOT_UID) { + long callingIdentity = Binder.clearCallingIdentity(); + try { + if ((flagMask & FLAG_PERMISSION_POLICY_FIXED) != 0) { + if (checkAdjustPolicyFlagPermission) { + mContext.enforceCallingOrSelfPermission( + Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, + "Need " + Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY + + " to change policy flags"); + } else if (!hasTargetSdkInUidLowerThan(callingUid, Build.VERSION_CODES.Q)) { + throw new IllegalArgumentException( + Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY + " needs " + + " to be checked for packages targeting " + + Build.VERSION_CODES.Q + " or later when changing policy " + + "flags"); + } + + overridePolicy = true; + } + } finally { + Binder.restoreCallingIdentity(callingIdentity); + } + } + mPermissionManager.updatePermissionFlags( - permName, packageName, flagMask, flagValues, getCallingUid(), userId, - mPermissionCallback); + permName, packageName, flagMask, flagValues, callingUid, userId, + overridePolicy, mPermissionCallback); } /** @@ -9309,12 +9413,12 @@ public class PackageManagerService extends IPackageManager.Stub options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY); for (PackageParser.Package depPackage : deps) { // TODO: Analyze and investigate if we (should) profile libraries. - pdo.performDexOpt(depPackage, null /* sharedLibraries */, instructionSets, + pdo.performDexOpt(depPackage, instructionSets, getOrCreateCompilerPackageStats(depPackage), mDexManager.getPackageUseInfoOrDefault(depPackage.packageName), libraryOptions); } } - return pdo.performDexOpt(p, p.usesLibraryInfos, instructionSets, + return pdo.performDexOpt(p, instructionSets, getOrCreateCompilerPackageStats(p), mDexManager.getPackageUseInfoOrDefault(p.packageName), options); } @@ -12683,6 +12787,11 @@ public class PackageManagerService extends IPackageManager.Stub @Override public int installExistingPackageAsUser(String packageName, int userId, int installFlags, int installReason) { + return installExistingPackageAsUser(packageName, userId, installFlags, installReason, null); + } + + int installExistingPackageAsUser(String packageName, int userId, int installFlags, + int installReason, IntentSender intentSender) { if (DEBUG_INSTALL) { Log.v(TAG, "installExistingPackageAsUser package=" + packageName + " userId=" + userId + " installFlags=" + installFlags + " installReason=" + installReason); @@ -12762,7 +12871,11 @@ public class PackageManagerService extends IPackageManager.Stub PackageInstalledInfo res = createPackageInstalledInfo(PackageManager.INSTALL_SUCCEEDED); res.pkg = pkgSetting.pkg; - restoreAndPostInstall(userId, res, null); + res.newUsers = new int[]{ userId }; + PostInstallData postInstallData = intentSender == null ? null : + new PostInstallData(null, res, () -> onRestoreComplete(res.returnCode, + mContext, intentSender)); + restoreAndPostInstall(userId, res, postInstallData); } } finally { Binder.restoreCallingIdentity(callingId); @@ -12771,6 +12884,16 @@ public class PackageManagerService extends IPackageManager.Stub return PackageManager.INSTALL_SUCCEEDED; } + static void onRestoreComplete(int returnCode, Context context, IntentSender target) { + Intent fillIn = new Intent(); + fillIn.putExtra(PackageInstaller.EXTRA_STATUS, + PackageManager.installStatusToPublicStatus(returnCode)); + try { + target.sendIntent(context, 0, fillIn, null, null); + } catch (SendIntentException ignored) { + } + } + static void setInstantAppForUser(PackageSetting pkgSetting, int userId, boolean instantApp, boolean fullApp) { // no state specified; do nothing @@ -12867,8 +12990,14 @@ public class PackageManagerService extends IPackageManager.Stub "setPackagesSuspendedAsUser"); final int callingUid = Binder.getCallingUid(); - if (callingUid != Process.ROOT_UID && callingUid != Process.SYSTEM_UID - && getPackageUid(callingPackage, 0, userId) != callingUid) { + final int packageUid = getPackageUid(callingPackage, 0, userId); + final boolean allowedCallingUid = callingUid == Process.ROOT_UID + || callingUid == Process.SYSTEM_UID; + final boolean allowedPackageUid = packageUid == callingUid; + final boolean allowedShell = callingUid == SHELL_UID + && UserHandle.isSameApp(packageUid, callingUid); + + if (!allowedCallingUid && !allowedShell && !allowedPackageUid) { throw new SecurityException("Calling package " + callingPackage + " in user " + userId + " does not belong to calling uid " + callingUid); } @@ -13058,6 +13187,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public String[] getUnsuspendablePackagesForUser(String[] packageNames, int userId) { + Preconditions.checkNotNull("packageNames cannot be null", packageNames); mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS, "getUnsuspendablePackagesForUser"); final int callingUid = Binder.getCallingUid(); @@ -13726,7 +13856,7 @@ public class PackageManagerService extends IPackageManager.Stub } for (InstallRequest request : installRequests) { restoreAndPostInstall(request.args.user.getIdentifier(), request.installResult, - new PostInstallData(request.args, request.installResult)); + new PostInstallData(request.args, request.installResult, null)); } }); } @@ -14216,27 +14346,8 @@ public class PackageManagerService extends IPackageManager.Stub } if (dataOwnerPkg != null) { - // If installed, the package will get access to data left on the device by its - // predecessor. As a security measure, this is permited only if this is not a - // version downgrade or if the predecessor package is marked as debuggable and - // a downgrade is explicitly requested. - // - // On debuggable platform builds, downgrades are permitted even for - // non-debuggable packages to make testing easier. Debuggable platform builds do - // not offer security guarantees and thus it's OK to disable some security - // mechanisms to make debugging/testing easier on those builds. However, even on - // debuggable builds downgrades of packages are permitted only if requested via - // installFlags. This is because we aim to keep the behavior of debuggable - // platform builds as close as possible to the behavior of non-debuggable - // platform builds. - final boolean downgradeRequested = - (installFlags & PackageManager.INSTALL_ALLOW_DOWNGRADE) != 0; - final boolean packageDebuggable = - (dataOwnerPkg.applicationInfo.flags - & ApplicationInfo.FLAG_DEBUGGABLE) != 0; - final boolean downgradePermitted = - (downgradeRequested) && ((Build.IS_DEBUGGABLE) || (packageDebuggable)); - if (!downgradePermitted) { + if (!PackageManagerServiceUtils.isDowngradePermitted(installFlags, + dataOwnerPkg.applicationInfo.flags)) { try { checkDowngrade(dataOwnerPkg, pkgLite); } catch (PackageManagerException e) { @@ -16194,7 +16305,7 @@ public class PackageManagerService extends IPackageManager.Stub REASON_INSTALL, DexoptOptions.DEXOPT_BOOT_COMPLETE | DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE); - mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryInfos, + mPackageDexOptimizer.performDexOpt(pkg, null /* instructionSets */, getOrCreateCompilerPackageStats(pkg), mDexManager.getPackageUseInfoOrDefault(packageName), @@ -17040,7 +17151,7 @@ public class PackageManagerService extends IPackageManager.Stub if (!legacyMode) { // fs-verity is optional for now. Only set up if signature is provided. - if (new File(signaturePath).exists()) { + if (new File(signaturePath).exists() && !VerityUtils.hasFsverity(filePath)) { try { VerityUtils.setUpFsverity(filePath, signaturePath); } catch (IOException | DigestException | NoSuchAlgorithmException @@ -19947,8 +20058,11 @@ public class PackageManagerService extends IPackageManager.Stub private @Nullable String getDocumenterPackageName() { final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("*/*"); + final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver()); - final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null, + final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, resolvedType, MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE | MATCH_DISABLED_COMPONENTS, UserHandle.myUserId()); @@ -20103,13 +20217,9 @@ public class PackageManagerService extends IPackageManager.Stub } // Only allow apps with CHANGE_COMPONENT_ENABLED_STATE permission to change hidden // app details activity - if (AppDetailsActivity.class.getName().equals(className)) { - if (mContext.checkCallingOrSelfPermission( - android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE) - != PackageManager.PERMISSION_GRANTED) { - Slog.e(TAG, "Cannot disable a protected component: " + packageName); - return; - } + if (PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className) + && !allowedByPermission) { + throw new SecurityException("Cannot disable a system-generated component"); } synchronized (mPackages) { @@ -20577,6 +20687,7 @@ public class PackageManagerService extends IPackageManager.Stub storage.registerListener(mStorageListener); mInstallerService.systemReady(); + mApexManager.systemReady(); mPackageDexOptimizer.systemReady(); getStorageManagerInternal().addExternalStoragePolicy( @@ -22989,6 +23100,18 @@ public class PackageManagerService extends IPackageManager.Stub } return 0; } + + @Override + public boolean[] isAudioPlaybackCaptureAllowed(String[] packageNames) + throws RemoteException { + int callingUser = UserHandle.getUserId(Binder.getCallingUid()); + boolean[] results = new boolean[packageNames.length]; + for (int i = results.length - 1; i >= 0; --i) { + ApplicationInfo appInfo = getApplicationInfo(packageNames[i], 0, callingUser); + results[i] = appInfo == null ? false : appInfo.isAudioPlaybackCaptureAllowed(); + } + return results; + } } private class PackageManagerInternalImpl extends PackageManagerInternal { @@ -22996,7 +23119,7 @@ public class PackageManagerService extends IPackageManager.Stub public void updatePermissionFlagsTEMP(String permName, String packageName, int flagMask, int flagValues, int userId) { PackageManagerService.this.updatePermissionFlags( - permName, packageName, flagMask, flagValues, userId); + permName, packageName, flagMask, flagValues, true, userId); } @Override diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java index 6134d3098e3b..3218c8608d77 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java @@ -32,6 +32,7 @@ import android.annotation.Nullable; import android.app.AppGlobals; import android.content.Context; import android.content.Intent; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfoLite; import android.content.pm.PackageManager; import android.content.pm.PackageParser; @@ -796,6 +797,36 @@ public class PackageManagerServiceUtils { } /** + * Checks whenever downgrade of an app is permitted. + * + * @param installFlags flags of the current install. + * @param applicationFlags flags of the currently installed version of the app. + * @return {@code true} if downgrade is permitted according to the {@code installFlags} and + * {@code applicationFlags}. + */ + public static boolean isDowngradePermitted(int installFlags, int applicationFlags) { + // If installed, the package will get access to data left on the device by its + // predecessor. As a security measure, this is permited only if this is not a + // version downgrade or if the predecessor package is marked as debuggable and + // a downgrade is explicitly requested. + // + // On debuggable platform builds, downgrades are permitted even for + // non-debuggable packages to make testing easier. Debuggable platform builds do + // not offer security guarantees and thus it's OK to disable some security + // mechanisms to make debugging/testing easier on those builds. However, even on + // debuggable builds downgrades of packages are permitted only if requested via + // installFlags. This is because we aim to keep the behavior of debuggable + // platform builds as close as possible to the behavior of non-debuggable + // platform builds. + final boolean downgradeRequested = + (installFlags & PackageManager.INSTALL_ALLOW_DOWNGRADE) != 0; + final boolean packageDebuggable = + (applicationFlags + & ApplicationInfo.FLAG_DEBUGGABLE) != 0; + return (downgradeRequested) && ((Build.IS_DEBUGGABLE) || (packageDebuggable)); + } + + /** * Copy package to the target location. * * @param packagePath absolute path to the package to be copied. Can be diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 2eb762b59be4..114810d9127b 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -300,9 +300,9 @@ class PackageManagerShellCommand extends ShellCommand { pw.println("appPackageName = " + session.getAppPackageName() + "; sessionId = " + session.getSessionId() + "; isStaged = " + session.isStaged() - + "; isSessionReady = " + session.isSessionReady() - + "; isSessionApplied = " + session.isSessionApplied() - + "; isSessionFailed = " + session.isSessionFailed() + ";"); + + "; isStagedSessionReady = " + session.isStagedSessionReady() + + "; isStagedSessionApplied = " + session.isStagedSessionApplied() + + "; isStagedSessionFailed = " + session.isStagedSessionFailed() + ";"); } } catch (RemoteException e) { pw.println("Failure [" @@ -1114,6 +1114,7 @@ class PackageManagerShellCommand extends ShellCommand { int userId = UserHandle.USER_SYSTEM; int installFlags = 0; String opt; + boolean waitTillComplete = false; while ((opt = getNextOption()) != null) { switch (opt) { case "--user": @@ -1128,6 +1129,9 @@ class PackageManagerShellCommand extends ShellCommand { installFlags &= ~PackageManager.INSTALL_INSTANT_APP; installFlags |= PackageManager.INSTALL_FULL_APP; break; + case "--wait": + waitTillComplete = true; + break; default: pw.println("Error: Unknown option: " + opt); return 1; @@ -1140,9 +1144,23 @@ class PackageManagerShellCommand extends ShellCommand { return 1; } + int installReason = PackageManager.INSTALL_REASON_UNKNOWN; try { + if (waitTillComplete) { + final LocalIntentReceiver receiver = new LocalIntentReceiver(); + final IPackageInstaller installer = mInterface.getPackageInstaller(); + pw.println("Installing package " + packageName + " for user: " + userId); + installer.installExistingPackage(packageName, installFlags, installReason, + receiver.getIntentSender(), userId); + final Intent result = receiver.getResult(); + final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, + PackageInstaller.STATUS_FAILURE); + pw.println("Received intent for package install"); + return status == PackageInstaller.STATUS_SUCCESS ? 0 : 1; + } + final int res = mInterface.installExistingPackageAsUser(packageName, userId, - installFlags, PackageManager.INSTALL_REASON_UNKNOWN); + installFlags, installReason); if (res == PackageManager.INSTALL_FAILED_INVALID_URI) { throw new NameNotFoundException("Package " + packageName + " doesn't exist"); } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 92fe377e9495..59e5d7716369 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -4372,6 +4372,7 @@ public final class Settings { ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE, "PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE", ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION, "PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION", ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE, "PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE", + ApplicationInfo.PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE, "ALLOW_AUDIO_PLAYBACK_CAPTURE", ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND, "BACKUP_IN_FOREGROUND", ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE, "CANT_SAVE_STATE", ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE, "DEFAULT_TO_DEVICE_PROTECTED_STORAGE", diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index ff6d7a888950..9deea9ec8622 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -2173,7 +2173,8 @@ public class ShortcutService extends IShortcutService.Stub { public boolean hasShareTargets(String packageName, String packageToCheck, @UserIdInt int userId) { verifyCaller(packageName, userId); - enforceSystem(); + enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_APP_PREDICTIONS, + "hasShareTargets"); synchronized (mLock) { throwIfUserLockedL(userId); diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java index 9dfd477a564d..22a85eb20206 100644 --- a/services/core/java/com/android/server/pm/StagingManager.java +++ b/services/core/java/com/android/server/pm/StagingManager.java @@ -39,6 +39,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.ParcelFileDescriptor; +import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; import android.util.Slog; @@ -68,15 +69,18 @@ public class StagingManager { private final PackageInstallerService mPi; private final PackageManagerService mPm; private final ApexManager mApexManager; + private final PowerManager mPowerManager; private final Handler mBgHandler; @GuardedBy("mStagedSessions") private final SparseArray<PackageInstallerSession> mStagedSessions = new SparseArray<>(); - StagingManager(PackageManagerService pm, PackageInstallerService pi, ApexManager am) { + StagingManager(PackageManagerService pm, PackageInstallerService pi, ApexManager am, + Context context) { mPm = pm; mPi = pi; mApexManager = am; + mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); mBgHandler = BackgroundThread.getHandler(); } @@ -140,12 +144,40 @@ public class StagingManager { private boolean submitSessionToApexService(@NonNull PackageInstallerSession session, List<PackageInstallerSession> childSessions, ApexInfoList apexInfoList) { - return mApexManager.submitStagedSession( + boolean submittedToApexd = mApexManager.submitStagedSession( session.sessionId, childSessions != null ? childSessions.stream().mapToInt(s -> s.sessionId).toArray() : new int[]{}, apexInfoList); + if (!submittedToApexd) { + session.setStagedSessionFailed( + SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, + "APEX staging failed, check logcat messages from apexd for more details."); + return false; + } + for (ApexInfo newPackage : apexInfoList.apexInfos) { + PackageInfo activePackage = mApexManager.getActivePackage(newPackage.packageName); + if (activePackage == null) { + continue; + } + long activeVersion = activePackage.applicationInfo.longVersionCode; + boolean allowsDowngrade = PackageManagerServiceUtils.isDowngradePermitted( + session.params.installFlags, activePackage.applicationInfo.flags); + if (activeVersion > newPackage.versionCode && !allowsDowngrade) { + session.setStagedSessionFailed( + SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, + "Downgrade of APEX package " + newPackage.packageName + + " is not allowed. Active version: " + activeVersion + + " attempted: " + newPackage.versionCode); + + if (!mApexManager.abortActiveSession()) { + Slog.e(TAG, "Failed to abort apex session " + session.sessionId); + } + return false; + } + } + return true; } private static boolean isApexSession(@NonNull PackageInstallerSession session) { @@ -180,9 +212,7 @@ public class StagingManager { } if (!success) { - session.setStagedSessionFailed( - SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, - "APEX staging failed, check logcat messages from apexd for more details."); + // submitSessionToApexService will populate error. return; } @@ -257,7 +287,7 @@ public class StagingManager { + "activated"); return; } - if (apexSessionInfo.isActivationFailed || apexSessionInfo.isUnknown) { + if (isApexSessionFailed(apexSessionInfo)) { session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, "APEX activation failed. Check logcat messages from apexd for " + "more information."); @@ -286,6 +316,20 @@ public class StagingManager { session.setStagedSessionFailed(SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, "Staged installation of APKs failed. Check logcat messages for" + "more information."); + + if (!hasApex) { + return; + } + + if (!mApexManager.abortActiveSession()) { + Slog.e(TAG, "Failed to abort APEXd session"); + } else { + Slog.e(TAG, + "Successfully aborted apexd session. Rebooting device in order to revert " + + "to the previous state of APEXd."); + mPowerManager.reboot(null); + } + return; } @@ -419,7 +463,12 @@ public class StagingManager { if (apkChildSession == null) { return false; } - apkParentSession.addChildSessionId(apkChildSession.sessionId); + try { + apkParentSession.addChildSessionId(apkChildSession.sessionId); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to add a child session for installing the APK files", e); + return false; + } } return commitApkSession(apkParentSession, session.sessionId); } @@ -440,11 +489,40 @@ public class StagingManager { void abortSession(@NonNull PackageInstallerSession session) { synchronized (mStagedSessions) { - updateStoredSession(session); mStagedSessions.remove(session.sessionId); } } + void abortCommittedSession(@NonNull PackageInstallerSession session) { + if (session.isStagedSessionApplied()) { + Slog.w(TAG, "Cannot abort applied session!"); + return; + } + if (isStagedSessionFinalized(session.sessionId)) { + Slog.w(TAG, "Cannot abort session because it is not active or APEXD is not reachable"); + return; + } + + mApexManager.abortActiveSession(); + + abortSession(session); + } + + private boolean isStagedSessionFinalized(int sessionId) { + ApexSessionInfo session = mApexManager.getStagedSessionInfo(sessionId); + + /* checking if the session is in a final state, i.e., not active anymore */ + return session.isUnknown || session.isActivationFailed || session.isSuccess + || session.isRolledBack; + } + + private static boolean isApexSessionFailed(ApexSessionInfo apexSessionInfo) { + // isRollbackInProgress is included to cover the scenario, when a device is rebooted in + // during the rollback, and apexd fails to resume the rollback after reboot. + return apexSessionInfo.isActivationFailed || apexSessionInfo.isUnknown + || apexSessionInfo.isRolledBack || apexSessionInfo.isRollbackInProgress; + } + @GuardedBy("mStagedSessions") private boolean isMultiPackageSessionComplete(@NonNull PackageInstallerSession session) { // This method assumes that the argument is either a parent session of a multi-package diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index d0f192d597c6..3744f68afbfe 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -1324,6 +1324,7 @@ public class UserManagerService extends IUserManager.Stub { @Override public ParcelFileDescriptor getUserIcon(int targetUserId) { + checkManageUsersPermission("get user icon"); String iconPath; synchronized (mPackagesLock) { UserInfo targetUserInfo = getUserInfoNoChecks(targetUserId); diff --git a/services/core/java/com/android/server/pm/dex/TEST_MAPPING b/services/core/java/com/android/server/pm/dex/TEST_MAPPING index c93af2a5ba41..1c86c4f72ff9 100644 --- a/services/core/java/com/android/server/pm/dex/TEST_MAPPING +++ b/services/core/java/com/android/server/pm/dex/TEST_MAPPING @@ -9,7 +9,7 @@ ] }, { - "name": "DexLoggerIntegrationTests" + "name": "DynamicCodeLoggerIntegrationTests" } ] } diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index 636917921652..447234ed290b 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -206,6 +206,11 @@ public final class DefaultPermissionGrantPolicy { // STOPSHIP(b/112545973): remove once feature enabled by default if (StorageManager.hasIsolatedStorage()) { MEDIA_AURAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_AUDIO); + + // STOPSHIP(b/124466734): remove these manual grants once the legacy + // permission logic is unified with PermissionController + MEDIA_AURAL_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE); + MEDIA_AURAL_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE); } } @@ -215,6 +220,11 @@ public final class DefaultPermissionGrantPolicy { if (StorageManager.hasIsolatedStorage()) { MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_VIDEO); MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.READ_MEDIA_IMAGES); + + // STOPSHIP(b/124466734): remove these manual grants once the legacy + // permission logic is unified with PermissionController + MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE); + MEDIA_VISUAL_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE); } } @@ -642,16 +652,10 @@ public final class DefaultPermissionGrantPolicy { // Location if (locationPackageNames != null) { for (String packageName : locationPackageNames) { - // STOPSHIP: remove this force-granting of legacy storage - // permissions once b/124466734 is resolved - final Set<String> storageWorkaround = new ArraySet<>(); - storageWorkaround.add(Manifest.permission.READ_EXTERNAL_STORAGE); - storageWorkaround.add(Manifest.permission.WRITE_EXTERNAL_STORAGE); - grantPermissionsToSystemPackage(packageName, userId, CONTACTS_PERMISSIONS, CALENDAR_PERMISSIONS, MICROPHONE_PERMISSIONS, PHONE_PERMISSIONS, SMS_PERMISSIONS, CAMERA_PERMISSIONS, - SENSORS_PERMISSIONS, STORAGE_PERMISSIONS, storageWorkaround); + SENSORS_PERMISSIONS, STORAGE_PERMISSIONS, MEDIA_AURAL_PERMISSIONS); grantSystemFixedPermissionsToSystemPackage(packageName, userId, LOCATION_PERMISSIONS, ACTIVITY_RECOGNITION_PERMISSIONS); } @@ -846,7 +850,7 @@ public final class DefaultPermissionGrantPolicy { } for (String packageName : packageNames) { grantPermissionsToSystemPackage(packageName, userId, - PHONE_PERMISSIONS, LOCATION_PERMISSIONS, SMS_PERMISSIONS); + PHONE_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS, SMS_PERMISSIONS); } } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 8df5a71de43e..03da962b6ac6 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -35,6 +35,7 @@ import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQU import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED; import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED; import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET; +import static android.content.pm.PackageManager.MASK_PERMISSION_FLAGS; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; import static android.os.UserHandle.getAppId; import static android.os.UserHandle.getUid; @@ -1031,7 +1032,8 @@ public class PermissionManagerService { updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId); } - permissionsState.updatePermissionFlags(bp, userId, flags, flags); + permissionsState.updatePermissionFlags(bp, userId, + MASK_PERMISSION_FLAGS, flags); } } break; @@ -1081,7 +1083,8 @@ public class PermissionManagerService { updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId); } - permissionsState.updatePermissionFlags(bp, userId, flags, flags); + permissionsState.updatePermissionFlags(bp, userId, + MASK_PERMISSION_FLAGS, flags); } } break; @@ -1198,29 +1201,23 @@ public class PermissionManagerService { if ((flags & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED) != 0) { BasePermission bp = mSettings.getPermissionLocked(permission); - ps.updatePermissionFlags(bp, userId, - FLAG_PERMISSION_REVOKE_WHEN_REQUESTED - | FLAG_PERMISSION_USER_FIXED | FLAG_PERMISSION_USER_SET, - 0); - updatedUserIds = ArrayUtils.appendInt(updatedUserIds, - userId); + int flagsToRemove = FLAG_PERMISSION_REVOKE_WHEN_REQUESTED; if ((flags & (FLAG_PERMISSION_GRANTED_BY_DEFAULT | FLAG_PERMISSION_POLICY_FIXED | FLAG_PERMISSION_SYSTEM_FIXED)) - == 0) { - if (supportsRuntimePermissions) { - int revokeResult = ps.revokeRuntimePermission(bp, userId); - if (revokeResult != PERMISSION_OPERATION_FAILURE) { - if (DEBUG_PERMISSIONS) { - Slog.i(TAG, "Revoking runtime permission " - + permission + " for " + pkgName - + " as it is now requested"); - } + == 0 && supportsRuntimePermissions) { + int revokeResult = ps.revokeRuntimePermission(bp, userId); + if (revokeResult != PERMISSION_OPERATION_FAILURE) { + if (DEBUG_PERMISSIONS) { + Slog.i(TAG, "Revoking runtime permission " + + permission + " for " + pkgName + + " as it is now requested"); } - } else { - setAppOpMode(permission, pkg, userId, MODE_IGNORED); } + flagsToRemove |= + FLAG_PERMISSION_USER_FIXED | FLAG_PERMISSION_USER_SET; + List<String> fgPerms = mBackgroundPermissions.get(permission); if (fgPerms != null) { int numFgPerms = fgPerms.size(); @@ -1238,6 +1235,9 @@ public class PermissionManagerService { } } } + + ps.updatePermissionFlags(bp, userId, flagsToRemove, 0); + updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId); } } } @@ -1935,7 +1935,7 @@ public class PermissionManagerService { if ((flags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) { updatePermissionFlags(permission, pkg.packageName, PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED, 0, callingUid, - userId, callback); + userId, false, callback); } } } @@ -2441,7 +2441,8 @@ public class PermissionManagerService { } private void updatePermissionFlags(String permName, String packageName, int flagMask, - int flagValues, int callingUid, int userId, PermissionCallback callback) { + int flagValues, int callingUid, int userId, boolean overridePolicy, + PermissionCallback callback) { if (!mUserManagerInt.exists(userId)) { return; } @@ -2454,6 +2455,11 @@ public class PermissionManagerService { false, // requirePermissionWhenSameUser "updatePermissionFlags"); + if ((flagMask & FLAG_PERMISSION_POLICY_FIXED) != 0 && !overridePolicy) { + throw new SecurityException("updatePermissionFlags requires " + + Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY); + } + // Only the system can change these flags and nothing else. if (callingUid != Process.SYSTEM_UID) { flagMask &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED; @@ -2745,9 +2751,11 @@ public class PermissionManagerService { } @Override public void updatePermissionFlags(String permName, String packageName, int flagMask, - int flagValues, int callingUid, int userId, PermissionCallback callback) { + int flagValues, int callingUid, int userId, boolean overridePolicy, + PermissionCallback callback) { PermissionManagerService.this.updatePermissionFlags( - permName, packageName, flagMask, flagValues, callingUid, userId, callback); + permName, packageName, flagMask, flagValues, callingUid, userId, + overridePolicy, callback); } @Override public boolean updatePermissionFlagsForAllApps(int flagMask, int flagValues, int callingUid, diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java index 1dd2408686c1..305f165ae85f 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java @@ -147,7 +147,7 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager */ public abstract void updatePermissionFlags(@NonNull String permName, @NonNull String packageName, int flagMask, int flagValues, int callingUid, int userId, - @Nullable PermissionCallback callback); + boolean overridePolicy, @Nullable PermissionCallback callback); /** * Updates the flags for all applications by replacing the flags in the specified mask * with the provided flag values. diff --git a/services/core/java/com/android/server/policy/DisplayFoldController.java b/services/core/java/com/android/server/policy/DisplayFoldController.java index cf026e97b69f..0db3d78fed5b 100644 --- a/services/core/java/com/android/server/policy/DisplayFoldController.java +++ b/services/core/java/com/android/server/policy/DisplayFoldController.java @@ -35,10 +35,12 @@ import com.android.server.wm.WindowManagerInternal; /** * Controls the behavior of foldable devices whose screen can literally bend and fold. + * TODO(b/126160895): Move DisplayFoldController from PhoneWindowManager to DisplayPolicy. */ class DisplayFoldController { private static final String TAG = "DisplayFoldController"; + private final WindowManagerInternal mWindowManagerInternal; private final DisplayManagerInternal mDisplayManagerInternal; private final int mDisplayId; @@ -52,6 +54,8 @@ class DisplayFoldController { private final DisplayInfo mNonOverrideDisplayInfo = new DisplayInfo(); private final RemoteCallbackList<IDisplayFoldListener> mListeners = new RemoteCallbackList<>(); private Boolean mFolded; + private String mFocusedApp; + private final DisplayFoldDurationLogger mDurationLogger = new DisplayFoldDurationLogger(); DisplayFoldController(WindowManagerInternal windowManagerInternal, DisplayManagerInternal displayManagerInternal, int displayId, Rect foldedArea, @@ -63,6 +67,14 @@ class DisplayFoldController { mHandler = handler; } + void finishedGoingToSleep() { + mDurationLogger.onFinishedGoingToSleep(); + } + + void finishedWakingUp() { + mDurationLogger.onFinishedWakingUp(mFolded); + } + void requestDeviceFolded(boolean folded) { mHandler.post(() -> setDeviceFolded(folded)); } @@ -97,6 +109,8 @@ class DisplayFoldController { mWindowManagerInternal.clearForcedDisplaySize(mDisplayId); mDisplayManagerInternal.setDisplayOffsets(mDisplayId, 0, 0); } + mDurationLogger.setDeviceFolded(folded); + mDurationLogger.logFocusedAppWithFoldState(folded, mFocusedApp); mFolded = folded; final int n = mListeners.beginBroadcast(); @@ -167,6 +181,10 @@ class DisplayFoldController { return result; } + void onDefaultDisplayFocusChanged(String pkg) { + mFocusedApp = pkg; + } + static DisplayFoldController create(Context context, int displayId) { final DisplayManagerInternal displayService = LocalServices.getService(DisplayManagerInternal.class); diff --git a/services/core/java/com/android/server/policy/DisplayFoldDurationLogger.java b/services/core/java/com/android/server/policy/DisplayFoldDurationLogger.java new file mode 100644 index 000000000000..bdcd2cde2e4e --- /dev/null +++ b/services/core/java/com/android/server/policy/DisplayFoldDurationLogger.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.policy; + +import android.annotation.IntDef; +import android.metrics.LogMaker; +import android.os.SystemClock; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Logger for tracking duration of usage in folded vs unfolded state. + */ +class DisplayFoldDurationLogger { + static final int SCREEN_STATE_UNKNOWN = -1; + static final int SCREEN_STATE_OFF = 0; + static final int SCREEN_STATE_ON_UNFOLDED = 1; + static final int SCREEN_STATE_ON_FOLDED = 2; + + @IntDef(flag = true, prefix = {"SCREEN_STATE_"}, value = { + SCREEN_STATE_UNKNOWN, + SCREEN_STATE_OFF, + SCREEN_STATE_ON_UNFOLDED, + SCREEN_STATE_ON_FOLDED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ScreenState {} + + private @ScreenState int mScreenState = SCREEN_STATE_UNKNOWN; + private Long mLastChanged = null; + + private static final int LOG_SUBTYPE_UNFOLDED = 0; + private static final int LOG_SUBTYPE_FOLDED = 1; + private static final int LOG_SUBTYPE_DURATION_MASK = 0x80000000; + + private final MetricsLogger mLogger = new MetricsLogger(); + + void onFinishedWakingUp(Boolean folded) { + if (folded == null) { + mScreenState = SCREEN_STATE_UNKNOWN; + } else if (folded) { + mScreenState = SCREEN_STATE_ON_FOLDED; + } else { + mScreenState = SCREEN_STATE_ON_UNFOLDED; + } + mLastChanged = SystemClock.uptimeMillis(); + } + + void onFinishedGoingToSleep() { + log(); + mScreenState = SCREEN_STATE_OFF; + mLastChanged = null; + } + + void setDeviceFolded(boolean folded) { + // This function is called even when the screen is in ADO mode, but we're only + // interested in the case that the screen is actually on. + if (!isOn()) { + return; + } + log(); + mScreenState = folded ? SCREEN_STATE_ON_FOLDED : SCREEN_STATE_ON_UNFOLDED; + mLastChanged = SystemClock.uptimeMillis(); + } + + void logFocusedAppWithFoldState(boolean folded, String packageName) { + mLogger.write( + new LogMaker(MetricsProto.MetricsEvent.ACTION_DISPLAY_FOLD) + .setType(MetricsProto.MetricsEvent.TYPE_ACTION) + .setSubtype(folded ? LOG_SUBTYPE_FOLDED : LOG_SUBTYPE_UNFOLDED) + .setPackageName(packageName)); + } + + private void log() { + if (mLastChanged == null) { + return; + } + int subtype; + switch (mScreenState) { + case SCREEN_STATE_ON_UNFOLDED: + subtype = LOG_SUBTYPE_UNFOLDED | LOG_SUBTYPE_DURATION_MASK; + break; + case SCREEN_STATE_ON_FOLDED: + subtype = LOG_SUBTYPE_FOLDED | LOG_SUBTYPE_DURATION_MASK; + break; + default: + return; + } + mLogger.write( + new LogMaker(MetricsProto.MetricsEvent.ACTION_DISPLAY_FOLD) + .setType(MetricsProto.MetricsEvent.TYPE_ACTION) + .setSubtype(subtype) + .setLatency(SystemClock.uptimeMillis() - mLastChanged)); + } + + private boolean isOn() { + return mScreenState == SCREEN_STATE_ON_UNFOLDED || mScreenState == SCREEN_STATE_ON_FOLDED; + } +} diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index c87a81db16e4..c0e597497740 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -86,6 +86,9 @@ import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED; +import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_BEHAVIOR_LOCK; +import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_BEHAVIOR_NONE; +import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_BEHAVIOR_SLEEP; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN; import static com.android.server.wm.WindowManagerPolicyProto.KEYGUARD_DELEGATE; @@ -473,8 +476,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { int mLidKeyboardAccessibility; int mLidNavigationAccessibility; - boolean mLidControlsScreenLock; - boolean mLidControlsSleep; private boolean mLidControlsDisplayFold; int mShortPressOnPowerBehavior; int mLongPressOnPowerBehavior; @@ -808,7 +809,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { public void onWakeUp() { synchronized (mLock) { if (shouldEnableWakeGestureLp()) { - performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false, + performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, false, "Wake Up"); wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromWakeGesture, PowerManager.WAKE_REASON_GESTURE, "android.policy:GESTURE"); @@ -1195,6 +1196,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + private int getLidBehavior() { + return Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.LID_BEHAVIOR, LID_BEHAVIOR_NONE); + } + private int getMaxMultiPressPowerCount() { if (mTriplePressOnPowerBehavior != MULTI_PRESS_POWER_NOTHING) { return 3; @@ -1212,21 +1218,21 @@ public class PhoneWindowManager implements WindowManagerPolicy { break; case LONG_PRESS_POWER_GLOBAL_ACTIONS: mPowerKeyHandled = true; - performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false, + performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false, "Power - Long Press - Global Actions"); showGlobalActionsInternal(); break; case LONG_PRESS_POWER_SHUT_OFF: case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM: mPowerKeyHandled = true; - performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false, + performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false, "Power - Long Press - Shut Off"); sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS); mWindowManagerFuncs.shutdown(behavior == LONG_PRESS_POWER_SHUT_OFF); break; case LONG_PRESS_POWER_GO_TO_VOICE_ASSIST: mPowerKeyHandled = true; - performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false, + performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false, "Power - Long Press - Go To Voice Assist"); final boolean keyguardActive = mKeyguardDelegate == null ? false @@ -1249,7 +1255,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { break; case VERY_LONG_PRESS_POWER_GLOBAL_ACTIONS: mPowerKeyHandled = true; - performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false, + performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false, "Power - Very Long Press - Show Global Actions"); showGlobalActionsInternal(); break; @@ -1400,7 +1406,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { @Override public void run() { mEndCallKeyHandled = true; - performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false, + performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false, "End Call - Long Press - Show Global Actions"); showGlobalActionsInternal(); } @@ -1667,7 +1673,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { return; } mHomeConsumed = true; - performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false, + performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false, "Home - Long Press"); switch (mLongPressOnHomeBehavior) { case LONG_PRESS_HOME_ALL_APPS: @@ -1796,10 +1802,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { com.android.internal.R.integer.config_lidKeyboardAccessibility); mLidNavigationAccessibility = mContext.getResources().getInteger( com.android.internal.R.integer.config_lidNavigationAccessibility); - mLidControlsScreenLock = mContext.getResources().getBoolean( - com.android.internal.R.bool.config_lidControlsScreenLock); - mLidControlsSleep = mContext.getResources().getBoolean( - com.android.internal.R.bool.config_lidControlsSleep); mLidControlsDisplayFold = mContext.getResources().getBoolean( com.android.internal.R.bool.config_lidControlsDisplayFold); @@ -2040,7 +2042,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { private boolean shouldEnableWakeGestureLp() { return mWakeGestureEnabledSetting && !mDefaultDisplayPolicy.isAwake() - && (!mLidControlsSleep || mDefaultDisplayPolicy.getLidState() != LID_CLOSED) + && (getLidBehavior() != LID_BEHAVIOR_SLEEP + || mDefaultDisplayPolicy.getLidState() != LID_CLOSED) && mWakeGestureListener.isSupported(); } @@ -2316,14 +2319,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { boolean allowWhenLocked = (win.isInputMethodWindow() || imeTarget == this) && showImeOverKeyguard; - if (isKeyguardLocked() && isKeyguardOccluded()) { + final boolean isKeyguardShowing = mKeyguardDelegate.isShowing(); + + if (isKeyguardShowing && isKeyguardOccluded()) { // Show SHOW_WHEN_LOCKED windows if Keyguard is occluded. allowWhenLocked |= win.canShowWhenLocked() // Show error dialogs over apps that are shown on lockscreen || (attrs.privateFlags & PRIVATE_FLAG_SYSTEM_ERROR) != 0; } - boolean keyguardLocked = isKeyguardLocked(); boolean hideDockDivider = attrs.type == TYPE_DOCK_DIVIDER && !mWindowManagerInternal.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); // If AOD is showing, the IME should be hidden. However, sometimes the AOD is considered @@ -2333,7 +2337,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { // now shown. final boolean hideIme = win.isInputMethodWindow() && (mAodShowing || !mDefaultDisplayPolicy.isWindowManagerDrawComplete()); - return (keyguardLocked && !allowWhenLocked && win.getDisplayId() == DEFAULT_DISPLAY) + return (isKeyguardShowing && !allowWhenLocked && win.getDisplayId() == DEFAULT_DISPLAY) || hideDockDivider || hideIme; } @@ -3237,6 +3241,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { } @Override + public void onDefaultDisplayFocusChangedLw(WindowState newFocus) { + if (mDisplayFoldController != null) { + mDisplayFoldController.onDefaultDisplayFocusChanged( + newFocus != null ? newFocus.getOwningPackage() : null); + } + } + + @Override public void registerShortcutKey(long shortcutCode, IShortcutService shortcutService) throws RemoteException { synchronized (mLock) { @@ -3276,7 +3288,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } private void launchAssistLongPressAction() { - performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false, + performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, false, "Assist - Long Press"); sendCloseSystemWindows(SYSTEM_DIALOG_REASON_ASSIST); @@ -3545,7 +3557,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (lidOpen) { wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromLidSwitch, PowerManager.WAKE_REASON_LID, "android.policy:LID"); - } else if (!mLidControlsSleep) { + } else if (getLidBehavior() != LID_BEHAVIOR_SLEEP) { mPowerManager.userActivity(SystemClock.uptimeMillis(), false); } } @@ -4038,7 +4050,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } if (useHapticFeedback) { - performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false, + performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, false, "Virtual Key - Press"); } @@ -4436,6 +4448,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { mKeyguardDelegate.onFinishedGoingToSleep(why, mCameraGestureTriggeredDuringGoingToSleep); } + if (mDisplayFoldController != null) { + mDisplayFoldController.finishedGoingToSleep(); + } mCameraGestureTriggeredDuringGoingToSleep = false; } @@ -4476,6 +4491,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (mKeyguardDelegate != null) { mKeyguardDelegate.onFinishedWakingUp(); } + if (mDisplayFoldController != null) { + mDisplayFoldController.finishedWakingUp(); + } } private void wakeUpFromPowerKey(long eventTime) { @@ -4759,7 +4777,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { public void setSafeMode(boolean safeMode) { mSafeMode = safeMode; if (safeMode) { - performHapticFeedbackLw(null, HapticFeedbackConstants.SAFE_MODE_ENABLED, true, + performHapticFeedback(HapticFeedbackConstants.SAFE_MODE_ENABLED, true, "Safe Mode Enabled"); } } @@ -5040,11 +5058,22 @@ public class PhoneWindowManager implements WindowManagerPolicy { final int lidState = mDefaultDisplayPolicy.getLidState(); if (mLidControlsDisplayFold && mDisplayFoldController != null) { mDisplayFoldController.requestDeviceFolded(lidState == LID_CLOSED); - } else if (lidState == LID_CLOSED && mLidControlsSleep) { - goToSleep(SystemClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH, - PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE); - } else if (lidState == LID_CLOSED && mLidControlsScreenLock) { - mWindowManagerFuncs.lockDeviceNow(); + } else if (lidState == LID_CLOSED) { + int lidBehavior = getLidBehavior(); + switch (lidBehavior) { + case LID_BEHAVIOR_LOCK: + mWindowManagerFuncs.lockDeviceNow(); + break; + case LID_BEHAVIOR_SLEEP: + goToSleep(SystemClock.uptimeMillis(), + PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH, + PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE); + break; + case LID_BEHAVIOR_NONE: + // fall through + default: + break; + } } synchronized (mLock) { @@ -5236,9 +5265,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { Settings.Global.THEATER_MODE_ON, 0) == 1; } + private boolean performHapticFeedback(int effectId, boolean always, String reason) { + return performHapticFeedback(Process.myUid(), mContext.getOpPackageName(), + effectId, always, reason); + } + @Override - public boolean performHapticFeedbackLw(WindowState win, int effectId, boolean always, - String reason) { + public boolean performHapticFeedback(int uid, String packageName, int effectId, + boolean always, String reason) { if (!mVibrator.hasVibrator()) { return false; } @@ -5253,16 +5287,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { return false; } - int owningUid; - String owningPackage; - if (win != null) { - owningUid = win.getOwningUid(); - owningPackage = win.getOwningPackage(); - } else { - owningUid = android.os.Process.myUid(); - owningPackage = mContext.getOpPackageName(); - } - mVibrator.vibrate(owningUid, owningPackage, effect, reason, VIBRATION_ATTRIBUTES); + mVibrator.vibrate(uid, packageName, effect, reason, VIBRATION_ATTRIBUTES); return true; } @@ -5404,8 +5429,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { pw.print(prefix); pw.print("mLidKeyboardAccessibility="); pw.print(mLidKeyboardAccessibility); pw.print(" mLidNavigationAccessibility="); pw.print(mLidNavigationAccessibility); - pw.print(" mLidControlsScreenLock="); pw.println(mLidControlsScreenLock); - pw.print(prefix); pw.print("mLidControlsSleep="); pw.println(mLidControlsSleep); + pw.print(" getLidBehavior="); pw.println(lidBehaviorToString(getLidBehavior())); pw.print(prefix); pw.print("mLongPressOnBackBehavior="); pw.println(longPressOnBackBehaviorToString(mLongPressOnBackBehavior)); @@ -5639,6 +5663,19 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + private static String lidBehaviorToString(int behavior) { + switch (behavior) { + case LID_BEHAVIOR_LOCK: + return "LID_BEHAVIOR_LOCK"; + case LID_BEHAVIOR_SLEEP: + return "LID_BEHAVIOR_SLEEP"; + case LID_BEHAVIOR_NONE: + return "LID_BEHAVIOR_NONE"; + default: + return Integer.toString(behavior); + } + } + @Override public boolean setAodShowing(boolean aodShowing) { if (mAodShowing != aodShowing) { diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index d7e4b6cff4d8..d58707cf2eec 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -518,6 +518,10 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { public static final int LID_CLOSED = 0; public static final int LID_OPEN = 1; + public static final int LID_BEHAVIOR_NONE = 0; + public static final int LID_BEHAVIOR_SLEEP = 1; + public static final int LID_BEHAVIOR_LOCK = 2; + public static final int CAMERA_LENS_COVER_ABSENT = -1; public static final int CAMERA_LENS_UNCOVERED = 0; public static final int CAMERA_LENS_COVERED = 1; @@ -1300,8 +1304,8 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { /** * Call from application to perform haptic feedback on its window. */ - public boolean performHapticFeedbackLw(WindowState win, int effectId, boolean always, - String reason); + public boolean performHapticFeedback(int uid, String packageName, int effectId, + boolean always, String reason); /** * Called when we have started keeping the screen on because a window @@ -1485,6 +1489,11 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { } /** + * A new window on default display has been focused. + */ + default void onDefaultDisplayFocusChangedLw(WindowState newFocus) {} + + /** * Updates the flag about whether AOD is showing. * * @return whether the value was changed. diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java index ae1090cfb045..c40854923bf7 100644 --- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java +++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java @@ -209,11 +209,8 @@ public class KeyguardServiceDelegate { mKeyguardState.reset(); mHandler.post(() -> { try { - // There are no longer any keyguard windows on secondary displays, so pass - // {@code null}. All that means is that showWhenLocked activities on - // external displays now get to show. ActivityTaskManager.getService().setLockScreenShown(true /* keyguardShowing */, - false /* aodShowing */, null /* secondaryDisplaysShowing */); + false /* aodShowing */); } catch (RemoteException e) { // Local call. } diff --git a/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java b/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java index 888dd9992bdf..77bf930fb4d7 100644 --- a/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java +++ b/services/core/java/com/android/server/policy/role/LegacyRoleResolutionPolicy.java @@ -130,6 +130,12 @@ public class LegacyRoleResolutionPolicy implements RoleManagerService.RoleHolder String packageName = componentName != null ? componentName.getPackageName() : null; return CollectionUtils.singletonOrEmpty(packageName); } + case RoleManager.ROLE_EMERGENCY: { + String defaultEmergencyApp = Settings.Secure.getStringForUser( + mContext.getContentResolver(), + Settings.Secure.EMERGENCY_ASSISTANCE_APPLICATION, userId); + return CollectionUtils.singletonOrEmpty(defaultEmergencyApp); + } default: { Slog.e(LOG_TAG, "Don't know how to find legacy role holders for " + roleName); return Collections.emptyList(); diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 176dbbf6a965..89d24b194b13 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -36,6 +36,7 @@ import android.content.res.Resources; import android.database.ContentObserver; import android.hardware.SensorManager; import android.hardware.SystemSensorManager; +import android.hardware.display.AmbientDisplayConfiguration; import android.hardware.display.DisplayManagerInternal; import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest; import android.hardware.power.V1_0.PowerHint; @@ -81,7 +82,6 @@ import android.view.Display; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IAppOpsService; import com.android.internal.app.IBatteryStats; -import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.internal.os.BackgroundThread; import com.android.internal.util.DumpUtils; import com.android.server.EventLogTags; diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java index e6fa500eaef3..af78995b6faa 100644 --- a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java +++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java @@ -280,7 +280,7 @@ public class BatterySaverStateMachine { Settings.Global.DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD, mDynamicPowerSavingsDefaultDisableThreshold); final boolean isStickyAutoDisableEnabled = getGlobalSetting( - Settings.Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED, 0) != 0; + Settings.Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED, 1) != 0; final int stickyAutoDisableThreshold = getGlobalSetting( Settings.Global.LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL, 90); diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java index c145a22de6cd..17d3066bd37a 100644 --- a/services/core/java/com/android/server/role/RoleManagerService.java +++ b/services/core/java/com/android/server/role/RoleManagerService.java @@ -237,6 +237,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C migrateRoleIfNecessary(RoleManager.ROLE_SMS, userId); migrateRoleIfNecessary(RoleManager.ROLE_ASSISTANT, userId); migrateRoleIfNecessary(RoleManager.ROLE_DIALER, userId); + migrateRoleIfNecessary(RoleManager.ROLE_EMERGENCY, userId); // Some vital packages state has changed since last role grant // Run grants again diff --git a/services/core/java/com/android/server/role/TEST_MAPPING b/services/core/java/com/android/server/role/TEST_MAPPING index 0b967be5169f..0d7bc1476bd1 100644 --- a/services/core/java/com/android/server/role/TEST_MAPPING +++ b/services/core/java/com/android/server/role/TEST_MAPPING @@ -7,11 +7,14 @@ "include-filter": "android.cts.statsd.atom.UidAtomTests#testRoleHolder" } ] - } - ], - "postsubmit": [ + }, { - "name": "CtsRoleTestCases" + "name": "CtsRoleTestCases", + "options": [ + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + } + ] } ] } diff --git a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java index f3b838560ebd..36f18f05c23e 100644 --- a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java +++ b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java @@ -29,6 +29,8 @@ import com.android.server.pm.Installer; import com.android.server.pm.Installer.InstallerException; import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; @@ -49,16 +51,11 @@ public class AppDataRollbackHelper { } /** - * Creates an app data snapshot for a specified {@code packageName} for {@code installedUsers}, - * a specified set of users for whom the package is installed. - * - * @return a {@link SnapshotAppDataResult}/ - * @see SnapshotAppDataResult + * Creates an app data snapshot for a specified {@code packageRollbackInfo}. Updates said {@code + * packageRollbackInfo} with the inodes of the CE user data snapshot folders. */ - public SnapshotAppDataResult snapshotAppData(String packageName, int[] installedUsers) { - final IntArray pendingBackups = new IntArray(); - final SparseLongArray ceSnapshotInodes = new SparseLongArray(); - + public void snapshotAppData(int snapshotId, PackageRollbackInfo packageRollbackInfo) { + final int[] installedUsers = packageRollbackInfo.getInstalledUsers().toArray(); for (int user : installedUsers) { final int storageFlags; if (isUserCredentialLocked(user)) { @@ -66,51 +63,38 @@ public class AppDataRollbackHelper { // across app user data until the user unlocks their device. Log.v(TAG, "User: " + user + " isn't unlocked, skipping CE userdata backup."); storageFlags = Installer.FLAG_STORAGE_DE; - pendingBackups.add(user); + packageRollbackInfo.addPendingBackup(user); } else { storageFlags = Installer.FLAG_STORAGE_CE | Installer.FLAG_STORAGE_DE; } try { - long ceSnapshotInode = mInstaller.snapshotAppData(packageName, user, storageFlags); + long ceSnapshotInode = mInstaller.snapshotAppData( + packageRollbackInfo.getPackageName(), user, snapshotId, storageFlags); if ((storageFlags & Installer.FLAG_STORAGE_CE) != 0) { - ceSnapshotInodes.put(user, ceSnapshotInode); + packageRollbackInfo.putCeSnapshotInode(user, ceSnapshotInode); } } catch (InstallerException ie) { - Log.e(TAG, "Unable to create app data snapshot for: " + packageName - + ", userId: " + user, ie); + Log.e(TAG, "Unable to create app data snapshot for: " + + packageRollbackInfo.getPackageName() + ", userId: " + user, ie); } } - - return new SnapshotAppDataResult(pendingBackups, ceSnapshotInodes); } /** - * Restores an app data snapshot for a specified package ({@code packageName}, - * {@code rollbackData}) for a specified {@code userId}. + * Restores an app data snapshot for a specified {@code packageRollbackInfo}, for a specified + * {@code userId}. * - * @return {@code true} iff. a change to the {@code rollbackData} has been made. Changes to - * {@code rollbackData} are restricted to the removal or addition of {@code userId} to - * the list of pending backups or restores. + * @return {@code true} iff. a change to the {@code packageRollbackInfo} has been made. Changes + * to {@code packageRollbackInfo} are restricted to the removal or addition of {@code + * userId} to the list of pending backups or restores. */ - public boolean restoreAppData(String packageName, RollbackData rollbackData, - int userId, int appId, long ceDataInode, String seInfo) { - if (rollbackData == null) { - return false; - } - - if (!rollbackData.inProgress) { - Log.e(TAG, "Request to restore userData for: " + packageName - + ", but no rollback in progress."); - return false; - } - - PackageRollbackInfo packageInfo = RollbackManagerServiceImpl.getPackageRollbackInfo( - rollbackData, packageName); + public boolean restoreAppData(int rollbackId, PackageRollbackInfo packageRollbackInfo, + int userId, int appId, String seInfo) { int storageFlags = Installer.FLAG_STORAGE_DE; - final IntArray pendingBackups = packageInfo.getPendingBackups(); - final List<RestoreInfo> pendingRestores = packageInfo.getPendingRestores(); + final IntArray pendingBackups = packageRollbackInfo.getPendingBackups(); + final List<RestoreInfo> pendingRestores = packageRollbackInfo.getPendingRestores(); boolean changedRollbackData = false; // If we still have a userdata backup pending for this user, it implies that the user @@ -134,53 +118,60 @@ public class AppDataRollbackHelper { } try { - mInstaller.restoreAppDataSnapshot(packageName, appId, ceDataInode, - seInfo, userId, storageFlags); + mInstaller.restoreAppDataSnapshot(packageRollbackInfo.getPackageName(), appId, seInfo, + userId, rollbackId, storageFlags); } catch (InstallerException ie) { - Log.e(TAG, "Unable to restore app data snapshot: " + packageName, ie); + Log.e(TAG, "Unable to restore app data snapshot: " + + packageRollbackInfo.getPackageName(), ie); } return changedRollbackData; } /** - * Deletes an app data data snapshot for a specified package {@code packageName} for a - * given {@code user}. + * Deletes an app data snapshot with a given {@code rollbackId} for a specified package + * {@code packageName} for a given {@code user}. */ - public void destroyAppDataSnapshot(String packageName, int user, long ceSnapshotInode) { + public void destroyAppDataSnapshot(int rollbackId, PackageRollbackInfo packageRollbackInfo, + int user) { int storageFlags = Installer.FLAG_STORAGE_DE; + final SparseLongArray ceSnapshotInodes = packageRollbackInfo.getCeSnapshotInodes(); + long ceSnapshotInode = ceSnapshotInodes.get(user); if (ceSnapshotInode > 0) { storageFlags |= Installer.FLAG_STORAGE_CE; } try { - mInstaller.destroyAppDataSnapshot(packageName, user, ceSnapshotInode, storageFlags); + mInstaller.destroyAppDataSnapshot(packageRollbackInfo.getPackageName(), user, + ceSnapshotInode, rollbackId, storageFlags); + if ((storageFlags & Installer.FLAG_STORAGE_CE) != 0) { + ceSnapshotInodes.delete(user); + } } catch (InstallerException ie) { - Log.e(TAG, "Unable to delete app data snapshot for " + packageName, ie); + Log.e(TAG, "Unable to delete app data snapshot for " + + packageRollbackInfo.getPackageName(), ie); } } /** - * Computes the list of pending backups and restores for {@code userId} given lists of - * available and recent rollbacks. Packages pending backup for the given user are added - * to {@code pendingBackups} and packages pending restore are added to {@code pendingRestores} - * along with their corresponding {@code RestoreInfo}. + * Computes the list of pending backups for {@code userId} given lists of available rollbacks. + * Packages pending backup for the given user are added to {@code pendingBackupPackages} along + * with their corresponding {@code PackageRollbackInfo}. * - * @return the list of {@code RollbackData} that have been modified during this computation. + * @return the list of {@code RollbackData} that has pending backups. Note that some of the + * backups won't be performed, because they might be counteracted by pending restores. */ - public List<RollbackData> computePendingBackupsAndRestores(int userId, - ArrayList<String> pendingBackupPackages, Map<String, RestoreInfo> pendingRestores, - List<RollbackData> availableRollbacks, List<RollbackInfo> recentRollbacks) { + private static List<RollbackData> computePendingBackups(int userId, + Map<String, PackageRollbackInfo> pendingBackupPackages, + List<RollbackData> availableRollbacks) { List<RollbackData> rd = new ArrayList<>(); - // First check with the list of available rollbacks to see whether there are any - // pending backup operations that we've not managed to execute. + for (RollbackData data : availableRollbacks) { - for (PackageRollbackInfo info : data.packages) { + for (PackageRollbackInfo info : data.info.getPackages()) { final IntArray pendingBackupUsers = info.getPendingBackups(); if (pendingBackupUsers != null) { final int idx = pendingBackupUsers.indexOf(userId); if (idx != -1) { - pendingBackupPackages.add(info.getPackageName()); - pendingBackupUsers.remove(idx); + pendingBackupPackages.put(info.getPackageName(), info); if (rd.indexOf(data) == -1) { rd.add(data); } @@ -188,23 +179,30 @@ public class AppDataRollbackHelper { } } } + return rd; + } + + /** + * Computes the list of pending restores for {@code userId} given lists of recent rollbacks. + * Packages pending restore are added to {@code pendingRestores} along with their corresponding + * {@code PackageRollbackInfo}. + * + * @return the list of {@code RollbackInfo} that has pending restores. Note that some of the + * restores won't be performed, because they might be counteracted by pending backups. + */ + private static List<RollbackInfo> computePendingRestores(int userId, + Map<String, PackageRollbackInfo> pendingRestorePackages, + List<RollbackInfo> recentRollbacks) { + List<RollbackInfo> rd = new ArrayList<>(); - // Then check with the list of recently executed rollbacks to see whether there are - // any rollback operations for (RollbackInfo data : recentRollbacks) { for (PackageRollbackInfo info : data.getPackages()) { final RestoreInfo ri = info.getRestoreInfo(userId); if (ri != null) { - if (pendingBackupPackages.contains(info.getPackageName())) { - // This implies that the user hasn't unlocked their device between - // the request to backup data for this user and the request to restore - // it, so we do nothing here. - pendingBackupPackages.remove(info.getPackageName()); - } else { - pendingRestores.put(info.getPackageName(), ri); + pendingRestorePackages.put(info.getPackageName(), info); + if (rd.indexOf(data) == -1) { + rd.add(data); } - - info.removeRestoreInfo(ri); } } } @@ -216,47 +214,77 @@ public class AppDataRollbackHelper { * Commits the list of pending backups and restores for a given {@code userId}. For the pending * backups updates corresponding {@code changedRollbackData} with a mapping from {@code userId} * to a inode of theirs CE user data snapshot. + * + * @return a list {@code RollbackData} that have been changed and should be stored on disk. */ - public void commitPendingBackupAndRestoreForUser(int userId, - ArrayList<String> pendingBackups, Map<String, RestoreInfo> pendingRestores, - List<RollbackData> changedRollbackData) { - if (!pendingBackups.isEmpty()) { - for (String packageName : pendingBackups) { - try { - long ceSnapshotInode = mInstaller.snapshotAppData(packageName, userId, - Installer.FLAG_STORAGE_CE); - for (RollbackData data : changedRollbackData) { - for (PackageRollbackInfo info : data.packages) { - if (info.getPackageName().equals(packageName)) { - info.putCeSnapshotInode(userId, ceSnapshotInode); - } + public List<RollbackData> commitPendingBackupAndRestoreForUser(int userId, + List<RollbackData> availableRollbacks, List<RollbackInfo> recentlyExecutedRollbacks) { + + final Map<String, PackageRollbackInfo> pendingBackupPackages = new HashMap<>(); + final List<RollbackData> pendingBackups = computePendingBackups(userId, + pendingBackupPackages, availableRollbacks); + + final Map<String, PackageRollbackInfo> pendingRestorePackages = new HashMap<>(); + final List<RollbackInfo> pendingRestores = computePendingRestores(userId, + pendingRestorePackages, recentlyExecutedRollbacks); + + // First remove unnecessary backups, i.e. when user did not unlock their phone between the + // request to backup data and the request to restore it. + Iterator<Map.Entry<String, PackageRollbackInfo>> iter = + pendingBackupPackages.entrySet().iterator(); + while (iter.hasNext()) { + PackageRollbackInfo backupPackage = iter.next().getValue(); + PackageRollbackInfo restorePackage = + pendingRestorePackages.get(backupPackage.getPackageName()); + if (restorePackage != null) { + backupPackage.removePendingBackup(userId); + backupPackage.removePendingRestoreInfo(userId); + iter.remove(); + pendingRestorePackages.remove(backupPackage.getPackageName()); + } + } + + if (!pendingBackupPackages.isEmpty()) { + for (RollbackData data : pendingBackups) { + for (PackageRollbackInfo info : data.info.getPackages()) { + final IntArray pendingBackupUsers = info.getPendingBackups(); + final int idx = pendingBackupUsers.indexOf(userId); + if (idx != -1) { + try { + long ceSnapshotInode = mInstaller.snapshotAppData(info.getPackageName(), + userId, data.info.getRollbackId(), Installer.FLAG_STORAGE_CE); + info.putCeSnapshotInode(userId, ceSnapshotInode); + pendingBackupUsers.remove(idx); + } catch (InstallerException ie) { + Log.e(TAG, + "Unable to create app data snapshot for: " + + info.getPackageName() + ", userId: " + userId, ie); } } - } catch (InstallerException ie) { - Log.e(TAG, "Unable to create app data snapshot for: " + packageName - + ", userId: " + userId, ie); } } } - // TODO(narayan): Should we perform the restore before the backup for packages that have - // both backups and restores pending ? We could get into this case if we have a pending - // restore from a rollback + a snapshot request from a new restore. - if (!pendingRestores.isEmpty()) { - for (String packageName : pendingRestores.keySet()) { - try { - final RestoreInfo ri = pendingRestores.get(packageName); - - // TODO(narayan): Verify that the user of "0" for ceDataInode is accurate - // here. We know that the user has unlocked (and that their CE data is - // available) so we shouldn't need to resort to the fallback path. - mInstaller.restoreAppDataSnapshot(packageName, ri.appId, - 0 /* ceDataInode */, ri.seInfo, userId, Installer.FLAG_STORAGE_CE); - } catch (InstallerException ie) { - Log.e(TAG, "Unable to restore app data snapshot for: " + packageName, ie); + if (!pendingRestorePackages.isEmpty()) { + for (RollbackInfo data : pendingRestores) { + for (PackageRollbackInfo info : data.getPackages()) { + final RestoreInfo ri = info.getRestoreInfo(userId); + if (ri != null) { + try { + mInstaller.restoreAppDataSnapshot(info.getPackageName(), ri.appId, + ri.seInfo, userId, data.getRollbackId(), + Installer.FLAG_STORAGE_CE); + info.removeRestoreInfo(ri); + } catch (InstallerException ie) { + Log.e(TAG, "Unable to restore app data snapshot for: " + + info.getPackageName(), ie); + } + } } } } + + return pendingBackups; } /** @@ -267,26 +295,4 @@ public class AppDataRollbackHelper { return StorageManager.isFileEncryptedNativeOrEmulated() && !StorageManager.isUserKeyUnlocked(userId); } - - /** - * Encapsulates a result of {@link #snapshotAppData} method. - */ - public static final class SnapshotAppDataResult { - - /** - * A list of users for which the snapshot is pending, usually because data for one or more - * users is still credential locked. - */ - public final IntArray pendingBackups; - - /** - * A mapping between user and an inode of theirs CE data snapshot. - */ - public final SparseLongArray ceSnapshotInodes; - - public SnapshotAppDataResult(IntArray pendingBackups, SparseLongArray ceSnapshotInodes) { - this.pendingBackups = pendingBackups; - this.ceSnapshotInodes = ceSnapshotInodes; - } - } } diff --git a/services/core/java/com/android/server/rollback/RollbackData.java b/services/core/java/com/android/server/rollback/RollbackData.java index fcd5297f1363..655bf4ab57a7 100644 --- a/services/core/java/com/android/server/rollback/RollbackData.java +++ b/services/core/java/com/android/server/rollback/RollbackData.java @@ -16,12 +16,11 @@ package com.android.server.rollback; -import android.content.rollback.PackageRollbackInfo; +import android.content.rollback.RollbackInfo; import java.io.File; import java.time.Instant; import java.util.ArrayList; -import java.util.List; /** * Information about a rollback available for a set of atomically installed @@ -29,14 +28,9 @@ import java.util.List; */ class RollbackData { /** - * A unique identifier for this rollback. + * The rollback info for this rollback. */ - public final int rollbackId; - - /** - * The per-package rollback information. - */ - public final List<PackageRollbackInfo> packages = new ArrayList<>(); + public final RollbackInfo info; /** * The directory where the rollback data is stored. @@ -69,24 +63,49 @@ class RollbackData { public int apkSessionId = -1; /** - * Whether this Rollback is currently in progress. This field is true from the point - * we commit a {@code PackageInstaller} session containing these packages to the point the - * {@code PackageInstaller} calls into the {@code onFinished} callback. + * True if we are expecting the package manager to call restoreUserData + * for this rollback because it has just been committed but the rollback + * has not yet been fully applied. */ // NOTE: All accesses to this field are from the RollbackManager handler thread. - public boolean inProgress = false; + public boolean restoreUserDataInProgress = false; - RollbackData(int rollbackId, File backupDir, int stagedSessionId, boolean isAvailable) { - this.rollbackId = rollbackId; + /** + * Constructs a new, empty RollbackData instance. + * + * @param rollbackId the id of the rollback. + * @param backupDir the directory where the rollback data is stored. + * @param stagedSessionId the session id if this is a staged rollback, -1 otherwise. + */ + RollbackData(int rollbackId, File backupDir, int stagedSessionId) { + this.info = new RollbackInfo(rollbackId, + /* packages */ new ArrayList<>(), + /* isStaged */ stagedSessionId != -1, + /* causePackages */ new ArrayList<>(), + /* committedSessionId */ -1); + this.backupDir = backupDir; + this.stagedSessionId = stagedSessionId; + this.isAvailable = (stagedSessionId == -1); + } + + /** + * Constructs a RollbackData instance with full rollback data information. + */ + RollbackData(RollbackInfo info, File backupDir, Instant timestamp, int stagedSessionId, + boolean isAvailable, int apkSessionId, boolean restoreUserDataInProgress) { + this.info = info; this.backupDir = backupDir; + this.timestamp = timestamp; this.stagedSessionId = stagedSessionId; this.isAvailable = isAvailable; + this.apkSessionId = apkSessionId; + this.restoreUserDataInProgress = restoreUserDataInProgress; } /** * Whether the rollback is for rollback of a staged install. */ public boolean isStaged() { - return stagedSessionId != -1; + return info.isStaged(); } } diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java index 05d3c17e5c25..52d441255da6 100644 --- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java +++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java @@ -23,6 +23,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; @@ -32,7 +33,6 @@ import android.content.pm.ParceledListSlice; import android.content.pm.VersionedPackage; import android.content.rollback.IRollbackManager; import android.content.rollback.PackageRollbackInfo; -import android.content.rollback.PackageRollbackInfo.RestoreInfo; import android.content.rollback.RollbackInfo; import android.content.rollback.RollbackManager; import android.os.Binder; @@ -41,6 +41,7 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.ParcelFileDescriptor; import android.os.Process; +import android.os.SystemClock; import android.provider.DeviceConfig; import android.util.IntArray; import android.util.Log; @@ -48,6 +49,7 @@ import android.util.SparseBooleanArray; import android.util.SparseLongArray; import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.ArrayUtils; import com.android.server.LocalServices; import com.android.server.pm.Installer; @@ -122,6 +124,13 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { private final RollbackPackageHealthObserver mPackageHealthObserver; private final AppDataRollbackHelper mAppDataRollbackHelper; + // This field stores the difference in Millis between the uptime (millis since device + // has booted) and current time (device wall clock) - it's used to update rollback data + // timestamps when the time is changed, by the user or by change of timezone. + // No need for guarding with lock because value is only accessed in handler thread. + private long mRelativeBootTime = calculateRelativeBootTime(); + + RollbackManagerServiceImpl(Context context) { mContext = context; // Note that we're calling onStart here because this object is only constructed on @@ -218,6 +227,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } } }, enableRollbackFilter, null, getHandler()); + + registerTimeChangeReceiver(); } @Override @@ -232,8 +243,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { for (int i = 0; i < mAvailableRollbacks.size(); ++i) { RollbackData data = mAvailableRollbacks.get(i); if (data.isAvailable) { - rollbacks.add(new RollbackInfo(data.rollbackId, - data.packages, data.isStaged())); + rollbacks.add(data.info); } } return new ParceledListSlice<>(rollbacks); @@ -269,6 +279,45 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { callerPackageName, statusReceiver)); } + private void registerTimeChangeReceiver() { + final BroadcastReceiver timeChangeIntentReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final long oldRelativeBootTime = mRelativeBootTime; + mRelativeBootTime = calculateRelativeBootTime(); + final long timeDifference = mRelativeBootTime - oldRelativeBootTime; + + synchronized (mLock) { + ensureRollbackDataLoadedLocked(); + + Iterator<RollbackData> iter = mAvailableRollbacks.iterator(); + while (iter.hasNext()) { + RollbackData data = iter.next(); + + data.timestamp = data.timestamp.plusMillis(timeDifference); + try { + mRollbackStore.saveRollbackData(data); + } catch (IOException ioe) { + // TODO: figure out the right way to deal with this, especially if + // it fails for some data and succeeds for others. + Log.e(TAG, "Unable to save rollback info for : " + + data.info.getRollbackId(), ioe); + } + } + + } + } + }; + final IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_TIME_CHANGED); + mContext.registerReceiver(timeChangeIntentReceiver, filter, + null /* broadcastPermission */, getHandler()); + } + + private static long calculateRelativeBootTime() { + return System.currentTimeMillis() - SystemClock.elapsedRealtime(); + } + /** * Performs the actual work to commit a rollback. * The work is done on the current thread. This may be a long running @@ -285,7 +334,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { return; } - if (data.inProgress) { + if (data.restoreUserDataInProgress) { sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE, "Rollback for package is already in progress."); return; @@ -299,7 +348,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { // rollback racing with a roll-forward fix of a buggy package. // Figure out how to ensure we don't commit the rollback if // roll forward happens at the same time. - for (PackageRollbackInfo info : data.packages) { + for (PackageRollbackInfo info : data.info.getPackages()) { VersionedPackage installedVersion = getInstalledPackageVersion(info.getPackageName()); if (installedVersion == null) { // TODO: Test this case @@ -341,7 +390,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { int parentSessionId = packageInstaller.createSession(parentParams); PackageInstaller.Session parentSession = packageInstaller.openSession(parentSessionId); - for (PackageRollbackInfo info : data.packages) { + for (PackageRollbackInfo info : data.info.getPackages()) { PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( PackageInstaller.SessionParams.MODE_FULL_INSTALL); // TODO: We can't get the installerPackageName for apex @@ -363,20 +412,24 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { int sessionId = packageInstaller.createSession(params); PackageInstaller.Session session = packageInstaller.openSession(sessionId); - File packageCode = RollbackStore.getPackageCode(data, info.getPackageName()); - if (packageCode == null) { + File[] packageCodePaths = RollbackStore.getPackageCodePaths( + data, info.getPackageName()); + if (packageCodePaths == null) { sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE, - "Backup copy of package code inaccessible"); + "Backup copy of package inaccessible"); return; } - try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(packageCode, - ParcelFileDescriptor.MODE_READ_ONLY)) { - final long token = Binder.clearCallingIdentity(); - try { - session.write(packageCode.getName(), 0, packageCode.length(), fd); - } finally { - Binder.restoreCallingIdentity(token); + for (File packageCodePath : packageCodePaths) { + try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(packageCodePath, + ParcelFileDescriptor.MODE_READ_ONLY)) { + final long token = Binder.clearCallingIdentity(); + try { + session.write(packageCodePath.getName(), 0, packageCodePath.length(), + fd); + } finally { + Binder.restoreCallingIdentity(token); + } } } parentSession.addChildSessionId(sessionId); @@ -387,7 +440,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { getHandler().post(() -> { // We've now completed the rollback, so we mark it as no longer in // progress. - data.inProgress = false; + data.restoreUserDataInProgress = false; int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE); @@ -399,9 +452,9 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { return; } - addRecentlyExecutedRollback(new RollbackInfo( - data.rollbackId, data.packages, data.isStaged(), - causePackages, parentSessionId)); + data.info.setCommittedSessionId(parentSessionId); + data.info.getCausePackages().addAll(causePackages); + addRecentlyExecutedRollback(data.info); sendSuccess(statusReceiver); Intent broadcast = new Intent(Intent.ACTION_ROLLBACK_COMMITTED); @@ -415,7 +468,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } ); - data.inProgress = true; + data.restoreUserDataInProgress = true; parentSession.commit(receiver.getIntentSender()); } catch (IOException e) { Log.e(TAG, "Rollback failed", e); @@ -435,7 +488,10 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { mAvailableRollbacks = null; mRecentlyExecutedRollbacks = null; } - getHandler().post(() -> ensureRollbackDataLoaded()); + getHandler().post(() -> { + updateRollbackLifetimeDurationInMillis(); + ensureRollbackDataLoaded(); + }); } @Override @@ -453,7 +509,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { Iterator<RollbackData> iter = mAvailableRollbacks.iterator(); while (iter.hasNext()) { RollbackData data = iter.next(); - for (PackageRollbackInfo info : data.packages) { + for (PackageRollbackInfo info : data.info.getPackages()) { if (info.getPackageName().equals(packageName)) { iter.remove(); deleteRollback(data); @@ -466,24 +522,24 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { void onUnlockUser(int userId) { getHandler().post(() -> { - final ArrayList<String> pendingBackupPackages = new ArrayList<>(); - final Map<String, RestoreInfo> pendingRestorePackages = new HashMap<>(); - final List<RollbackData> changed; + final List<RollbackData> availableRollbacks; + final List<RollbackInfo> recentlyExecutedRollbacks; synchronized (mLock) { ensureRollbackDataLoadedLocked(); - changed = mAppDataRollbackHelper.computePendingBackupsAndRestores(userId, - pendingBackupPackages, pendingRestorePackages, mAvailableRollbacks, - mRecentlyExecutedRollbacks); + availableRollbacks = new ArrayList<>(mAvailableRollbacks); + recentlyExecutedRollbacks = new ArrayList<>(mRecentlyExecutedRollbacks); } - mAppDataRollbackHelper.commitPendingBackupAndRestoreForUser(userId, - pendingBackupPackages, pendingRestorePackages, changed); + final List<RollbackData> changed = + mAppDataRollbackHelper.commitPendingBackupAndRestoreForUser(userId, + availableRollbacks, recentlyExecutedRollbacks); for (RollbackData rd : changed) { try { - mRollbackStore.saveAvailableRollback(rd); + mRollbackStore.saveRollbackData(rd); } catch (IOException ioe) { - Log.e(TAG, "Unable to save rollback info for : " + rd.rollbackId, ioe); + Log.e(TAG, "Unable to save rollback info for : " + + rd.info.getRollbackId(), ioe); } } @@ -519,7 +575,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { synchronized (mLock) { ensureRollbackDataLoadedLocked(); for (RollbackData data : mAvailableRollbacks) { - if (data.stagedSessionId != -1) { + if (!data.isAvailable && data.isStaged()) { staged.add(data); } } @@ -530,17 +586,17 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { PackageInstaller.SessionInfo session = installer.getSessionInfo( data.stagedSessionId); if (session != null) { - if (session.isSessionApplied()) { + if (session.isStagedSessionApplied()) { synchronized (mLock) { data.isAvailable = true; } try { - mRollbackStore.saveAvailableRollback(data); + mRollbackStore.saveRollbackData(data); } catch (IOException ioe) { Log.e(TAG, "Unable to save rollback info for : " - + data.rollbackId, ioe); + + data.info.getRollbackId(), ioe); } - } else if (session.isSessionFailed()) { + } else if (session.isStagedSessionFailed()) { // TODO: Do we need to remove this from // mAvailableRollbacks, or is it okay to leave as // unavailable until the next reboot when it will go @@ -585,7 +641,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { private void loadAllRollbackDataLocked() { mAvailableRollbacks = mRollbackStore.loadAvailableRollbacks(); for (RollbackData data : mAvailableRollbacks) { - mAllocatedRollbackIds.put(data.rollbackId, true); + mAllocatedRollbackIds.put(data.info.getRollbackId(), true); } mRecentlyExecutedRollbacks = mRollbackStore.loadRecentlyExecutedRollbacks(); @@ -609,7 +665,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { Iterator<RollbackData> iter = mAvailableRollbacks.iterator(); while (iter.hasNext()) { RollbackData data = iter.next(); - for (PackageRollbackInfo info : data.packages) { + for (PackageRollbackInfo info : data.info.getPackages()) { if (info.getPackageName().equals(packageName) && !packageVersionsEqual( info.getVersionRolledBackFrom(), @@ -841,24 +897,19 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { return false; } String packageName = newPackage.packageName; - for (PackageRollbackInfo info : rd.packages) { + for (PackageRollbackInfo info : rd.info.getPackages()) { if (info.getPackageName().equals(packageName)) { info.getInstalledUsers().addAll(IntArray.wrap(installedUsers)); - AppDataRollbackHelper.SnapshotAppDataResult rs = - mAppDataRollbackHelper.snapshotAppData(packageName, installedUsers); - info.getPendingBackups().addAll(rs.pendingBackups); - for (int i = 0; i < rs.ceSnapshotInodes.size(); i++) { - info.putCeSnapshotInode(rs.ceSnapshotInodes.keyAt(i), - rs.ceSnapshotInodes.valueAt(i)); - } + mAppDataRollbackHelper.snapshotAppData(rd.info.getRollbackId(), info); try { - mRollbackStore.saveAvailableRollback(rd); + mRollbackStore.saveRollbackData(rd); } catch (IOException ioe) { // TODO: Hopefully this is okay because we will try // again to save the rollback when the staged session // is applied. Just so long as the device doesn't // reboot before then. - Log.e(TAG, "Unable to save rollback info for : " + rd.rollbackId, ioe); + Log.e(TAG, "Unable to save rollback info for : " + + rd.info.getRollbackId(), ioe); } return true; } @@ -919,18 +970,10 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { VersionedPackage installedVersion = new VersionedPackage(packageName, pkgInfo.getLongVersionCode()); - final AppDataRollbackHelper.SnapshotAppDataResult result; - if (snapshotUserData && !isApex) { - result = mAppDataRollbackHelper.snapshotAppData(packageName, installedUsers); - } else { - result = new AppDataRollbackHelper.SnapshotAppDataResult(IntArray.wrap(new int[0]), - new SparseLongArray()); - } - PackageRollbackInfo info = new PackageRollbackInfo(newVersion, installedVersion, - result.pendingBackups, new ArrayList<>(), isApex, IntArray.wrap(installedUsers), - result.ceSnapshotInodes); - + new IntArray() /* pendingBackups */, new ArrayList<>() /* pendingRestores */, + isApex, IntArray.wrap(installedUsers), + new SparseLongArray() /* ceSnapshotInodes */); RollbackData data; try { int childSessionId = session.getSessionId(); @@ -947,22 +990,31 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { if (data == null) { int rollbackId = allocateRollbackIdLocked(); if (session.isStaged()) { - data = mRollbackStore.createPendingStagedRollback(rollbackId, - parentSessionId); + data = mRollbackStore.createStagedRollback(rollbackId, parentSessionId); } else { - data = mRollbackStore.createAvailableRollback(rollbackId); + data = mRollbackStore.createNonStagedRollback(rollbackId); } mPendingRollbacks.put(parentSessionId, data); } - data.packages.add(info); + data.info.getPackages().add(info); } } catch (IOException e) { Log.e(TAG, "Unable to create rollback for " + packageName, e); return false; } + if (snapshotUserData && !isApex) { + mAppDataRollbackHelper.snapshotAppData(data.info.getRollbackId(), info); + } + try { - RollbackStore.backupPackageCode(data, packageName, pkgInfo.applicationInfo.sourceDir); + ApplicationInfo appInfo = pkgInfo.applicationInfo; + RollbackStore.backupPackageCodePath(data, packageName, appInfo.sourceDir); + if (!ArrayUtils.isEmpty(appInfo.splitSourceDirs)) { + for (String sourceDir : appInfo.splitSourceDirs) { + RollbackStore.backupPackageCodePath(data, packageName, sourceDir); + } + } } catch (IOException e) { Log.e(TAG, "Unable to copy package for rollback for " + packageName, e); return false; @@ -978,29 +1030,45 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } getHandler().post(() -> { - final RollbackData rollbackData = getRollbackForPackage(packageName); - for (int userId : userIds) { - final boolean changedRollbackData = mAppDataRollbackHelper.restoreAppData( - packageName, rollbackData, userId, appId, ceDataInode, seInfo); - // We've updated metadata about this rollback, so save it to flash. - if (changedRollbackData) { - try { - mRollbackStore.saveAvailableRollback(rollbackData); - } catch (IOException ioe) { - // TODO(narayan): What is the right thing to do here ? This isn't a fatal - // error, since it will only result in us trying to restore data again, - // which will be a no-op if there's no data available. - Log.e(TAG, "Unable to save available rollback: " + packageName, ioe); - } - } - } - + restoreUserDataInternal(packageName, userIds, appId, ceDataInode, seInfo, token); final PackageManagerInternal pmi = LocalServices.getService( PackageManagerInternal.class); pmi.finishPackageInstall(token, false); }); } + private void restoreUserDataInternal(String packageName, int[] userIds, int appId, + long ceDataInode, String seInfo, int token) { + final RollbackData rollbackData = getRollbackForPackage(packageName); + if (rollbackData == null) { + return; + } + + if (!rollbackData.restoreUserDataInProgress) { + Log.e(TAG, "Request to restore userData for: " + packageName + + ", but no rollback in progress."); + return; + } + + for (int userId : userIds) { + final PackageRollbackInfo info = getPackageRollbackInfo(rollbackData, packageName); + final boolean changedRollbackData = mAppDataRollbackHelper.restoreAppData( + rollbackData.info.getRollbackId(), info, userId, appId, seInfo); + + // We've updated metadata about this rollback, so save it to flash. + if (changedRollbackData) { + try { + mRollbackStore.saveRollbackData(rollbackData); + } catch (IOException ioe) { + // TODO(narayan): What is the right thing to do here ? This isn't a fatal + // error, since it will only result in us trying to restore data again, + // which will be a no-op if there's no data available. + Log.e(TAG, "Unable to save available rollback: " + packageName, ioe); + } + } + } + } + @Override public boolean notifyStagedSession(int sessionId) { final LinkedBlockingQueue<Boolean> result = new LinkedBlockingQueue<>(); @@ -1069,9 +1137,10 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { if (rd != null) { try { - mRollbackStore.saveAvailableRollback(rd); + mRollbackStore.saveRollbackData(rd); } catch (IOException ioe) { - Log.e(TAG, "Unable to save rollback info for : " + rd.rollbackId, ioe); + Log.e(TAG, "Unable to save rollback info for : " + + rd.info.getRollbackId(), ioe); } } }); @@ -1144,7 +1213,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { try { data.timestamp = Instant.now(); - mRollbackStore.saveAvailableRollback(data); + mRollbackStore.saveRollbackData(data); synchronized (mLock) { // Note: There is a small window of time between when // the session has been committed by the package @@ -1165,8 +1234,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { // After enabling and commiting any rollback, observe packages and // prepare to rollback if packages crashes too frequently. List<String> packages = new ArrayList<>(); - for (int i = 0; i < data.packages.size(); i++) { - packages.add(data.packages.get(i).getPackageName()); + for (int i = 0; i < data.info.getPackages().size(); i++) { + packages.add(data.info.getPackages().get(i).getPackageName()); } mPackageHealthObserver.startObservingHealth(packages, mRollbackLifetimeDurationInMillis); @@ -1191,13 +1260,13 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } if (pi.isStaged()) { - if (!pi.isSessionFailed()) { + if (!pi.isStagedSessionFailed()) { // TODO: The session really isn't "enabled" at this point, since more work might // be required post reboot. // TODO: We need to make this case consistent with the call from onFinished. // Ideally, we'd call completeEnableRollback excatly once per multi-package session // with the parentSessionId only. - completeEnableRollback(pi.sessionId, pi.isSessionReady()); + completeEnableRollback(pi.sessionId, pi.isStagedSessionReady()); } else { // TODO: Clean up the saved rollback when the session fails. This may need to be // unified with the case where things fail post reboot. @@ -1239,7 +1308,7 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { ensureRollbackDataLoadedLocked(); for (int i = 0; i < mAvailableRollbacks.size(); ++i) { RollbackData data = mAvailableRollbacks.get(i); - if (data.isAvailable && data.rollbackId == rollbackId) { + if (data.isAvailable && data.info.getRollbackId() == rollbackId) { return data; } } @@ -1252,9 +1321,9 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { * Returns the {@code PackageRollbackInfo} associated with {@code packageName} from * a specified {@code RollbackData}. */ - static PackageRollbackInfo getPackageRollbackInfo(RollbackData data, + private static PackageRollbackInfo getPackageRollbackInfo(RollbackData data, String packageName) { - for (PackageRollbackInfo info : data.packages) { + for (PackageRollbackInfo info : data.info.getPackages()) { if (info.getPackageName().equals(packageName)) { return info; } @@ -1279,15 +1348,14 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub { } private void deleteRollback(RollbackData rollbackData) { - for (PackageRollbackInfo info : rollbackData.packages) { + for (PackageRollbackInfo info : rollbackData.info.getPackages()) { IntArray installedUsers = info.getInstalledUsers(); - SparseLongArray ceSnapshotInodes = info.getCeSnapshotInodes(); for (int i = 0; i < installedUsers.size(); i++) { int userId = installedUsers.get(i); - mAppDataRollbackHelper.destroyAppDataSnapshot(info.getPackageName(), userId, - ceSnapshotInodes.get(userId, 0)); + mAppDataRollbackHelper.destroyAppDataSnapshot(rollbackData.info.getRollbackId(), + info, userId); } } - mRollbackStore.deleteAvailableRollback(rollbackData); + mRollbackStore.deleteRollbackData(rollbackData); } } diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java index 3a2b69f8a6e1..d24f21781b39 100644 --- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java +++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java @@ -218,10 +218,10 @@ public final class RollbackPackageHealthObserver implements PackageHealthObserve && (sessionId != PackageInstaller.SessionInfo.INVALID_ID)) { PackageInstaller.SessionInfo sessionInfo = packageInstaller.getSessionInfo(sessionId); - if (sessionInfo.isSessionReady()) { + if (sessionInfo.isStagedSessionReady()) { mContext.unregisterReceiver(listener); mContext.getSystemService(PowerManager.class).reboot("Rollback staged install"); - } else if (sessionInfo.isSessionFailed()) { + } else if (sessionInfo.isStagedSessionFailed()) { mContext.unregisterReceiver(listener); } } diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java index be904eacebff..ecdb2ccd872b 100644 --- a/services/core/java/com/android/server/rollback/RollbackStore.java +++ b/services/core/java/com/android/server/rollback/RollbackStore.java @@ -183,6 +183,25 @@ class RollbackStore { return ceSnapshotInodes; } + private static JSONObject rollbackInfoToJson(RollbackInfo rollback) throws JSONException { + JSONObject json = new JSONObject(); + json.put("rollbackId", rollback.getRollbackId()); + json.put("packages", toJson(rollback.getPackages())); + json.put("isStaged", rollback.isStaged()); + json.put("causePackages", versionedPackagesToJson(rollback.getCausePackages())); + json.put("committedSessionId", rollback.getCommittedSessionId()); + return json; + } + + private static RollbackInfo rollbackInfoFromJson(JSONObject json) throws JSONException { + return new RollbackInfo( + json.getInt("rollbackId"), + packageRollbackInfosFromJson(json.getJSONArray("packages")), + json.getBoolean("isStaged"), + versionedPackagesFromJson(json.getJSONArray("causePackages")), + json.getInt("committedSessionId")); + } + /** * Reads the list of recently executed rollbacks from persistent storage. */ @@ -197,17 +216,7 @@ class RollbackStore { JSONObject object = new JSONObject(jsonString); JSONArray array = object.getJSONArray("recentlyExecuted"); for (int i = 0; i < array.length(); ++i) { - JSONObject element = array.getJSONObject(i); - int rollbackId = element.getInt("rollbackId"); - List<PackageRollbackInfo> packages = packageRollbackInfosFromJson( - element.getJSONArray("packages")); - boolean isStaged = element.getBoolean("isStaged"); - List<VersionedPackage> causePackages = versionedPackagesFromJson( - element.getJSONArray("causePackages")); - int committedSessionId = element.getInt("committedSessionId"); - RollbackInfo rollback = new RollbackInfo(rollbackId, packages, isStaged, - causePackages, committedSessionId); - recentlyExecutedRollbacks.add(rollback); + recentlyExecutedRollbacks.add(rollbackInfoFromJson(array.getJSONObject(i))); } } catch (IOException | JSONException e) { // TODO: What to do here? Surely we shouldn't just forget about @@ -220,23 +229,30 @@ class RollbackStore { } /** - * Creates a new RollbackData instance with backupDir assigned. + * Creates a new RollbackData instance for a non-staged rollback with + * backupDir assigned. */ - RollbackData createAvailableRollback(int rollbackId) throws IOException { + RollbackData createNonStagedRollback(int rollbackId) throws IOException { File backupDir = new File(mAvailableRollbacksDir, Integer.toString(rollbackId)); - return new RollbackData(rollbackId, backupDir, -1, true); + return new RollbackData(rollbackId, backupDir, -1); } - RollbackData createPendingStagedRollback(int rollbackId, int stagedSessionId) + /** + * Creates a new RollbackData instance for a staged rollback with + * backupDir assigned. + */ + RollbackData createStagedRollback(int rollbackId, int stagedSessionId) throws IOException { File backupDir = new File(mAvailableRollbacksDir, Integer.toString(rollbackId)); - return new RollbackData(rollbackId, backupDir, stagedSessionId, false); + return new RollbackData(rollbackId, backupDir, stagedSessionId); } /** - * Creates a backup copy of the apk or apex for a package. + * Creates a backup copy of an apk or apex for a package. + * For packages containing splits, this method should be called for each + * of the package's split apks in addition to the base apk. */ - static void backupPackageCode(RollbackData data, String packageName, String codePath) + static void backupPackageCodePath(RollbackData data, String packageName, String codePath) throws IOException { File sourceFile = new File(codePath); File targetDir = new File(data.backupDir, packageName); @@ -248,30 +264,30 @@ class RollbackStore { } /** - * Returns the apk or apex file backed up for the given package. - * Returns null if none found. + * Returns the apk or apex files backed up for the given package. + * Includes the base apk and any splits. Returns null if none found. */ - static File getPackageCode(RollbackData data, String packageName) { + static File[] getPackageCodePaths(RollbackData data, String packageName) { File targetDir = new File(data.backupDir, packageName); File[] files = targetDir.listFiles(); - if (files == null || files.length != 1) { + if (files == null || files.length == 0) { return null; } - return files[0]; + return files; } /** - * Writes the metadata for an available rollback to persistent storage. + * Saves the rollback data to persistent storage. */ - void saveAvailableRollback(RollbackData data) throws IOException { + void saveRollbackData(RollbackData data) throws IOException { try { JSONObject dataJson = new JSONObject(); - dataJson.put("rollbackId", data.rollbackId); - dataJson.put("packages", toJson(data.packages)); + dataJson.put("info", rollbackInfoToJson(data.info)); dataJson.put("timestamp", data.timestamp.toString()); dataJson.put("stagedSessionId", data.stagedSessionId); dataJson.put("isAvailable", data.isAvailable); dataJson.put("apkSessionId", data.apkSessionId); + dataJson.put("restoreUserDataInProgress", data.restoreUserDataInProgress); PrintWriter pw = new PrintWriter(new File(data.backupDir, "rollback.json")); pw.println(dataJson.toString()); @@ -282,10 +298,9 @@ class RollbackStore { } /** - * Removes all persistant storage associated with the given available - * rollback. + * Removes all persistant storage associated with the given rollback data. */ - void deleteAvailableRollback(RollbackData data) { + void deleteRollbackData(RollbackData data) { removeFile(data.backupDir); } @@ -300,13 +315,7 @@ class RollbackStore { for (int i = 0; i < recentlyExecutedRollbacks.size(); ++i) { RollbackInfo rollback = recentlyExecutedRollbacks.get(i); - JSONObject element = new JSONObject(); - element.put("rollbackId", rollback.getRollbackId()); - element.put("packages", toJson(rollback.getPackages())); - element.put("isStaged", rollback.isStaged()); - element.put("causePackages", versionedPackagesToJson(rollback.getCausePackages())); - element.put("committedSessionId", rollback.getCommittedSessionId()); - array.put(element); + array.put(rollbackInfoToJson(rollback)); } PrintWriter pw = new PrintWriter(mRecentlyExecutedRollbacksFile); @@ -322,40 +331,39 @@ class RollbackStore { * Reads the metadata for a rollback from the given directory. * @throws IOException in case of error reading the data. */ - private RollbackData loadRollbackData(File backupDir) throws IOException { + private static RollbackData loadRollbackData(File backupDir) throws IOException { try { File rollbackJsonFile = new File(backupDir, "rollback.json"); JSONObject dataJson = new JSONObject( IoUtils.readFileAsString(rollbackJsonFile.getAbsolutePath())); - int rollbackId = dataJson.getInt("rollbackId"); - int stagedSessionId = dataJson.getInt("stagedSessionId"); - boolean isAvailable = dataJson.getBoolean("isAvailable"); - RollbackData data = new RollbackData(rollbackId, backupDir, - stagedSessionId, isAvailable); - data.packages.addAll(packageRollbackInfosFromJson(dataJson.getJSONArray("packages"))); - data.timestamp = Instant.parse(dataJson.getString("timestamp")); - data.apkSessionId = dataJson.getInt("apkSessionId"); - return data; + return new RollbackData( + rollbackInfoFromJson(dataJson.getJSONObject("info")), + backupDir, + Instant.parse(dataJson.getString("timestamp")), + dataJson.getInt("stagedSessionId"), + dataJson.getBoolean("isAvailable"), + dataJson.getInt("apkSessionId"), + dataJson.getBoolean("restoreUserDataInProgress")); } catch (JSONException | DateTimeParseException e) { throw new IOException(e); } } - private JSONObject toJson(VersionedPackage pkg) throws JSONException { + private static JSONObject toJson(VersionedPackage pkg) throws JSONException { JSONObject json = new JSONObject(); json.put("packageName", pkg.getPackageName()); json.put("longVersionCode", pkg.getLongVersionCode()); return json; } - private VersionedPackage versionedPackageFromJson(JSONObject json) throws JSONException { + private static VersionedPackage versionedPackageFromJson(JSONObject json) throws JSONException { String packageName = json.getString("packageName"); long longVersionCode = json.getLong("longVersionCode"); return new VersionedPackage(packageName, longVersionCode); } - private JSONObject toJson(PackageRollbackInfo info) throws JSONException { + private static JSONObject toJson(PackageRollbackInfo info) throws JSONException { JSONObject json = new JSONObject(); json.put("versionRolledBackFrom", toJson(info.getVersionRolledBackFrom())); json.put("versionRolledBackTo", toJson(info.getVersionRolledBackTo())); @@ -374,7 +382,8 @@ class RollbackStore { return json; } - private PackageRollbackInfo packageRollbackInfoFromJson(JSONObject json) throws JSONException { + private static PackageRollbackInfo packageRollbackInfoFromJson(JSONObject json) + throws JSONException { VersionedPackage versionRolledBackFrom = versionedPackageFromJson( json.getJSONObject("versionRolledBackFrom")); VersionedPackage versionRolledBackTo = versionedPackageFromJson( @@ -395,7 +404,7 @@ class RollbackStore { pendingBackups, pendingRestores, isApex, installedUsers, ceSnapshotInodes); } - private JSONArray versionedPackagesToJson(List<VersionedPackage> packages) + private static JSONArray versionedPackagesToJson(List<VersionedPackage> packages) throws JSONException { JSONArray json = new JSONArray(); for (VersionedPackage pkg : packages) { @@ -404,7 +413,8 @@ class RollbackStore { return json; } - private List<VersionedPackage> versionedPackagesFromJson(JSONArray json) throws JSONException { + private static List<VersionedPackage> versionedPackagesFromJson(JSONArray json) + throws JSONException { List<VersionedPackage> packages = new ArrayList<>(); for (int i = 0; i < json.length(); ++i) { packages.add(versionedPackageFromJson(json.getJSONObject(i))); @@ -412,7 +422,7 @@ class RollbackStore { return packages; } - private JSONArray toJson(List<PackageRollbackInfo> infos) throws JSONException { + private static JSONArray toJson(List<PackageRollbackInfo> infos) throws JSONException { JSONArray json = new JSONArray(); for (PackageRollbackInfo info : infos) { json.put(toJson(info)); @@ -420,7 +430,7 @@ class RollbackStore { return json; } - private List<PackageRollbackInfo> packageRollbackInfosFromJson(JSONArray json) + private static List<PackageRollbackInfo> packageRollbackInfosFromJson(JSONArray json) throws JSONException { List<PackageRollbackInfo> infos = new ArrayList<>(); for (int i = 0; i < json.length(); ++i) { diff --git a/services/core/java/com/android/server/timezone/RulesManagerService.java b/services/core/java/com/android/server/timezone/RulesManagerService.java index 4b413e5f76bb..296a6526f5a6 100644 --- a/services/core/java/com/android/server/timezone/RulesManagerService.java +++ b/services/core/java/com/android/server/timezone/RulesManagerService.java @@ -16,14 +16,13 @@ package com.android.server.timezone; -import com.android.internal.annotations.VisibleForTesting; -import com.android.server.EventLogTags; -import com.android.server.SystemService; -import com.android.timezone.distro.DistroException; -import com.android.timezone.distro.DistroVersion; -import com.android.timezone.distro.StagedDistroOperation; -import com.android.timezone.distro.TimeZoneDistro; -import com.android.timezone.distro.installer.TimeZoneDistroInstaller; +import static android.app.timezone.RulesState.DISTRO_STATUS_INSTALLED; +import static android.app.timezone.RulesState.DISTRO_STATUS_NONE; +import static android.app.timezone.RulesState.DISTRO_STATUS_UNKNOWN; +import static android.app.timezone.RulesState.STAGED_OPERATION_INSTALL; +import static android.app.timezone.RulesState.STAGED_OPERATION_NONE; +import static android.app.timezone.RulesState.STAGED_OPERATION_UNINSTALL; +import static android.app.timezone.RulesState.STAGED_OPERATION_UNKNOWN; import android.app.timezone.Callback; import android.app.timezone.DistroFormatVersion; @@ -37,6 +36,21 @@ import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.util.Slog; +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.EventLogTags; +import com.android.server.SystemService; +import com.android.timezone.distro.DistroException; +import com.android.timezone.distro.DistroVersion; +import com.android.timezone.distro.StagedDistroOperation; +import com.android.timezone.distro.TimeZoneDistro; +import com.android.timezone.distro.installer.TimeZoneDistroInstaller; + +import libcore.icu.ICU; +import libcore.timezone.TimeZoneDataFiles; +import libcore.timezone.TimeZoneFinder; +import libcore.timezone.TzDataSetVersion; +import libcore.timezone.ZoneInfoDB; + import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; @@ -46,18 +60,6 @@ import java.io.PrintWriter; import java.util.Arrays; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; -import libcore.icu.ICU; -import libcore.timezone.TzDataSetVersion; -import libcore.timezone.TimeZoneFinder; -import libcore.timezone.ZoneInfoDB; - -import static android.app.timezone.RulesState.DISTRO_STATUS_INSTALLED; -import static android.app.timezone.RulesState.DISTRO_STATUS_NONE; -import static android.app.timezone.RulesState.DISTRO_STATUS_UNKNOWN; -import static android.app.timezone.RulesState.STAGED_OPERATION_INSTALL; -import static android.app.timezone.RulesState.STAGED_OPERATION_NONE; -import static android.app.timezone.RulesState.STAGED_OPERATION_UNINSTALL; -import static android.app.timezone.RulesState.STAGED_OPERATION_UNKNOWN; public final class RulesManagerService extends IRulesManager.Stub { @@ -96,8 +98,6 @@ public final class RulesManagerService extends IRulesManager.Stub { @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) static final String REQUIRED_QUERY_PERMISSION = android.Manifest.permission.QUERY_TIME_ZONE_RULES; - private static final File SYSTEM_TZ_DATA_FILE = new File("/system/usr/share/zoneinfo/tzdata"); - private static final File TZ_DATA_DIR = new File("/data/misc/zoneinfo"); private final AtomicBoolean mOperationInProgress = new AtomicBoolean(false); private final PermissionHelper mPermissionHelper; @@ -108,12 +108,14 @@ public final class RulesManagerService extends IRulesManager.Stub { private static RulesManagerService create(Context context) { RulesManagerServiceHelperImpl helper = new RulesManagerServiceHelperImpl(context); + File baseVersionFile = new File(TimeZoneDataFiles.getRuntimeModuleTzVersionFile()); + File tzDataDir = new File(TimeZoneDataFiles.getDataTimeZoneRootDir()); return new RulesManagerService( helper /* permissionHelper */, helper /* executor */, helper /* intentHelper */, PackageTracker.create(context), - new TimeZoneDistroInstaller(TAG, SYSTEM_TZ_DATA_FILE, TZ_DATA_DIR)); + new TimeZoneDistroInstaller(TAG, baseVersionFile, tzDataDir)); } // A constructor that can be used by tests to supply mocked / faked dependencies. @@ -143,11 +145,11 @@ public final class RulesManagerService extends IRulesManager.Stub { /** Like {@link #getRulesState()} without the permission check. */ private RulesState getRulesStateInternal() { synchronized(this) { - String systemRulesVersion; + TzDataSetVersion baseVersion; try { - systemRulesVersion = mInstaller.getSystemRulesVersion(); + baseVersion = mInstaller.readBaseVersion(); } catch (IOException e) { - Slog.w(TAG, "Failed to read system rules", e); + Slog.w(TAG, "Failed to read base rules version", e); return null; } @@ -196,7 +198,7 @@ public final class RulesManagerService extends IRulesManager.Stub { Slog.w(TAG, "Failed to read staged distro.", e); } } - return new RulesState(systemRulesVersion, DISTRO_FORMAT_VERSION_SUPPORTED, + return new RulesState(baseVersion.rulesVersion, DISTRO_FORMAT_VERSION_SUPPORTED, operationInProgress, stagedOperationStatus, stagedDistroRulesVersion, distroStatus, installedDistroRulesVersion); } @@ -454,13 +456,13 @@ public final class RulesManagerService extends IRulesManager.Stub { pw.println("Operation in progress: " + value); break; } - case 's': { - // Report system image rules version + case 'b': { + // Report base rules version String value = "Unknown"; if (rulesState != null) { - value = rulesState.getSystemRulesVersion(); + value = rulesState.getBaseRulesVersion(); } - pw.println("System rules version: " + value); + pw.println("Base rules version: " + value); break; } case 'c': { diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 087de69b6c12..e976975bd675 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -2751,7 +2751,9 @@ final class ActivityRecord extends ConfigurationContainer { final Configuration srcConfig = task.getConfiguration(); overrideConfig.colorMode = srcConfig.colorMode; overrideConfig.densityDpi = srcConfig.densityDpi; - overrideConfig.screenLayout = srcConfig.screenLayout; + overrideConfig.screenLayout = srcConfig.screenLayout + & (Configuration.SCREENLAYOUT_LONG_MASK + | Configuration.SCREENLAYOUT_SIZE_MASK); // The smallest screen width is the short side of screen bounds. Because the bounds // and density won't be changed, smallestScreenWidthDp is also fixed. overrideConfig.smallestScreenWidthDp = srcConfig.smallestScreenWidthDp; diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index 932cfd3ae007..a4457e291cc3 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -4019,7 +4019,7 @@ class ActivityStack extends ConfigurationContainer { "Prepare close transition: finishing " + r); if (endTask) { mService.getTaskChangeNotificationController().notifyTaskRemovalStarted( - task.taskId); + task.getTaskInfo()); } getDisplay().mDisplayContent.prepareAppTransition(transit, false); @@ -4924,8 +4924,7 @@ class ActivityStack extends ConfigurationContainer { mRootActivityContainer.resumeFocusedStacksTopActivities(); EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, tr.userId, tr.taskId); - - mService.getTaskChangeNotificationController().notifyTaskMovedToFront(tr.taskId); + mService.getTaskChangeNotificationController().notifyTaskMovedToFront(tr.getTaskInfo()); } finally { getDisplay().continueUpdateImeTarget(); } diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index 6ffd5543439c..7ae48a52c7fd 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -317,14 +317,14 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { * receivers to launch an activity and get that to run before the device * goes back to sleep. */ - PowerManager.WakeLock mLaunchingActivity; + PowerManager.WakeLock mLaunchingActivityWakeLock; /** * Set when the system is going to sleep, until we have * successfully paused the current activity and released our wake lock. * At that point the system is allowed to actually sleep. */ - PowerManager.WakeLock mGoingToSleep; + PowerManager.WakeLock mGoingToSleepWakeLock; /** * Temporary rect used during docked stack resize calculation so we don't need to create a new @@ -467,10 +467,10 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { */ void initPowerManagement() { mPowerManager = mService.mContext.getSystemService(PowerManager.class); - mGoingToSleep = mPowerManager + mGoingToSleepWakeLock = mPowerManager .newWakeLock(PARTIAL_WAKE_LOCK, "ActivityManager-Sleep"); - mLaunchingActivity = mPowerManager.newWakeLock(PARTIAL_WAKE_LOCK, "*launch*"); - mLaunchingActivity.setReferenceCounted(false); + mLaunchingActivityWakeLock = mPowerManager.newWakeLock(PARTIAL_WAKE_LOCK, "*launch*"); + mLaunchingActivityWakeLock.setReferenceCounted(false); } void setWindowManager(WindowManagerService wm) { @@ -1213,14 +1213,14 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { } void setLaunchSource(int uid) { - mLaunchingActivity.setWorkSource(new WorkSource(uid)); + mLaunchingActivityWakeLock.setWorkSource(new WorkSource(uid)); } void acquireLaunchWakelock() { if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != Process.myUid()) { throw new IllegalStateException("Calling must be system uid"); } - mLaunchingActivity.acquire(); + mLaunchingActivityWakeLock.acquire(); if (!mHandler.hasMessages(LAUNCH_TIMEOUT_MSG)) { // To be safe, don't allow the wake lock to be held for too long. mHandler.sendEmptyMessageDelayed(LAUNCH_TIMEOUT_MSG, LAUNCH_TIMEOUT); @@ -1302,13 +1302,13 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { mService.scheduleAppGcsLocked(); } - if (mLaunchingActivity.isHeld()) { + if (mLaunchingActivityWakeLock.isHeld()) { mHandler.removeMessages(LAUNCH_TIMEOUT_MSG); if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != Process.myUid()) { throw new IllegalStateException("Calling must be system uid"); } - mLaunchingActivity.release(); + mLaunchingActivityWakeLock.release(); } mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); } @@ -1972,13 +1972,13 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { void goingToSleepLocked() { scheduleSleepTimeout(); - if (!mGoingToSleep.isHeld()) { - mGoingToSleep.acquire(); - if (mLaunchingActivity.isHeld()) { + if (!mGoingToSleepWakeLock.isHeld()) { + mGoingToSleepWakeLock.acquire(); + if (mLaunchingActivityWakeLock.isHeld()) { if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != Process.myUid()) { throw new IllegalStateException("Calling must be system uid"); } - mLaunchingActivity.release(); + mLaunchingActivityWakeLock.release(); mHandler.removeMessages(LAUNCH_TIMEOUT_MSG); } } @@ -2020,8 +2020,8 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { void comeOutOfSleepIfNeededLocked() { removeSleepTimeouts(); - if (mGoingToSleep.isHeld()) { - mGoingToSleep.release(); + if (mGoingToSleepWakeLock.isHeld()) { + mGoingToSleepWakeLock.release(); } } @@ -2051,8 +2051,8 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { removeSleepTimeouts(); - if (mGoingToSleep.isHeld()) { - mGoingToSleep.release(); + if (mGoingToSleepWakeLock.isHeld()) { + mGoingToSleepWakeLock.release(); } if (mService.mShuttingDown) { mService.mGlobalLock.notifyAll(); @@ -2351,6 +2351,9 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { // Suppress the warning toast if the preferredDisplay was set to singleTask. // The singleTaskInstance displays will only contain one task and any attempt to // launch new task will re-route to the default display. + mService.getTaskChangeNotificationController() + .notifyActivityLaunchOnSecondaryDisplayRerouted(task.getTaskInfo(), + preferredDisplayId); return; } @@ -2358,7 +2361,8 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { Slog.w(TAG, "Failed to put " + task + " on display " + preferredDisplayId); // Display a warning toast that we failed to put a task on a secondary display. mService.getTaskChangeNotificationController() - .notifyActivityLaunchOnSecondaryDisplayFailed(); + .notifyActivityLaunchOnSecondaryDisplayFailed(task.getTaskInfo(), + preferredDisplayId); return; } else if (!forceNonResizable && handleForcedResizableTask(task, FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY)) { @@ -2561,13 +2565,13 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { } break; case LAUNCH_TIMEOUT_MSG: { synchronized (mService.mGlobalLock) { - if (mLaunchingActivity.isHeld()) { + if (mLaunchingActivityWakeLock.isHeld()) { Slog.w(TAG, "Launch timeout has expired, giving up wake lock!"); if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != Process.myUid()) { throw new IllegalStateException("Calling must be system uid"); } - mLaunchingActivity.release(); + mLaunchingActivityWakeLock.release(); } } } break; diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 538813e9b21c..9f04166ea4c0 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -46,6 +46,7 @@ import static android.content.Intent.FLAG_ACTIVITY_REORDER_TO_FRONT; import static android.content.Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED; import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS; import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP; +import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME; import static android.content.pm.ActivityInfo.DOCUMENT_LAUNCH_ALWAYS; import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE; @@ -938,7 +939,7 @@ class ActivityStarter { return false; } // don't abort if the callingUid is in the foreground or is a persistent system process - final int callingUidProcState = mService.getUidStateLocked(callingUid); + final int callingUidProcState = mService.getUidState(callingUid); final boolean callingUidHasAnyVisibleWindow = mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(callingUid); final boolean isCallingUidForeground = callingUidHasAnyVisibleWindow @@ -951,7 +952,7 @@ class ActivityStarter { // take realCallingUid into consideration final int realCallingUidProcState = (callingUid == realCallingUid) ? callingUidProcState - : mService.getUidStateLocked(realCallingUid); + : mService.getUidState(realCallingUid); final boolean realCallingUidHasAnyVisibleWindow = (callingUid == realCallingUid) ? callingUidHasAnyVisibleWindow : mService.mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(realCallingUid); @@ -1457,6 +1458,14 @@ class ActivityStarter { // This task was started because of movement of the activity based on affinity... // Now that we are actually launching it, we can assign the base intent. reusedActivity.getTaskRecord().setIntent(mStartActivity); + } else { + final boolean taskOnHome = + (mStartActivity.intent.getFlags() & FLAG_ACTIVITY_TASK_ON_HOME) != 0; + if (taskOnHome) { + reusedActivity.getTaskRecord().intent.addFlags(FLAG_ACTIVITY_TASK_ON_HOME); + } else { + reusedActivity.getTaskRecord().intent.removeFlags(FLAG_ACTIVITY_TASK_ON_HOME); + } } // This code path leads to delivering a new intent, we want to make sure we schedule it diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 9b35e85a85f4..486a4ea24f65 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -26,7 +26,6 @@ import static android.Manifest.permission.REMOVE_TASKS; import static android.Manifest.permission.START_TASKS_FROM_RECENTS; import static android.Manifest.permission.STOP_APP_SWITCHES; import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; -import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT; import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.ActivityTaskManager.RESIZE_MODE_PRESERVE_WINDOW; @@ -272,6 +271,10 @@ import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.lang.ref.WeakReference; import java.text.DateFormat; import java.util.ArrayList; @@ -363,7 +366,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { private UserManagerService mUserManager; private AppOpsService mAppOpsService; /** All active uids in the system. */ - private final SparseArray<Integer> mActiveUids = new SparseArray<>(); + private final MirrorActiveUids mActiveUids = new MirrorActiveUids(); private final SparseArray<String> mPendingTempWhitelist = new SparseArray<>(); /** All processes currently running that might have a window organized by name. */ final ProcessMap<WindowProcessController> mProcessNames = new ProcessMap<>(); @@ -647,6 +650,17 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } + /** Indicates that the method may be invoked frequently or is sensitive to performance. */ + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.SOURCE) + @interface HotPath { + int NONE = 0; + int OOM_ADJUSTMENT = 1; + int LRU_UPDATE = 2; + int PROCESS_CHANGE = 3; + int caller() default NONE; + } + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public ActivityTaskManagerService(Context context) { mContext = context; @@ -2763,7 +2777,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { r.setTaskDescription(td); final TaskRecord task = r.getTaskRecord(); task.updateTaskDescription(); - mTaskChangeNotificationController.notifyTaskDescriptionChanged(task.taskId, td); + mTaskChangeNotificationController.notifyTaskDescriptionChanged(task.getTaskInfo()); } } } @@ -3084,8 +3098,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public void setLockScreenShown(boolean keyguardShowing, boolean aodShowing, - int[] secondaryDisplaysShowing) { + public void setLockScreenShown(boolean keyguardShowing, boolean aodShowing) { if (checkCallingPermission(android.Manifest.permission.DEVICE_POWER) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Requires permission " @@ -3102,8 +3115,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mH.sendMessage(msg); } try { - mKeyguardController.setKeyguardShown(keyguardShowing, aodShowing, - secondaryDisplaysShowing); + mKeyguardController.setKeyguardShown(keyguardShowing, aodShowing); } finally { Binder.restoreCallingIdentity(ident); } @@ -5720,12 +5732,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return null; } - int getUidStateLocked(int uid) { - return mActiveUids.get(uid, PROCESS_STATE_NONEXISTENT); + int getUidState(int uid) { + return mActiveUids.getUidState(uid); } boolean isUidForeground(int uid) { - return (getUidStateLocked(uid) == ActivityManager.PROCESS_STATE_TOP) + return (getUidState(uid) == ActivityManager.PROCESS_STATE_TOP) || mWindowManager.mRoot.isAnyNonToastWindowVisibleForUid(uid); } @@ -6118,23 +6130,26 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } + @HotPath(caller = HotPath.PROCESS_CHANGE) @Override public void onProcessAdded(WindowProcessController proc) { - synchronized (mGlobalLock) { + synchronized (mGlobalLockWithoutBoost) { mProcessNames.put(proc.mName, proc.mUid, proc); } } + @HotPath(caller = HotPath.PROCESS_CHANGE) @Override public void onProcessRemoved(String name, int uid) { - synchronized (mGlobalLock) { + synchronized (mGlobalLockWithoutBoost) { mProcessNames.remove(name, uid); } } + @HotPath(caller = HotPath.PROCESS_CHANGE) @Override public void onCleanUpApplicationRecord(WindowProcessController proc) { - synchronized (mGlobalLock) { + synchronized (mGlobalLockWithoutBoost) { if (proc == mHomeProcess) { mHomeProcess = null; } @@ -6144,23 +6159,26 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } + @HotPath(caller = HotPath.OOM_ADJUSTMENT) @Override public int getTopProcessState() { - synchronized (mGlobalLock) { + synchronized (mGlobalLockWithoutBoost) { return mTopProcessState; } } + @HotPath(caller = HotPath.OOM_ADJUSTMENT) @Override public boolean isHeavyWeightProcess(WindowProcessController proc) { - synchronized (mGlobalLock) { + synchronized (mGlobalLockWithoutBoost) { return proc == mHeavyWeightProcess; } } + @HotPath(caller = HotPath.PROCESS_CHANGE) @Override public void clearHeavyWeightProcessIfEquals(WindowProcessController proc) { - synchronized (mGlobalLock) { + synchronized (mGlobalLockWithoutBoost) { ActivityTaskManagerService.this.clearHeavyWeightProcessIfEquals(proc); } } @@ -6176,9 +6194,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } + @HotPath(caller = HotPath.OOM_ADJUSTMENT) @Override public boolean isSleeping() { - synchronized (mGlobalLock) { + synchronized (mGlobalLockWithoutBoost) { return isSleepingLocked(); } } @@ -6422,9 +6441,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } + @HotPath(caller = HotPath.PROCESS_CHANGE) @Override public boolean isFactoryTestProcess(WindowProcessController wpc) { - synchronized (mGlobalLock) { + synchronized (mGlobalLockWithoutBoost) { if (mFactoryTest == FACTORY_TEST_OFF) { return false; } @@ -6477,10 +6497,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } + @HotPath(caller = HotPath.PROCESS_CHANGE) @Override public void handleAppDied(WindowProcessController wpc, boolean restarting, Runnable finishInstrumentationCallback) { - synchronized (mGlobalLock) { + synchronized (mGlobalLockWithoutBoost) { // Remove this application's activities from active lists. boolean hasVisibleActivities = mRootActivityContainer.handleAppDied(wpc); @@ -6579,16 +6600,18 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } + @HotPath(caller = HotPath.PROCESS_CHANGE) @Override public void preBindApplication(WindowProcessController wpc) { - synchronized (mGlobalLock) { + synchronized (mGlobalLockWithoutBoost) { mStackSupervisor.getActivityMetricsLogger().notifyBindApplication(wpc.mInfo); } } + @HotPath(caller = HotPath.PROCESS_CHANGE) @Override public boolean attachApplication(WindowProcessController wpc) throws RemoteException { - synchronized (mGlobalLock) { + synchronized (mGlobalLockWithoutBoost) { return mRootActivityContainer.attachApplication(wpc); } } @@ -6816,8 +6839,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { pw.println(" mController=" + mController + " mControllerIsAMonkey=" + mControllerIsAMonkey); } - pw.println(" mGoingToSleep=" + mStackSupervisor.mGoingToSleep); - pw.println(" mLaunchingActivity=" + mStackSupervisor.mLaunchingActivity); + pw.println(" mGoingToSleepWakeLock=" + mStackSupervisor.mGoingToSleepWakeLock); + pw.println(" mLaunchingActivityWakeLock=" + + mStackSupervisor.mLaunchingActivityWakeLock); } return needSep; @@ -6849,8 +6873,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { proto.write(IS_A_MONKEY, mControllerIsAMonkey); proto.end(token); } - mStackSupervisor.mGoingToSleep.writeToProto(proto, GOING_TO_SLEEP); - mStackSupervisor.mLaunchingActivity.writeToProto(proto, LAUNCHING_ACTIVITY); + mStackSupervisor.mGoingToSleepWakeLock.writeToProto(proto, GOING_TO_SLEEP); + mStackSupervisor.mLaunchingActivityWakeLock.writeToProto(proto, + LAUNCHING_ACTIVITY); } if (mHomeProcess != null && (dumpPackage == null @@ -6916,6 +6941,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } + @HotPath(caller = HotPath.OOM_ADJUSTMENT) @Override public WindowProcessController getTopApp() { synchronized (mGlobalLockWithoutBoost) { @@ -6924,6 +6950,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } + @HotPath(caller = HotPath.OOM_ADJUSTMENT) @Override public void rankTaskLayersIfNeeded() { synchronized (mGlobalLockWithoutBoost) { @@ -6968,34 +6995,28 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } + @HotPath(caller = HotPath.OOM_ADJUSTMENT) @Override public void onUidActive(int uid, int procState) { - synchronized (mGlobalLockWithoutBoost) { - mActiveUids.put(uid, procState); - } + mActiveUids.onUidActive(uid, procState); } + @HotPath(caller = HotPath.OOM_ADJUSTMENT) @Override public void onUidInactive(int uid) { - synchronized (mGlobalLockWithoutBoost) { - mActiveUids.remove(uid); - } + mActiveUids.onUidInactive(uid); } + @HotPath(caller = HotPath.OOM_ADJUSTMENT) @Override public void onActiveUidsCleared() { - synchronized (mGlobalLockWithoutBoost) { - mActiveUids.clear(); - } + mActiveUids.onActiveUidsCleared(); } + @HotPath(caller = HotPath.OOM_ADJUSTMENT) @Override public void onUidProcStateChanged(int uid, int procState) { - synchronized (mGlobalLockWithoutBoost) { - if (mActiveUids.get(uid) != null) { - mActiveUids.put(uid, procState); - } - } + mActiveUids.onUidProcStateChanged(uid, procState); } @Override diff --git a/services/core/java/com/android/server/wm/AppWindowThumbnail.java b/services/core/java/com/android/server/wm/AppWindowThumbnail.java index 5519729c17f5..bbbf11d2a7a2 100644 --- a/services/core/java/com/android/server/wm/AppWindowThumbnail.java +++ b/services/core/java/com/android/server/wm/AppWindowThumbnail.java @@ -118,7 +118,8 @@ class AppWindowThumbnail implements Animatable { anim.scaleCurrentDuration(mAppToken.mWmService.getTransitionAnimationScaleLocked()); mSurfaceAnimator.startAnimation(t, new LocalAnimationAdapter( new WindowAnimationSpec(anim, position, - mAppToken.getDisplayContent().mAppTransition.canSkipFirstFrame()), + mAppToken.getDisplayContent().mAppTransition.canSkipFirstFrame(), + mAppToken.mWmService.mWindowCornerRadius), mAppToken.mWmService.mSurfaceAnimationRunner), false /* hidden */); } diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 88c8b953a2e9..78199d4412a6 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -118,7 +118,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ToBooleanFunction; import com.android.server.AttributeCache; import com.android.server.LocalServices; -import com.android.server.display.ColorDisplayService; +import com.android.server.display.color.ColorDisplayService; import com.android.server.policy.WindowManagerPolicy; import com.android.server.policy.WindowManagerPolicy.StartingSurface; import com.android.server.wm.RemoteAnimationController.RemoteAnimationRecord; @@ -582,6 +582,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } } } + // Changes in opening apps and closing apps may cause orientation change. + reportDescendantOrientationChangeIfNeeded(); return; } @@ -729,11 +731,31 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } SurfaceControl.closeTransaction(); } + + // Visibility changes may cause orientation request change. + reportDescendantOrientationChangeIfNeeded(); } return delayed; } + private void reportDescendantOrientationChangeIfNeeded() { + // Orientation request is exposed only when we're visible. Therefore visibility change + // will change requested orientation. Notify upward the hierarchy ladder to adjust + // configuration. This is important to cases where activities with incompatible + // orientations launch, or user goes back from an activity of bi-orientation to an + // activity with specified orientation. + if (mActivityRecord.getRequestedConfigurationOrientation() == getConfiguration().orientation + || getOrientationIgnoreVisibility() == SCREEN_ORIENTATION_UNSET) { + return; + } + + final IBinder freezeToken = + mActivityRecord.mayFreezeScreenLocked(mActivityRecord.app) + ? mActivityRecord.appToken : null; + onDescendantOrientationChanged(freezeToken, mActivityRecord); + } + /** * @return The to top most child window for which {@link LayoutParams#isFullscreen()} returns * true. @@ -2526,7 +2548,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree new WindowAnimationSpec(a, mTmpPoint, mTmpRect, getDisplayContent().mAppTransition.canSkipFirstFrame(), appStackClipMode, - true /* isAppAnimation */), + true /* isAppAnimation */, + mWmService.mWindowCornerRadius), mWmService.mSurfaceAnimationRunner); if (a.getZAdjustment() == Animation.ZORDER_TOP) { mNeedsZBoost = true; diff --git a/services/core/java/com/android/server/wm/BlackFrame.java b/services/core/java/com/android/server/wm/BlackFrame.java index 497e4121f12e..84ba5ca94f8c 100644 --- a/services/core/java/com/android/server/wm/BlackFrame.java +++ b/services/core/java/com/android/server/wm/BlackFrame.java @@ -48,7 +48,7 @@ public class BlackFrame { surface = dc.makeOverlay() .setName("BlackSurface") - .setColorLayer(true) + .setColorLayer() .setParent(null) // TODO: Work-around for b/69259549 .build(); transaction.setWindowCrop(surface, w, h); diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java index 1373e1879d14..ee280846ff40 100644 --- a/services/core/java/com/android/server/wm/Dimmer.java +++ b/services/core/java/com/android/server/wm/Dimmer.java @@ -164,7 +164,7 @@ class Dimmer { private SurfaceControl makeDimLayer() { return mHost.makeChildSurface(null) .setParent(mHost.getSurfaceControl()) - .setColorLayer(true) + .setColorLayer() .setName("Dim Layer for - " + mHost.getName()) .build(); } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index a19721f39957..ae76740b5d26 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -899,7 +899,9 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mDividerControllerLocked = new DockedStackDividerController(service, this); mPinnedStackControllerLocked = new PinnedStackController(service, this); - final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(mSession).setOpaque(true); + final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(mSession) + .setOpaque(true) + .setContainerLayer(); mWindowingLayer = b.setName("Display Root").build(); mOverlayLayer = b.setName("Display Overlays").build(); @@ -4647,7 +4649,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo @Override SurfaceControl.Builder makeChildSurface(WindowContainer child) { SurfaceSession s = child != null ? child.getSession() : getSession(); - final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(s); + final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(s).setContainerLayer(); if (child == null) { return b; } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 91d573defc16..b028569ccb08 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -2787,6 +2787,9 @@ public class DisplayPolicy { public int focusChangedLw(WindowState lastFocus, WindowState newFocus) { mFocusedWindow = newFocus; mLastFocusedWindow = lastFocus; + if (mDisplayContent.isDefaultDisplay) { + mService.mPolicy.onDefaultDisplayFocusChangedLw(newFocus); + } if ((updateSystemUiVisibilityLw() & SYSTEM_UI_CHANGING_LAYOUT) != 0) { // If the navigation bar has been hidden or shown, we need to do another // layout pass to update that window. @@ -3271,7 +3274,12 @@ public class DisplayPolicy { } final WindowState w = mTopFullscreenOpaqueWindowState; - if (w != mFocusedWindow) { + if (w == null || w != mFocusedWindow) { + return false; + } + // If the bounds of activity window is different from its parent, then reject to be seamless + // because the window position may change after rotation that will look like a sudden jump. + if (w.mAppToken != null && !w.mAppToken.matchParentBounds()) { return false; } @@ -3279,8 +3287,7 @@ public class DisplayPolicy { // it and is in the fullscreen opaque state. Seamless rotation // requires freezing various Surface states and won't work well // with animations, so we disable it in the animation case for now. - if (w != null && !w.isAnimatingLw() - && w.getAttrs().rotationAnimation == ROTATION_ANIMATION_SEAMLESS) { + if (!w.isAnimatingLw() && w.getAttrs().rotationAnimation == ROTATION_ANIMATION_SEAMLESS) { return true; } return false; diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index 543f19655350..34a480291863 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -21,6 +21,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import android.annotation.IntDef; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.content.ContentResolver; @@ -49,6 +50,8 @@ import com.android.server.policy.WindowOrientationListener; import com.android.server.statusbar.StatusBarManagerInternal; import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; /** * Defines the mapping between orientation and rotation of a display. @@ -96,11 +99,36 @@ public class DisplayRotation { private int mUserRotation = Surface.ROTATION_0; /** + * Flag that indicates this is a display that may run better when fixed to user rotation. + */ + private boolean mDefaultFixedToUserRotation; + + /** + * No overridden behavior is provided in terms of fixing rotation to user rotation. Use other + * flags to derive the default behavior, such as {@link WindowManagerService#mIsPc} and + * {@link WindowManagerService#mForceDesktopModeOnExternalDisplays}. + */ + static final int FIXED_TO_USER_ROTATION_DEFAULT = 0; + /** + * Don't fix display rotation to {@link #mUserRotation} only. Always allow other factors to play + * a role in deciding display rotation. + */ + static final int FIXED_TO_USER_ROTATION_DISABLED = 1; + /** + * Only use {@link #mUserRotation} as the display rotation. + */ + static final int FIXED_TO_USER_ROTATION_ENABLED = 2; + @IntDef({ FIXED_TO_USER_ROTATION_DEFAULT, FIXED_TO_USER_ROTATION_DISABLED, + FIXED_TO_USER_ROTATION_ENABLED }) + @Retention(RetentionPolicy.SOURCE) + @interface FixedToUserRotation {} + + /** * A flag to indicate if the display rotation should be fixed to user specified rotation * regardless of all other states (including app requrested orientation). {@code true} the * display rotation should be fixed to user specified rotation, {@code false} otherwise. */ - private boolean mFixedToUserRotation; + private int mFixedToUserRotation = FIXED_TO_USER_ROTATION_DEFAULT; private int mDemoHdmiRotation; private int mDemoRotation; @@ -208,31 +236,23 @@ public class DisplayRotation { } mDemoRotationLock = SystemProperties.getBoolean("persist.demo.rotationlock", false); - // Only force the default orientation if the screen is xlarge, at least 960dp x 720dp, per - // http://developer.android.com/guide/practices/screens_support.html#range - // For car, ignore the dp limitation. It's physically impossible to rotate the car's screen - // so if the orientation is forced, we need to respect that no matter what. + // It's physically impossible to rotate the car's screen. final boolean isCar = mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_AUTOMOTIVE); - // For TV, it's usually 960dp x 540dp, ignore the size limitation. - // so if the orientation is forced, we need to respect that no matter what. + // It's also not likely to rotate a TV screen. final boolean isTv = mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_LEANBACK); + // Not much of use to rotate the display since it's close to square. final boolean isCloseToSquare = isNonDecorDisplayCloseToSquare(Surface.ROTATION_0, width, height); - final boolean forceDefaultOrientationInRes = - res.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation); - final boolean forceDefaultOrienation = - ((longSizeDp >= 960 && shortSizeDp >= 720) || isCar || isTv || isCloseToSquare) - && forceDefaultOrientationInRes - // For debug purposes the next line turns this feature off with: - // $ adb shell setprop config.override_forced_orient true - // $ adb shell wm size reset - && !"true".equals(SystemProperties.get("config.override_forced_orient")); - // Configuration says we force to use the default orientation. We can fall back to fix - // rotation to only user rotation. As long as OEM doesn't change user rotation then the - // rotation of this display is effectively stuck at 0 deg. - setFixedToUserRotation(forceDefaultOrienation); + final boolean forceDesktopMode = + mService.mForceDesktopModeOnExternalDisplays && !isDefaultDisplay; + mDefaultFixedToUserRotation = + (isCar || isTv || mService.mIsPc || forceDesktopMode || isCloseToSquare) + // For debug purposes the next line turns this feature off with: + // $ adb shell setprop config.override_forced_orient true + // $ adb shell wm size reset + && !"true".equals(SystemProperties.get("config.override_forced_orient")); } private boolean isNonDecorDisplayCloseToSquare(int rotation, int width, int height) { @@ -263,7 +283,7 @@ public class DisplayRotation { } void restoreSettings(int userRotationMode, int userRotation, - boolean fixedToUserRotation) { + @FixedToUserRotation int fixedToUserRotation) { mFixedToUserRotation = fixedToUserRotation; // We will retrieve user rotation and user rotation mode from settings for default display. @@ -285,14 +305,13 @@ public class DisplayRotation { mUserRotation = userRotation; } - void setFixedToUserRotation(boolean fixedToUserRotation) { + void setFixedToUserRotation(@FixedToUserRotation int fixedToUserRotation) { if (mFixedToUserRotation == fixedToUserRotation) { return; } mFixedToUserRotation = fixedToUserRotation; - mDisplayWindowSettings.setFixedToUserRotation(mDisplayContent, - fixedToUserRotation); + mDisplayWindowSettings.setFixedToUserRotation(mDisplayContent, fixedToUserRotation); mService.updateRotation(true /* alwaysSendConfiguration */, false /* forceRelayout */); } @@ -346,7 +365,14 @@ public class DisplayRotation { } boolean isFixedToUserRotation() { - return mFixedToUserRotation; + switch (mFixedToUserRotation) { + case FIXED_TO_USER_ROTATION_DISABLED: + return false; + case FIXED_TO_USER_ROTATION_ENABLED: + return true; + default: + return mDefaultFixedToUserRotation; + } } /** @@ -355,7 +381,7 @@ public class DisplayRotation { * false} is when {@link #isFixedToUserRotation()} is {@code true}. */ boolean respectAppRequestedOrientation() { - return !mFixedToUserRotation; + return !isFixedToUserRotation(); } public int getLandscapeRotation() { @@ -461,7 +487,7 @@ public class DisplayRotation { * screen is switched off. */ private boolean needSensorRunning() { - if (mFixedToUserRotation) { + if (isFixedToUserRotation()) { // We are sure we only respect user rotation settings, so we are sure we will not // support sensor rotation. return false; @@ -527,7 +553,7 @@ public class DisplayRotation { ); } - if (mFixedToUserRotation) { + if (isFixedToUserRotation()) { return mUserRotation; } @@ -739,7 +765,7 @@ public class DisplayRotation { // demo, hdmi, vr, etc mode. // Determine if the rotation is currently forced. - if (mFixedToUserRotation) { + if (isFixedToUserRotation()) { return false; // Rotation is forced to user settings. } @@ -899,7 +925,7 @@ public class DisplayRotation { pw.print(" mDemoHdmiRotationLock=" + mDemoHdmiRotationLock); pw.println(" mUndockedHdmiRotation=" + Surface.rotationToString(mUndockedHdmiRotation)); pw.println(prefix + " mLidOpenRotation=" + Surface.rotationToString(mLidOpenRotation)); - pw.println(prefix + " mFixedToUserRotation=" + mFixedToUserRotation); + pw.println(prefix + " mFixedToUserRotation=" + isFixedToUserRotation()); } private class OrientationListener extends WindowOrientationListener { diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java index 5cfa7dec386e..4617890be722 100644 --- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java +++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java @@ -22,6 +22,7 @@ import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED; import static com.android.server.wm.DisplayContent.FORCE_SCALING_MODE_AUTO; import static com.android.server.wm.DisplayContent.FORCE_SCALING_MODE_DISABLED; +import static com.android.server.wm.DisplayRotation.FIXED_TO_USER_ROTATION_DEFAULT; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -80,7 +81,8 @@ class DisplayWindowSettings { private boolean mShouldShowWithInsecureKeyguard = false; private boolean mShouldShowSystemDecors = false; private boolean mShouldShowIme = false; - private boolean mFixedToUserRotation; + private @DisplayRotation.FixedToUserRotation int mFixedToUserRotation = + FIXED_TO_USER_ROTATION_DEFAULT; private Entry(String name) { mName = name; @@ -99,7 +101,7 @@ class DisplayWindowSettings { && !mShouldShowWithInsecureKeyguard && !mShouldShowSystemDecors && !mShouldShowIme - && !mFixedToUserRotation; + && mFixedToUserRotation == FIXED_TO_USER_ROTATION_DEFAULT; } } @@ -188,7 +190,8 @@ class DisplayWindowSettings { writeSettingsIfNeeded(entry, displayInfo); } - void setFixedToUserRotation(DisplayContent displayContent, boolean fixedToUserRotation) { + void setFixedToUserRotation(DisplayContent displayContent, + @DisplayRotation.FixedToUserRotation int fixedToUserRotation) { final DisplayInfo displayInfo = displayContent.getDisplayInfo(); final Entry entry = getOrCreateEntry(displayInfo); entry.mFixedToUserRotation = fixedToUserRotation; @@ -464,8 +467,7 @@ class DisplayWindowSettings { "shouldShowWithInsecureKeyguard"); entry.mShouldShowSystemDecors = getBooleanAttribute(parser, "shouldShowSystemDecors"); entry.mShouldShowIme = getBooleanAttribute(parser, "shouldShowIme"); - entry.mFixedToUserRotation = getBooleanAttribute(parser, - "fixedToUserRotation"); + entry.mFixedToUserRotation = getIntAttribute(parser, "fixedToUserRotation"); mEntries.put(name, entry); } XmlUtils.skipCurrentTag(parser); @@ -549,9 +551,9 @@ class DisplayWindowSettings { if (entry.mShouldShowIme) { out.attribute(null, "shouldShowIme", Boolean.toString(entry.mShouldShowIme)); } - if (entry.mFixedToUserRotation) { + if (entry.mFixedToUserRotation != FIXED_TO_USER_ROTATION_DEFAULT) { out.attribute(null, "fixedToUserRotation", - Boolean.toString(entry.mFixedToUserRotation)); + Integer.toString(entry.mFixedToUserRotation)); } out.endTag(null, "display"); } diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java index 3f77e1c6886b..2b2231ae6477 100644 --- a/services/core/java/com/android/server/wm/DragState.java +++ b/services/core/java/com/android/server/wm/DragState.java @@ -164,7 +164,7 @@ class DragState { if (mInputSurface == null) { mInputSurface = mService.makeSurfaceBuilder(mService.mRoot.getDisplayContent(displayId) - .getSession()).setContainerLayer(true) + .getSession()).setContainerLayer() .setName("Drag and Drop Input Consumer").build(); } final InputWindowHandle h = getInputWindowHandle(); diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java index 4df5a0b5ad9e..ab95e4b52dc6 100644 --- a/services/core/java/com/android/server/wm/InputConsumerImpl.java +++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java @@ -22,13 +22,11 @@ import android.os.IBinder; import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; -import android.view.InputChannel; -import android.view.WindowManager; - import android.view.InputApplicationHandle; +import android.view.InputChannel; import android.view.InputWindowHandle; import android.view.SurfaceControl; -import android.util.Slog; +import android.view.WindowManager; import java.io.PrintWriter; @@ -89,7 +87,7 @@ class InputConsumerImpl implements IBinder.DeathRecipient { mWindowHandle.scaleFactor = 1.0f; mInputSurface = mService.makeSurfaceBuilder(mService.mRoot.getDisplayContent(displayId) - .getSession()).setContainerLayer(true).setName("Input Consumer " + name) + .getSession()).setContainerLayer().setName("Input Consumer " + name) .build(); } diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java index b5be2ac5df98..8c8b05f1307a 100644 --- a/services/core/java/com/android/server/wm/KeyguardController.java +++ b/services/core/java/com/android/server/wm/KeyguardController.java @@ -29,6 +29,7 @@ import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE; import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER; +import static com.android.server.am.KeyguardControllerProto.AOD_SHOWING; import static com.android.server.am.KeyguardControllerProto.KEYGUARD_OCCLUDED_STATES; import static com.android.server.am.KeyguardControllerProto.KEYGUARD_SHOWING; import static com.android.server.am.KeyguardOccludedProto.DISPLAY_ID; @@ -49,7 +50,6 @@ import com.android.server.policy.WindowManagerPolicy; import com.android.server.wm.ActivityTaskManagerInternal.SleepToken; import java.io.PrintWriter; -import java.util.Arrays; /** * Controls Keyguard occluding, dismissing and transitions depending on what kind of activities are @@ -87,7 +87,7 @@ class KeyguardController { /** * @return true if either Keyguard or AOD are showing, not going away, and not being occluded - * on the given display, false otherwise + * on the given display, false otherwise. */ boolean isKeyguardOrAodShowing(int displayId) { return (mKeyguardShowing || mAodShowing) && !mKeyguardGoingAway @@ -95,6 +95,16 @@ class KeyguardController { } /** + * @return {@code true} if 1) Keyguard is showing, not going away, and not being occluded on the + * given display, or 2) AOD is showing, {@code false} otherwise. + * TODO(b/125198167): Replace isKeyguardOrAodShowing() by this logic. + */ + boolean isKeyguardUnoccludedOrAodShowing(int displayId) { + return (mKeyguardShowing && !mKeyguardGoingAway && !isDisplayOccluded(displayId)) + || mAodShowing; + } + + /** * @return true if Keyguard is showing, not going away, and not being occluded on the given * display, false otherwise */ @@ -120,18 +130,15 @@ class KeyguardController { /** * Update the Keyguard showing state. */ - void setKeyguardShown(boolean keyguardShowing, boolean aodShowing, - int[] secondaryDisplaysShowing) { + void setKeyguardShown(boolean keyguardShowing, boolean aodShowing) { boolean showingChanged = keyguardShowing != mKeyguardShowing || aodShowing != mAodShowing; // If keyguard is going away, but SystemUI aborted the transition, need to reset state. showingChanged |= mKeyguardGoingAway && keyguardShowing; - if (!showingChanged && Arrays.equals(secondaryDisplaysShowing, - mSecondaryDisplayIdsShowing)) { + if (!showingChanged) { return; } mKeyguardShowing = keyguardShowing; mAodShowing = aodShowing; - mSecondaryDisplayIdsShowing = secondaryDisplaysShowing; mWindowManager.setAodShowing(aodShowing); if (showingChanged) { dismissDockedStackIfNeeded(); @@ -384,10 +391,11 @@ class KeyguardController { for (int displayNdx = mRootActivityContainer.getChildCount() - 1; displayNdx >= 0; displayNdx--) { final ActivityDisplay display = mRootActivityContainer.getChildAt(displayNdx); - final KeyguardDisplayState state = getDisplay(display.mDisplayId); - if (isKeyguardOrAodShowing(display.mDisplayId) && state.mSleepToken == null) { + final int displayId = display.mDisplayId; + final KeyguardDisplayState state = getDisplay(displayId); + if (isKeyguardUnoccludedOrAodShowing(displayId) && state.mSleepToken == null) { state.acquiredSleepToken(); - } else if (!isKeyguardOrAodShowing(display.mDisplayId) && state.mSleepToken != null) { + } else if (!isKeyguardUnoccludedOrAodShowing(displayId) && state.mSleepToken != null) { state.releaseSleepToken(); } } @@ -532,6 +540,7 @@ class KeyguardController { void writeToProto(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); + proto.write(AOD_SHOWING, mAodShowing); proto.write(KEYGUARD_SHOWING, mKeyguardShowing); writeDisplayStatesToProto(proto, KEYGUARD_OCCLUDED_STATES); proto.end(token); diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java index d67193ea9e69..f8f693c53888 100644 --- a/services/core/java/com/android/server/wm/Letterbox.java +++ b/services/core/java/com/android/server/wm/Letterbox.java @@ -233,7 +233,7 @@ public class Letterbox { private void createSurface() { mSurface = mFactory.get().setName("Letterbox - " + mType) - .setFlags(HIDDEN).setColorLayer(true).build(); + .setFlags(HIDDEN).setColorLayer().build(); mSurface.setLayer(-1); mSurface.setColor(new float[]{0, 0, 0}); } diff --git a/services/core/java/com/android/server/wm/MirrorActiveUids.java b/services/core/java/com/android/server/wm/MirrorActiveUids.java new file mode 100644 index 000000000000..00479428525a --- /dev/null +++ b/services/core/java/com/android/server/wm/MirrorActiveUids.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT; + +import android.util.SparseIntArray; + +/** + * This is a partial mirror of {@link @com.android.server.am.ActiveUids}. It is already thread + * safe so the heavy service lock is not needed when updating state from activity manager (oom + * adjustment) or getting state from window manager (background start check). + */ +class MirrorActiveUids { + private SparseIntArray mUidStates = new SparseIntArray(); + + synchronized void onUidActive(int uid, int procState) { + mUidStates.put(uid, procState); + } + + synchronized void onUidInactive(int uid) { + mUidStates.delete(uid); + } + + synchronized void onActiveUidsCleared() { + mUidStates.clear(); + } + + synchronized void onUidProcStateChanged(int uid, int procState) { + final int index = mUidStates.indexOfKey(uid); + if (index >= 0) { + mUidStates.setValueAt(index, procState); + } + } + + synchronized int getUidState(int uid) { + return mUidStates.get(uid, PROCESS_STATE_NONEXISTENT); + } +} diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java index 15478b49c08e..0480d438e3cd 100644 --- a/services/core/java/com/android/server/wm/RecentTasks.java +++ b/services/core/java/com/android/server/wm/RecentTasks.java @@ -70,7 +70,6 @@ import android.util.SparseBooleanArray; import com.android.internal.annotations.VisibleForTesting; import com.android.server.am.ActivityManagerService; -import com.android.server.wm.TaskRecord.TaskActivitiesReport; import com.google.android.collect.Sets; @@ -180,7 +179,6 @@ class RecentTasks { private final HashMap<ComponentName, ActivityInfo> mTmpAvailActCache = new HashMap<>(); private final HashMap<String, ApplicationInfo> mTmpAvailAppCache = new HashMap<>(); private final SparseBooleanArray mTmpQuietProfileUserIds = new SparseBooleanArray(); - private final TaskActivitiesReport mTmpReport = new TaskActivitiesReport(); @VisibleForTesting RecentTasks(ActivityTaskManagerService service, TaskPersister taskPersister) { @@ -1161,7 +1159,8 @@ class RecentTasks { /** * @return whether the given active task should be presented to the user through SystemUI. */ - private boolean isVisibleRecentTask(TaskRecord task) { + @VisibleForTesting + boolean isVisibleRecentTask(TaskRecord task) { if (DEBUG_RECENTS_TRIM_TASKS) Slog.d(TAG, "isVisibleRecentTask: task=" + task + " minVis=" + mMinNumVisibleTasks + " maxVis=" + mMaxNumVisibleTasks + " sessionDuration=" + mActiveTasksSessionDurationMs @@ -1197,6 +1196,17 @@ class RecentTasks { } } + // Tasks managed by/associated with an ActivityView should be excluded from recents. + // singleTaskInstance is set on the VirtualDisplay managed by ActivityView + // TODO(b/126185105): Find a different signal to use besides isSingleTaskInstance + final ActivityStack stack = task.getStack(); + if (stack != null) { + ActivityDisplay display = stack.getDisplay(); + if (display != null && display.isSingleTaskInstance()) { + return false; + } + } + // If we're in lock task mode, ignore the root task if (task == mService.getLockTaskController().getRootTask()) { return false; @@ -1587,7 +1597,7 @@ class RecentTasks { */ ActivityManager.RecentTaskInfo createRecentTaskInfo(TaskRecord tr) { ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo(); - tr.fillTaskInfo(rti, mTmpReport); + tr.fillTaskInfo(rti); // Fill in some deprecated values rti.id = rti.isRunning ? rti.taskId : INVALID_TASK_ID; rti.persistentId = rti.taskId; diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java index 34282cdb7ed9..3bf437d38bcc 100644 --- a/services/core/java/com/android/server/wm/RunningTasks.java +++ b/services/core/java/com/android/server/wm/RunningTasks.java @@ -35,8 +35,6 @@ class RunningTasks { private static final Comparator<TaskRecord> LAST_ACTIVE_TIME_COMPARATOR = (o1, o2) -> Long.signum(o2.lastActiveTime - o1.lastActiveTime); - private final TaskRecord.TaskActivitiesReport mTmpReport = - new TaskRecord.TaskActivitiesReport(); private final TreeSet<TaskRecord> mTmpSortedSet = new TreeSet<>(LAST_ACTIVE_TIME_COMPARATOR); private final ArrayList<TaskRecord> mTmpStackTasks = new ArrayList<>(); @@ -80,7 +78,7 @@ class RunningTasks { */ private RunningTaskInfo createRunningTaskInfo(TaskRecord task) { final RunningTaskInfo rti = new RunningTaskInfo(); - task.fillTaskInfo(rti, mTmpReport); + task.fillTaskInfo(rti); // Fill in some deprecated values rti.id = rti.taskId; return rti; diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java index 84cd8d1632cf..2d5c97f9a3a5 100644 --- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java +++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java @@ -257,7 +257,7 @@ class ScreenRotationAnimation { mOriginalWidth = originalWidth; mOriginalHeight = originalHeight; - final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + final SurfaceControl.Transaction t = mService.mTransactionFactory.make(); try { mSurfaceControl = displayContent.makeOverlay() .setName("ScreenshotSurface") @@ -267,13 +267,13 @@ class ScreenRotationAnimation { // In case display bounds change, screenshot buffer and surface may mismatch so set a // scaling mode. - SurfaceControl.Transaction t2 = new SurfaceControl.Transaction(); + SurfaceControl.Transaction t2 = mService.mTransactionFactory.make(); t2.setOverrideScalingMode(mSurfaceControl, Surface.SCALING_MODE_SCALE_TO_WINDOW); t2.apply(true /* sync */); // Capture a screenshot into the surface we just created. final int displayId = display.getDisplayId(); - final Surface surface = new Surface(); + final Surface surface = mService.mSurfaceFactory.make(); surface.copyFrom(mSurfaceControl); if (mService.mDisplayManagerInternal.screenshot(displayId, surface)) { t.setLayer(mSurfaceControl, SCREEN_FREEZE_LAYER_SCREENSHOT); diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 58cf73a9a2bd..dc8c7b79feef 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -245,17 +245,13 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { } @Override - public boolean performHapticFeedback(IWindow window, int effectId, - boolean always) { - synchronized (mService.mGlobalLock) { - long ident = Binder.clearCallingIdentity(); - try { - return mService.mPolicy.performHapticFeedbackLw( - mService.windowForClientLocked(this, window, true), + public boolean performHapticFeedback(int effectId, boolean always) { + long ident = Binder.clearCallingIdentity(); + try { + return mService.mPolicy.performHapticFeedback(mUid, mPackageName, effectId, always, null); - } finally { - Binder.restoreCallingIdentity(ident); - } + } finally { + Binder.restoreCallingIdentity(ident); } } diff --git a/core/java/android/service/contentcapture/ContentCaptureEventsRequest.aidl b/services/core/java/com/android/server/wm/SurfaceFactory.java index c032cfdb9677..076b7df63c46 100644 --- a/core/java/android/service/contentcapture/ContentCaptureEventsRequest.aidl +++ b/services/core/java/com/android/server/wm/SurfaceFactory.java @@ -1,11 +1,11 @@ -/** - * Copyright (c) 2018, The Android Open Source Project +/* + * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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, @@ -14,6 +14,14 @@ * limitations under the License. */ -package android.service.contentcapture; +package com.android.server.wm; + +import android.view.Surface; + +/** + * Helper class to inject custom {@link Surface} objects into window manager. + */ +interface SurfaceFactory { + Surface make(); +}; -parcelable ContentCaptureEventsRequest; diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 888d74163163..499cbaf915a1 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -330,7 +330,7 @@ class Task extends WindowContainer<AppWindowToken> implements ConfigurationConta // No one in higher hierarchy handles this request, let's adjust our bounds to fulfill // it if possible. // TODO: Move to TaskRecord after unification is done. - if (mTaskRecord != null) { + if (mTaskRecord != null && mTaskRecord.getParent() != null) { mTaskRecord.onConfigurationChanged(mTaskRecord.getParent().getConfiguration()); return true; } diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java index bb3df02146d7..42d25833000d 100644 --- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java +++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java @@ -16,9 +16,11 @@ package com.android.server.wm; +import android.app.ActivityManager; +import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityManager.TaskSnapshot; import android.app.ITaskStackListener; -import android.app.ActivityManager.TaskDescription; +import android.app.TaskInfo; import android.content.ComponentName; import android.os.Binder; import android.os.Handler; @@ -48,6 +50,7 @@ class TaskChangeNotificationController { private static final int NOTIFY_PINNED_STACK_ANIMATION_STARTED_LISTENERS_MSG = 16; private static final int NOTIFY_ACTIVITY_UNPINNED_LISTENERS_MSG = 17; private static final int NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG = 18; + private static final int NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED_MSG = 19; // Delay in notifying task stack change listeners (in millis) private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100; @@ -80,11 +83,11 @@ class TaskChangeNotificationController { }; private final TaskStackConsumer mNotifyTaskMovedToFront = (l, m) -> { - l.onTaskMovedToFront(m.arg1); + l.onTaskMovedToFront((RunningTaskInfo) m.obj); }; private final TaskStackConsumer mNotifyTaskDescriptionChanged = (l, m) -> { - l.onTaskDescriptionChanged(m.arg1, (TaskDescription) m.obj); + l.onTaskDescriptionChanged((RunningTaskInfo) m.obj); }; private final TaskStackConsumer mNotifyActivityRequestedOrientationChanged = (l, m) -> { @@ -92,7 +95,7 @@ class TaskChangeNotificationController { }; private final TaskStackConsumer mNotifyTaskRemovalStarted = (l, m) -> { - l.onTaskRemovalStarted(m.arg1); + l.onTaskRemovalStarted((RunningTaskInfo) m.obj); }; private final TaskStackConsumer mNotifyActivityPinned = (l, m) -> { @@ -125,7 +128,11 @@ class TaskChangeNotificationController { }; private final TaskStackConsumer mNotifyActivityLaunchOnSecondaryDisplayFailed = (l, m) -> { - l.onActivityLaunchOnSecondaryDisplayFailed(); + l.onActivityLaunchOnSecondaryDisplayFailed((RunningTaskInfo) m.obj, m.arg1); + }; + + private final TaskStackConsumer mNotifyActivityLaunchOnSecondaryDisplayRerouted = (l, m) -> { + l.onActivityLaunchOnSecondaryDisplayRerouted((RunningTaskInfo) m.obj, m.arg1); }; private final TaskStackConsumer mNotifyTaskProfileLocked = (l, m) -> { @@ -200,6 +207,9 @@ class TaskChangeNotificationController { case NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG: forAllRemoteListeners(mNotifyActivityLaunchOnSecondaryDisplayFailed, msg); break; + case NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED_MSG: + forAllRemoteListeners(mNotifyActivityLaunchOnSecondaryDisplayRerouted, msg); + break; case NOTIFY_TASK_PROFILE_LOCKED_LISTENERS_MSG: forAllRemoteListeners(mNotifyTaskProfileLocked, msg); break; @@ -344,14 +354,24 @@ class TaskChangeNotificationController { msg.sendToTarget(); } - void notifyActivityLaunchOnSecondaryDisplayFailed() { + void notifyActivityLaunchOnSecondaryDisplayFailed(TaskInfo ti, int requestedDisplayId) { mHandler.removeMessages(NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG); final Message msg = mHandler.obtainMessage( - NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG); + NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_FAILED_MSG, requestedDisplayId, + 0 /* unused */, ti); forAllLocalListeners(mNotifyActivityLaunchOnSecondaryDisplayFailed, msg); msg.sendToTarget(); } + void notifyActivityLaunchOnSecondaryDisplayRerouted(TaskInfo ti, int requestedDisplayId) { + mHandler.removeMessages(NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED_MSG); + final Message msg = mHandler.obtainMessage( + NOTIFY_ACTIVITY_LAUNCH_ON_SECONDARY_DISPLAY_REROUTED_MSG, requestedDisplayId, + 0 /* unused */, ti); + forAllLocalListeners(mNotifyActivityLaunchOnSecondaryDisplayRerouted, msg); + msg.sendToTarget(); + } + void notifyTaskCreated(int taskId, ComponentName componentName) { final Message msg = mHandler.obtainMessage(NOTIFY_TASK_ADDED_LISTENERS_MSG, taskId, 0 /* unused */, componentName); @@ -366,16 +386,15 @@ class TaskChangeNotificationController { msg.sendToTarget(); } - void notifyTaskMovedToFront(int taskId) { - final Message msg = mHandler.obtainMessage(NOTIFY_TASK_MOVED_TO_FRONT_LISTENERS_MSG, - taskId, 0 /* unused */); + void notifyTaskMovedToFront(TaskInfo ti) { + final Message msg = mHandler.obtainMessage(NOTIFY_TASK_MOVED_TO_FRONT_LISTENERS_MSG, ti); forAllLocalListeners(mNotifyTaskMovedToFront, msg); msg.sendToTarget(); } - void notifyTaskDescriptionChanged(int taskId, TaskDescription taskDescription) { + void notifyTaskDescriptionChanged(TaskInfo taskInfo) { final Message msg = mHandler.obtainMessage(NOTIFY_TASK_DESCRIPTION_CHANGED_LISTENERS_MSG, - taskId, 0 /* unused */, taskDescription); + taskInfo); forAllLocalListeners(mNotifyTaskDescriptionChanged, msg); msg.sendToTarget(); @@ -393,12 +412,10 @@ class TaskChangeNotificationController { * the window manager. This allows interested parties to perform relevant animations before * the window disappears. */ - void notifyTaskRemovalStarted(int taskId) { - final Message msg = mHandler.obtainMessage(NOTIFY_TASK_REMOVAL_STARTED_LISTENERS, taskId, - 0 /* unused */); + void notifyTaskRemovalStarted(ActivityManager.RunningTaskInfo taskInfo) { + final Message msg = mHandler.obtainMessage(NOTIFY_TASK_REMOVAL_STARTED_LISTENERS, taskInfo); forAllLocalListeners(mNotifyTaskRemovalStarted, msg); msg.sendToTarget(); - } /** diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java index f3050a903c70..904c50348b2c 100644 --- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java +++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java @@ -235,8 +235,7 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { // STEP 2.3: Adjust launch parameters as needed for freeform display. We enforce the policy // that legacy (pre-D) apps and those apps that can't handle multiple screen density well // are forced to be maximized. The rest of this step is to define the default policy when - // there is no initial bounds or a fully resolved current params from callers. Right now we - // launch all possible tasks/activities that can handle freeform into freeform mode. + // there is no initial bounds or a fully resolved current params from callers. if (display.inFreeformWindowingMode()) { if (launchMode == WINDOWING_MODE_PINNED) { if (DEBUG) appendLog("picture-in-picture"); @@ -247,17 +246,6 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { launchMode = WINDOWING_MODE_FULLSCREEN; outParams.mBounds.setEmpty(); if (DEBUG) appendLog("forced-maximize"); - } else if (fullyResolvedCurrentParam) { - // Don't adjust launch mode if that's inherited, except when we're launching an - // activity that should be forced to maximize. - if (DEBUG) appendLog("skip-adjustment-fully-resolved-params"); - } else if (launchMode != WINDOWING_MODE_FREEFORM - && (isNOrGreater(root) || isPreNResizeable(root))) { - // We're launching a pre-N and post-D activity that supports resizing, or a post-N - // activity. They can handle freeform nicely so launch them in freeform. - // Use undefined because we know we're in a freeform display. - launchMode = WINDOWING_MODE_UNDEFINED; - if (DEBUG) appendLog("should-be-freeform"); } } else { if (DEBUG) appendLog("non-freeform-display"); @@ -441,10 +429,6 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { return !root.isResizeable(); } - private boolean isNOrGreater(@NonNull ActivityRecord root) { - return root.appInfo.targetSdkVersion >= Build.VERSION_CODES.N; - } - /** * Resolves activity requested orientation to 4 categories: * 1) {@link ActivityInfo#SCREEN_ORIENTATION_LOCKED} indicating app wants to lock down @@ -485,10 +469,6 @@ class TaskLaunchParamsModifier implements LaunchParamsModifier { return orientation; } - private boolean isPreNResizeable(ActivityRecord root) { - return root.appInfo.targetSdkVersion < Build.VERSION_CODES.N && root.isResizeable(); - } - private void cascadeBounds(@NonNull Rect srcBounds, @NonNull ActivityDisplay display, @NonNull Rect outBounds) { outBounds.set(srcBounds); diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java index 916316549f39..cdcb85720724 100644 --- a/services/core/java/com/android/server/wm/TaskPositioningController.java +++ b/services/core/java/com/android/server/wm/TaskPositioningController.java @@ -83,7 +83,7 @@ class TaskPositioningController { final DisplayContent dc = mService.mRoot.getDisplayContent(displayId); if (mInputSurface == null) { mInputSurface = mService.makeSurfaceBuilder(dc.getSession()) - .setContainerLayer(true) + .setContainerLayer() .setName("Drag and Drop Input Consumer").build(); } diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java index 9d3112fec0f2..1392762fca66 100644 --- a/services/core/java/com/android/server/wm/TaskRecord.java +++ b/services/core/java/com/android/server/wm/TaskRecord.java @@ -341,6 +341,9 @@ class TaskRecord extends ConfigurationContainer { // TODO: remove after unification Task mTask; + /** Used by fillTaskInfo */ + final TaskActivitiesReport mReuseActivitiesReport = new TaskActivitiesReport(); + /** * Don't use constructor directly. Use {@link #create(ActivityTaskManagerService, int, * ActivityInfo, Intent, TaskDescription)} instead. @@ -2319,26 +2322,24 @@ class TaskRecord extends ConfigurationContainer { /** * Fills in a {@link TaskInfo} with information from this task. * @param info the {@link TaskInfo} to fill in - * @param reuseActivitiesReport a temporary activities report that we can reuse to fetch the - * running activities */ - void fillTaskInfo(TaskInfo info, TaskActivitiesReport reuseActivitiesReport) { - getNumRunningActivities(reuseActivitiesReport); + void fillTaskInfo(TaskInfo info) { + getNumRunningActivities(mReuseActivitiesReport); info.userId = userId; info.stackId = getStackId(); info.taskId = taskId; info.displayId = mStack == null ? Display.INVALID_DISPLAY : mStack.mDisplayId; info.isRunning = getTopActivity() != null; info.baseIntent = new Intent(getBaseIntent()); - info.baseActivity = reuseActivitiesReport.base != null - ? reuseActivitiesReport.base.intent.getComponent() + info.baseActivity = mReuseActivitiesReport.base != null + ? mReuseActivitiesReport.base.intent.getComponent() : null; - info.topActivity = reuseActivitiesReport.top != null - ? reuseActivitiesReport.top.mActivityComponent + info.topActivity = mReuseActivitiesReport.top != null + ? mReuseActivitiesReport.top.mActivityComponent : null; info.origActivity = origActivity; info.realActivity = realActivity; - info.numActivities = reuseActivitiesReport.numActivities; + info.numActivities = mReuseActivitiesReport.numActivities; info.lastActiveTime = lastActiveTime; info.taskDescription = new ActivityManager.TaskDescription(lastTaskDescription); info.supportsSplitScreenMultiWindow = supportsSplitScreenWindowingMode(); @@ -2346,6 +2347,15 @@ class TaskRecord extends ConfigurationContainer { info.configuration.setTo(getConfiguration()); } + /** + * Returns a {@link TaskInfo} with information from this task. + */ + ActivityManager.RunningTaskInfo getTaskInfo() { + ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo(); + fillTaskInfo(info); + return info; + } + void dump(PrintWriter pw, String prefix) { pw.print(prefix); pw.print("userId="); pw.print(userId); pw.print(" effectiveUid="); UserHandle.formatUid(pw, effectiveUid); diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java index 2d3e3aee4b7f..938c8b452e29 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java @@ -61,12 +61,12 @@ import android.util.MergedConfiguration; import android.util.Slog; import android.view.DisplayCutout; import android.view.IWindowSession; +import android.view.InsetsState; import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceSession; import android.view.View; import android.view.ViewGroup.LayoutParams; -import android.view.InsetsState; import android.view.WindowManager; import android.view.WindowManagerGlobal; diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index b91519903eb8..241f14e371bc 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -71,6 +71,7 @@ import android.view.DisplayCutout; import android.view.DisplayInfo; import android.view.SurfaceControl; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.policy.DividerSnapAlgorithm; import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget; import com.android.internal.policy.DockedDividerUtils; @@ -787,6 +788,14 @@ public class TaskStack extends WindowContainer<Task> implements return 0; } + @Override + void getRelativeDisplayedPosition(Point outPos) { + super.getRelativeDisplayedPosition(outPos); + final int outset = getStackOutset(); + outPos.x -= outset; + outPos.y -= outset; + } + private void updateSurfaceSize(SurfaceControl.Transaction transaction) { if (mSurfaceControl == null) { return; @@ -807,6 +816,11 @@ public class TaskStack extends WindowContainer<Task> implements mLastSurfaceSize.set(width, height); } + @VisibleForTesting + Point getLastSurfaceSize() { + return mLastSurfaceSize; + } + @Override void onDisplayChanged(DisplayContent dc) { if (mDisplayContent != null && mDisplayContent != dc) { @@ -818,7 +832,7 @@ public class TaskStack extends WindowContainer<Task> implements updateSurfaceBounds(); if (mAnimationBackgroundSurface == null) { - mAnimationBackgroundSurface = makeChildSurface(null).setColorLayer(true) + mAnimationBackgroundSurface = makeChildSurface(null).setColorLayer() .setName("animation background stackId=" + mStackId) .build(); } diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index 74fb3fa85f24..dddc6b755db0 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -50,6 +50,7 @@ import android.view.SurfaceControl; import android.view.WindowManager; import android.view.animation.Animation; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ToBooleanFunction; import java.io.PrintWriter; @@ -700,24 +701,37 @@ class WallpaperController { mWallpaperTokens.remove(token); } - /** - * Take a screenshot of the wallpaper if it's visible. - * - * @return Bitmap of the wallpaper - */ - Bitmap screenshotWallpaperLocked() { + + @VisibleForTesting + boolean canScreenshotWallpaper() { + return canScreenshotWallpaper(getTopVisibleWallpaper()); + } + + private boolean canScreenshotWallpaper(WindowState wallpaperWindowState) { if (!mService.mPolicy.isScreenOn()) { if (DEBUG_SCREENSHOT) { Slog.i(TAG_WM, "Attempted to take screenshot while display was off."); } - return null; + return false; } - final WindowState wallpaperWindowState = getTopVisibleWallpaper(); if (wallpaperWindowState == null) { if (DEBUG_SCREENSHOT) { Slog.i(TAG_WM, "No visible wallpaper to screenshot"); } + return false; + } + return true; + } + + /** + * Take a screenshot of the wallpaper if it's visible. + * + * @return Bitmap of the wallpaper + */ + Bitmap screenshotWallpaperLocked() { + final WindowState wallpaperWindowState = getTopVisibleWallpaper(); + if (!canScreenshotWallpaper(wallpaperWindowState)) { return null; } diff --git a/services/core/java/com/android/server/wm/WindowAnimationSpec.java b/services/core/java/com/android/server/wm/WindowAnimationSpec.java index 98c77ac719f9..57311e19bc76 100644 --- a/services/core/java/com/android/server/wm/WindowAnimationSpec.java +++ b/services/core/java/com/android/server/wm/WindowAnimationSpec.java @@ -17,10 +17,10 @@ package com.android.server.wm; import static com.android.server.wm.AnimationAdapter.STATUS_BAR_TRANSITION_DURATION; -import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM; -import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE; import static com.android.server.wm.AnimationSpecProto.WINDOW; import static com.android.server.wm.WindowAnimationSpecProto.ANIMATION; +import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM; +import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE; import android.graphics.Point; import android.graphics.Rect; @@ -51,18 +51,22 @@ public class WindowAnimationSpec implements AnimationSpec { private final Rect mStackBounds = new Rect(); private int mStackClipMode; private final Rect mTmpRect = new Rect(); + private final float mWindowCornerRadius; - public WindowAnimationSpec(Animation animation, Point position, boolean canSkipFirstFrame) { + public WindowAnimationSpec(Animation animation, Point position, boolean canSkipFirstFrame, + float windowCornerRadius) { this(animation, position, null /* stackBounds */, canSkipFirstFrame, STACK_CLIP_NONE, - false /* isAppAnimation */); + false /* isAppAnimation */, windowCornerRadius); } public WindowAnimationSpec(Animation animation, Point position, Rect stackBounds, - boolean canSkipFirstFrame, int stackClipMode, boolean isAppAnimation) { + boolean canSkipFirstFrame, int stackClipMode, boolean isAppAnimation, + float windowCornerRadius) { mAnimation = animation; if (position != null) { mPosition.set(position.x, position.y); } + mWindowCornerRadius = windowCornerRadius; mCanSkipFirstFrame = canSkipFirstFrame; mIsAppAnimation = isAppAnimation; mStackClipMode = stackClipMode; @@ -101,6 +105,9 @@ public class WindowAnimationSpec implements AnimationSpec { mTmpRect.intersect(tmp.transformation.getClipRect()); t.setWindowCrop(leash, mTmpRect); } + if (mAnimation.hasRoundedCorners() && mWindowCornerRadius > 0) { + t.setCornerRadius(leash, mWindowCornerRadius); + } } @Override diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 76f080b233a8..05d47609de4c 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -1292,6 +1292,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< * Called when an animation has finished running. */ protected void onAnimationFinished() { + mWmService.onAnimationFinished(); } /** diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 39df0e42e27c..cbafb3f2f297 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -209,6 +209,7 @@ import android.view.IWindowSession; import android.view.IWindowSessionCallback; import android.view.InputChannel; import android.view.InputDevice; +import android.view.InputEvent; import android.view.InputEventReceiver; import android.view.InsetsState; import android.view.KeyEvent; @@ -233,6 +234,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.IResultReceiver; import com.android.internal.policy.IKeyguardDismissCallback; import com.android.internal.policy.IShortcutService; +import com.android.internal.policy.ScreenDecorationsUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.LatencyTracker; @@ -371,6 +373,8 @@ public class WindowManagerService extends IWindowManager.Stub private static final int TRANSITION_ANIMATION_SCALE = 1; private static final int ANIMATION_DURATION_SCALE = 2; + private static final int ANIMATION_COMPLETED_TIMEOUT_MS = 5000; + final WindowTracing mWindowTracing; final private KeyguardDisableHandler mKeyguardDisableHandler; @@ -750,24 +754,27 @@ public class WindowManagerService extends IWindowManager.Stub final DisplayManager mDisplayManager; final ActivityTaskManagerService mAtmService; - // Indicates whether this device supports wide color gamut / HDR rendering + /** Corner radius that windows should have in order to match the display. */ + final float mWindowCornerRadius; + + /** Indicates whether this device supports wide color gamut / HDR rendering */ private boolean mHasWideColorGamutSupport; private boolean mHasHdrSupport; - // Who is holding the screen on. + /** Who is holding the screen on. */ private Session mHoldingScreenOn; private PowerManager.WakeLock mHoldingScreenWakeLock; - // Whether or not a layout can cause a wake up when theater mode is enabled. + /** Whether or not a layout can cause a wake up when theater mode is enabled. */ boolean mAllowTheaterModeWakeFromLayout; final TaskPositioningController mTaskPositioningController; final DragDropController mDragDropController; - // For frozen screen animations. + /** For frozen screen animations. */ private int mExitAnimId, mEnterAnimId; - // The display that the rotation animation is applying to. + /** The display that the rotation animation is applying to. */ private int mFrozenDisplayId; /** Skip repeated AppWindowTokens initialization. Note that AppWindowsToken's version of this @@ -814,8 +821,9 @@ public class WindowManagerService extends IWindowManager.Stub SurfaceBuilderFactory mSurfaceBuilderFactory = SurfaceControl.Builder::new; TransactionFactory mTransactionFactory = SurfaceControl.Transaction::new; + SurfaceFactory mSurfaceFactory = Surface::new; - private final SurfaceControl.Transaction mTransaction = mTransactionFactory.make(); + private final SurfaceControl.Transaction mTransaction; static void boostPriorityForLockedSection() { sThreadPriorityBooster.boost(); @@ -909,9 +917,21 @@ public class WindowManagerService extends IWindowManager.Stub public static WindowManagerService main(final Context context, final InputManagerService im, final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy, ActivityTaskManagerService atm) { + return main(context, im, showBootMsgs, onlyCore, policy, atm, + SurfaceControl.Transaction::new); + } + + /** + * Creates and returns an instance of the WindowManagerService. This call allows the caller + * to override the {@link TransactionFactory} to stub functionality under test. + */ + @VisibleForTesting + public static WindowManagerService main(final Context context, final InputManagerService im, + final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy, + ActivityTaskManagerService atm, TransactionFactory transactionFactory) { DisplayThread.getHandler().runWithScissors(() -> sInstance = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy, - atm), 0); + atm, transactionFactory), 0); return sInstance; } @@ -933,7 +953,7 @@ public class WindowManagerService extends IWindowManager.Stub private WindowManagerService(Context context, InputManagerService inputManager, boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy, - ActivityTaskManagerService atm) { + ActivityTaskManagerService atm, TransactionFactory transactionFactory) { installLock(this, INDEX_WINDOW); mGlobalLock = atm.getGlobalLock(); mAtmService = atm; @@ -961,7 +981,10 @@ public class WindowManagerService extends IWindowManager.Stub mInputManager = inputManager; // Must be before createDisplayContentLocked. mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); mDisplayWindowSettings = new DisplayWindowSettings(this); + mWindowCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context.getResources()); + mTransactionFactory = transactionFactory; + mTransaction = mTransactionFactory.make(); mPolicy = policy; mAnimator = new WindowAnimator(this); mRoot = new RootWindowContainer(this); @@ -3503,14 +3526,15 @@ public class WindowManagerService extends IWindowManager.Stub } } - void setRotateForApp(int displayId, boolean enabled) { + void setRotateForApp(int displayId, + @DisplayRotation.FixedToUserRotation int fixedToUserRotation) { synchronized (mGlobalLock) { final DisplayContent display = mRoot.getDisplayContent(displayId); if (display == null) { Slog.w(TAG, "Trying to set rotate for app for a missing display."); return; } - display.getDisplayRotation().setFixedToUserRotation(enabled); + display.getDisplayRotation().setFixedToUserRotation(fixedToUserRotation); } } @@ -7403,4 +7427,41 @@ public class WindowManagerService extends IWindowManager.Stub } } } + + @Override + public boolean injectInputAfterTransactionsApplied(InputEvent ev, int mode) { + waitForAnimationsToComplete(); + + synchronized (mGlobalLock) { + mWindowPlacerLocked.performSurfacePlacementIfScheduled(); + } + + new SurfaceControl.Transaction().syncInputWindows().apply(true); + + return mInputManager.injectInputEvent(ev, mode); + } + + private void waitForAnimationsToComplete() { + synchronized (mGlobalLock) { + long timeoutRemaining = ANIMATION_COMPLETED_TIMEOUT_MS; + while (mRoot.isSelfOrChildAnimating() && timeoutRemaining > 0) { + long startTime = System.currentTimeMillis(); + try { + mGlobalLock.wait(timeoutRemaining); + } catch (InterruptedException e) { + } + timeoutRemaining -= (System.currentTimeMillis() - startTime); + } + + if (mRoot.isSelfOrChildAnimating()) { + Log.w(TAG, "Timed out waiting for animations to complete."); + } + } + } + + void onAnimationFinished() { + synchronized (mGlobalLock) { + mGlobalLock.notifyAll(); + } + } } diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java index d13ee459c115..7384bb7e1587 100644 --- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java +++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java @@ -342,21 +342,24 @@ public class WindowManagerShellCommand extends ShellCommand { arg = getNextArgRequired(); } - final boolean enabled; + final @DisplayRotation.FixedToUserRotation int fixedToUserRotation; switch (arg) { case "enabled": - enabled = true; + fixedToUserRotation = DisplayRotation.FIXED_TO_USER_ROTATION_ENABLED; break; case "disabled": - enabled = false; + fixedToUserRotation = DisplayRotation.FIXED_TO_USER_ROTATION_DISABLED; + break; + case "default": + fixedToUserRotation = DisplayRotation.FIXED_TO_USER_ROTATION_DISABLED; break; default: - getErrPrintWriter().println("Error: expecting enabled or disabled, but we get " - + arg); + getErrPrintWriter().println("Error: expecting enabled, disabled or default, but we " + + "get " + arg); return -1; } - mInternal.setRotateForApp(displayId, enabled); + mInternal.setRotateForApp(displayId, fixedToUserRotation); return 0; } diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 465f4131ee8e..dceed28d4c08 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -57,6 +57,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.HeavyWeightSwitcherActivity; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.Watchdog; +import com.android.server.wm.ActivityTaskManagerService.HotPath; import java.io.IOException; import java.io.PrintWriter; @@ -408,12 +409,14 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio return null; } + @HotPath(caller = HotPath.PROCESS_CHANGE) public void addPackage(String packageName) { synchronized (mAtm.mGlobalLockWithoutBoost) { mPkgList.add(packageName); } } + @HotPath(caller = HotPath.PROCESS_CHANGE) public void clearPackageList() { synchronized (mAtm.mGlobalLockWithoutBoost) { mPkgList.clear(); @@ -441,12 +444,14 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio mActivities.clear(); } + @HotPath(caller = HotPath.OOM_ADJUSTMENT) public boolean hasActivities() { synchronized (mAtm.mGlobalLockWithoutBoost) { return !mActivities.isEmpty(); } } + @HotPath(caller = HotPath.OOM_ADJUSTMENT) public boolean hasVisibleActivities() { synchronized (mAtm.mGlobalLockWithoutBoost) { for (int i = mActivities.size() - 1; i >= 0; --i) { @@ -459,6 +464,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio return false; } + @HotPath(caller = HotPath.LRU_UPDATE) public boolean hasActivitiesOrRecentTasks() { synchronized (mAtm.mGlobalLockWithoutBoost) { return !mActivities.isEmpty() || !mRecentTasks.isEmpty(); @@ -670,6 +676,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio void onOtherActivity(); } + @HotPath(caller = HotPath.OOM_ADJUSTMENT) public int computeOomAdjFromActivities(int minTaskLayer, ComputeOomAdjCallback callback) { synchronized (mAtm.mGlobalLockWithoutBoost) { final int activitiesSize = mActivities.size(); @@ -903,6 +910,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio mRecentTasks.remove(task); } + @HotPath(caller = HotPath.OOM_ADJUSTMENT) public boolean hasRecentTasks() { synchronized (mAtm.mGlobalLockWithoutBoost) { return !mRecentTasks.isEmpty(); @@ -966,18 +974,21 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio return false; } + @HotPath(caller = HotPath.OOM_ADJUSTMENT) public void onTopProcChanged() { synchronized (mAtm.mGlobalLockWithoutBoost) { mAtm.mVrController.onTopProcChangedLocked(this); } } + @HotPath(caller = HotPath.OOM_ADJUSTMENT) public boolean isHomeProcess() { synchronized (mAtm.mGlobalLockWithoutBoost) { return this == mAtm.mHomeProcess; } } + @HotPath(caller = HotPath.OOM_ADJUSTMENT) public boolean isPreviousProcess() { synchronized (mAtm.mGlobalLockWithoutBoost) { return this == mAtm.mPreviousProcess; diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 21a557e6809e..b7925f20be86 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -114,6 +114,7 @@ import static com.android.server.wm.WindowManagerService.localLOGV; import static com.android.server.wm.WindowStateAnimator.COMMIT_DRAW_PENDING; import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING; import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN; +import static com.android.server.wm.WindowStateAnimator.PRESERVED_SURFACE_LAYER; import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW; import static com.android.server.wm.WindowStateProto.ANIMATING_EXIT; import static com.android.server.wm.WindowStateProto.ANIMATOR; @@ -4372,7 +4373,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP Slog.d(TAG, "relayoutVisibleWindow: " + this + " mAnimatingExit=true, mRemoveOnExit=" + mRemoveOnExit + ", mDestroying=" + mDestroying); - mWinAnimator.cancelExitAnimationForNextAnimationLocked(); + // Cancel the existing exit animation for the next enter animation. + if (isSelfAnimating()) { + cancelAnimation(); + destroySurfaceUnchecked(); + } mAnimatingExit = false; } if (mDestroying) { @@ -4453,7 +4458,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP anim.restrictDuration(MAX_ANIMATION_DURATION); anim.scaleCurrentDuration(mWmService.getWindowAnimationScaleLocked()); final AnimationAdapter adapter = new LocalAnimationAdapter( - new WindowAnimationSpec(anim, mSurfacePosition, false /* canSkipFirstFrame */), + new WindowAnimationSpec(anim, mSurfacePosition, false /* canSkipFirstFrame */, + mWmService.mWindowCornerRadius), mWmService.mSurfaceAnimationRunner); startAnimation(mPendingTransaction, adapter); commitPendingTransaction(); @@ -4484,6 +4490,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP @Override protected void onAnimationFinished() { + super.onAnimationFinished(); mWinAnimator.onAnimationFinished(); } @@ -4777,7 +4784,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // then we can drop all negative layering on the windowing side and simply inherit // the default implementation here. public void assignChildLayers(Transaction t) { - int layer = 1; + // The surface of the main window might be preserved. So the child window on top of the main + // window should be also on top of the preserved surface. + int layer = PRESERVED_SURFACE_LAYER + 1; for (int i = 0; i < mChildren.size(); i++) { final WindowState w = mChildren.get(i); diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 6b4d6d2bfb3c..969ced43b942 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -75,6 +75,7 @@ import java.io.PrintWriter; class WindowStateAnimator { static final String TAG = TAG_WITH_CLASS_NAME ? "WindowStateAnimator" : TAG_WM; static final int WINDOW_FREEZE_LAYER = TYPE_LAYER_MULTIPLIER * 200; + static final int PRESERVED_SURFACE_LAYER = 1; /** * Mode how the window gets clipped by the stack bounds during an animation: The clipping should @@ -247,14 +248,6 @@ class WindowStateAnimator { mWallpaperControllerLocked = win.getDisplayContent().mWallpaperController; } - void cancelExitAnimationForNextAnimationLocked() { - if (DEBUG_ANIM) Slog.d(TAG, - "cancelExitAnimationForNextAnimationLocked: " + mWin); - - mWin.cancelAnimation(); - mWin.destroySurfaceUnchecked(); - } - void onAnimationFinished() { // Done animating, clean up. if (DEBUG_ANIM) Slog.v( @@ -373,8 +366,8 @@ class WindowStateAnimator { if (mSurfaceController != null) { // Our SurfaceControl is always at layer 0 within the parent Surface managed by // window-state. We want this old Surface to stay on top of the new one - // until we do the swap, so we place it at layer 1. - mSurfaceController.mSurfaceControl.setLayer(1); + // until we do the swap, so we place it at a positive layer. + mSurfaceController.mSurfaceControl.setLayer(PRESERVED_SURFACE_LAYER); } mDestroyPreservedSurfaceUponRedraw = true; mSurfaceDestroyDeferred = true; diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java index 2ee58fe3f574..cc7917879634 100644 --- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java +++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java @@ -85,6 +85,12 @@ class WindowSurfacePlacer { return mDeferDepth > 0; } + void performSurfacePlacementIfScheduled() { + if (mTraversalScheduled) { + performSurfacePlacement(); + } + } + final void performSurfacePlacement() { performSurfacePlacement(false /* force */); } diff --git a/services/core/java/com/android/server/wm/WindowTraceBuffer.java b/services/core/java/com/android/server/wm/WindowTraceBuffer.java index 2ce6e6c1d049..a4ee9077c150 100644 --- a/services/core/java/com/android/server/wm/WindowTraceBuffer.java +++ b/services/core/java/com/android/server/wm/WindowTraceBuffer.java @@ -98,9 +98,8 @@ class WindowTraceBuffer { ProtoOutputStream proto = new ProtoOutputStream(); proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE); os.write(proto.getBytes()); - while (!mBuffer.isEmpty()) { - proto = mBuffer.poll(); - mBufferUsedSize -= proto.getRawSize(); + for (ProtoOutputStream protoOutputStream : mBuffer) { + proto = protoOutputStream; byte[] protoBytes = proto.getBytes(); os.write(protoBytes); } diff --git a/services/core/java/com/android/server/wm/WindowTracing.java b/services/core/java/com/android/server/wm/WindowTracing.java index 3b17abc8732f..48b9340c4857 100644 --- a/services/core/java/com/android/server/wm/WindowTracing.java +++ b/services/core/java/com/android/server/wm/WindowTracing.java @@ -208,6 +208,7 @@ class WindowTracing { pw.println(" frame: Log trace once per frame"); pw.println(" transaction: Log each transaction"); pw.println(" size: Set the maximum log size (in KB)"); + pw.println(" status: Print trace status"); pw.println(" level [lvl]: Set the log level between"); pw.println(" lvl may be one of:"); pw.println(" critical: Only visible windows with reduced information"); diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp index d178c3abc906..d39f20c0f214 100644 --- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp +++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp @@ -36,13 +36,14 @@ #include "android_runtime/Log.h" #include <arpa/inet.h> +#include <cinttypes> +#include <iomanip> #include <limits> #include <linux/in.h> #include <linux/in6.h> #include <pthread.h> #include <string.h> -#include <cinttypes> -#include <iomanip> +#include <utils/SystemClock.h> static jobject mCallbacksObj = nullptr; @@ -111,10 +112,9 @@ using android::hardware::Void; using android::hardware::hidl_vec; using android::hardware::hidl_string; using android::hardware::hidl_death_recipient; + using android::hardware::gnss::V1_0::GnssConstellationType; -using android::hardware::gnss::V1_0::GnssLocation; using android::hardware::gnss::V1_0::GnssLocationFlags; - using android::hardware::gnss::V1_0::IAGnssRilCallback; using android::hardware::gnss::V1_0::IGnssBatching; using android::hardware::gnss::V1_0::IGnssBatchingCallback; @@ -128,13 +128,17 @@ using android::hardware::gnss::V1_0::IGnssNiCallback; using android::hardware::gnss::V1_0::IGnssXtra; using android::hardware::gnss::V1_0::IGnssXtraCallback; +using android::hardware::gnss::V2_0::ElapsedRealtimeFlags; using android::hardware::gnss::V2_0::IGnssCallback; + using android::hardware::gnss::measurement_corrections::V1_0::MeasurementCorrections; using android::hardware::gnss::measurement_corrections::V1_0::SingleSatCorrection; using android::hardware::gnss::measurement_corrections::V1_0::ReflectingPlane; using android::hidl::base::V1_0::IBase; +using GnssLocation_V1_0 = android::hardware::gnss::V1_0::GnssLocation; +using GnssLocation_V2_0 = android::hardware::gnss::V2_0::GnssLocation; using IGnss_V1_0 = android::hardware::gnss::V1_0::IGnss; using IGnss_V1_1 = android::hardware::gnss::V1_1::IGnss; using IGnss_V2_0 = android::hardware::gnss::V2_0::IGnss; @@ -204,6 +208,20 @@ sp<IGnssVisibilityControl> gnssVisibilityControlIface = nullptr; namespace android { +namespace { + +// Returns true if location has lat/long information. +bool hasLatLong(const GnssLocation_V1_0& location) { + return (static_cast<uint32_t>(location.gnssLocationFlags) & + GnssLocationFlags::HAS_LAT_LONG) != 0; +} + +// Returns true if location has lat/long information. +bool hasLatLong(const GnssLocation_V2_0& location) { + return hasLatLong(location.v1_0); +} + +} // namespace template<class T> class JavaMethodHelper { public: @@ -216,7 +234,7 @@ class JavaMethodHelper { T value); private: - static const char *const signature_; + static const char* const signature_; }; template<class T> @@ -234,6 +252,8 @@ class JavaObject { public: JavaObject(JNIEnv* env, const char* class_name); JavaObject(JNIEnv* env, const char* class_name, const char * sz_arg_1); + JavaObject(JNIEnv* env, const char* class_name, jobject object); + virtual ~JavaObject(); template<class T> @@ -260,6 +280,11 @@ JavaObject::JavaObject(JNIEnv* env, const char* class_name, const char * sz_arg_ object_ = env_->NewObject(clazz_, ctor, env->NewStringUTF(sz_arg_1)); } +JavaObject::JavaObject(JNIEnv* env, const char* class_name, jobject object) + : env_(env), object_(object) { + clazz_ = env_->FindClass(class_name); +} + JavaObject::~JavaObject() { env_->DeleteLocalRef(clazz_); } @@ -303,6 +328,8 @@ const char *const JavaMethodHelper<uint32_t>::signature_ = "(I)V"; template<> const char *const JavaMethodHelper<int64_t>::signature_ = "(J)V"; template<> +const char *const JavaMethodHelper<uint64_t>::signature_ = "(J)V"; +template<> const char *const JavaMethodHelper<float>::signature_ = "(F)V"; template<> const char *const JavaMethodHelper<double>::signature_ = "(D)V"; @@ -416,7 +443,8 @@ static JNIEnv* getJniEnv() { return env; } -static jobject translateLocation(JNIEnv* env, const GnssLocation& location) { +static jobject translateGnssLocation(JNIEnv* env, + const GnssLocation_V1_0& location) { JavaObject object(env, "android/location/Location", "gps"); uint16_t flags = static_cast<uint32_t>(location.gnssLocationFlags); @@ -446,23 +474,33 @@ static jobject translateLocation(JNIEnv* env, const GnssLocation& location) { SET(BearingAccuracyDegrees, location.bearingAccuracyDegrees); } SET(Time, location.timestamp); + SET(ElapsedRealtimeNanos, android::elapsedRealtimeNano()); return object.get(); } -static GnssLocation createGnssLocation( - jint gnssLocationFlags, - jdouble latitudeDegrees, - jdouble longitudeDegrees, - jdouble altitudeMeters, - jfloat speedMetersPerSec, - jfloat bearingDegrees, - jfloat horizontalAccuracyMeters, - jfloat verticalAccuracyMeters, - jfloat speedAccuracyMetersPerSecond, - jfloat bearingAccuracyDegrees, +static jobject translateGnssLocation(JNIEnv* env, + const GnssLocation_V2_0& location) { + JavaObject object(env, "android/location/Location", + translateGnssLocation(env, location.v1_0)); + + const uint16_t flags = static_cast<uint16_t>(location.elapsedRealtime.flags); + + // Overwrite ElapsedRealtimeNanos when available from HAL. + if (flags & ElapsedRealtimeFlags::HAS_TIMESTAMP_NS) { + SET(ElapsedRealtimeNanos, location.elapsedRealtime.timestampNs); + } + + return object.get(); +} + +static GnssLocation_V1_0 createGnssLocation_V1_0( + jint gnssLocationFlags, jdouble latitudeDegrees, jdouble longitudeDegrees, + jdouble altitudeMeters, jfloat speedMetersPerSec, jfloat bearingDegrees, + jfloat horizontalAccuracyMeters, jfloat verticalAccuracyMeters, + jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees, jlong timestamp) { - GnssLocation location; + GnssLocation_V1_0 location; location.gnssLocationFlags = static_cast<uint16_t>(gnssLocationFlags); location.latitudeDegrees = static_cast<double>(latitudeDegrees); location.longitudeDegrees = static_cast<double>(longitudeDegrees); @@ -478,11 +516,30 @@ static GnssLocation createGnssLocation( return location; } +static GnssLocation_V2_0 createGnssLocation_V2_0( + jint gnssLocationFlags, jdouble latitudeDegrees, jdouble longitudeDegrees, + jdouble altitudeMeters, jfloat speedMetersPerSec, jfloat bearingDegrees, + jfloat horizontalAccuracyMeters, jfloat verticalAccuracyMeters, + jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees, + jlong timestamp, jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos) { + GnssLocation_V2_0 location; + location.v1_0 = createGnssLocation_V1_0( + gnssLocationFlags, latitudeDegrees, longitudeDegrees, altitudeMeters, + speedMetersPerSec, bearingDegrees, horizontalAccuracyMeters, + verticalAccuracyMeters, speedAccuracyMetersPerSecond, + bearingAccuracyDegrees, timestamp); + + location.elapsedRealtime.flags = static_cast<uint16_t>(elapsedRealtimeFlags); + location.elapsedRealtime.timestampNs = static_cast<uint64_t>(elapsedRealtimeNanos); + + return location; +} + /* * GnssCallback class implements the callback methods for IGnss interface. */ struct GnssCallback : public IGnssCallback { - Return<void> gnssLocationCb(const GnssLocation& location) override; + Return<void> gnssLocationCb(const GnssLocation_V1_0& location) override; Return<void> gnssStatusCb(const IGnssCallback::GnssStatusValue status) override; Return<void> gnssSvStatusCb(const IGnssCallback::GnssSvStatus& svStatus) override; Return<void> gnssNmeaCb(int64_t timestamp, const android::hardware::hidl_string& nmea) override; @@ -491,12 +548,21 @@ struct GnssCallback : public IGnssCallback { Return<void> gnssReleaseWakelockCb() override; Return<void> gnssRequestTimeCb() override; Return<void> gnssRequestLocationCb(const bool independentFromGnss) override; + Return<void> gnssSetSystemInfoCb(const IGnssCallback::GnssSystemInfo& info) override; // New in 1.1 Return<void> gnssNameCb(const android::hardware::hidl_string& name) override; + // New in 2.0 + Return<void> gnssRequestLocationCb_2_0(const bool independentFromGnss, const bool isUserEmergency) + override; Return<void> gnssSetCapabilitiesCb_2_0(uint32_t capabilities) override; + Return<void> gnssLocationCb_2_0(const GnssLocation_V2_0& location) override; + + // Templated implementation for gnnsLocationCb and gnnsLocationCb_2_0. + template <class T> + Return<void> gnssLocationCbImpl(const T& location); // TODO(b/73306084): Reconsider allocation cost vs threadsafety on these statics static const char* sNmeaString; @@ -517,22 +583,30 @@ Return<void> GnssCallback::gnssNameCb(const android::hardware::hidl_string& name const char* GnssCallback::sNmeaString = nullptr; size_t GnssCallback::sNmeaStringLength = 0; -Return<void> GnssCallback::gnssLocationCb(const GnssLocation& location) { +template<class T> +Return<void> GnssCallback::gnssLocationCbImpl(const T& location) { JNIEnv* env = getJniEnv(); - jobject jLocation = translateLocation(env, location); - bool hasLatLong = (static_cast<uint32_t>(location.gnssLocationFlags) & - GnssLocationFlags::HAS_LAT_LONG) != 0; + jobject jLocation = translateGnssLocation(env, location); env->CallVoidMethod(mCallbacksObj, method_reportLocation, - boolToJbool(hasLatLong), + boolToJbool(hasLatLong(location)), jLocation); checkAndClearExceptionFromCallback(env, __FUNCTION__); env->DeleteLocalRef(jLocation); return Void(); } +Return<void> GnssCallback::gnssLocationCb(const GnssLocation_V1_0& location) { + return gnssLocationCbImpl<GnssLocation_V1_0>(location); +} + +Return<void> +GnssCallback::gnssLocationCb_2_0(const GnssLocation_V2_0& location) { + return gnssLocationCbImpl<GnssLocation_V2_0>(location); +} + Return<void> GnssCallback::gnssStatusCb(const IGnssCallback::GnssStatusValue status) { JNIEnv* env = getJniEnv(); env->CallVoidMethod(mCallbacksObj, method_reportStatus, status); @@ -642,8 +716,15 @@ Return<void> GnssCallback::gnssRequestTimeCb() { } Return<void> GnssCallback::gnssRequestLocationCb(const bool independentFromGnss) { + return GnssCallback::gnssRequestLocationCb_2_0(independentFromGnss, /* isUserEmergency= */ + false); +} + +Return<void> GnssCallback::gnssRequestLocationCb_2_0(const bool independentFromGnss, const bool + isUserEmergency) { JNIEnv* env = getJniEnv(); - env->CallVoidMethod(mCallbacksObj, method_requestLocation, boolToJbool(independentFromGnss)); + env->CallVoidMethod(mCallbacksObj, method_requestLocation, boolToJbool(independentFromGnss), + boolToJbool(isUserEmergency)); checkAndClearExceptionFromCallback(env, __FUNCTION__); return Void(); } @@ -681,12 +762,13 @@ struct GnssGeofenceCallback : public IGnssGeofenceCallback { // Methods from ::android::hardware::gps::V1_0::IGnssGeofenceCallback follow. Return<void> gnssGeofenceTransitionCb( int32_t geofenceId, - const GnssLocation& location, + const GnssLocation_V1_0& location, GeofenceTransition transition, hardware::gnss::V1_0::GnssUtcTime timestamp) override; - Return<void> gnssGeofenceStatusCb( + Return<void> + gnssGeofenceStatusCb( GeofenceAvailability status, - const GnssLocation& location) override; + const GnssLocation_V1_0& location) override; Return<void> gnssGeofenceAddCb(int32_t geofenceId, GeofenceStatus status) override; Return<void> gnssGeofenceRemoveCb(int32_t geofenceId, @@ -698,13 +780,12 @@ struct GnssGeofenceCallback : public IGnssGeofenceCallback { }; Return<void> GnssGeofenceCallback::gnssGeofenceTransitionCb( - int32_t geofenceId, - const GnssLocation& location, + int32_t geofenceId, const GnssLocation_V1_0& location, GeofenceTransition transition, hardware::gnss::V1_0::GnssUtcTime timestamp) { JNIEnv* env = getJniEnv(); - jobject jLocation = translateLocation(env, location); + jobject jLocation = translateGnssLocation(env, location); env->CallVoidMethod(mCallbacksObj, method_reportGeofenceTransition, @@ -718,16 +799,14 @@ Return<void> GnssGeofenceCallback::gnssGeofenceTransitionCb( return Void(); } -Return<void> GnssGeofenceCallback::gnssGeofenceStatusCb( - GeofenceAvailability status, - const GnssLocation& location) { +Return<void> +GnssGeofenceCallback::gnssGeofenceStatusCb(GeofenceAvailability status, + const GnssLocation_V1_0& location) { JNIEnv* env = getJniEnv(); - jobject jLocation = translateLocation(env, location); + jobject jLocation = translateGnssLocation(env, location); - env->CallVoidMethod(mCallbacksObj, - method_reportGeofenceStatus, - status, + env->CallVoidMethod(mCallbacksObj, method_reportGeofenceStatus, status, jLocation); checkAndClearExceptionFromCallback(env, __FUNCTION__); env->DeleteLocalRef(jLocation); @@ -1296,18 +1375,18 @@ struct GnssBatchingCallback : public IGnssBatchingCallback { * Methods from ::android::hardware::gps::V1_0::IGnssBatchingCallback * follow. */ - Return<void> gnssLocationBatchCb(const hidl_vec<GnssLocation> & locations) - override; + Return<void> gnssLocationBatchCb(const hidl_vec<GnssLocation_V1_0>& locations) override; }; -Return<void> GnssBatchingCallback::gnssLocationBatchCb(const hidl_vec<GnssLocation> & locations) { +Return<void> GnssBatchingCallback::gnssLocationBatchCb( + const hidl_vec<GnssLocation_V1_0>& locations) { JNIEnv* env = getJniEnv(); jobjectArray jLocations = env->NewObjectArray(locations.size(), env->FindClass("android/location/Location"), nullptr); for (uint16_t i = 0; i < locations.size(); ++i) { - jobject jLocation = translateLocation(env, locations[i]); + jobject jLocation = translateGnssLocation(env, locations[i]); env->SetObjectArrayElement(jLocations, i, jLocation); env->DeleteLocalRef(jLocation); } @@ -1353,7 +1432,7 @@ static void android_location_GnssLocationProvider_init_once(JNIEnv* env, jclass method_xtraDownloadRequest = env->GetMethodID(clazz, "xtraDownloadRequest", "()V"); method_reportNiNotification = env->GetMethodID(clazz, "reportNiNotification", "(IIIIILjava/lang/String;Ljava/lang/String;II)V"); - method_requestLocation = env->GetMethodID(clazz, "requestLocation", "(Z)V"); + method_requestLocation = env->GetMethodID(clazz, "requestLocation", "(ZZ)V"); method_requestRefLocation = env->GetMethodID(clazz, "requestRefLocation", "()V"); method_requestSetID = env->GetMethodID(clazz, "requestSetID", "(I)V"); method_requestUtcTime = env->GetMethodID(clazz, "requestUtcTime", "()V"); @@ -1760,7 +1839,7 @@ static void android_location_GnssLocationProvider_agps_set_reference_location_ce agnssRilIface->setRefLocation(location); } -static void android_location_GnssLocationProvider_agps_set_id(JNIEnv *env, jobject /* obj */, +static void android_location_GnssLocationProvider_agps_set_id(JNIEnv* env, jobject /* obj */, jint type, jstring setid_string) { if (agnssRilIface == nullptr) { ALOGE("no AGPS RIL interface in agps_set_id"); @@ -1806,9 +1885,34 @@ static void android_location_GnssLocationProvider_inject_best_location( jfloat verticalAccuracyMeters, jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees, - jlong timestamp) { + jlong timestamp, + jint elapsedRealtimeFlags, + jlong elapsedRealtimeNanos) { + if (gnssHal_V2_0 != nullptr) { + GnssLocation_V2_0 location = createGnssLocation_V2_0( + gnssLocationFlags, + latitudeDegrees, + longitudeDegrees, + altitudeMeters, + speedMetersPerSec, + bearingDegrees, + horizontalAccuracyMeters, + verticalAccuracyMeters, + speedAccuracyMetersPerSecond, + bearingAccuracyDegrees, + timestamp, + elapsedRealtimeFlags, + elapsedRealtimeNanos); + auto result = gnssHal_V2_0->injectBestLocation_2_0(location); + + if (!result.isOk() || !result) { + ALOGE("%s: Gnss injectBestLocation() failed.", __func__); + } + return; + } + if (gnssHal_V1_1 != nullptr) { - GnssLocation location = createGnssLocation( + GnssLocation_V1_0 location = createGnssLocation_V1_0( gnssLocationFlags, latitudeDegrees, longitudeDegrees, @@ -1821,12 +1925,14 @@ static void android_location_GnssLocationProvider_inject_best_location( bearingAccuracyDegrees, timestamp); auto result = gnssHal_V1_1->injectBestLocation(location); + if (!result.isOk() || !result) { ALOGE("%s: Gnss injectBestLocation() failed.", __func__); } - } else { - ALOGE("%s: injectBestLocation() is called but gnssHal_V1_1 is not available.", __func__); + return; } + + ALOGE("%s: injectBestLocation() is called but gnssHal_V1_1 is not available.", __func__); } static void android_location_GnssLocationProvider_inject_location(JNIEnv* /* env */, @@ -2248,7 +2354,7 @@ static jboolean android_location_GnssMeasurementsProvider_inject_gnss_measuremen measCorrClass, "getToaGpsNanosecondsOfWeek", "()J"); method_correctionsGetSingleSatCorrectionList = env->GetMethodID( - measCorrClass, "getSingleSatCorrectionList", "()Ljava.util.List;"); + measCorrClass, "getSingleSatelliteCorrectionList", "()Ljava.util.List;"); } jdouble latitudeDegreesCorr = env->CallDoubleMethod( @@ -2285,15 +2391,15 @@ static jboolean android_location_GnssMeasurementsProvider_inject_gnss_measuremen if (firstGnssMeasurementCorrectionInjected == false) { jclass singleSatCorrClass = env->GetObjectClass(singleSatCorrectionObj); method_correctionSatFlags = env->GetMethodID( - singleSatCorrClass, "getSingleSatCorrectionFlags", "()I"); + singleSatCorrClass, "getSingleSatelliteCorrectionFlags", "()I"); method_correctionSatConstType = env->GetMethodID( singleSatCorrClass, "getConstellationType", "()I"); method_correctionSatId= env->GetMethodID( - singleSatCorrClass, "getSatId", "()I"); + singleSatCorrClass, "getSatelliteId", "()I"); method_correctionSatCarrierFreq = env->GetMethodID( singleSatCorrClass, "getCarrierFrequencyHz", "()F"); method_correctionSatIsLosProb = env->GetMethodID( - singleSatCorrClass,"getProbSatIsLos", "()F"); + singleSatCorrClass,"getProbabilityLineOfSight", "()F"); method_correctionSatEpl = env->GetMethodID( singleSatCorrClass, "getExcessPathLengthMeters", "()F"); method_correctionSatEplUnc = env->GetMethodID( @@ -2695,45 +2801,36 @@ static const JNINativeMethod sMethods[] = { {"native_init", "()Z", reinterpret_cast<void *>(android_location_GnssLocationProvider_init)}, {"native_cleanup", "()V", reinterpret_cast<void *>( android_location_GnssLocationProvider_cleanup)}, - {"native_set_position_mode", - "(IIIIIZ)Z", - reinterpret_cast<void*>(android_location_GnssLocationProvider_set_position_mode)}, - {"native_start", "()Z", reinterpret_cast<void*>(android_location_GnssLocationProvider_start)}, - {"native_stop", "()Z", reinterpret_cast<void*>(android_location_GnssLocationProvider_stop)}, - {"native_delete_aiding_data", - "(I)V", - reinterpret_cast<void*>(android_location_GnssLocationProvider_delete_aiding_data)}, + {"native_set_position_mode", "(IIIIIZ)Z", reinterpret_cast<void *>( + android_location_GnssLocationProvider_set_position_mode)}, + {"native_start", "()Z", reinterpret_cast<void *>( + android_location_GnssLocationProvider_start)}, + {"native_stop", "()Z", reinterpret_cast<void *>( + android_location_GnssLocationProvider_stop)}, + {"native_delete_aiding_data", "(I)V", reinterpret_cast<void *>( + android_location_GnssLocationProvider_delete_aiding_data)}, {"native_read_nmea", "([BI)I", reinterpret_cast<void *>( android_location_GnssLocationProvider_read_nmea)}, {"native_inject_time", "(JJI)V", reinterpret_cast<void *>( android_location_GnssLocationProvider_inject_time)}, - {"native_inject_best_location", - "(IDDDFFFFFFJ)V", - reinterpret_cast<void *>(android_location_GnssLocationProvider_inject_best_location)}, - {"native_inject_location", - "(DDF)V", - reinterpret_cast<void *>(android_location_GnssLocationProvider_inject_location)}, + {"native_inject_best_location", "(IDDDFFFFFFJIJ)V", reinterpret_cast<void *>( + android_location_GnssLocationProvider_inject_best_location)}, + {"native_inject_location", "(DDF)V", reinterpret_cast<void *>( + android_location_GnssLocationProvider_inject_location)}, {"native_supports_xtra", "()Z", reinterpret_cast<void *>( android_location_GnssLocationProvider_supports_xtra)}, - {"native_inject_xtra_data", - "([BI)V", - reinterpret_cast<void *>(android_location_GnssLocationProvider_inject_xtra_data)}, - {"native_agps_set_id", - "(ILjava/lang/String;)V", - reinterpret_cast<void *>(android_location_GnssLocationProvider_agps_set_id)}, - {"native_agps_set_ref_location_cellid", - "(IIIII)V", - reinterpret_cast<void *>( - android_location_GnssLocationProvider_agps_set_reference_location_cellid)}, - {"native_set_agps_server", - "(ILjava/lang/String;I)V", - reinterpret_cast<void *>(android_location_GnssLocationProvider_set_agps_server)}, - {"native_send_ni_response", - "(II)V", - reinterpret_cast<void *>(android_location_GnssLocationProvider_send_ni_response)}, - {"native_get_internal_state", - "()Ljava/lang/String;", - reinterpret_cast<void *>(android_location_GnssLocationProvider_get_internal_state)}, + {"native_inject_xtra_data", "([BI)V", reinterpret_cast<void *>( + android_location_GnssLocationProvider_inject_xtra_data)}, + {"native_agps_set_id", "(ILjava/lang/String;)V", reinterpret_cast<void *>( + android_location_GnssLocationProvider_agps_set_id)}, + {"native_agps_set_ref_location_cellid", "(IIIII)V", reinterpret_cast<void *>( + android_location_GnssLocationProvider_agps_set_reference_location_cellid)}, + {"native_set_agps_server", "(ILjava/lang/String;I)V", reinterpret_cast<void *>( + android_location_GnssLocationProvider_set_agps_server)}, + {"native_send_ni_response", "(II)V", reinterpret_cast<void *>( + android_location_GnssLocationProvider_send_ni_response)}, + {"native_get_internal_state", "()Ljava/lang/String;", reinterpret_cast<void *>( + android_location_GnssLocationProvider_get_internal_state)}, {"native_is_gnss_visibility_control_supported", "()Z", reinterpret_cast<void *>( android_location_GnssLocationProvider_is_gnss_visibility_control_supported)}, }; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index ae48dad7b8f9..f496e817bc6c 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -134,6 +134,7 @@ import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.PermissionChecker; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageDataObserver; @@ -171,6 +172,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.ParcelFileDescriptor; +import android.os.ParcelableException; import android.os.PersistableBundle; import android.os.PowerManager; import android.os.PowerManagerInternal; @@ -187,6 +189,7 @@ import android.os.UserManager; import android.os.UserManagerInternal; import android.os.UserManagerInternal.UserRestrictionsListener; import android.os.storage.StorageManager; +import android.permission.PermissionControllerManager; import android.provider.CalendarContract; import android.provider.ContactsContract.QuickContact; import android.provider.ContactsInternal; @@ -1890,6 +1893,21 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return LocalServices.getService(ActivityTaskManagerInternal.class); } + @NonNull PermissionControllerManager getPermissionControllerManager( + @NonNull UserHandle user) { + if (user.equals(mContext.getUser())) { + return mContext.getSystemService(PermissionControllerManager.class); + } else { + try { + return mContext.createPackageContextAsUser(mContext.getPackageName(), 0, + user).getSystemService(PermissionControllerManager.class); + } catch (NameNotFoundException notPossible) { + // not possible + throw new IllegalStateException(notPossible); + } + } + } + UsageStatsManagerInternal getUsageStatsManagerInternal() { return LocalServices.getService(UsageStatsManagerInternal.class); } @@ -5028,7 +5046,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (quality == DevicePolicyManager.PASSWORD_QUALITY_MANAGED) { quality = PASSWORD_QUALITY_UNSPECIFIED; } - final PasswordMetrics metrics = PasswordMetrics.computeForPassword(password); + // TODO(b/120484642): remove getBytes() below + final PasswordMetrics metrics = PasswordMetrics.computeForPassword(password.getBytes()); final int realQuality = metrics.quality; if (realQuality < quality && quality != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) { @@ -5115,16 +5134,22 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { try { if (token == null) { if (!TextUtils.isEmpty(password)) { - mLockPatternUtils.saveLockPassword(password, null, quality, userHandle); + mLockPatternUtils.saveLockPassword(password.getBytes(), null, quality, + userHandle); } else { mLockPatternUtils.clearLock(null, userHandle); } result = true; } else { - result = mLockPatternUtils.setLockCredentialWithToken(password, - TextUtils.isEmpty(password) ? LockPatternUtils.CREDENTIAL_TYPE_NONE - : LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, - quality, tokenHandle, token, userHandle); + if (!TextUtils.isEmpty(password)) { + result = mLockPatternUtils.setLockCredentialWithToken(password.getBytes(), + LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, + quality, tokenHandle, token, userHandle); + } else { + result = mLockPatternUtils.setLockCredentialWithToken(null, + LockPatternUtils.CREDENTIAL_TYPE_NONE, + quality, tokenHandle, token, userHandle); + } } boolean requireEntry = (flags & DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY) != 0; if (requireEntry) { @@ -11582,8 +11607,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override - public boolean setPermissionGrantState(ComponentName admin, String callerPackage, - String packageName, String permission, int grantState) throws RemoteException { + public void setPermissionGrantState(ComponentName admin, String callerPackage, + String packageName, String permission, int grantState, RemoteCallback callback) + throws RemoteException { + Preconditions.checkNotNull(callback); + UserHandle user = mInjector.binderGetCallingUserHandle(); synchronized (getLockObject()) { // Ensure the caller is a DO/PO or a permission grant state delegate. @@ -11591,53 +11619,60 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { DELEGATION_PERMISSION_GRANT); long ident = mInjector.binderClearCallingIdentity(); try { - if (getTargetSdk(packageName, user.getIdentifier()) - < android.os.Build.VERSION_CODES.M) { - return false; + boolean isPostQAdmin = getTargetSdk(callerPackage, user.getIdentifier()) + >= android.os.Build.VERSION_CODES.Q; + if (!isPostQAdmin) { + // Legacy admins assume that they cannot control pre-M apps + if (getTargetSdk(packageName, user.getIdentifier()) + < android.os.Build.VERSION_CODES.M) { + callback.sendResult(null); + return; + } } - if (!isRuntimePermission(permission)) { - return false; + try { + if (!isRuntimePermission(permission)) { + callback.sendResult(null); + return; + } + } catch (NameNotFoundException e) { + throw new RemoteException( + "Cannot check if " + permission + "is a runtime permission", e, false, + true); + } + + if (grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED + || grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED + || grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT) { + mInjector.getPermissionControllerManager(user) + .setRuntimePermissionGrantStateByDeviceAdmin(callerPackage, + packageName, permission, grantState, mContext.getMainExecutor(), + (permissionWasSet) -> { + if (isPostQAdmin && !permissionWasSet) { + callback.sendResult(null); + return; + } + + final boolean isDelegate = (admin == null); + DevicePolicyEventLogger + .createEvent(DevicePolicyEnums + .SET_PERMISSION_GRANT_STATE) + .setAdmin(callerPackage) + .setStrings(permission) + .setInt(grantState) + .setBoolean(isDelegate) + .write(); + + callback.sendResult(Bundle.EMPTY); + }); } - final PackageManager packageManager = mInjector.getPackageManager(); - switch (grantState) { - case DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED: { - mInjector.getPackageManagerInternal().grantRuntimePermission(packageName, - permission, user.getIdentifier(), true /* override policy */); - packageManager.updatePermissionFlags(permission, packageName, - PackageManager.FLAG_PERMISSION_POLICY_FIXED, - PackageManager.FLAG_PERMISSION_POLICY_FIXED, user); - } break; - - case DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED: { - mInjector.getPackageManagerInternal().revokeRuntimePermission(packageName, - permission, user.getIdentifier(), true /* override policy */); - packageManager.updatePermissionFlags(permission, packageName, - PackageManager.FLAG_PERMISSION_POLICY_FIXED, - PackageManager.FLAG_PERMISSION_POLICY_FIXED, user); - } break; - - case DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT: { - packageManager.updatePermissionFlags(permission, packageName, - PackageManager.FLAG_PERMISSION_POLICY_FIXED, 0, user); - } break; - } - } catch (SecurityException se) { - return false; - } catch (NameNotFoundException e) { - return false; + } catch (SecurityException e) { + Slog.e(LOG_TAG, "Could not set permission grant state", e); + + callback.sendResult(null); } finally { mInjector.binderRestoreCallingIdentity(ident); } } - final boolean isDelegate = (admin == null); - DevicePolicyEventLogger - .createEvent(DevicePolicyEnums.SET_PERMISSION_GRANT_STATE) - .setAdmin(callerPackage) - .setStrings(permission) - .setInt(grantState) - .setBoolean(isDelegate) - .write(); - return true; } @Override @@ -11654,8 +11689,26 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { synchronized (getLockObject()) { long ident = mInjector.binderClearCallingIdentity(); try { - int granted = mIPackageManager.checkPermission(permission, - packageName, user.getIdentifier()); + int granted; + if (getTargetSdk(callerPackage, user.getIdentifier()) + < android.os.Build.VERSION_CODES.Q) { + // The per-Q behavior was to not check the app-ops state. + granted = mIPackageManager.checkPermission(permission, packageName, + user.getIdentifier()); + } else { + try { + int uid = packageManager.getPackageUidAsUser(packageName, + user.getIdentifier()); + + // TODO: Prevent noting the app-op + granted = PermissionChecker.checkPermission(mContext, permission, -1, + uid, packageName); + } catch (NameNotFoundException e) { + throw new RemoteException( + "Cannot check if " + permission + "is a runtime permission", e, + false, true); + } + } int permFlags = packageManager.getPermissionFlags(permission, packageName, user); if ((permFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != PackageManager.FLAG_PERMISSION_POLICY_FIXED) { diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index aae159c2edcb..512a745f5abe 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -89,8 +89,8 @@ import com.android.server.clipboard.ClipboardService; import com.android.server.connectivity.IpConnectivityMetrics; import com.android.server.coverage.CoverageService; import com.android.server.devicepolicy.DevicePolicyManagerService; -import com.android.server.display.ColorDisplayService; import com.android.server.display.DisplayManagerService; +import com.android.server.display.color.ColorDisplayService; import com.android.server.dreams.DreamManagerService; import com.android.server.emergency.EmergencyAffordanceService; import com.android.server.gpu.GpuService; @@ -278,6 +278,8 @@ public final class SystemServer { private static final String UNCRYPT_PACKAGE_FILE = "/cache/recovery/uncrypt_file"; private static final String BLOCK_MAP_FILE = "/cache/recovery/block.map"; + private static final String GSI_RUNNING_PROP = "ro.gsid.image_running"; + // maximum number of binder threads used for system_server // will be higher than the system default private static final int sMaxBinderThreads = 31; @@ -308,6 +310,7 @@ public final class SystemServer { private boolean mOnlyCore; private boolean mFirstBoot; + private final int mStartCount; private final boolean mRuntimeRestart; private final long mRuntimeStartElapsedTime; private final long mRuntimeStartUptime; @@ -315,6 +318,9 @@ public final class SystemServer { private static final String START_SENSOR_SERVICE = "StartSensorService"; private static final String START_HIDL_SERVICES = "StartHidlServices"; + private static final String SYSPROP_START_COUNT = "sys.system_server.start_count"; + private static final String SYSPROP_START_ELAPSED = "sys.system_server.start_elapsed"; + private static final String SYSPROP_START_UPTIME = "sys.system_server.start_uptime"; private Future<?> mSensorServiceStart; private Future<?> mZygotePreload; @@ -344,16 +350,33 @@ public final class SystemServer { public SystemServer() { // Check for factory test mode. mFactoryTestMode = FactoryTest.getMode(); - // Remember if it's runtime restart(when sys.boot_completed is already set) or reboot - mRuntimeRestart = "1".equals(SystemProperties.get("sys.boot_completed")); + // Record process start information. + // Note SYSPROP_START_COUNT will increment by *2* on a FDE device when it fully boots; + // one for the password screen, second for the actual boot. + mStartCount = SystemProperties.getInt(SYSPROP_START_COUNT, 0) + 1; mRuntimeStartElapsedTime = SystemClock.elapsedRealtime(); mRuntimeStartUptime = SystemClock.uptimeMillis(); + + // Remember if it's runtime restart(when sys.boot_completed is already set) or reboot + // We don't use "mStartCount > 1" here because it'll be wrong on a FDE device. + // TODO: mRuntimeRestart will *not* be set to true if the proccess crashes before + // sys.boot_completed is set. Fix it. + mRuntimeRestart = "1".equals(SystemProperties.get("sys.boot_completed")); } private void run() { try { traceBeginAndSlog("InitBeforeStartServices"); + + // Record the process start information in sys props. + SystemProperties.set(SYSPROP_START_COUNT, String.valueOf(mStartCount)); + SystemProperties.set(SYSPROP_START_ELAPSED, String.valueOf(mRuntimeStartElapsedTime)); + SystemProperties.set(SYSPROP_START_UPTIME, String.valueOf(mRuntimeStartUptime)); + + EventLog.writeEvent(EventLogTags.SYSTEM_SERVER_START, + mStartCount, mRuntimeStartUptime, mRuntimeStartElapsedTime); + // If a device's clock is before 1970 (before 0), a lot of // APIs crash dealing with negative numbers, notably // java.io.File#setLastModified, so instead we fake it and @@ -1167,7 +1190,8 @@ public final class SystemServer { traceEnd(); final boolean hasPdb = !SystemProperties.get(PERSISTENT_DATA_BLOCK_PROP).equals(""); - if (hasPdb) { + final boolean hasGsi = SystemProperties.getInt(GSI_RUNNING_PROP, 0) > 0; + if (hasPdb && !hasGsi) { traceBeginAndSlog("StartPersistentDataBlock"); mSystemServiceManager.startService(PersistentDataBlockService.class); traceEnd(); @@ -2215,12 +2239,12 @@ public final class SystemServer { private void startContentCaptureService(@NonNull Context context) { // First check if it was explicitly enabled by DeviceConfig - boolean explicitlySupported = false; + boolean explicitlyEnabled = false; String settings = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_CONTENT_CAPTURE, ContentCaptureManager.DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED); if (settings != null && !settings.equalsIgnoreCase("default")) { - explicitlySupported = Boolean.parseBoolean(settings); - if (explicitlySupported) { + explicitlyEnabled = Boolean.parseBoolean(settings); + if (explicitlyEnabled) { Slog.d(TAG, "ContentCaptureService explicitly enabled by DeviceConfig"); } else { Slog.d(TAG, "ContentCaptureService explicitly disabled by DeviceConfig"); @@ -2229,7 +2253,7 @@ public final class SystemServer { } // Then check if OEM overlaid the resource that defines the service. - if (!explicitlySupported) { + if (!explicitlyEnabled) { final String serviceName = context .getString(com.android.internal.R.string.config_defaultContentCaptureService); if (TextUtils.isEmpty(serviceName)) { diff --git a/services/net/java/android/net/NetworkStackClient.java b/services/net/java/android/net/NetworkStackClient.java index 1eb7b98d801a..830dbbe8b8c0 100644 --- a/services/net/java/android/net/NetworkStackClient.java +++ b/services/net/java/android/net/NetworkStackClient.java @@ -30,6 +30,7 @@ import android.content.pm.PackageManager; import android.net.dhcp.DhcpServingParamsParcel; import android.net.dhcp.IDhcpServerCallbacks; import android.net.ip.IIpClientCallbacks; +import android.net.util.SharedLog; import android.os.Binder; import android.os.IBinder; import android.os.Process; @@ -40,6 +41,7 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; +import java.io.PrintWriter; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; @@ -61,6 +63,9 @@ public class NetworkStackClient { @GuardedBy("mPendingNetStackRequests") private INetworkStackConnector mConnector; + @GuardedBy("mLog") + private final SharedLog mLog = new SharedLog(TAG); + private volatile boolean mNetworkStackStartRequested = false; private interface NetworkStackCallback { @@ -129,13 +134,14 @@ public class NetworkStackClient { private class NetworkStackConnection implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder service) { + log("Network stack service connected"); registerNetworkStackService(service); } @Override public void onServiceDisconnected(ComponentName name) { // TODO: crash/reboot the system ? - Slog.wtf(TAG, "Lost network stack connector"); + logWtf("Lost network stack connector", null); } }; @@ -144,6 +150,7 @@ public class NetworkStackClient { ServiceManager.addService(Context.NETWORK_STACK_SERVICE, service, false /* allowIsolated */, DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL); + log("Network stack service registered"); final ArrayList<NetworkStackCallback> requests; synchronized (mPendingNetStackRequests) { @@ -166,6 +173,7 @@ public class NetworkStackClient { * started. */ public void start(Context context) { + log("Starting network stack"); mNetworkStackStartRequested = true; // Try to bind in-process if the library is available IBinder connector = null; @@ -177,7 +185,7 @@ public class NetworkStackClient { connector = (IBinder) service.getMethod("makeConnector", Context.class) .invoke(null, context); } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { - Slog.wtf(TAG, "Could not create network stack connector from NetworkStackService"); + logWtf("Could not create network stack connector from NetworkStackService", e); // TODO: crash/reboot system here ? return; } catch (ClassNotFoundException e) { @@ -186,26 +194,28 @@ public class NetworkStackClient { // In-process network stack. Add the service to the service manager here. if (connector != null) { + log("Registering in-process network stack connector"); registerNetworkStackService(connector); return; } // Start the network stack process. The service will be added to the service manager in // NetworkStackConnection.onServiceConnected(). + log("Starting network stack process"); final Intent intent = new Intent(INetworkStackConnector.class.getName()); final ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0); intent.setComponent(comp); if (comp == null) { - Slog.wtf(TAG, "Could not resolve the network stack with " + intent); + logWtf("Could not resolve the network stack with " + intent, null); // TODO: crash/reboot system server ? return; } final PackageManager pm = context.getPackageManager(); int uid = -1; try { - uid = pm.getPackageUid(comp.getPackageName(), UserHandle.USER_SYSTEM); + uid = pm.getPackageUidAsUser(comp.getPackageName(), UserHandle.USER_SYSTEM); } catch (PackageManager.NameNotFoundException e) { - Slog.wtf("Network stack package not found", e); + logWtf("Network stack package not found", e); // Fall through } if (uid != Process.NETWORK_STACK_UID) { @@ -221,10 +231,31 @@ public class NetworkStackClient { if (!context.bindServiceAsUser(intent, new NetworkStackConnection(), Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) { - Slog.wtf(TAG, - "Could not bind to network stack in-process, or in app with " + intent); + logWtf("Could not bind to network stack in-process, or in app with " + intent, null); + return; // TODO: crash/reboot system server if no network stack after a timeout ? } + + log("Network stack service start requested"); + } + + private void log(@NonNull String message) { + synchronized (mLog) { + mLog.log(message); + } + } + + private void logWtf(@NonNull String message, @Nullable Throwable e) { + Slog.wtf(TAG, message); + synchronized (mLog) { + mLog.e(message, e); + } + } + + private void loge(@NonNull String message, @Nullable Throwable e) { + synchronized (mLog) { + mLog.e(message, e); + } } /** @@ -243,12 +274,12 @@ public class NetworkStackClient { while ((connector = ServiceManager.getService(Context.NETWORK_STACK_SERVICE)) == null) { Thread.sleep(20); if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) { - Slog.e(TAG, "Timeout waiting for NetworkStack connector"); + loge("Timeout waiting for NetworkStack connector", null); return null; } } } catch (InterruptedException e) { - Slog.e(TAG, "Error waiting for NetworkStack connector", e); + loge("Error waiting for NetworkStack connector", e); return null; } @@ -286,4 +317,20 @@ public class NetworkStackClient { request.onNetworkStackConnected(connector); } + + /** + * Dump NetworkStackClient logs to the specified {@link PrintWriter}. + */ + public void dump(PrintWriter pw) { + // dump is thread-safe on SharedLog + mLog.dump(null, pw, null); + + final int requestsQueueLength; + synchronized (mPendingNetStackRequests) { + requestsQueueLength = mPendingNetStackRequests.size(); + } + + pw.println(); + pw.println("pendingNetStackRequests length: " + requestsQueueLength); + } } diff --git a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java index 8e3023bc08d4..339607bbc73d 100644 --- a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java +++ b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java @@ -275,6 +275,9 @@ public class RouterAdvertisementDaemon { public void stop() { closeSocket(); + // Wake up mMulticastTransmitter thread to interrupt a potential 1 day sleep before + // the thread's termination. + maybeNotifyMulticastTransmitter(); mMulticastTransmitter = null; mUnicastResponder = null; } diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp index ebc816dc45da..782196dc0048 100644 --- a/services/tests/mockingservicestests/Android.bp +++ b/services/tests/mockingservicestests/Android.bp @@ -23,6 +23,7 @@ android_test { "androidx.test.runner", "mockito-target-extended-minus-junit4", "platform-test-annotations", + "truth-prebuilt", ], libs: [ diff --git a/services/tests/mockingservicestests/src/com/android/server/am/AppCompactorTest.java b/services/tests/mockingservicestests/src/com/android/server/am/AppCompactorTest.java new file mode 100644 index 000000000000..e100d162813b --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/am/AppCompactorTest.java @@ -0,0 +1,412 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +import static com.android.server.am.ActivityManagerService.Injector; +import static com.android.server.am.AppCompactor.compactActionIntToString; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.Handler; +import android.os.HandlerThread; +import android.provider.DeviceConfig; + +import com.android.server.appop.AppOpsService; +import com.android.server.testables.TestableDeviceConfig; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.io.File; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * Tests for {@link AppCompactor}. + * + * Build/Install/Run: + * atest FrameworksServicesTests:AppCompactorTest + */ +@RunWith(MockitoJUnitRunner.class) +public final class AppCompactorTest { + + @Mock + private AppOpsService mAppOpsService; + private AppCompactor mCompactorUnderTest; + private HandlerThread mHandlerThread; + private Handler mHandler; + private CountDownLatch mCountDown; + + @Rule + public TestableDeviceConfig mDeviceConfig = new TestableDeviceConfig(); + + @Before + public void setUp() { + mHandlerThread = new HandlerThread(""); + mHandlerThread.start(); + mHandler = new Handler(mHandlerThread.getLooper()); + ActivityManagerService ams = new ActivityManagerService(new TestInjector()); + mCompactorUnderTest = new AppCompactor(ams, + new AppCompactor.PropertyChangedCallbackForTest() { + @Override + public void onPropertyChanged() { + if (mCountDown != null) { + mCountDown.countDown(); + } + } + }); + } + + @After + public void tearDown() { + mHandlerThread.quit(); + mCountDown = null; + } + + @Test + public void init_setsDefaults() { + mCompactorUnderTest.init(); + assertThat(mCompactorUnderTest.useCompaction()).isEqualTo( + AppCompactor.DEFAULT_USE_COMPACTION); + assertThat(mCompactorUnderTest.mCompactActionSome).isEqualTo( + compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_1)); + assertThat(mCompactorUnderTest.mCompactActionFull).isEqualTo( + compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_2)); + assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_1); + assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_2); + assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_3); + assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_4); + assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo( + AppCompactor.DEFAULT_STATSD_SAMPLE_RATE); + } + + @Test + public void init_withDeviceConfigSetsParameters() { + // When the DeviceConfig already has a flag value stored (note this test will need to + // change if the default value changes from false). + assertThat(AppCompactor.DEFAULT_USE_COMPACTION).isFalse(); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + AppCompactor.KEY_USE_COMPACTION, "true", false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + AppCompactor.KEY_COMPACT_ACTION_1, + Integer.toString((AppCompactor.DEFAULT_COMPACT_ACTION_1 + 1 % 4) + 1), false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + AppCompactor.KEY_COMPACT_ACTION_2, + Integer.toString((AppCompactor.DEFAULT_COMPACT_ACTION_2 + 1 % 4) + 1), false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + AppCompactor.KEY_COMPACT_THROTTLE_1, + Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1), false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + AppCompactor.KEY_COMPACT_THROTTLE_2, + Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1), false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + AppCompactor.KEY_COMPACT_THROTTLE_3, + Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1), false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + AppCompactor.KEY_COMPACT_THROTTLE_4, + Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1), false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + AppCompactor.KEY_COMPACT_STATSD_SAMPLE_RATE, + Float.toString(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false); + + // Then calling init will read and set that flag. + mCompactorUnderTest.init(); + assertThat(mCompactorUnderTest.useCompaction()).isTrue(); + assertThat(mCompactorUnderTest.mCompactionThread.isAlive()).isTrue(); + + assertThat(mCompactorUnderTest.mCompactActionSome).isEqualTo( + compactActionIntToString((AppCompactor.DEFAULT_COMPACT_ACTION_1 + 1 % 4) + 1)); + assertThat(mCompactorUnderTest.mCompactActionFull).isEqualTo( + compactActionIntToString((AppCompactor.DEFAULT_COMPACT_ACTION_2 + 1 % 4) + 1)); + assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1); + assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1); + assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1); + assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1); + assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo( + AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f); + } + + @Test + public void useCompaction_listensToDeviceConfigChanges() throws InterruptedException { + assertThat(mCompactorUnderTest.useCompaction()).isEqualTo( + AppCompactor.DEFAULT_USE_COMPACTION); + // When we call init and change some the flag value... + mCompactorUnderTest.init(); + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + AppCompactor.KEY_USE_COMPACTION, "true", false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + + // Then that new flag value is updated in the implementation. + assertThat(mCompactorUnderTest.useCompaction()).isTrue(); + assertThat(mCompactorUnderTest.mCompactionThread.isAlive()).isTrue(); + + // And again, setting the flag the other way. + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + AppCompactor.KEY_USE_COMPACTION, "false", false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + assertThat(mCompactorUnderTest.useCompaction()).isFalse(); + } + + @Test + public void useCompaction_listensToDeviceConfigChangesBadValues() throws InterruptedException { + assertThat(mCompactorUnderTest.useCompaction()).isEqualTo( + AppCompactor.DEFAULT_USE_COMPACTION); + mCompactorUnderTest.init(); + + // When we push an invalid flag value... + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + AppCompactor.KEY_USE_COMPACTION, "foobar", false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + + // Then we set the default. + assertThat(mCompactorUnderTest.useCompaction()).isEqualTo( + AppCompactor.DEFAULT_USE_COMPACTION); + } + + @Test + public void compactAction_listensToDeviceConfigChanges() throws InterruptedException { + mCompactorUnderTest.init(); + + // When we override new values for the compaction action with reasonable values... + + // There are four possible values for compactAction[Some|Full]. + for (int i = 1; i < 5; i++) { + mCountDown = new CountDownLatch(2); + int expectedSome = (AppCompactor.DEFAULT_COMPACT_ACTION_1 + i) % 4 + 1; + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + AppCompactor.KEY_COMPACT_ACTION_1, Integer.toString(expectedSome), false); + int expectedFull = (AppCompactor.DEFAULT_COMPACT_ACTION_2 + i) % 4 + 1; + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + AppCompactor.KEY_COMPACT_ACTION_2, Integer.toString(expectedFull), false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + + // Then the updates are reflected in the flags. + assertThat(mCompactorUnderTest.mCompactActionSome).isEqualTo( + compactActionIntToString(expectedSome)); + assertThat(mCompactorUnderTest.mCompactActionFull).isEqualTo( + compactActionIntToString(expectedFull)); + } + } + + @Test + public void compactAction_listensToDeviceConfigChangesBadValues() throws InterruptedException { + mCompactorUnderTest.init(); + + // When we override new values for the compaction action with bad values ... + mCountDown = new CountDownLatch(2); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + AppCompactor.KEY_COMPACT_ACTION_1, "foo", false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + AppCompactor.KEY_COMPACT_ACTION_2, "foo", false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + + // Then the default values are reflected in the flag + assertThat(mCompactorUnderTest.mCompactActionSome).isEqualTo( + compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_1)); + assertThat(mCompactorUnderTest.mCompactActionFull).isEqualTo( + compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_2)); + + mCountDown = new CountDownLatch(2); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + AppCompactor.KEY_COMPACT_ACTION_1, "", false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + AppCompactor.KEY_COMPACT_ACTION_2, "", false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + + assertThat(mCompactorUnderTest.mCompactActionSome).isEqualTo( + compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_1)); + assertThat(mCompactorUnderTest.mCompactActionFull).isEqualTo( + compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_2)); + } + + @Test + public void compactThrottle_listensToDeviceConfigChanges() throws InterruptedException { + mCompactorUnderTest.init(); + + // When we override new reasonable throttle values after init... + mCountDown = new CountDownLatch(4); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + AppCompactor.KEY_COMPACT_THROTTLE_1, + Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1), false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + AppCompactor.KEY_COMPACT_THROTTLE_2, + Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1), false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + AppCompactor.KEY_COMPACT_THROTTLE_3, + Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1), false); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + AppCompactor.KEY_COMPACT_THROTTLE_4, + Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1), false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + + // Then those flags values are reflected in the compactor. + assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1); + assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1); + assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1); + assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1); + } + + @Test + public void compactThrottle_listensToDeviceConfigChangesBadValues() + throws InterruptedException { + mCompactorUnderTest.init(); + + // When one of the throttles is overridden with a bad value... + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + AppCompactor.KEY_COMPACT_THROTTLE_1, "foo", false); + // Then all the throttles have the defaults set. + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_1); + assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_2); + assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_3); + assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_4); + + // Repeat for each of the throttle keys. + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + AppCompactor.KEY_COMPACT_THROTTLE_2, "foo", false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_1); + assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_2); + assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_3); + assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_4); + + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + AppCompactor.KEY_COMPACT_THROTTLE_3, "foo", false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_1); + assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_2); + assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_3); + assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_4); + + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + AppCompactor.KEY_COMPACT_THROTTLE_4, "foo", false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + assertThat(mCompactorUnderTest.mCompactThrottleSomeSome).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_1); + assertThat(mCompactorUnderTest.mCompactThrottleSomeFull).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_2); + assertThat(mCompactorUnderTest.mCompactThrottleFullSome).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_3); + assertThat(mCompactorUnderTest.mCompactThrottleFullFull).isEqualTo( + AppCompactor.DEFAULT_COMPACT_THROTTLE_4); + } + + @Test + public void statsdSampleRate_listensToDeviceConfigChanges() throws InterruptedException { + mCompactorUnderTest.init(); + + // When we override mStatsdSampleRate with a reasonable values ... + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + AppCompactor.KEY_COMPACT_STATSD_SAMPLE_RATE, + Float.toString(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + + // Then that override is reflected in the compactor. + assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo( + AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f); + } + + @Test + public void statsdSanokeRate_listensToDeviceConfigChangesBadValues() + throws InterruptedException { + mCompactorUnderTest.init(); + + // When we override mStatsdSampleRate with a reasonable values ... + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + AppCompactor.KEY_COMPACT_STATSD_SAMPLE_RATE, "foo", false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + + // Then that override is reflected in the compactor. + assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo( + AppCompactor.DEFAULT_STATSD_SAMPLE_RATE); + } + + @Test + public void statsdSanokeRate_listensToDeviceConfigChangesOutOfRangeValues() + throws InterruptedException { + mCompactorUnderTest.init(); + + // When we override mStatsdSampleRate with an value outside of [0..1]... + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + AppCompactor.KEY_COMPACT_STATSD_SAMPLE_RATE, + Float.toString(-1.0f), false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + + // Then the values is capped in the range. + assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo(0.0f); + + mCountDown = new CountDownLatch(1); + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + AppCompactor.KEY_COMPACT_STATSD_SAMPLE_RATE, + Float.toString(1.01f), false); + assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue(); + + // Then the values is capped in the range. + assertThat(mCompactorUnderTest.mStatsdSampleRate).isEqualTo(1.0f); + } + + private class TestInjector extends Injector { + @Override + public AppOpsService getAppOpsService(File file, Handler handler) { + return mAppOpsService; + } + + @Override + public Handler getUiHandler(ActivityManagerService service) { + return mHandler; + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java b/services/tests/mockingservicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java index 0fd59216fa21..5db8867658be 100644 --- a/services/tests/servicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/SettingsToPropertiesMapperTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,52 +16,98 @@ package com.android.server.am; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; + import android.content.ContentResolver; +import android.os.SystemProperties; import android.provider.Settings; -import android.test.mock.MockContentResolver; import android.text.TextUtils; -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; - -import com.android.internal.util.Preconditions; -import com.android.internal.util.test.FakeSettingsProvider; +import com.android.dx.mockito.inline.extended.ExtendedMockito; +import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.Mock; +import org.mockito.MockitoSession; +import org.mockito.quality.Strictness; +import org.mockito.stubbing.Answer; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Map; /** - * Tests for {@link SettingsToPropertiesMapper} - * - * Build/Install/Run: - * atest FrameworksServicesTests:SettingsToPropertiesMapperTest + * Test SettingsToPropertiesMapper. */ -@RunWith(AndroidJUnit4.class) -@SmallTest public class SettingsToPropertiesMapperTest { private static final String NAME_VALID_CHARACTERS_REGEX = "^[\\w\\-@:]*$"; private static final String[] TEST_MAPPING = new String[] { Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS }; - private TestMapper mTestMapper; - private MockContentResolver mMockContentResolver; + private MockitoSession mSession; + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private ContentResolver mMockContentResolver; + + private SettingsToPropertiesMapper mTestMapper; + + private HashMap<String, String> mSystemSettingsMap; + private HashMap<String, String> mGlobalSettingsMap; @Before - public void setupForEach() { - // Use FakeSettingsProvider to not affect global state - mMockContentResolver = new MockContentResolver(InstrumentationRegistry.getContext()); - mMockContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); - mTestMapper = new TestMapper(mMockContentResolver); + public void setUp() throws Exception { + mSession = + ExtendedMockito.mockitoSession().initMocks( + this) + .strictness(Strictness.LENIENT) + .spyStatic(SystemProperties.class) + .spyStatic(Settings.Global.class) + .spyStatic(SettingsToPropertiesMapper.class) + .startMocking(); + mSystemSettingsMap = new HashMap<>(); + mGlobalSettingsMap = new HashMap<>(); + + // Mock SystemProperties setter and various getters + doAnswer((Answer<Void>) invocationOnMock -> { + String key = invocationOnMock.getArgument(0); + String value = invocationOnMock.getArgument(1); + + mSystemSettingsMap.put(key, value); + return null; + } + ).when(() -> SystemProperties.set(anyString(), anyString())); + + doAnswer((Answer<String>) invocationOnMock -> { + String key = invocationOnMock.getArgument(0); + + String storedValue = mSystemSettingsMap.get(key); + return storedValue == null ? "" : storedValue; + } + ).when(() -> SystemProperties.get(anyString())); + + // Mock Settings.Global methods + doAnswer((Answer<String>) invocationOnMock -> { + String key = invocationOnMock.getArgument(1); + + return mGlobalSettingsMap.get(key); + } + ).when(() -> Settings.Global.getString(any(), anyString())); + + mTestMapper = new SettingsToPropertiesMapper( + mMockContentResolver, TEST_MAPPING, new String[] {}); + } + + @After + public void tearDown() throws Exception { + mSession.finishMocking(); } @Test @@ -108,30 +154,27 @@ public class SettingsToPropertiesMapperTest { @Test public void testUpdatePropertiesFromSettings() { - Settings.Global.putString(mMockContentResolver, - Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, "testValue"); + mGlobalSettingsMap.put(Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, "testValue"); String systemPropertyName = "persist.device_config.global_settings." + "sqlite_compatibility_wal_flags"; mTestMapper.updatePropertiesFromSettings(); - String propValue = mTestMapper.systemPropertiesGet(systemPropertyName); + String propValue = mSystemSettingsMap.get(systemPropertyName); Assert.assertEquals("testValue", propValue); - Settings.Global.putString(mMockContentResolver, - Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, "testValue2"); + mGlobalSettingsMap.put(Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, "testValue2"); mTestMapper.updatePropertyFromSetting( Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, systemPropertyName); - propValue = mTestMapper.systemPropertiesGet(systemPropertyName); + propValue = mSystemSettingsMap.get(systemPropertyName); Assert.assertEquals("testValue2", propValue); - Settings.Global.putString(mMockContentResolver, - Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, null); + mGlobalSettingsMap.put(Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, null); mTestMapper.updatePropertyFromSetting( Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS, systemPropertyName); - propValue = mTestMapper.systemPropertiesGet(systemPropertyName); + propValue = mSystemSettingsMap.get(systemPropertyName); Assert.assertEquals("", propValue); } @@ -163,71 +206,37 @@ public class SettingsToPropertiesMapperTest { public void testUpdatePropertiesFromSettings_PropertyAndSettingNotPresent() { // Test that empty property will not not be set if setting is not set mTestMapper.updatePropertiesFromSettings(); - String propValue = mTestMapper.systemPropertiesGet("TestProperty"); + String propValue = mSystemSettingsMap.get("TestProperty"); Assert.assertNull("Property should not be set if setting is null", propValue); } @Test public void testIsNativeFlagsResetPerformed() { - mTestMapper.systemPropertiesSet("device_config.reset_performed", "true"); + mSystemSettingsMap.put("device_config.reset_performed", "true"); Assert.assertTrue(mTestMapper.isNativeFlagsResetPerformed()); - mTestMapper.systemPropertiesSet("device_config.reset_performed", "false"); + mSystemSettingsMap.put("device_config.reset_performed", "false"); Assert.assertFalse(mTestMapper.isNativeFlagsResetPerformed()); - mTestMapper.systemPropertiesSet("device_config.reset_performed", ""); + mSystemSettingsMap.put("device_config.reset_performed", ""); Assert.assertFalse(mTestMapper.isNativeFlagsResetPerformed()); } @Test public void testGetResetNativeCategories() { - mTestMapper.systemPropertiesSet("device_config.reset_performed", ""); - Assert.assertEquals(mTestMapper.getResetNativeCategories().length, 0); + doReturn("persist.device_config.category1.flag;" + + "persist.device_config.category2.flag;persist.device_config.category3.flag;" + + "persist.device_config.category3.flag2") + .when(() -> SettingsToPropertiesMapper.getResetFlagsFileContent()); - mTestMapper.systemPropertiesSet("device_config.reset_performed", "true"); - mTestMapper.setFileContent(""); + mSystemSettingsMap.put("device_config.reset_performed", ""); Assert.assertEquals(mTestMapper.getResetNativeCategories().length, 0); - mTestMapper.systemPropertiesSet("device_config.reset_performed", "true"); - mTestMapper.setFileContent("persist.device_config.category1.flag;" - + "persist.device_config.category2.flag;persist.device_config.category3.flag;" - + "persist.device_config.category3.flag2"); + mSystemSettingsMap.put("device_config.reset_performed", "true"); List<String> categories = Arrays.asList(mTestMapper.getResetNativeCategories()); Assert.assertEquals(3, categories.size()); Assert.assertTrue(categories.contains("category1")); Assert.assertTrue(categories.contains("category2")); Assert.assertTrue(categories.contains("category3")); } - - private static class TestMapper extends SettingsToPropertiesMapper { - private final Map<String, String> mProps = new HashMap<>(); - - private String mFileContent = ""; - - TestMapper(ContentResolver contentResolver) { - super(contentResolver, TEST_MAPPING, new String[] {}); - } - - @Override - protected String systemPropertiesGet(String key) { - Preconditions.checkNotNull(key); - return mProps.get(key); - } - - @Override - protected void systemPropertiesSet(String key, String value) { - Preconditions.checkNotNull(value); - Preconditions.checkNotNull(key); - mProps.put(key, value); - } - - protected void setFileContent(String fileContent) { - mFileContent = fileContent; - } - - @Override - protected String getResetFlagsFileContent() { - return mFileContent; - } - } } diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java new file mode 100644 index 000000000000..b76682269c2e --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfig.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.testables; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyString; + +import android.provider.DeviceConfig; +import android.util.Pair; + +import com.android.dx.mockito.inline.extended.StaticMockitoSession; + +import org.junit.rules.TestRule; +import org.junit.rules.TestWatcher; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; +import org.mockito.quality.Strictness; +import org.mockito.stubbing.Answer; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executor; + +/** + * TestableDeviceConfig uses ExtendedMockito to replace the real implementation of DeviceConfig + * with essentially a local HashMap in the callers process. This allows for unit testing that do not + * modify the real DeviceConfig on the device at all. + * + * <p>TestableDeviceConfig should be defined as a rule on your test so it can clean up after itself. + * Like the following:</p> + * <pre class="prettyprint"> + * @Rule + * public final TestableDeviceConfig mTestableDeviceConfig = new TestableDeviceConfig(); + * </pre> + */ +public final class TestableDeviceConfig implements TestRule { + + private StaticMockitoSession mMockitoSession; + private Map<DeviceConfig.OnPropertyChangedListener, Pair<String, Executor>> + mOnPropertyChangedListenerMap = new HashMap<>(); + private Map<String, String> mKeyValueMap = new ConcurrentHashMap<>(); + + /** + * Clears out all local overrides. + */ + public void clearDeviceConfig() { + mKeyValueMap.clear(); + } + + @Override + public Statement apply(Statement base, Description description) { + mMockitoSession = mockitoSession() + .initMocks(this) + .strictness(Strictness.LENIENT) + .spyStatic(DeviceConfig.class) + .startMocking(); + + doAnswer((Answer<Void>) invocationOnMock -> { + String namespace = invocationOnMock.getArgument(0); + Executor executor = invocationOnMock.getArgument(1); + DeviceConfig.OnPropertyChangedListener onPropertyChangedListener = + invocationOnMock.getArgument(2); + mOnPropertyChangedListenerMap.put( + onPropertyChangedListener, new Pair<>(namespace, executor)); + return null; + }).when(() -> DeviceConfig.addOnPropertyChangedListener( + anyString(), any(Executor.class), + any(DeviceConfig.OnPropertyChangedListener.class))); + + doAnswer((Answer<Boolean>) invocationOnMock -> { + String namespace = invocationOnMock.getArgument(0); + String name = invocationOnMock.getArgument(1); + String value = invocationOnMock.getArgument(2); + mKeyValueMap.put(getKey(namespace, name), value); + for (DeviceConfig.OnPropertyChangedListener listener : + mOnPropertyChangedListenerMap.keySet()) { + if (namespace.equals(mOnPropertyChangedListenerMap.get(listener).first)) { + mOnPropertyChangedListenerMap.get(listener).second.execute( + () -> listener.onPropertyChanged(namespace, name, value)); + } + } + return true; + } + ).when(() -> DeviceConfig.setProperty(anyString(), anyString(), anyString(), anyBoolean())); + + doAnswer((Answer<String>) invocationOnMock -> { + String namespace = invocationOnMock.getArgument(0); + String name = invocationOnMock.getArgument(1); + return mKeyValueMap.get(getKey(namespace, name)); + }).when(() -> DeviceConfig.getProperty(anyString(), anyString())); + + + return new TestWatcher() { + @Override + protected void succeeded(Description description) { + mMockitoSession.finishMocking(); + mOnPropertyChangedListenerMap.clear(); + } + + @Override + protected void failed(Throwable e, Description description) { + mMockitoSession.finishMocking(e); + mOnPropertyChangedListenerMap.clear(); + } + }.apply(base, description); + } + + private static String getKey(String namespace, String name) { + return namespace + "/" + name; + } + +} diff --git a/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java new file mode 100644 index 000000000000..39b5840f12d8 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/testables/TestableDeviceConfigTest.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.testables; + +import static android.provider.DeviceConfig.OnPropertyChangedListener; + +import static com.google.common.truth.Truth.assertThat; + +import android.app.ActivityThread; +import android.platform.test.annotations.Presubmit; +import android.provider.DeviceConfig; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** Tests that ensure appropriate settings are backed up. */ +@Presubmit +@RunWith(AndroidJUnit4.class) +@SmallTest +public class TestableDeviceConfigTest { + private static final String sNamespace = "namespace1"; + private static final String sKey = "key1"; + private static final String sValue = "value1"; + private static final long WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS = 2000; // 2 sec + + @Rule + public TestableDeviceConfig mTestableDeviceConfig = new TestableDeviceConfig(); + + @Test + public void getProperty_empty() { + String result = DeviceConfig.getProperty(sNamespace, sKey); + assertThat(result).isNull(); + } + + @Test + public void setAndGetProperty_sameNamespace() { + DeviceConfig.setProperty(sNamespace, sKey, sValue, false); + String result = DeviceConfig.getProperty(sNamespace, sKey); + assertThat(result).isEqualTo(sValue); + } + + @Test + public void setAndGetProperty_differentNamespace() { + String newNamespace = "namespace2"; + DeviceConfig.setProperty(sNamespace, sKey, sValue, false); + String result = DeviceConfig.getProperty(newNamespace, sKey); + assertThat(result).isNull(); + } + + @Test + public void setAndGetProperty_multipleNamespaces() { + String newNamespace = "namespace2"; + String newValue = "value2"; + DeviceConfig.setProperty(sNamespace, sKey, sValue, false); + DeviceConfig.setProperty(newNamespace, sKey, newValue, false); + String result = DeviceConfig.getProperty(sNamespace, sKey); + assertThat(result).isEqualTo(sValue); + result = DeviceConfig.getProperty(newNamespace, sKey); + assertThat(result).isEqualTo(newValue); + } + + @Test + public void setAndGetProperty_overrideValue() { + String newValue = "value2"; + DeviceConfig.setProperty(sNamespace, sKey, sValue, false); + DeviceConfig.setProperty(sNamespace, sKey, newValue, false); + String result = DeviceConfig.getProperty(sNamespace, sKey); + assertThat(result).isEqualTo(newValue); + } + + @Test + public void testListener() throws InterruptedException { + CountDownLatch countDownLatch = new CountDownLatch(1); + + OnPropertyChangedListener changeListener = (namespace, name, value) -> { + assertThat(namespace).isEqualTo(sNamespace); + assertThat(name).isEqualTo(sKey); + assertThat(value).isEqualTo(sValue); + countDownLatch.countDown(); + }; + try { + DeviceConfig.addOnPropertyChangedListener(sNamespace, + ActivityThread.currentApplication().getMainExecutor(), changeListener); + DeviceConfig.setProperty(sNamespace, sKey, sValue, false); + assertThat(countDownLatch.await( + WAIT_FOR_PROPERTY_CHANGE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue(); + } finally { + DeviceConfig.removeOnPropertyChangedListener(changeListener); + } + } + +} + + diff --git a/services/tests/rescueparty/how_to_run.txt b/services/tests/rescueparty/how_to_run.txt index 9528d39358eb..a3a26d6ccaa7 100644 --- a/services/tests/rescueparty/how_to_run.txt +++ b/services/tests/rescueparty/how_to_run.txt @@ -1,4 +1,3 @@ -# Per http://go/westworld-local-development#step3-test-atom-and-metric-locally-on-device , # In one terminal: make statsd_testdrive ./out/host/linux-x86/bin/statsd_testdrive 122 diff --git a/services/tests/servicestests/src/com/android/server/am/AppCompactorTest.java b/services/tests/servicestests/src/com/android/server/am/AppCompactorTest.java deleted file mode 100644 index 63015be6ef3f..000000000000 --- a/services/tests/servicestests/src/com/android/server/am/AppCompactorTest.java +++ /dev/null @@ -1,449 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.am; - -import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_ACTION_1; -import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_ACTION_2; -import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_STATSD_SAMPLE_RATE; -import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_1; -import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_2; -import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_3; -import static android.provider.DeviceConfig.ActivityManager.KEY_COMPACT_THROTTLE_4; -import static android.provider.DeviceConfig.ActivityManager.KEY_USE_COMPACTION; - -import static com.android.server.am.ActivityManagerService.Injector; -import static com.android.server.am.AppCompactor.compactActionIntToString; - -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; - -import android.os.Handler; -import android.os.HandlerThread; -import android.provider.DeviceConfig; -import android.support.test.uiautomator.UiDevice; - -import androidx.test.InstrumentationRegistry; -import androidx.test.runner.AndroidJUnit4; - -import com.android.server.appop.AppOpsService; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.io.File; -import java.io.IOException; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -/** - * Tests for {@link AppCompactor}. - * - * Build/Install/Run: - * atest FrameworksServicesTests:AppCompactorTest - */ -@RunWith(AndroidJUnit4.class) -public final class AppCompactorTest { - - private static final String CLEAR_DEVICE_CONFIG_KEY_CMD = - "device_config delete activity_manager"; - - @Mock private AppOpsService mAppOpsService; - private AppCompactor mCompactorUnderTest; - private HandlerThread mHandlerThread; - private Handler mHandler; - private CountDownLatch mCountDown; - - private static void clearDeviceConfig() throws IOException { - UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); - uiDevice.executeShellCommand( - CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_USE_COMPACTION); - uiDevice.executeShellCommand( - CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_ACTION_1); - uiDevice.executeShellCommand( - CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_ACTION_2); - uiDevice.executeShellCommand( - CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_THROTTLE_1); - uiDevice.executeShellCommand( - CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_THROTTLE_2); - uiDevice.executeShellCommand( - CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_THROTTLE_3); - uiDevice.executeShellCommand( - CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_THROTTLE_4); - uiDevice.executeShellCommand( - CLEAR_DEVICE_CONFIG_KEY_CMD + " " + KEY_COMPACT_STATSD_SAMPLE_RATE); - } - - @Before - public void setUp() throws IOException { - MockitoAnnotations.initMocks(this); - clearDeviceConfig(); - mHandlerThread = new HandlerThread(""); - mHandlerThread.start(); - mHandler = new Handler(mHandlerThread.getLooper()); - ActivityManagerService ams = new ActivityManagerService(new TestInjector()); - mCompactorUnderTest = new AppCompactor(ams, - new AppCompactor.PropertyChangedCallbackForTest() { - @Override - public void onPropertyChanged() { - if (mCountDown != null) { - mCountDown.countDown(); - } - } - }); - } - - @After - public void tearDown() throws IOException { - mHandlerThread.quit(); - mCountDown = null; - clearDeviceConfig(); - } - - @Test - public void init_setsDefaults() { - mCompactorUnderTest.init(); - assertThat(mCompactorUnderTest.useCompaction(), - is(mCompactorUnderTest.DEFAULT_USE_COMPACTION)); - assertThat(mCompactorUnderTest.mCompactActionSome, is( - compactActionIntToString(mCompactorUnderTest.DEFAULT_COMPACT_ACTION_1))); - assertThat(mCompactorUnderTest.mCompactActionFull, is( - compactActionIntToString(mCompactorUnderTest.DEFAULT_COMPACT_ACTION_2))); - assertThat(mCompactorUnderTest.mCompactThrottleSomeSome, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_1)); - assertThat(mCompactorUnderTest.mCompactThrottleSomeFull, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_2)); - assertThat(mCompactorUnderTest.mCompactThrottleFullSome, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3)); - assertThat(mCompactorUnderTest.mCompactThrottleFullFull, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4)); - assertThat(mCompactorUnderTest.mStatsdSampleRate, - is(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE)); - } - - @Test - public void init_withDeviceConfigSetsParameters() { - // When the DeviceConfig already has a flag value stored (note this test will need to - // change if the default value changes from false). - assertThat(mCompactorUnderTest.DEFAULT_USE_COMPACTION, is(false)); - DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, - KEY_USE_COMPACTION, "true", false); - DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, - KEY_COMPACT_ACTION_1, - Integer.toString((AppCompactor.DEFAULT_COMPACT_ACTION_1 + 1 % 4) + 1), false); - DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, - KEY_COMPACT_ACTION_2, - Integer.toString((AppCompactor.DEFAULT_COMPACT_ACTION_2 + 1 % 4) + 1), false); - DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, - KEY_COMPACT_THROTTLE_1, - Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1), false); - DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, - KEY_COMPACT_THROTTLE_2, - Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1), false); - DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, - KEY_COMPACT_THROTTLE_3, - Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1), false); - DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, - KEY_COMPACT_THROTTLE_4, - Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1), false); - DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, - KEY_COMPACT_STATSD_SAMPLE_RATE, - Float.toString(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false); - - // Then calling init will read and set that flag. - mCompactorUnderTest.init(); - assertThat(mCompactorUnderTest.useCompaction(), is(true)); - assertThat(mCompactorUnderTest.mCompactionThread.isAlive(), is(true)); - - assertThat(mCompactorUnderTest.mCompactActionSome, - is(compactActionIntToString((AppCompactor.DEFAULT_COMPACT_ACTION_1 + 1 % 4) + 1))); - assertThat(mCompactorUnderTest.mCompactActionFull, - is(compactActionIntToString((AppCompactor.DEFAULT_COMPACT_ACTION_2 + 1 % 4) + 1))); - assertThat(mCompactorUnderTest.mCompactThrottleSomeSome, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1)); - assertThat(mCompactorUnderTest.mCompactThrottleSomeFull, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1)); - assertThat(mCompactorUnderTest.mCompactThrottleFullSome, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1)); - assertThat(mCompactorUnderTest.mCompactThrottleFullFull, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1)); - assertThat(mCompactorUnderTest.mStatsdSampleRate, - is(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f)); - } - - @Test - public void useCompaction_listensToDeviceConfigChanges() throws InterruptedException { - assertThat(mCompactorUnderTest.useCompaction(), - is(mCompactorUnderTest.DEFAULT_USE_COMPACTION)); - // When we call init and change some the flag value... - mCompactorUnderTest.init(); - mCountDown = new CountDownLatch(1); - DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, - KEY_USE_COMPACTION, "true", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true)); - - // Then that new flag value is updated in the implementation. - assertThat(mCompactorUnderTest.useCompaction(), is(true)); - assertThat(mCompactorUnderTest.mCompactionThread.isAlive(), is(true)); - - // And again, setting the flag the other way. - mCountDown = new CountDownLatch(1); - DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, - KEY_USE_COMPACTION, "false", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true)); - assertThat(mCompactorUnderTest.useCompaction(), is(false)); - } - - @Test - public void useCompaction_listensToDeviceConfigChangesBadValues() throws InterruptedException { - assertThat(mCompactorUnderTest.useCompaction(), - is(mCompactorUnderTest.DEFAULT_USE_COMPACTION)); - mCompactorUnderTest.init(); - - // When we push an invalid flag value... - mCountDown = new CountDownLatch(1); - DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, - KEY_USE_COMPACTION, "foobar", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true)); - - // Then we set the default. - assertThat(mCompactorUnderTest.useCompaction(), is(AppCompactor.DEFAULT_USE_COMPACTION)); - } - - @Test - public void compactAction_listensToDeviceConfigChanges() throws InterruptedException { - mCompactorUnderTest.init(); - - // When we override new values for the compaction action with reasonable values... - - // There are four possible values for compactAction[Some|Full]. - for (int i = 1; i < 5; i++) { - mCountDown = new CountDownLatch(2); - int expectedSome = (mCompactorUnderTest.DEFAULT_COMPACT_ACTION_1 + i) % 4 + 1; - DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, - KEY_COMPACT_ACTION_1, Integer.toString(expectedSome), false); - int expectedFull = (mCompactorUnderTest.DEFAULT_COMPACT_ACTION_2 + i) % 4 + 1; - DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, - KEY_COMPACT_ACTION_2, Integer.toString(expectedFull), false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true)); - - // Then the updates are reflected in the flags. - assertThat(mCompactorUnderTest.mCompactActionSome, - is(compactActionIntToString(expectedSome))); - assertThat(mCompactorUnderTest.mCompactActionFull, - is(compactActionIntToString(expectedFull))); - } - } - - @Test - public void compactAction_listensToDeviceConfigChangesBadValues() throws InterruptedException { - mCompactorUnderTest.init(); - - // When we override new values for the compaction action with bad values ... - mCountDown = new CountDownLatch(2); - DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, - KEY_COMPACT_ACTION_1, "foo", false); - DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, - KEY_COMPACT_ACTION_2, "foo", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true)); - - // Then the default values are reflected in the flag - assertThat(mCompactorUnderTest.mCompactActionSome, - is(compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_1))); - assertThat(mCompactorUnderTest.mCompactActionFull, - is(compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_2))); - - mCountDown = new CountDownLatch(2); - DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, - KEY_COMPACT_ACTION_1, "", false); - DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, - KEY_COMPACT_ACTION_2, "", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true)); - - assertThat(mCompactorUnderTest.mCompactActionSome, - is(compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_1))); - assertThat(mCompactorUnderTest.mCompactActionFull, - is(compactActionIntToString(AppCompactor.DEFAULT_COMPACT_ACTION_2))); - } - - @Test - public void compactThrottle_listensToDeviceConfigChanges() throws InterruptedException { - mCompactorUnderTest.init(); - - // When we override new reasonable throttle values after init... - mCountDown = new CountDownLatch(4); - DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, - KEY_COMPACT_THROTTLE_1, - Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1), false); - DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, - KEY_COMPACT_THROTTLE_2, - Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1), false); - DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, - KEY_COMPACT_THROTTLE_3, - Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1), false); - DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, - KEY_COMPACT_THROTTLE_4, - Long.toString(AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1), false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true)); - - // Then those flags values are reflected in the compactor. - assertThat(mCompactorUnderTest.mCompactThrottleSomeSome, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_1 + 1)); - assertThat(mCompactorUnderTest.mCompactThrottleSomeFull, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_2 + 1)); - assertThat(mCompactorUnderTest.mCompactThrottleFullSome, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3 + 1)); - assertThat(mCompactorUnderTest.mCompactThrottleFullFull, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4 + 1)); - } - - @Test - public void compactThrottle_listensToDeviceConfigChangesBadValues() - throws IOException, InterruptedException { - mCompactorUnderTest.init(); - - // When one of the throttles is overridden with a bad value... - mCountDown = new CountDownLatch(1); - DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, - KEY_COMPACT_THROTTLE_1, "foo", false); - // Then all the throttles have the defaults set. - assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true)); - assertThat(mCompactorUnderTest.mCompactThrottleSomeSome, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_1)); - assertThat(mCompactorUnderTest.mCompactThrottleSomeFull, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_2)); - assertThat(mCompactorUnderTest.mCompactThrottleFullSome, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3)); - assertThat(mCompactorUnderTest.mCompactThrottleFullFull, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4)); - clearDeviceConfig(); - - // Repeat for each of the throttle keys. - mCountDown = new CountDownLatch(1); - DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, - KEY_COMPACT_THROTTLE_2, "foo", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true)); - assertThat(mCompactorUnderTest.mCompactThrottleSomeSome, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_1)); - assertThat(mCompactorUnderTest.mCompactThrottleSomeFull, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_2)); - assertThat(mCompactorUnderTest.mCompactThrottleFullSome, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3)); - assertThat(mCompactorUnderTest.mCompactThrottleFullFull, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4)); - clearDeviceConfig(); - - mCountDown = new CountDownLatch(1); - DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, - KEY_COMPACT_THROTTLE_3, "foo", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true)); - assertThat(mCompactorUnderTest.mCompactThrottleSomeSome, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_1)); - assertThat(mCompactorUnderTest.mCompactThrottleSomeFull, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_2)); - assertThat(mCompactorUnderTest.mCompactThrottleFullSome, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3)); - assertThat(mCompactorUnderTest.mCompactThrottleFullFull, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4)); - clearDeviceConfig(); - - mCountDown = new CountDownLatch(1); - DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, - KEY_COMPACT_THROTTLE_4, "foo", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true)); - assertThat(mCompactorUnderTest.mCompactThrottleSomeSome, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_1)); - assertThat(mCompactorUnderTest.mCompactThrottleSomeFull, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_2)); - assertThat(mCompactorUnderTest.mCompactThrottleFullSome, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_3)); - assertThat(mCompactorUnderTest.mCompactThrottleFullFull, - is(AppCompactor.DEFAULT_COMPACT_THROTTLE_4)); - } - - @Test - public void statsdSampleRate_listensToDeviceConfigChanges() throws InterruptedException { - mCompactorUnderTest.init(); - - // When we override mStatsdSampleRate with a reasonable values ... - mCountDown = new CountDownLatch(1); - DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, - KEY_COMPACT_STATSD_SAMPLE_RATE, - Float.toString(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f), false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true)); - - // Then that override is reflected in the compactor. - assertThat(mCompactorUnderTest.mStatsdSampleRate, - is(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE + 0.1f)); - } - - @Test - public void statsdSanokeRate_listensToDeviceConfigChangesBadValues() - throws InterruptedException { - mCompactorUnderTest.init(); - - // When we override mStatsdSampleRate with a reasonable values ... - mCountDown = new CountDownLatch(1); - DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, - KEY_COMPACT_STATSD_SAMPLE_RATE, "foo", false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true)); - - // Then that override is reflected in the compactor. - assertThat(mCompactorUnderTest.mStatsdSampleRate, - is(AppCompactor.DEFAULT_STATSD_SAMPLE_RATE)); - } - - @Test - public void statsdSanokeRate_listensToDeviceConfigChangesOutOfRangeValues() - throws InterruptedException { - mCompactorUnderTest.init(); - - // When we override mStatsdSampleRate with an value outside of [0..1]... - mCountDown = new CountDownLatch(1); - DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, - KEY_COMPACT_STATSD_SAMPLE_RATE, - Float.toString(-1.0f), false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true)); - - // Then the values is capped in the range. - assertThat(mCompactorUnderTest.mStatsdSampleRate, is(0.0f)); - - mCountDown = new CountDownLatch(1); - DeviceConfig.setProperty(DeviceConfig.ActivityManager.NAMESPACE, - KEY_COMPACT_STATSD_SAMPLE_RATE, - Float.toString(1.01f), false); - assertThat(mCountDown.await(5, TimeUnit.SECONDS), is(true)); - - // Then the values is capped in the range. - assertThat(mCompactorUnderTest.mStatsdSampleRate, is(1.0f)); - } - - private class TestInjector extends Injector { - @Override - public AppOpsService getAppOpsService(File file, Handler handler) { - return mAppOpsService; - } - - @Override - public Handler getUiHandler(ActivityManagerService service) { - return mHandler; - } - } -} diff --git a/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java b/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java index 5cb6cbb93b5b..26b122411c6b 100644 --- a/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java +++ b/services/tests/servicestests/src/com/android/server/backup/testutils/IPackageManagerStub.java @@ -213,7 +213,8 @@ public class IPackageManagerStub implements IPackageManager { @Override public void updatePermissionFlags(String permissionName, String packageName, int flagMask, - int flagValues, int userId) throws RemoteException { + int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) + throws RemoteException { } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index de782a52eb53..4293247e7d0c 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -4167,7 +4167,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertTrue(dpm.isResetPasswordTokenActive(admin1)); // test reset password with token - when(getServices().lockPatternUtils.setLockCredentialWithToken(eq(password), + when(getServices().lockPatternUtils.setLockCredentialWithToken(eq(password.getBytes()), eq(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD), eq(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC), eq(handle), eq(token), eq(UserHandle.USER_SYSTEM))) @@ -5214,7 +5214,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { .thenReturn(DpmMockContext.CALLER_USER_HANDLE); dpms.mUserPasswordMetrics.put( DpmMockContext.CALLER_USER_HANDLE, - PasswordMetrics.computeForPassword("asdf")); + PasswordMetrics.computeForPassword("asdf".getBytes())); assertEquals(PASSWORD_COMPLEXITY_MEDIUM, dpm.getPasswordComplexity()); } @@ -5231,10 +5231,10 @@ public class DevicePolicyManagerTest extends DpmTestBase { dpms.mUserPasswordMetrics.put( DpmMockContext.CALLER_USER_HANDLE, - PasswordMetrics.computeForPassword("asdf")); + PasswordMetrics.computeForPassword("asdf".getBytes())); dpms.mUserPasswordMetrics.put( parentUser.id, - PasswordMetrics.computeForPassword("parentUser")); + PasswordMetrics.computeForPassword("parentUser".getBytes())); assertEquals(PASSWORD_COMPLEXITY_HIGH, dpm.getPasswordComplexity()); } diff --git a/services/tests/servicestests/src/com/android/server/display/AppSaturationControllerTest.java b/services/tests/servicestests/src/com/android/server/display/color/AppSaturationControllerTest.java index e51884429718..7c9a81d2e094 100644 --- a/services/tests/servicestests/src/com/android/server/display/AppSaturationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/display/color/AppSaturationControllerTest.java @@ -14,9 +14,9 @@ * limitations under the License. */ -package com.android.server.display; +package com.android.server.display.color; -import static com.android.server.display.AppSaturationController.TRANSLATION_VECTOR; +import static com.android.server.display.color.AppSaturationController.TRANSLATION_VECTOR; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -29,7 +29,7 @@ import android.os.UserHandle; import androidx.test.runner.AndroidJUnit4; -import com.android.server.display.ColorDisplayService.ColorTransformController; +import com.android.server.display.color.ColorDisplayService.ColorTransformController; import org.junit.After; import org.junit.Before; diff --git a/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java b/services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java index 01759d2e8f4a..2f427b0e26f9 100644 --- a/services/tests/servicestests/src/com/android/server/display/ColorDisplayServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.display; +package com.android.server.display.color; import static com.google.common.truth.Truth.assertWithMessage; @@ -70,7 +70,7 @@ public class ColorDisplayServiceTest { private MockTwilightManager mTwilightManager; - private ColorDisplayService mColorDisplayService; + private ColorDisplayService mCds; private ColorDisplayService.BinderService mBinderService; @BeforeClass @@ -96,17 +96,17 @@ public class ColorDisplayServiceTest { mTwilightManager = new MockTwilightManager(); LocalServices.addService(TwilightManager.class, mTwilightManager); - mColorDisplayService = new ColorDisplayService(mContext); - mBinderService = mColorDisplayService.new BinderService(); + mCds = new ColorDisplayService(mContext); + mBinderService = mCds.new BinderService(); LocalServices.addService(ColorDisplayService.ColorDisplayServiceInternal.class, - mColorDisplayService.new ColorDisplayServiceInternal()); + mCds.new ColorDisplayServiceInternal()); } @After public void tearDown() { LocalServices.removeServiceForTest(TwilightManager.class); - mColorDisplayService = null; + mCds = null; mTwilightManager = null; @@ -1003,7 +1003,7 @@ public class ColorDisplayServiceTest { /* Since we are using FakeSettingsProvider which could not trigger observer change, * force an update here.*/ - mColorDisplayService.updateDisplayWhiteBalanceStatus(); + mCds.updateDisplayWhiteBalanceStatus(); assertDwbActive(false); } @@ -1015,12 +1015,12 @@ public class ColorDisplayServiceTest { setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */); setActivated(true /* activated */, -30 /* lastActivatedTimeOffset */); - mColorDisplayService.updateDisplayWhiteBalanceStatus(); + mCds.updateDisplayWhiteBalanceStatus(); assertDwbActive(false); /* Disable nightlight */ setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */); - mColorDisplayService.updateDisplayWhiteBalanceStatus(); + mCds.updateDisplayWhiteBalanceStatus(); assertDwbActive(true); } @@ -1031,48 +1031,48 @@ public class ColorDisplayServiceTest { startService(); mBinderService.setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL); - mColorDisplayService.updateDisplayWhiteBalanceStatus(); + mCds.updateDisplayWhiteBalanceStatus(); assertDwbActive(true); } @Test public void displayWhiteBalance_setTemperatureOverMax() { - int max = mColorDisplayService.mDisplayWhiteBalanceTintController.mTemperatureMax; + int max = mCds.mDisplayWhiteBalanceTintController.mTemperatureMax; ColorDisplayService.ColorDisplayServiceInternal cdsInternal = LocalServices.getService( - ColorDisplayService.ColorDisplayServiceInternal.class); - cdsInternal.setDisplayWhiteBalanceColorTemperature(max+1); + ColorDisplayService.ColorDisplayServiceInternal.class); + cdsInternal.setDisplayWhiteBalanceColorTemperature(max + 1); assertWithMessage("Unexpected temperature set") - .that(mColorDisplayService.mDisplayWhiteBalanceTintController.mCurrentColorTemperature) + .that(mCds.mDisplayWhiteBalanceTintController.mCurrentColorTemperature) .isEqualTo(max); } @Test public void displayWhiteBalance_setTemperatureBelowMin() { - int min = mColorDisplayService.mDisplayWhiteBalanceTintController.mTemperatureMin; + int min = mCds.mDisplayWhiteBalanceTintController.mTemperatureMin; ColorDisplayService.ColorDisplayServiceInternal cdsInternal = LocalServices.getService( - ColorDisplayService.ColorDisplayServiceInternal.class); + ColorDisplayService.ColorDisplayServiceInternal.class); cdsInternal.setDisplayWhiteBalanceColorTemperature(min - 1); assertWithMessage("Unexpected temperature set") - .that(mColorDisplayService.mDisplayWhiteBalanceTintController.mCurrentColorTemperature) + .that(mCds.mDisplayWhiteBalanceTintController.mCurrentColorTemperature) .isEqualTo(min); } @Test public void displayWhiteBalance_setValidTemperature() { - int min = mColorDisplayService.mDisplayWhiteBalanceTintController.mTemperatureMin; - int max = mColorDisplayService.mDisplayWhiteBalanceTintController.mTemperatureMax; + int min = mCds.mDisplayWhiteBalanceTintController.mTemperatureMin; + int max = mCds.mDisplayWhiteBalanceTintController.mTemperatureMax; int valToSet = (min + max) / 2; ColorDisplayService.ColorDisplayServiceInternal cdsInternal = LocalServices.getService( - ColorDisplayService.ColorDisplayServiceInternal.class); + ColorDisplayService.ColorDisplayServiceInternal.class); cdsInternal.setDisplayWhiteBalanceColorTemperature(valToSet); assertWithMessage("Unexpected temperature set") - .that(mColorDisplayService.mDisplayWhiteBalanceTintController.mCurrentColorTemperature) + .that(mCds.mDisplayWhiteBalanceTintController.mCurrentColorTemperature) .isEqualTo(valToSet); } @@ -1171,14 +1171,14 @@ public class ColorDisplayServiceTest { } /** - * Convenience method to start {@link #mColorDisplayService}. + * Convenience method to start {@link #mCds}. */ private void startService() { Secure.putIntForUser(mContext.getContentResolver(), Secure.USER_SETUP_COMPLETE, 1, mUserId); InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { - mColorDisplayService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); - mColorDisplayService.onStartUser(mUserId); + mCds.onBootPhase(SystemService.PHASE_BOOT_COMPLETED); + mCds.onStartUser(mUserId); }); } @@ -1224,7 +1224,7 @@ public class ColorDisplayServiceTest { */ private void assertDwbActive(boolean enabled) { assertWithMessage("Incorrect Display White Balance state") - .that(mColorDisplayService.mDisplayWhiteBalanceTintController.isActivated()) + .that(mCds.mDisplayWhiteBalanceTintController.isActivated()) .isEqualTo(enabled); } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java index cf89cb8f7a15..aadf92448a52 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java @@ -87,6 +87,7 @@ public class BaseLockSettingsServiceTests extends AndroidTestCase { MockSyntheticPasswordManager mSpManager; IAuthSecret mAuthSecretService; WindowManagerInternal mMockWindowManager; + FakeGsiService mGsiService; protected boolean mHasSecureLockScreen; @Override @@ -101,6 +102,7 @@ public class BaseLockSettingsServiceTests extends AndroidTestCase { mDevicePolicyManager = mock(DevicePolicyManager.class); mDevicePolicyManagerInternal = mock(DevicePolicyManagerInternal.class); mMockWindowManager = mock(WindowManagerInternal.class); + mGsiService = new FakeGsiService(); LocalServices.removeServiceForTest(LockSettingsInternal.class); LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class); @@ -137,7 +139,7 @@ public class BaseLockSettingsServiceTests extends AndroidTestCase { mAuthSecretService = mock(IAuthSecret.class); mService = new LockSettingsServiceTestable(mContext, mLockPatternUtils, mStorage, mGateKeeperService, mKeyStore, setUpStorageManagerMock(), mActivityManager, - mSpManager, mAuthSecretService); + mSpManager, mAuthSecretService, mGsiService); when(mUserManager.getUserInfo(eq(PRIMARY_USER_ID))).thenReturn(PRIMARY_USER_INFO); mPrimaryUserProfiles.add(PRIMARY_USER_INFO); installChildProfile(MANAGED_PROFILE_USER_ID); diff --git a/services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java index d2caa0af0ba2..94d21ddeaa2b 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java @@ -18,24 +18,22 @@ package com.android.server.locksettings; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; -import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD; import static com.android.server.testutils.TestUtils.assertExpectException; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.when; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.os.RemoteException; import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.VerifyCredentialResponse; -import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult; - -import java.util.ArrayList; import org.mockito.ArgumentCaptor; +import java.util.ArrayList; + /** * Run the synthetic password tests with caching enabled. * @@ -56,10 +54,10 @@ public class CachedSyntheticPasswordTests extends SyntheticPasswordTests { } public void testSyntheticPasswordClearCredentialUntrusted() throws RemoteException { - final String PASSWORD = "testSyntheticPasswordClearCredential-password"; - final String NEWPASSWORD = "testSyntheticPasswordClearCredential-newpassword"; + final byte[] password = "testSyntheticPasswordClearCredential-password".getBytes(); + final byte[] newPassword = "testSyntheticPasswordClearCredential-newpassword".getBytes(); - initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID); + initializeCredentialUnderSP(password, PRIMARY_USER_ID); long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID); // clear password mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, null, @@ -67,45 +65,46 @@ public class CachedSyntheticPasswordTests extends SyntheticPasswordTests { assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); // set a new password - mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, + mService.setLockCredential(newPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID); - assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) - .getResponseCode()); + assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(newPassword, + LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) + .getResponseCode()); assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); } public void testSyntheticPasswordChangeCredentialUntrusted() throws RemoteException { - final String PASSWORD = "testSyntheticPasswordClearCredential-password"; - final String NEWPASSWORD = "testSyntheticPasswordClearCredential-newpassword"; + final byte[] password = "testSyntheticPasswordClearCredential-password".getBytes(); + final byte[] newPassword = "testSyntheticPasswordClearCredential-newpassword".getBytes(); - initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID); + initializeCredentialUnderSP(password, PRIMARY_USER_ID); long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID); // Untrusted change password - mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, + mService.setLockCredential(newPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID); assertNotEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); // Verify the password - assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) - .getResponseCode()); + assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(newPassword, + LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID).getResponseCode()); } public void testUntrustedCredentialChangeMaintainsAuthSecret() throws RemoteException { - final String PASSWORD = "testUntrustedCredentialChangeMaintainsAuthSecret-password"; - final String NEWPASSWORD = "testUntrustedCredentialChangeMaintainsAuthSecret-newpassword"; + final byte[] password = + "testUntrustedCredentialChangeMaintainsAuthSecret-password".getBytes(); + final byte[] newPassword = + "testUntrustedCredentialChangeMaintainsAuthSecret-newpassword".getBytes(); - initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID); + initializeCredentialUnderSP(password, PRIMARY_USER_ID); // Untrusted change password - mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, + mService.setLockCredential(newPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID); // Verify the password - assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) - .getResponseCode()); + assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(newPassword, + LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) + .getResponseCode()); // Ensure the same secret was passed each time ArgumentCaptor<ArrayList<Byte>> secret = ArgumentCaptor.forClass(ArrayList.class); @@ -114,27 +113,29 @@ public class CachedSyntheticPasswordTests extends SyntheticPasswordTests { } public void testUntrustedCredentialChangeBlockedIfSpNotCached() throws RemoteException { - final String PASSWORD = "testUntrustedCredentialChangeBlockedIfSpNotCached-password"; - final String NEWPASSWORD = "testUntrustedCredentialChangeBlockedIfSpNotCached-newpassword"; + final byte[] password = + "testUntrustedCredentialChangeBlockedIfSpNotCached-password".getBytes(); + final byte[] newPassword = + "testUntrustedCredentialChangeBlockedIfSpNotCached-newpassword".getBytes(); // Disable caching for this test enableSpCaching(false); - initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID); + initializeCredentialUnderSP(password, PRIMARY_USER_ID); long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID); // Untrusted change password assertExpectException(IllegalStateException.class, /* messageRegex= */ null, - () -> mService.setLockCredential( - NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, - null, PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID)); + () -> mService.setLockCredential(newPassword, + LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, + PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID)); assertEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); // Verify the new password doesn't work but the old one still does - assertEquals(VerifyCredentialResponse.RESPONSE_ERROR, mService.verifyCredential( - NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) + assertEquals(VerifyCredentialResponse.RESPONSE_ERROR, mService.verifyCredential(newPassword, + LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) .getResponseCode()); - assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) + assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(password, + LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) .getResponseCode()); } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/FakeGsiService.java b/services/tests/servicestests/src/com/android/server/locksettings/FakeGsiService.java new file mode 100644 index 000000000000..10331631cbe0 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/locksettings/FakeGsiService.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.locksettings; + +public class FakeGsiService { + private boolean mIsGsiRunning; + + public boolean isGsiRunning() { + return mIsGsiRunning; + } + + public void setIsGsiRunning(boolean isGsiRunning) { + mIsGsiRunning = isGsiRunning; + } +} diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java index fe683abe7e1b..f4632db3cb6d 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java @@ -44,11 +44,12 @@ public class LockSettingsServiceTestable extends LockSettingsService { private IStorageManager mStorageManager; private SyntheticPasswordManager mSpManager; private IAuthSecret mAuthSecretService; + private FakeGsiService mGsiService; public MockInjector(Context context, LockSettingsStorage storage, KeyStore keyStore, IActivityManager activityManager, LockPatternUtils lockPatternUtils, IStorageManager storageManager, SyntheticPasswordManager spManager, - IAuthSecret authSecretService) { + IAuthSecret authSecretService, FakeGsiService gsiService) { super(context); mLockSettingsStorage = storage; mKeyStore = keyStore; @@ -56,6 +57,7 @@ public class LockSettingsServiceTestable extends LockSettingsService { mLockPatternUtils = lockPatternUtils; mStorageManager = storageManager; mSpManager = spManager; + mGsiService = gsiService; } @Override @@ -104,28 +106,39 @@ public class LockSettingsServiceTestable extends LockSettingsService { } @Override + public boolean hasBiometrics() { + return false; + } + + @Override public int binderGetCallingUid() { return Process.SYSTEM_UID; } + + @Override + public boolean isGsiRunning() { + return mGsiService.isGsiRunning(); + } } protected LockSettingsServiceTestable(Context context, LockPatternUtils lockPatternUtils, LockSettingsStorage storage, FakeGateKeeperService gatekeeper, KeyStore keystore, IStorageManager storageManager, IActivityManager mActivityManager, - SyntheticPasswordManager spManager, IAuthSecret authSecretService) { + SyntheticPasswordManager spManager, IAuthSecret authSecretService, + FakeGsiService gsiService) { super(new MockInjector(context, storage, keystore, mActivityManager, lockPatternUtils, - storageManager, spManager, authSecretService)); + storageManager, spManager, authSecretService, gsiService)); mGateKeeperService = gatekeeper; mAuthSecretService = authSecretService; } @Override - protected void tieProfileLockToParent(int userId, String password) { - mStorage.writeChildProfileLock(userId, password.getBytes()); + protected void tieProfileLockToParent(int userId, byte[] password) { + mStorage.writeChildProfileLock(userId, password); } @Override - protected String getDecryptedPasswordForTiedProfile(int userId) throws FileNotFoundException, + protected byte[] getDecryptedPasswordForTiedProfile(int userId) throws FileNotFoundException, KeyPermanentlyInvalidatedException { byte[] storedData = mStorage.readChildProfileLock(userId); if (storedData == null) { @@ -138,7 +151,7 @@ public class LockSettingsServiceTestable extends LockSettingsService { } catch (RemoteException e) { // shouldn't happen. } - return new String(storedData); + return storedData; } } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java index 5124803ee298..6e0ba3cb366c 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java @@ -84,8 +84,8 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { initializeStorageWithCredential(PRIMARY_USER_ID, "password", CREDENTIAL_TYPE_PASSWORD, sid); try { - mService.setLockCredential("newpwd", CREDENTIAL_TYPE_PASSWORD, "badpwd", - PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID); + mService.setLockCredential("newpwd".getBytes(), CREDENTIAL_TYPE_PASSWORD, + "badpwd".getBytes(), PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID); fail("Did not fail when enrolling using incorrect credential"); } catch (RemoteException expected) { assertTrue(expected.getMessage().equals(FAILED_MESSAGE)); @@ -96,7 +96,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { public void testClearPasswordPrimaryUser() throws RemoteException { final String PASSWORD = "password"; initializeStorageWithCredential(PRIMARY_USER_ID, PASSWORD, CREDENTIAL_TYPE_PASSWORD, 1234); - mService.setLockCredential(null, CREDENTIAL_TYPE_NONE, PASSWORD, + mService.setLockCredential(null, CREDENTIAL_TYPE_NONE, PASSWORD.getBytes(), PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID); assertFalse(mService.havePassword(PRIMARY_USER_ID)); assertFalse(mService.havePattern(PRIMARY_USER_ID)); @@ -106,7 +106,8 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { public void testManagedProfileUnifiedChallenge() throws RemoteException { final String firstUnifiedPassword = "testManagedProfileUnifiedChallenge-pwd-1"; final String secondUnifiedPassword = "testManagedProfileUnifiedChallenge-pwd-2"; - mService.setLockCredential(firstUnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, + mService.setLockCredential(firstUnifiedPassword.getBytes(), + LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, PASSWORD_QUALITY_COMPLEX, PRIMARY_USER_ID); mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null); final long primarySid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID); @@ -125,8 +126,8 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { mGateKeeperService.clearAuthToken(TURNED_OFF_PROFILE_USER_ID); // verify credential assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - firstUnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) - .getResponseCode()); + firstUnifiedPassword.getBytes(), LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, + PRIMARY_USER_ID).getResponseCode()); // Verify that we have a new auth token for the profile assertNotNull(mGateKeeperService.getAuthToken(MANAGED_PROFILE_USER_ID)); @@ -141,15 +142,16 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { */ mStorageManager.setIgnoreBadUnlock(true); // Change primary password and verify that profile SID remains - mService.setLockCredential(secondUnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, - firstUnifiedPassword, PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID); + mService.setLockCredential(secondUnifiedPassword.getBytes(), + LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, + firstUnifiedPassword.getBytes(), PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID); mStorageManager.setIgnoreBadUnlock(false); assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID)); assertNull(mGateKeeperService.getAuthToken(TURNED_OFF_PROFILE_USER_ID)); // Clear unified challenge mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, - secondUnifiedPassword, PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID); + secondUnifiedPassword.getBytes(), PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID); assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); assertEquals(0, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID)); assertEquals(0, mGateKeeperService.getSecureUserId(TURNED_OFF_PROFILE_USER_ID)); @@ -158,14 +160,16 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { public void testManagedProfileSeparateChallenge() throws RemoteException { final String primaryPassword = "testManagedProfileSeparateChallenge-primary"; final String profilePassword = "testManagedProfileSeparateChallenge-profile"; - mService.setLockCredential(primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, + mService.setLockCredential(primaryPassword.getBytes(), + LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, PASSWORD_QUALITY_COMPLEX, PRIMARY_USER_ID); /* Currently in LockSettingsService.setLockCredential, unlockUser() is called with the new * credential as part of verifyCredential() before the new credential is committed in * StorageManager. So we relax the check in our mock StorageManager to allow that. */ mStorageManager.setIgnoreBadUnlock(true); - mService.setLockCredential(profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, + mService.setLockCredential(profilePassword.getBytes(), + LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, PASSWORD_QUALITY_COMPLEX, MANAGED_PROFILE_USER_ID); mStorageManager.setIgnoreBadUnlock(false); @@ -179,31 +183,32 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { mGateKeeperService.clearAuthToken(MANAGED_PROFILE_USER_ID); // verify primary credential assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) - .getResponseCode()); + primaryPassword.getBytes(), LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, + PRIMARY_USER_ID).getResponseCode()); assertNull(mGateKeeperService.getAuthToken(MANAGED_PROFILE_USER_ID)); // verify profile credential assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, + profilePassword.getBytes(), LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, MANAGED_PROFILE_USER_ID).getResponseCode()); assertNotNull(mGateKeeperService.getAuthToken(MANAGED_PROFILE_USER_ID)); assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID)); // Change primary credential and make sure we don't affect profile mStorageManager.setIgnoreBadUnlock(true); - mService.setLockCredential("pwd", LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, - primaryPassword, PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID); + mService.setLockCredential("pwd".getBytes(), LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, + primaryPassword.getBytes(), PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID); mStorageManager.setIgnoreBadUnlock(false); assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, + profilePassword.getBytes(), LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, MANAGED_PROFILE_USER_ID).getResponseCode()); assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID)); } private void testCreateCredential(int userId, String credential, int type, int quality) throws RemoteException { - mService.setLockCredential(credential, type, null, quality, userId); + mService.setLockCredential(credential.getBytes(), type, null, quality, + userId); assertVerifyCredentials(userId, credential, type, -1); } @@ -212,7 +217,8 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { mHasSecureLockScreen = false; try { - mService.setLockCredential(credential, type, null, quality, userId); + mService.setLockCredential(credential.getBytes(), type, null, quality, + userId); fail("An exception should have been thrown."); } catch (UnsupportedOperationException e) { // Success - the exception was expected. @@ -226,15 +232,16 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { String oldCredential, int oldType, int quality) throws RemoteException { final long sid = 1234; initializeStorageWithCredential(userId, oldCredential, oldType, sid); - mService.setLockCredential(newCredential, newType, oldCredential, quality, userId); + mService.setLockCredential(newCredential.getBytes(), newType, oldCredential.getBytes(), + quality, userId); assertVerifyCredentials(userId, newCredential, newType, sid); } private void assertVerifyCredentials(int userId, String credential, int type, long sid) throws RemoteException{ final long challenge = 54321; - VerifyCredentialResponse response = mService.verifyCredential(credential, type, challenge, - userId); + VerifyCredentialResponse response = mService.verifyCredential(credential.getBytes(), + type, challenge, userId); assertEquals(GateKeeperResponse.RESPONSE_OK, response.getResponseCode()); if (sid != -1) assertEquals(sid, mGateKeeperService.getSecureUserId(userId)); @@ -253,18 +260,19 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { incorrectType = LockPatternUtils.CREDENTIAL_TYPE_PASSWORD; } // check for bad type - assertEquals(GateKeeperResponse.RESPONSE_ERROR, mService.verifyCredential(credential, - incorrectType, challenge, userId).getResponseCode()); + assertEquals(GateKeeperResponse.RESPONSE_ERROR, mService.verifyCredential( + credential.getBytes(), incorrectType, challenge, userId).getResponseCode()); // check for bad credential - assertEquals(GateKeeperResponse.RESPONSE_ERROR, mService.verifyCredential("0" + credential, - type, challenge, userId).getResponseCode()); + assertEquals(GateKeeperResponse.RESPONSE_ERROR, mService.verifyCredential( + ("0" + credential).getBytes(), type, challenge, userId).getResponseCode()); } private void initializeStorageWithCredential(int userId, String credential, int type, long sid) throws RemoteException { + byte[] credentialBytes = credential == null ? null : credential.getBytes(); byte[] oldHash = new VerifyHandle(credential.getBytes(), sid).toBytes(); if (mService.shouldMigrateToSyntheticPasswordLocked(userId)) { - mService.initializeSyntheticPasswordLocked(oldHash, credential, type, + mService.initializeSyntheticPasswordLocked(oldHash, credentialBytes, type, type == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD ? PASSWORD_QUALITY_ALPHABETIC : PASSWORD_QUALITY_SOMETHING, userId); } else { diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java index 929c3b525db9..fcfc6d2267c5 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java @@ -85,23 +85,24 @@ public class LockSettingsShellCommandTest { public void testWrongPassword() throws Exception { when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false); when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true); - when(mLockPatternUtils.checkPassword("1234", mUserId)).thenReturn(false); + when(mLockPatternUtils.checkPassword("1234".getBytes(), mUserId)).thenReturn(false); assertEquals(-1, mCommand.exec(mBinder, in, out, err, new String[] { "set-pin", "--old", "1234" }, mShellCallback, mResultReceiver)); - verify(mLockPatternUtils, never()).saveLockPassword(any(), any(), anyInt(), anyInt()); + verify(mLockPatternUtils, never()).saveLockPassword(any(byte[].class), any(byte[].class), + anyInt(), anyInt()); } @Test public void testChangePin() throws Exception { when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false); when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true); - when(mLockPatternUtils.checkPassword("1234", mUserId)).thenReturn(true); + when(mLockPatternUtils.checkPassword("1234".getBytes(), mUserId)).thenReturn(true); assertEquals(0, mCommand.exec(new Binder(), in, out, err, new String[] { "set-pin", "--old", "1234", "4321" }, mShellCallback, mResultReceiver)); - verify(mLockPatternUtils).saveLockPassword("4321", "1234", PASSWORD_QUALITY_NUMERIC, - mUserId); + verify(mLockPatternUtils).saveLockPassword("4321".getBytes(), "1234".getBytes(), + PASSWORD_QUALITY_NUMERIC, mUserId); } @Test @@ -118,12 +119,12 @@ public class LockSettingsShellCommandTest { public void testChangePassword() throws Exception { when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false); when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true); - when(mLockPatternUtils.checkPassword("1234", mUserId)).thenReturn(true); + when(mLockPatternUtils.checkPassword("1234".getBytes(), mUserId)).thenReturn(true); assertEquals(0, mCommand.exec(new Binder(), in, out, err, new String[] { "set-password", "--old", "1234", "4321" }, mShellCallback, mResultReceiver)); - verify(mLockPatternUtils).saveLockPassword("4321", "1234", PASSWORD_QUALITY_ALPHABETIC, - mUserId); + verify(mLockPatternUtils).saveLockPassword("4321".getBytes(), "1234".getBytes(), + PASSWORD_QUALITY_ALPHABETIC, mUserId); } @Test @@ -144,7 +145,8 @@ public class LockSettingsShellCommandTest { assertEquals(0, mCommand.exec(new Binder(), in, out, err, new String[] { "set-pattern", "--old", "1234", "4321" }, mShellCallback, mResultReceiver)); - verify(mLockPatternUtils).saveLockPattern(stringToPattern("4321"), "1234", mUserId); + verify(mLockPatternUtils).saveLockPattern(stringToPattern("4321"), "1234".getBytes(), + mUserId); } @Test @@ -165,6 +167,6 @@ public class LockSettingsShellCommandTest { assertEquals(0, mCommand.exec(new Binder(), in, out, err, new String[] { "clear", "--old", "1234" }, mShellCallback, mResultReceiver)); - verify(mLockPatternUtils).clearLock("1234", mUserId); + verify(mLockPatternUtils).clearLock("1234".getBytes(), mUserId); } } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java b/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java index 6f681797b88a..b9cb730caae1 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/MockSyntheticPasswordManager.java @@ -93,9 +93,13 @@ public class MockSyntheticPasswordManager extends SyntheticPasswordManager { } @Override - protected byte[] scrypt(String password, byte[] salt, int N, int r, int p, int outLen) { + protected byte[] scrypt(byte[] password, byte[] salt, int n, int r, int p, int outLen) { try { - PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 10, outLen * 8); + char[] passwordChars = new char[password.length]; + for (int i = 0; i < password.length; i++) { + passwordChars[i] = (char) password[i]; + } + PBEKeySpec spec = new PBEKeySpec(passwordChars, salt, 10, outLen * 8); SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); return f.generateSecret(spec).getEncoded(); } catch (InvalidKeySpecException | NoSuchAlgorithmException e) { diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java index 0595a5b2e9a0..e6e020dbdb97 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java @@ -65,22 +65,23 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { public void testPasswordBasedSyntheticPassword() throws RemoteException { final int USER_ID = 10; - final String PASSWORD = "user-password"; - final String BADPASSWORD = "bad-password"; + final byte[] password = "user-password".getBytes(); + final byte[] badPassword = "bad-password".getBytes(); MockSyntheticPasswordManager manager = new MockSyntheticPasswordManager(mContext, mStorage, mGateKeeperService, mUserManager); AuthenticationToken authToken = manager.newSyntheticPasswordAndSid(mGateKeeperService, null, null, USER_ID); - long handle = manager.createPasswordBasedSyntheticPassword(mGateKeeperService, PASSWORD, - LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, authToken, PASSWORD_QUALITY_ALPHABETIC, - USER_ID); + long handle = manager.createPasswordBasedSyntheticPassword(mGateKeeperService, + password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, authToken, + PASSWORD_QUALITY_ALPHABETIC, USER_ID); AuthenticationResult result = manager.unwrapPasswordBasedSyntheticPassword( - mGateKeeperService, handle, PASSWORD, USER_ID, null); - assertEquals(result.authToken.deriveKeyStorePassword(), authToken.deriveKeyStorePassword()); + mGateKeeperService, handle, password, USER_ID, null); + assertArrayEquals(result.authToken.deriveKeyStorePassword(), + authToken.deriveKeyStorePassword()); result = manager.unwrapPasswordBasedSyntheticPassword(mGateKeeperService, handle, - BADPASSWORD, USER_ID, null); + badPassword, USER_ID, null); assertNull(result.authToken); } @@ -97,30 +98,30 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { } public void testPasswordMigration() throws RemoteException { - final String PASSWORD = "testPasswordMigration-password"; + final byte[] password = "testPasswordMigration-password".getBytes(); disableSyntheticPassword(); - mService.setLockCredential(PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, + mService.setLockCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID); long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID); final byte[] primaryStorageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID); enableSyntheticPassword(); // Performs migration assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) + password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) .getResponseCode()); assertEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); assertTrue(hasSyntheticPassword(PRIMARY_USER_ID)); // SP-based verification - assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) + assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(password, + LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) .getResponseCode()); assertArrayNotEquals(primaryStorageKey, mStorageManager.getUserUnlockToken(PRIMARY_USER_ID)); } - protected void initializeCredentialUnderSP(String password, int userId) throws RemoteException { + protected void initializeCredentialUnderSP(byte[] password, int userId) throws RemoteException { enableSyntheticPassword(); int quality = password != null ? PASSWORD_QUALITY_ALPHABETIC : PASSWORD_QUALITY_UNSPECIFIED; @@ -130,62 +131,64 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { } public void testSyntheticPasswordChangeCredential() throws RemoteException { - final String PASSWORD = "testSyntheticPasswordChangeCredential-password"; - final String NEWPASSWORD = "testSyntheticPasswordChangeCredential-newpassword"; + final byte[] password = "testSyntheticPasswordChangeCredential-password".getBytes(); + final byte[] newPassword = "testSyntheticPasswordChangeCredential-newpassword".getBytes(); - initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID); + initializeCredentialUnderSP(password, PRIMARY_USER_ID); long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID); - mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, PASSWORD, + mService.setLockCredential(newPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, password, PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID); assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) + newPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) .getResponseCode()); assertEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); } public void testSyntheticPasswordVerifyCredential() throws RemoteException { - final String PASSWORD = "testSyntheticPasswordVerifyCredential-password"; - final String BADPASSWORD = "testSyntheticPasswordVerifyCredential-badpassword"; + final byte[] password = "testSyntheticPasswordVerifyCredential-password".getBytes(); + final byte[] badPassword = "testSyntheticPasswordVerifyCredential-badpassword".getBytes(); - initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID); + initializeCredentialUnderSP(password, PRIMARY_USER_ID); assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) + password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) .getResponseCode()); assertEquals(VerifyCredentialResponse.RESPONSE_ERROR, mService.verifyCredential( - BADPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) - .getResponseCode()); + badPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) + .getResponseCode()); } public void testSyntheticPasswordClearCredential() throws RemoteException { - final String PASSWORD = "testSyntheticPasswordClearCredential-password"; - final String NEWPASSWORD = "testSyntheticPasswordClearCredential-newpassword"; + final byte[] password = "testSyntheticPasswordClearCredential-password".getBytes(); + final byte[] badPassword = "testSyntheticPasswordClearCredential-newpassword".getBytes(); - initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID); + initializeCredentialUnderSP(password, PRIMARY_USER_ID); long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID); // clear password - mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, PASSWORD, + mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, password, PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID); assertEquals(0 ,mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); // set a new password - mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, + mService.setLockCredential(badPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID); assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) - .getResponseCode()); + badPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) + .getResponseCode()); assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); } public void testSyntheticPasswordChangeCredentialKeepsAuthSecret() throws RemoteException { - final String PASSWORD = "testSyntheticPasswordChangeCredentialKeepsAuthSecret-password"; - final String NEWPASSWORD = "testSyntheticPasswordChangeCredentialKeepsAuthSecret-new"; + final byte[] password = + "testSyntheticPasswordChangeCredentialKeepsAuthSecret-password".getBytes(); + final byte[] badPassword = + "testSyntheticPasswordChangeCredentialKeepsAuthSecret-new".getBytes(); - initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID); - mService.setLockCredential(NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, PASSWORD, + initializeCredentialUnderSP(password, PRIMARY_USER_ID); + mService.setLockCredential(badPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, password, PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID); assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) + badPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) .getResponseCode()); // Check the same secret was passed each time @@ -195,24 +198,25 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { } public void testSyntheticPasswordVerifyPassesPrimaryUserAuthSecret() throws RemoteException { - final String PASSWORD = "testSyntheticPasswordVerifyPassesPrimaryUserAuthSecret-password"; - final String NEWPASSWORD = "testSyntheticPasswordVerifyPassesPrimaryUserAuthSecret-new"; + final byte[] password = + "testSyntheticPasswordVerifyPassesPrimaryUserAuthSecret-password".getBytes(); + final byte[] newPassword = + "testSyntheticPasswordVerifyPassesPrimaryUserAuthSecret-new".getBytes(); - initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID); + initializeCredentialUnderSP(password, PRIMARY_USER_ID); reset(mAuthSecretService); - assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) + assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(password, + LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) .getResponseCode()); verify(mAuthSecretService).primaryUserCredential(any(ArrayList.class)); } public void testSecondaryUserDoesNotPassAuthSecret() throws RemoteException { - final String PASSWORD = "testSecondaryUserDoesNotPassAuthSecret-password"; - final String NEWPASSWORD = "testSecondaryUserDoesNotPassAuthSecret-new"; + final byte[] password = "testSecondaryUserDoesNotPassAuthSecret-password".getBytes(); - initializeCredentialUnderSP(PASSWORD, SECONDARY_USER_ID); + initializeCredentialUnderSP(password, SECONDARY_USER_ID); assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, SECONDARY_USER_ID) + password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, SECONDARY_USER_ID) .getResponseCode()); verify(mAuthSecretService, never()).primaryUserCredential(any(ArrayList.class)); } @@ -228,8 +232,8 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { } public void testSyntheticPasswordAndCredentialDoesNotPassAuthSecret() throws RemoteException { - final String PASSWORD = "passwordForASyntheticPassword"; - initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID); + final byte[] password = "passwordForASyntheticPassword".getBytes(); + initializeCredentialUnderSP(password, PRIMARY_USER_ID); reset(mAuthSecretService); mService.onUnlockUser(PRIMARY_USER_ID); @@ -238,9 +242,9 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { } public void testSyntheticPasswordButNoCredentialPassesAuthSecret() throws RemoteException { - final String PASSWORD = "getASyntheticPassword"; - initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID); - mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, PASSWORD, + final byte[] password = "getASyntheticPassword".getBytes(); + initializeCredentialUnderSP(password, PRIMARY_USER_ID); + mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, password, PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID); reset(mAuthSecretService); @@ -250,7 +254,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { } public void testManagedProfileUnifiedChallengeMigration() throws RemoteException { - final String UnifiedPassword = "testManagedProfileUnifiedChallengeMigration-pwd"; + final byte[] UnifiedPassword = "testManagedProfileUnifiedChallengeMigration-pwd".getBytes(); disableSyntheticPassword(); mService.setLockCredential(UnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID); @@ -284,8 +288,10 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { } public void testManagedProfileSeparateChallengeMigration() throws RemoteException { - final String primaryPassword = "testManagedProfileSeparateChallengeMigration-primary"; - final String profilePassword = "testManagedProfileSeparateChallengeMigration-profile"; + final byte[] primaryPassword = + "testManagedProfileSeparateChallengeMigration-primary".getBytes(); + final byte[] profilePassword = + "testManagedProfileSeparateChallengeMigration-profile".getBytes(); disableSyntheticPassword(); mService.setLockCredential(primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID); @@ -326,92 +332,92 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { } public void testTokenBasedResetPassword() throws RemoteException { - final String PASSWORD = "password"; - final String PATTERN = "123654"; - final String TOKEN = "some-high-entropy-secure-token"; - initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID); + final byte[] password = "password".getBytes(); + final byte[] pattern = "123654".getBytes(); + final byte[] token = "some-high-entropy-secure-token".getBytes(); + initializeCredentialUnderSP(password, PRIMARY_USER_ID); final byte[] storageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID); - long handle = mLocalService.addEscrowToken(TOKEN.getBytes(), PRIMARY_USER_ID); + long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID); assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID)); - mService.verifyCredential(PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, + mService.verifyCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID).getResponseCode(); assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID)); - mLocalService.setLockCredentialWithToken(PATTERN, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, - handle, TOKEN.getBytes(), PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID); + mLocalService.setLockCredentialWithToken(pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, + handle, token, PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID); // Verify DPM gets notified about new device lock mService.mHandler.runWithScissors(() -> {}, 0 /*now*/); // Flush runnables on handler - PasswordMetrics metric = PasswordMetrics.computeForPassword(PATTERN); + PasswordMetrics metric = PasswordMetrics.computeForPassword(pattern); metric.quality = PASSWORD_QUALITY_SOMETHING; verify(mDevicePolicyManager).setActivePasswordState(metric, PRIMARY_USER_ID); assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - PATTERN, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, 0, PRIMARY_USER_ID) + pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, 0, PRIMARY_USER_ID) .getResponseCode()); assertArrayEquals(storageKey, mStorageManager.getUserUnlockToken(PRIMARY_USER_ID)); } public void testTokenBasedClearPassword() throws RemoteException { - final String PASSWORD = "password"; - final String PATTERN = "123654"; - final String TOKEN = "some-high-entropy-secure-token"; - initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID); + final byte[] password = "password".getBytes(); + final byte[] pattern = "123654".getBytes(); + final byte[] token = "some-high-entropy-secure-token".getBytes(); + initializeCredentialUnderSP(password, PRIMARY_USER_ID); final byte[] storageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID); - long handle = mLocalService.addEscrowToken(TOKEN.getBytes(), PRIMARY_USER_ID); + long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID); assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID)); - mService.verifyCredential(PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, + mService.verifyCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID).getResponseCode(); assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID)); mLocalService.setLockCredentialWithToken(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, - handle, TOKEN.getBytes(), PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID); - mLocalService.setLockCredentialWithToken(PATTERN, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, - handle, TOKEN.getBytes(), PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID); + handle, token, PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID); + mLocalService.setLockCredentialWithToken(pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, + handle, token, PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID); assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - PATTERN, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, 0, PRIMARY_USER_ID) + pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, 0, PRIMARY_USER_ID) .getResponseCode()); assertArrayEquals(storageKey, mStorageManager.getUserUnlockToken(PRIMARY_USER_ID)); } public void testTokenBasedResetPasswordAfterCredentialChanges() throws RemoteException { - final String PASSWORD = "password"; - final String PATTERN = "123654"; - final String NEWPASSWORD = "password"; - final String TOKEN = "some-high-entropy-secure-token"; - initializeCredentialUnderSP(PASSWORD, PRIMARY_USER_ID); + final byte[] password = "password".getBytes(); + final byte[] pattern = "123654".getBytes(); + final byte[] newPassword = "password".getBytes(); + final byte[] token = "some-high-entropy-secure-token".getBytes(); + initializeCredentialUnderSP(password, PRIMARY_USER_ID); final byte[] storageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID); - long handle = mLocalService.addEscrowToken(TOKEN.getBytes(), PRIMARY_USER_ID); + long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID); assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID)); - mService.verifyCredential(PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, + mService.verifyCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID).getResponseCode(); assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID)); - mService.setLockCredential(PATTERN, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, PASSWORD, + mService.setLockCredential(pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, password, PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID); - mLocalService.setLockCredentialWithToken(NEWPASSWORD, - LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, handle, TOKEN.getBytes(), + mLocalService.setLockCredentialWithToken(newPassword, + LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, handle, token, PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID); assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - NEWPASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) + newPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) .getResponseCode()); assertArrayEquals(storageKey, mStorageManager.getUserUnlockToken(PRIMARY_USER_ID)); } public void testEscrowTokenActivatedImmediatelyIfNoUserPasswordNeedsMigration() throws RemoteException { - final String TOKEN = "some-high-entropy-secure-token"; + final String token = "some-high-entropy-secure-token"; enableSyntheticPassword(); - long handle = mLocalService.addEscrowToken(TOKEN.getBytes(), PRIMARY_USER_ID); + long handle = mLocalService.addEscrowToken(token.getBytes(), PRIMARY_USER_ID); assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID)); assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); assertTrue(hasSyntheticPassword(PRIMARY_USER_ID)); @@ -419,9 +425,9 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { public void testEscrowTokenActivatedImmediatelyIfNoUserPasswordNoMigration() throws RemoteException { - final String TOKEN = "some-high-entropy-secure-token"; + final String token = "some-high-entropy-secure-token"; initializeCredentialUnderSP(null, PRIMARY_USER_ID); - long handle = mLocalService.addEscrowToken(TOKEN.getBytes(), PRIMARY_USER_ID); + long handle = mLocalService.addEscrowToken(token.getBytes(), PRIMARY_USER_ID); assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID)); assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); assertTrue(hasSyntheticPassword(PRIMARY_USER_ID)); @@ -429,38 +435,38 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { public void testEscrowTokenActivatedLaterWithUserPasswordNeedsMigration() throws RemoteException { - final String TOKEN = "some-high-entropy-secure-token"; - final String PASSWORD = "password"; + final byte[] token = "some-high-entropy-secure-token".getBytes(); + final byte[] password = "password".getBytes(); // Set up pre-SP user password disableSyntheticPassword(); - mService.setLockCredential(PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, + mService.setLockCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID); enableSyntheticPassword(); - long handle = mLocalService.addEscrowToken(TOKEN.getBytes(), PRIMARY_USER_ID); + long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID); // Token not activated immediately since user password exists assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID)); // Activate token (password gets migrated to SP at the same time) assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - PASSWORD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) + password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) .getResponseCode()); // Verify token is activated assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID)); } public void testSetLockCredentialWithTokenFailsWithoutLockScreen() throws Exception { - final String password = "password"; - final String pattern = "123654"; - final String token = "some-high-entropy-secure-token"; + final byte[] password = "password".getBytes(); + final byte[] pattern = "123654".getBytes(); + final byte[] token = "some-high-entropy-secure-token".getBytes(); mHasSecureLockScreen = false; enableSyntheticPassword(); - long handle = mLocalService.addEscrowToken(token.getBytes(), PRIMARY_USER_ID); + long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID); assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID)); try { mLocalService.setLockCredentialWithToken(password, - LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, handle, token.getBytes(), + LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, handle, token, PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID); fail("An exception should have been thrown."); } catch (UnsupportedOperationException e) { @@ -470,7 +476,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { try { mLocalService.setLockCredentialWithToken(pattern, - LockPatternUtils.CREDENTIAL_TYPE_PATTERN, handle, token.getBytes(), + LockPatternUtils.CREDENTIAL_TYPE_PATTERN, handle, token, PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID); fail("An exception should have been thrown."); } catch (UnsupportedOperationException e) { @@ -480,14 +486,14 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { } public void testgetHashFactorPrimaryUser() throws RemoteException { - final String password = "password"; + final byte[] password = "password".getBytes(); mService.setLockCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID); final byte[] hashFactor = mService.getHashFactor(password, PRIMARY_USER_ID); assertNotNull(hashFactor); - mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, password, - PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID); + mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, + password, PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID); final byte[] newHashFactor = mService.getHashFactor(null, PRIMARY_USER_ID); assertNotNull(newHashFactor); // Hash factor should never change after password change/removal @@ -495,16 +501,16 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { } public void testgetHashFactorManagedProfileUnifiedChallenge() throws RemoteException { - final String pattern = "1236"; - mService.setLockCredential(pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, null, - PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID); + final byte[] pattern = "1236".getBytes(); + mService.setLockCredential(pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, + null, PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID); mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null); assertNotNull(mService.getHashFactor(null, MANAGED_PROFILE_USER_ID)); } public void testgetHashFactorManagedProfileSeparateChallenge() throws RemoteException { - final String primaryPassword = "primary"; - final String profilePassword = "profile"; + final byte[] primaryPassword = "primary".getBytes(); + final byte[] profilePassword = "profile".getBytes(); mService.setLockCredential(primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID); mService.setLockCredential(profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, @@ -554,6 +560,18 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { assertArrayEquals(PAYLOAD2, deserialized.passwordHandle); } + public void testGsiDisablesAuthSecret() throws RemoteException { + mGsiService.setIsGsiRunning(true); + + final byte[] password = "testGsiDisablesAuthSecret-password".getBytes(); + + initializeCredentialUnderSP(password, PRIMARY_USER_ID); + assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( + password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) + .getResponseCode()); + verify(mAuthSecretService, never()).primaryUserCredential(any(ArrayList.class)); + } + // b/62213311 //TODO: add non-migration work profile case, and unify/un-unify transition. //TODO: test token after user resets password diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java index c2d4846b14c0..a992dd126f5d 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java @@ -139,7 +139,7 @@ public class KeySyncTaskTest { mSnapshotListenersStorage, TEST_USER_ID, TEST_CREDENTIAL_TYPE, - TEST_CREDENTIAL, + TEST_CREDENTIAL.getBytes(), /*credentialUpdated=*/ false, mPlatformKeyManager, mTestOnlyInsecureCertificateHelper, @@ -163,17 +163,17 @@ public class KeySyncTaskTest { @Test public void isPin_isTrueForNumericString() { - assertTrue(KeySyncTask.isPin("3298432574398654376547")); + assertTrue(KeySyncTask.isPin("3298432574398654376547".getBytes())); } @Test public void isPin_isFalseForStringContainingLetters() { - assertFalse(KeySyncTask.isPin("398i54369548654")); + assertFalse(KeySyncTask.isPin("398i54369548654".getBytes())); } @Test public void isPin_isFalseForStringContainingSymbols() { - assertFalse(KeySyncTask.isPin("-3987543643")); + assertFalse(KeySyncTask.isPin("-3987543643".getBytes())); } @Test @@ -182,8 +182,8 @@ public class KeySyncTaskTest { byte[] salt = randomBytes(16); assertArrayEquals( - KeySyncTask.hashCredentialsBySaltedSha256(salt, credentials), - KeySyncTask.hashCredentialsBySaltedSha256(salt, credentials)); + KeySyncTask.hashCredentialsBySaltedSha256(salt, credentials.getBytes()), + KeySyncTask.hashCredentialsBySaltedSha256(salt, credentials.getBytes())); } @Test @@ -192,8 +192,8 @@ public class KeySyncTaskTest { assertFalse( Arrays.equals( - KeySyncTask.hashCredentialsBySaltedSha256(salt, "password1234"), - KeySyncTask.hashCredentialsBySaltedSha256(salt, "password12345"))); + KeySyncTask.hashCredentialsBySaltedSha256(salt, "password1234".getBytes()), + KeySyncTask.hashCredentialsBySaltedSha256(salt, "password12345".getBytes()))); } @Test @@ -202,34 +202,38 @@ public class KeySyncTaskTest { assertFalse( Arrays.equals( - KeySyncTask.hashCredentialsBySaltedSha256(randomBytes(64), credentials), - KeySyncTask.hashCredentialsBySaltedSha256(randomBytes(64), credentials))); + KeySyncTask.hashCredentialsBySaltedSha256(randomBytes(64), + credentials.getBytes()), + KeySyncTask.hashCredentialsBySaltedSha256(randomBytes(64), + credentials.getBytes()))); } @Test public void hashCredentialsBySaltedSha256_returnsDifferentHashEvenIfConcatIsSame() { assertFalse( Arrays.equals( - KeySyncTask.hashCredentialsBySaltedSha256(utf8Bytes("123"), "4567"), - KeySyncTask.hashCredentialsBySaltedSha256(utf8Bytes("1234"), "567"))); + KeySyncTask.hashCredentialsBySaltedSha256(utf8Bytes("123"), + "4567".getBytes()), + KeySyncTask.hashCredentialsBySaltedSha256(utf8Bytes("1234"), + "567".getBytes()))); } @Test public void getUiFormat_returnsPinIfPin() { assertEquals(UI_FORMAT_PIN, - KeySyncTask.getUiFormat(CREDENTIAL_TYPE_PASSWORD, "1234")); + KeySyncTask.getUiFormat(CREDENTIAL_TYPE_PASSWORD, "1234".getBytes())); } @Test public void getUiFormat_returnsPasswordIfPassword() { assertEquals(UI_FORMAT_PASSWORD, - KeySyncTask.getUiFormat(CREDENTIAL_TYPE_PASSWORD, "1234a")); + KeySyncTask.getUiFormat(CREDENTIAL_TYPE_PASSWORD, "1234a".getBytes())); } @Test public void getUiFormat_returnsPatternIfPattern() { assertEquals(UI_FORMAT_PATTERN, - KeySyncTask.getUiFormat(CREDENTIAL_TYPE_PATTERN, "1234")); + KeySyncTask.getUiFormat(CREDENTIAL_TYPE_PATTERN, "1234".getBytes())); } @@ -291,7 +295,7 @@ public class KeySyncTaskTest { mSnapshotListenersStorage, TEST_USER_ID, CREDENTIAL_TYPE_PASSWORD, - /*credential=*/ password, + /*credential=*/ password.getBytes(), /*credentialUpdated=*/ false, mPlatformKeyManager, mTestOnlyInsecureCertificateHelper, @@ -332,7 +336,7 @@ public class KeySyncTaskTest { mSnapshotListenersStorage, TEST_USER_ID, CREDENTIAL_TYPE_PATTERN, - /*credential=*/ pattern, + /*credential=*/ pattern.getBytes(), /*credentialUpdated=*/ false, mPlatformKeyManager, mTestOnlyInsecureCertificateHelper, @@ -366,7 +370,7 @@ public class KeySyncTaskTest { mSnapshotListenersStorage, TEST_USER_ID, CREDENTIAL_TYPE_PASSWORD, - /*credential=*/ shortPassword, + /*credential=*/ shortPassword.getBytes(), /*credentialUpdated=*/ false, mPlatformKeyManager, mTestOnlyInsecureCertificateHelper, @@ -526,7 +530,7 @@ public class KeySyncTaskTest { verify(mSnapshotListenersStorage).recoverySnapshotAvailable(TEST_RECOVERY_AGENT_UID); byte[] lockScreenHash = KeySyncTask.hashCredentialsBySaltedSha256( keyDerivationParams.getSalt(), - TEST_CREDENTIAL); + TEST_CREDENTIAL.getBytes()); Long counterId = mRecoverableKeyStoreDb.getCounterId(TEST_USER_ID, TEST_RECOVERY_AGENT_UID); assertThat(counterId).isNotNull(); byte[] recoveryKey = decryptThmEncryptedKey( @@ -649,7 +653,7 @@ public class KeySyncTaskTest { mSnapshotListenersStorage, TEST_USER_ID, CREDENTIAL_TYPE_PASSWORD, - password, + password.getBytes(), /*credentialUpdated=*/ false, mPlatformKeyManager, mTestOnlyInsecureCertificateHelper, @@ -680,7 +684,7 @@ public class KeySyncTaskTest { mSnapshotListenersStorage, TEST_USER_ID, CREDENTIAL_TYPE_PASSWORD, - /*credential=*/ pin, + /*credential=*/ pin.getBytes(), /*credentialUpdated=*/ false, mPlatformKeyManager, mTestOnlyInsecureCertificateHelper, @@ -712,7 +716,7 @@ public class KeySyncTaskTest { mSnapshotListenersStorage, TEST_USER_ID, CREDENTIAL_TYPE_PATTERN, - "12345", + "12345".getBytes(), /*credentialUpdated=*/ false, mPlatformKeyManager, mTestOnlyInsecureCertificateHelper, @@ -796,7 +800,7 @@ public class KeySyncTaskTest { mSnapshotListenersStorage, TEST_USER_ID, /*credentialType=*/ 3, - "12345", + "12345".getBytes(), /*credentialUpdated=*/ false, mPlatformKeyManager, mTestOnlyInsecureCertificateHelper, diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java index 9b4c3beac582..6921bb27ceb2 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelperTest.java @@ -27,30 +27,30 @@ public class TestOnlyInsecureCertificateHelperTest { @Test public void testDoesCredentailSupportInsecureMode_forNonWhitelistedPassword() throws Exception { assertThat(mHelper.doesCredentialSupportInsecureMode( - LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, "secret12345")).isFalse(); + LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, "secret12345".getBytes())).isFalse(); assertThat(mHelper.doesCredentialSupportInsecureMode( - LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, "1234")).isFalse(); + LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, "1234".getBytes())).isFalse(); } @Test public void testDoesCredentailSupportInsecureMode_forWhitelistedPassword() throws Exception { assertThat(mHelper.doesCredentialSupportInsecureMode( LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, - TrustedRootCertificates.INSECURE_PASSWORD_PREFIX)).isTrue(); + TrustedRootCertificates.INSECURE_PASSWORD_PREFIX.getBytes())).isTrue(); assertThat(mHelper.doesCredentialSupportInsecureMode( LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, - TrustedRootCertificates.INSECURE_PASSWORD_PREFIX + "12")).isTrue(); + (TrustedRootCertificates.INSECURE_PASSWORD_PREFIX + "12").getBytes())).isTrue(); } @Test public void testDoesCredentailSupportInsecureMode_Pattern() throws Exception { assertThat(mHelper.doesCredentialSupportInsecureMode( LockPatternUtils.CREDENTIAL_TYPE_PATTERN, - TrustedRootCertificates.INSECURE_PASSWORD_PREFIX)).isFalse(); + TrustedRootCertificates.INSECURE_PASSWORD_PREFIX.getBytes())).isFalse(); assertThat(mHelper.doesCredentialSupportInsecureMode( LockPatternUtils.CREDENTIAL_TYPE_NONE, - TrustedRootCertificates.INSECURE_PASSWORD_PREFIX)).isFalse(); + TrustedRootCertificates.INSECURE_PASSWORD_PREFIX.getBytes())).isFalse(); } @Test diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java index 76beb8f99c18..7e6b7da4a058 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java @@ -6286,6 +6286,15 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { mManager.getShareTargets(filter); } + public void testHasShareTargets_permission() { + assertExpectException(SecurityException.class, "Missing permission", () -> + mManager.hasShareTargets(CALLING_PACKAGE_1)); + + // Has permission, now it should pass. + mCallerPermissions.add(permission.MANAGE_APP_PREDICTIONS); + mManager.hasShareTargets(CALLING_PACKAGE_1); + } + public void testDumpsys_crossProfile() { prepareCrossProfileDataSet(); dumpsysOnLogcat("test1", /* force= */ true); diff --git a/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java index 50dbaf570e9e..fc7ccc576abb 100644 --- a/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java +++ b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java @@ -16,19 +16,22 @@ package com.android.server.rollback; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; import android.content.pm.VersionedPackage; import android.content.rollback.PackageRollbackInfo; import android.content.rollback.PackageRollbackInfo.RestoreInfo; +import android.content.rollback.RollbackInfo; import android.util.IntArray; import android.util.SparseLongArray; @@ -42,7 +45,9 @@ import org.mockito.Mockito; import java.io.File; import java.util.ArrayList; -import java.util.HashMap; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; @RunWith(JUnit4.class) public class AppDataRollbackHelperTest { @@ -55,72 +60,54 @@ public class AppDataRollbackHelperTest { // All users are unlocked so we should snapshot data for them. doReturn(true).when(helper).isUserCredentialLocked(eq(10)); doReturn(true).when(helper).isUserCredentialLocked(eq(11)); - AppDataRollbackHelper.SnapshotAppDataResult result = helper.snapshotAppData("com.foo.bar", - new int[]{10, 11}); + PackageRollbackInfo info = createPackageRollbackInfo("com.foo.bar", new int[]{10, 11}); + helper.snapshotAppData(5, info); - assertEquals(2, result.pendingBackups.size()); - assertEquals(10, result.pendingBackups.get(0)); - assertEquals(11, result.pendingBackups.get(1)); + assertEquals(2, info.getPendingBackups().size()); + assertEquals(10, info.getPendingBackups().get(0)); + assertEquals(11, info.getPendingBackups().get(1)); - assertEquals(0, result.ceSnapshotInodes.size()); + assertEquals(0, info.getCeSnapshotInodes().size()); InOrder inOrder = Mockito.inOrder(installer); inOrder.verify(installer).snapshotAppData( - eq("com.foo.bar"), eq(10), eq(Installer.FLAG_STORAGE_DE)); + eq("com.foo.bar"), eq(10), eq(5), eq(Installer.FLAG_STORAGE_DE)); inOrder.verify(installer).snapshotAppData( - eq("com.foo.bar"), eq(11), eq(Installer.FLAG_STORAGE_DE)); + eq("com.foo.bar"), eq(11), eq(5), eq(Installer.FLAG_STORAGE_DE)); inOrder.verifyNoMoreInteractions(); // One of the users is unlocked but the other isn't doReturn(false).when(helper).isUserCredentialLocked(eq(10)); doReturn(true).when(helper).isUserCredentialLocked(eq(11)); - when(installer.snapshotAppData(anyString(), anyInt(), anyInt())).thenReturn(239L); + when(installer.snapshotAppData(anyString(), anyInt(), anyInt(), anyInt())).thenReturn(239L); - result = helper.snapshotAppData("com.foo.bar", new int[]{10, 11}); - assertEquals(1, result.pendingBackups.size()); - assertEquals(11, result.pendingBackups.get(0)); + PackageRollbackInfo info2 = createPackageRollbackInfo("com.foo.bar", new int[]{10, 11}); + helper.snapshotAppData(7, info2); + assertEquals(1, info2.getPendingBackups().size()); + assertEquals(11, info2.getPendingBackups().get(0)); - assertEquals(1, result.ceSnapshotInodes.size()); - assertEquals(239L, result.ceSnapshotInodes.get(10)); + assertEquals(1, info2.getCeSnapshotInodes().size()); + assertEquals(239L, info2.getCeSnapshotInodes().get(10)); inOrder = Mockito.inOrder(installer); inOrder.verify(installer).snapshotAppData( - eq("com.foo.bar"), eq(10), + eq("com.foo.bar"), eq(10), eq(7), eq(Installer.FLAG_STORAGE_CE | Installer.FLAG_STORAGE_DE)); inOrder.verify(installer).snapshotAppData( - eq("com.foo.bar"), eq(11), eq(Installer.FLAG_STORAGE_DE)); + eq("com.foo.bar"), eq(11), eq(7), eq(Installer.FLAG_STORAGE_DE)); inOrder.verifyNoMoreInteractions(); } - private static RollbackData createInProgressRollbackData(String packageName) { - RollbackData data = new RollbackData(1, new File("/does/not/exist"), -1, true); - data.packages.add(new PackageRollbackInfo( - new VersionedPackage(packageName, 1), new VersionedPackage(packageName, 1), - new IntArray(), new ArrayList<>(), false, new IntArray(), new SparseLongArray())); - data.inProgress = true; - - return data; + private static PackageRollbackInfo createPackageRollbackInfo(String packageName, + final int[] installedUsers) { + return new PackageRollbackInfo( + new VersionedPackage(packageName, 2), new VersionedPackage(packageName, 1), + new IntArray(), new ArrayList<>(), false, IntArray.wrap(installedUsers), + new SparseLongArray()); } - @Test - public void testRestoreAppDataSnapshot_noRollbackData() throws Exception { - Installer installer = mock(Installer.class); - AppDataRollbackHelper helper = spy(new AppDataRollbackHelper(installer)); - - assertFalse(helper.restoreAppData("com.foo", null, 0, 0, 0, "seinfo")); - verifyZeroInteractions(installer); - } - - @Test - public void testRestoreAppDataSnapshot_noRollbackInProgress() throws Exception { - Installer installer = mock(Installer.class); - AppDataRollbackHelper helper = spy(new AppDataRollbackHelper(installer)); - - RollbackData rd = createInProgressRollbackData("com.foo"); - // Override the in progress flag. - rd.inProgress = false; - assertFalse(helper.restoreAppData("com.foo", rd, 0, 0, 0, "seinfo")); - verifyZeroInteractions(installer); + private static PackageRollbackInfo createPackageRollbackInfo(String packageName) { + return createPackageRollbackInfo(packageName, new int[] {}); } @Test @@ -128,18 +115,20 @@ public class AppDataRollbackHelperTest { Installer installer = mock(Installer.class); AppDataRollbackHelper helper = spy(new AppDataRollbackHelper(installer)); - RollbackData rd = createInProgressRollbackData("com.foo"); - IntArray pendingBackups = rd.packages.get(0).getPendingBackups(); + PackageRollbackInfo info = createPackageRollbackInfo("com.foo"); + IntArray pendingBackups = info.getPendingBackups(); pendingBackups.add(10); pendingBackups.add(11); - assertTrue(helper.restoreAppData("com.foo", rd, 10 /* userId */, 1, 2, "seinfo")); + assertTrue(helper.restoreAppData(13 /* rollbackId */, info, 10 /* userId */, 1 /* appId */, + "seinfo")); // Should only require FLAG_STORAGE_DE here because we have a pending backup that we // didn't manage to execute. InOrder inOrder = Mockito.inOrder(installer); inOrder.verify(installer).restoreAppDataSnapshot( - eq("com.foo"), eq(1), eq(2L), eq("seinfo"), eq(10), eq(Installer.FLAG_STORAGE_DE)); + eq("com.foo"), eq(1) /* appId */, eq("seinfo"), eq(10) /* userId */, + eq(13) /* rollbackId */, eq(Installer.FLAG_STORAGE_DE)); inOrder.verifyNoMoreInteractions(); assertEquals(1, pendingBackups.size()); @@ -152,16 +141,18 @@ public class AppDataRollbackHelperTest { AppDataRollbackHelper helper = spy(new AppDataRollbackHelper(installer)); doReturn(true).when(helper).isUserCredentialLocked(eq(10)); - RollbackData rd = createInProgressRollbackData("com.foo"); + PackageRollbackInfo info = createPackageRollbackInfo("com.foo"); - assertTrue(helper.restoreAppData("com.foo", rd, 10 /* userId */, 1, 2, "seinfo")); + assertTrue(helper.restoreAppData(73 /* rollbackId */, info, 10 /* userId */, 1 /* appId */, + "seinfo")); InOrder inOrder = Mockito.inOrder(installer); inOrder.verify(installer).restoreAppDataSnapshot( - eq("com.foo"), eq(1), eq(2L), eq("seinfo"), eq(10), eq(Installer.FLAG_STORAGE_DE)); + eq("com.foo"), eq(1) /* appId */, eq("seinfo"), eq(10) /* userId */, + eq(73) /* rollbackId */, eq(Installer.FLAG_STORAGE_DE)); inOrder.verifyNoMoreInteractions(); - ArrayList<RestoreInfo> pendingRestores = rd.packages.get(0).getPendingRestores(); + ArrayList<RestoreInfo> pendingRestores = info.getPendingRestores(); assertEquals(1, pendingRestores.size()); assertEquals(10, pendingRestores.get(0).userId); assertEquals(1, pendingRestores.get(0).appId); @@ -169,21 +160,23 @@ public class AppDataRollbackHelperTest { } @Test - public void testRestoreAppDataSnapshot_availableBackupForUnockedUser() throws Exception { + public void testRestoreAppDataSnapshot_availableBackupForUnlockedUser() throws Exception { Installer installer = mock(Installer.class); AppDataRollbackHelper helper = spy(new AppDataRollbackHelper(installer)); doReturn(false).when(helper).isUserCredentialLocked(eq(10)); - RollbackData rd = createInProgressRollbackData("com.foo"); - assertFalse(helper.restoreAppData("com.foo", rd, 10 /* userId */, 1, 2, "seinfo")); + PackageRollbackInfo info = createPackageRollbackInfo("com.foo"); + assertFalse(helper.restoreAppData(101 /* rollbackId */, info, 10 /* userId */, + 1 /* appId */, "seinfo")); InOrder inOrder = Mockito.inOrder(installer); inOrder.verify(installer).restoreAppDataSnapshot( - eq("com.foo"), eq(1), eq(2L), eq("seinfo"), eq(10), + eq("com.foo"), eq(1) /* appId */, eq("seinfo"), eq(10) /* userId */, + eq(101) /* rollbackId */, eq(Installer.FLAG_STORAGE_DE | Installer.FLAG_STORAGE_CE)); inOrder.verifyNoMoreInteractions(); - ArrayList<RestoreInfo> pendingRestores = rd.packages.get(0).getPendingRestores(); + ArrayList<RestoreInfo> pendingRestores = info.getPendingRestores(); assertEquals(0, pendingRestores.size()); } @@ -191,48 +184,99 @@ public class AppDataRollbackHelperTest { public void destroyAppData() throws Exception { Installer installer = mock(Installer.class); AppDataRollbackHelper helper = new AppDataRollbackHelper(installer); - SparseLongArray ceSnapshotInodes = new SparseLongArray(); - ceSnapshotInodes.put(11, 239L); - helper.destroyAppDataSnapshot("com.foo.bar", 10, 0L); - helper.destroyAppDataSnapshot("com.foo.bar", 11, 239L); + PackageRollbackInfo info = createPackageRollbackInfo("com.foo.bar"); + info.putCeSnapshotInode(11, 239L); + helper.destroyAppDataSnapshot(5 /* rollbackId */, info, 10 /* userId */); + helper.destroyAppDataSnapshot(5 /* rollbackId */, info, 11 /* userId */); InOrder inOrder = Mockito.inOrder(installer); inOrder.verify(installer).destroyAppDataSnapshot( - eq("com.foo.bar"), eq(10), eq(0L), - eq(Installer.FLAG_STORAGE_DE)); + eq("com.foo.bar"), eq(10) /* userId */, eq(0L) /* ceSnapshotInode */, + eq(5) /* rollbackId */, eq(Installer.FLAG_STORAGE_DE)); inOrder.verify(installer).destroyAppDataSnapshot( - eq("com.foo.bar"), eq(11), eq(239L), - eq(Installer.FLAG_STORAGE_DE | Installer.FLAG_STORAGE_CE)); + eq("com.foo.bar"), eq(11) /* userId */, eq(239L) /* ceSnapshotInode */, + eq(5) /* rollbackId */, eq(Installer.FLAG_STORAGE_DE | Installer.FLAG_STORAGE_CE)); inOrder.verifyNoMoreInteractions(); + + assertEquals(0, info.getCeSnapshotInodes().size()); } @Test - public void commitPendingBackupAndRestoreForUser_updatesRollbackData() throws Exception { + public void commitPendingBackupAndRestoreForUser() throws Exception { Installer installer = mock(Installer.class); AppDataRollbackHelper helper = new AppDataRollbackHelper(installer); - ArrayList<RollbackData> changedRollbackData = new ArrayList<>(); - changedRollbackData.add(createInProgressRollbackData("com.foo.bar")); - - when(installer.snapshotAppData(anyString(), anyInt(), anyInt())).thenReturn(239L); + when(installer.snapshotAppData(anyString(), anyInt(), anyInt(), anyInt())).thenReturn(53L); + + // This one should be backed up. + PackageRollbackInfo pendingBackup = createPackageRollbackInfo("com.foo", new int[]{37, 73}); + pendingBackup.addPendingBackup(37); + + // Nothing should be done for this one. + PackageRollbackInfo wasRecentlyRestored = createPackageRollbackInfo("com.bar", + new int[]{37, 73}); + wasRecentlyRestored.addPendingBackup(37); + wasRecentlyRestored.getPendingRestores().add( + new RestoreInfo(37 /* userId */, 239 /* appId*/, "seInfo")); + + // This one should be restored + PackageRollbackInfo pendingRestore = createPackageRollbackInfo("com.abc", + new int[]{37, 73}); + pendingRestore.putCeSnapshotInode(37, 1543L); + pendingRestore.getPendingRestores().add( + new RestoreInfo(37 /* userId */, 57 /* appId*/, "seInfo")); + + // This one shouldn't be processed, because it hasn't pending backups/restores for userId + // 37. + PackageRollbackInfo ignoredInfo = createPackageRollbackInfo("com.bar", + new int[]{3, 73}); + wasRecentlyRestored.addPendingBackup(3); + wasRecentlyRestored.addPendingBackup(73); + wasRecentlyRestored.getPendingRestores().add( + new RestoreInfo(73 /* userId */, 239 /* appId*/, "seInfo")); + + RollbackData dataWithPendingBackup = new RollbackData(101, new File("/does/not/exist"), -1); + dataWithPendingBackup.info.getPackages().add(pendingBackup); + + RollbackData dataWithRecentRestore = new RollbackData(17239, new File("/does/not/exist"), + -1); + dataWithRecentRestore.info.getPackages().add(wasRecentlyRestored); + + RollbackData dataForDifferentUser = new RollbackData(17239, new File("/does/not/exist"), + -1); + dataForDifferentUser.info.getPackages().add(ignoredInfo); + + RollbackInfo rollbackInfo = new RollbackInfo(17239, + Arrays.asList(pendingRestore, wasRecentlyRestored), false, + new ArrayList<>(), -1); + + List<RollbackData> changed = helper.commitPendingBackupAndRestoreForUser(37, + Arrays.asList(dataWithPendingBackup, dataWithRecentRestore, dataForDifferentUser), + Collections.singletonList(rollbackInfo)); + InOrder inOrder = Mockito.inOrder(installer); - ArrayList<String> pendingBackups = new ArrayList<>(); - pendingBackups.add("com.foo.bar"); + // Check that pending backup and restore for the same package mutually destroyed each other. + assertEquals(-1, wasRecentlyRestored.getPendingBackups().indexOf(37)); + assertNull(wasRecentlyRestored.getRestoreInfo(37)); - helper.commitPendingBackupAndRestoreForUser(11, pendingBackups, - new HashMap<>() /* pendingRestores */, changedRollbackData); + // Check that backup was performed. + inOrder.verify(installer).snapshotAppData(eq("com.foo"), eq(37), eq(101), + eq(Installer.FLAG_STORAGE_CE)); + assertEquals(-1, pendingBackup.getPendingBackups().indexOf(37)); + assertEquals(53, pendingBackup.getCeSnapshotInodes().get(37)); - assertEquals(1, changedRollbackData.size()); - assertEquals(1, changedRollbackData.get(0).packages.size()); - PackageRollbackInfo info = changedRollbackData.get(0).packages.get(0); + // Check that changed returns correct RollbackData. + assertEquals(2, changed.size()); + assertEquals(dataWithPendingBackup, changed.get(0)); + assertEquals(dataWithRecentRestore, changed.get(1)); - assertEquals(1, info.getCeSnapshotInodes().size()); - assertEquals(239L, info.getCeSnapshotInodes().get(11)); + // Check that restore was performed. + inOrder.verify(installer).restoreAppDataSnapshot( + eq("com.abc"), eq(57) /* appId */, eq("seInfo"), eq(37) /* userId */, + eq(17239) /* rollbackId */, eq(Installer.FLAG_STORAGE_CE)); + assertNull(pendingRestore.getRestoreInfo(37)); - InOrder inOrder = Mockito.inOrder(installer); - inOrder.verify(installer).snapshotAppData("com.foo.bar", 11 /* userId */, - Installer.FLAG_STORAGE_CE); inOrder.verifyNoMoreInteractions(); } } diff --git a/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java index 1b106dd37163..5c6fe0fc4cad 100644 --- a/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java @@ -16,34 +16,9 @@ package com.android.server.timezone; -import com.android.timezone.distro.DistroVersion; -import com.android.timezone.distro.StagedDistroOperation; -import com.android.timezone.distro.TimeZoneDistro; -import com.android.timezone.distro.installer.TimeZoneDistroInstaller; - -import org.junit.Before; -import org.junit.Test; - -import android.app.timezone.Callback; -import android.app.timezone.DistroRulesVersion; -import android.app.timezone.ICallback; -import android.app.timezone.RulesManager; -import android.app.timezone.RulesState; -import android.os.ParcelFileDescriptor; - -import java.io.File; -import java.io.FileDescriptor; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.concurrent.Executor; -import javax.annotation.Nullable; - -import libcore.io.IoUtils; -import libcore.timezone.TzDataSetVersion; - import static com.android.server.timezone.RulesManagerService.REQUIRED_QUERY_PERMISSION; import static com.android.server.timezone.RulesManagerService.REQUIRED_UPDATER_PERMISSION; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -61,11 +36,43 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; +import android.app.timezone.Callback; +import android.app.timezone.DistroRulesVersion; +import android.app.timezone.ICallback; +import android.app.timezone.RulesManager; +import android.app.timezone.RulesState; +import android.os.ParcelFileDescriptor; + +import com.android.timezone.distro.DistroVersion; +import com.android.timezone.distro.StagedDistroOperation; +import com.android.timezone.distro.TimeZoneDistro; +import com.android.timezone.distro.installer.TimeZoneDistroInstaller; + +import libcore.io.IoUtils; +import libcore.timezone.TzDataSetVersion; + +import org.junit.Before; +import org.junit.Test; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.concurrent.Executor; + +import javax.annotation.Nullable; + /** * White box interaction / unit testing of the {@link RulesManagerService}. */ public class RulesManagerServiceTest { + private static final int CURRENT_FORMAT_MAJOR_VERSION = + TzDataSetVersion.currentFormatMajorVersion(); + private static final int CURRENT_FORMAT_MINOR_VERSION = + TzDataSetVersion.currentFormatMinorVersion(); + private RulesManagerService mRulesManagerService; private FakeExecutor mFakeExecutor; @@ -116,8 +123,8 @@ public class RulesManagerServiceTest { } @Test - public void getRulesState_systemRulesError() throws Exception { - configureDeviceCannotReadSystemRulesVersion(); + public void getRulesState_baseVersionError() throws Exception { + configureDeviceCannotReadBaseVersion(); assertNull(mRulesManagerService.getRulesState()); } @@ -126,18 +133,18 @@ public class RulesManagerServiceTest { public void getRulesState_stagedInstall() throws Exception { configureCallerHasPermission(); - configureDeviceSystemRulesVersion("2016a"); + configureDeviceBaseVersion("2016a"); DistroVersion stagedDistroVersion = new DistroVersion( - TzDataSetVersion.currentFormatMajorVersion(), - TzDataSetVersion.currentFormatMinorVersion() - 1, + CURRENT_FORMAT_MAJOR_VERSION, + CURRENT_FORMAT_MINOR_VERSION - 1, "2016c", - 3); + 3 /* revision */); configureStagedInstall(stagedDistroVersion); DistroVersion installedDistroVersion = new DistroVersion( - TzDataSetVersion.currentFormatMajorVersion(), - TzDataSetVersion.currentFormatMinorVersion() - 1, + CURRENT_FORMAT_MAJOR_VERSION, + CURRENT_FORMAT_MINOR_VERSION - 1, "2016b", 4); configureInstalledDistroVersion(installedDistroVersion); @@ -158,13 +165,13 @@ public class RulesManagerServiceTest { public void getRulesState_nothingStaged() throws Exception { configureCallerHasPermission(); - configureDeviceSystemRulesVersion("2016a"); + configureDeviceBaseVersion("2016a"); configureNoStagedOperation(); DistroVersion installedDistroVersion = new DistroVersion( - TzDataSetVersion.currentFormatMajorVersion(), - TzDataSetVersion.currentFormatMinorVersion() - 1, + CURRENT_FORMAT_MAJOR_VERSION, + CURRENT_FORMAT_MINOR_VERSION - 1, "2016b", 4); configureInstalledDistroVersion(installedDistroVersion); @@ -183,13 +190,13 @@ public class RulesManagerServiceTest { public void getRulesState_uninstallStaged() throws Exception { configureCallerHasPermission(); - configureDeviceSystemRulesVersion("2016a"); + configureDeviceBaseVersion("2016a"); configureStagedUninstall(); DistroVersion installedDistroVersion = new DistroVersion( - TzDataSetVersion.currentFormatMajorVersion(), - TzDataSetVersion.currentFormatMinorVersion() - 1, + CURRENT_FORMAT_MAJOR_VERSION, + CURRENT_FORMAT_MINOR_VERSION - 1, "2016b", 4); configureInstalledDistroVersion(installedDistroVersion); @@ -208,8 +215,8 @@ public class RulesManagerServiceTest { public void getRulesState_installedRulesError() throws Exception { configureCallerHasPermission(); - String systemRulesVersion = "2016a"; - configureDeviceSystemRulesVersion(systemRulesVersion); + String baseRulesVersion = "2016a"; + configureDeviceBaseVersion(baseRulesVersion); configureStagedUninstall(); configureDeviceCannotReadInstalledDistroVersion(); @@ -226,14 +233,14 @@ public class RulesManagerServiceTest { public void getRulesState_stagedRulesError() throws Exception { configureCallerHasPermission(); - String systemRulesVersion = "2016a"; - configureDeviceSystemRulesVersion(systemRulesVersion); + String baseRulesVersion = "2016a"; + configureDeviceBaseVersion(baseRulesVersion); configureDeviceCannotReadStagedDistroOperation(); DistroVersion installedDistroVersion = new DistroVersion( - TzDataSetVersion.currentFormatMajorVersion(), - TzDataSetVersion.currentFormatMinorVersion() - 1, + CURRENT_FORMAT_MAJOR_VERSION, + CURRENT_FORMAT_MINOR_VERSION - 1, "2016b", 4); configureInstalledDistroVersion(installedDistroVersion); @@ -252,13 +259,13 @@ public class RulesManagerServiceTest { public void getRulesState_noInstalledRules() throws Exception { configureCallerHasPermission(); - String systemRulesVersion = "2016a"; - configureDeviceSystemRulesVersion(systemRulesVersion); + String baseRulesVersion = "2016a"; + configureDeviceBaseVersion(baseRulesVersion); configureNoStagedOperation(); configureInstalledDistroVersion(null); RulesState expectedRuleState = new RulesState( - systemRulesVersion, RulesManagerService.DISTRO_FORMAT_VERSION_SUPPORTED, + baseRulesVersion, RulesManagerService.DISTRO_FORMAT_VERSION_SUPPORTED, false /* operationInProgress */, RulesState.STAGED_OPERATION_NONE, null /* stagedDistroRulesVersion */, RulesState.DISTRO_STATUS_NONE, null /* installedDistroRulesVersion */); @@ -269,15 +276,15 @@ public class RulesManagerServiceTest { public void getRulesState_operationInProgress() throws Exception { configureCallerHasPermission(); - String systemRulesVersion = "2016a"; + String baseRulesVersion = "2016a"; String installedRulesVersion = "2016b"; int revision = 3; - configureDeviceSystemRulesVersion(systemRulesVersion); + configureDeviceBaseVersion(baseRulesVersion); DistroVersion installedDistroVersion = new DistroVersion( - TzDataSetVersion.currentFormatMajorVersion(), - TzDataSetVersion.currentFormatMinorVersion() - 1, + CURRENT_FORMAT_MAJOR_VERSION, + CURRENT_FORMAT_MINOR_VERSION - 1, installedRulesVersion, revision); configureInstalledDistroVersion(installedDistroVersion); @@ -297,7 +304,7 @@ public class RulesManagerServiceTest { DistroRulesVersion expectedInstalledDistroRulesVersion = new DistroRulesVersion(installedRulesVersion, revision); RulesState expectedRuleState = new RulesState( - systemRulesVersion, RulesManagerService.DISTRO_FORMAT_VERSION_SUPPORTED, + baseRulesVersion, RulesManagerService.DISTRO_FORMAT_VERSION_SUPPORTED, true /* operationInProgress */, RulesState.STAGED_OPERATION_UNKNOWN, null /* stagedDistroRulesVersion */, RulesState.DISTRO_STATUS_INSTALLED, expectedInstalledDistroRulesVersion); @@ -858,11 +865,20 @@ public class RulesManagerServiceTest { .thenReturn(true); // Set up the mocks to return (arbitrary) information about the current device state. - when(mMockTimeZoneDistroInstaller.getSystemRulesVersion()).thenReturn("2017a"); - when(mMockTimeZoneDistroInstaller.getInstalledDistroVersion()).thenReturn( - new DistroVersion(2, 3, "2017b", 4)); + TzDataSetVersion baseVersion = new TzDataSetVersion( + CURRENT_FORMAT_MAJOR_VERSION, CURRENT_FORMAT_MINOR_VERSION, "2017a", + 1 /* revision */); + when(mMockTimeZoneDistroInstaller.readBaseVersion()).thenReturn(baseVersion); + DistroVersion installedDistroVersion = new DistroVersion( + CURRENT_FORMAT_MAJOR_VERSION, CURRENT_FORMAT_MINOR_VERSION, "2017b", + 4 /* revision */); + when(mMockTimeZoneDistroInstaller.getInstalledDistroVersion()) + .thenReturn(installedDistroVersion); + DistroVersion stagedDistroVersion = new DistroVersion( + CURRENT_FORMAT_MAJOR_VERSION, CURRENT_FORMAT_MINOR_VERSION, "2017c", + 7 /* revision */); when(mMockTimeZoneDistroInstaller.getStagedDistroOperation()).thenReturn( - StagedDistroOperation.install(new DistroVersion(5, 6, "2017c", 7))); + StagedDistroOperation.install(stagedDistroVersion)); // Do the dump call. String dumpedOutput = doDumpCallAndCapture(rulesManagerService, args); @@ -973,8 +989,11 @@ public class RulesManagerServiceTest { return new CheckToken(1, new PackageVersions(1, 1)); } - private void configureDeviceSystemRulesVersion(String systemRulesVersion) throws Exception { - when(mMockTimeZoneDistroInstaller.getSystemRulesVersion()).thenReturn(systemRulesVersion); + private void configureDeviceBaseVersion(String baseRulesVersion) throws Exception { + TzDataSetVersion tzDataSetVersion = new TzDataSetVersion( + CURRENT_FORMAT_MAJOR_VERSION, CURRENT_FORMAT_MINOR_VERSION, baseRulesVersion, + 1 /* revision */); + when(mMockTimeZoneDistroInstaller.readBaseVersion()).thenReturn(tzDataSetVersion); } private void configureInstalledDistroVersion(@Nullable DistroVersion installedDistroVersion) @@ -1002,8 +1021,8 @@ public class RulesManagerServiceTest { .thenThrow(new IOException("Simulated failure")); } - private void configureDeviceCannotReadSystemRulesVersion() throws Exception { - when(mMockTimeZoneDistroInstaller.getSystemRulesVersion()) + private void configureDeviceCannotReadBaseVersion() throws Exception { + when(mMockTimeZoneDistroInstaller.readBaseVersion()) .thenThrow(new IOException("Simulated failure")); } diff --git a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java index 8d9b3cfcff35..e74b959b6abe 100644 --- a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java +++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java @@ -22,13 +22,14 @@ import static junit.framework.TestCase.fail; import static org.testng.Assert.assertEquals; -import android.app.usage.EventList; +import android.app.usage.TimeSparseArray; import android.app.usage.UsageEvents.Event; import android.app.usage.UsageStats; import android.app.usage.UsageStatsManager; import android.content.Context; import android.content.res.Configuration; import android.test.suitebuilder.annotation.SmallTest; +import android.util.AtomicFile; import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; @@ -38,6 +39,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.util.List; import java.util.Locale; @@ -127,10 +129,6 @@ public class UsageStatsDatabaseTest { mIntervalStats.keyguardHiddenTracker.count = 5; mIntervalStats.keyguardHiddenTracker.duration = 4444444; - if (mIntervalStats.events == null) { - mIntervalStats.events = new EventList(); - } - for (int i = 0; i < numberOfEvents; i++) { Event event = new Event(); final int packageInt = ((i / 3) % 7); //clusters of 3 events from 7 "apps" @@ -317,20 +315,9 @@ public class UsageStatsDatabaseTest { } } assertEquals(stats1.activeConfiguration, stats2.activeConfiguration); - - if (stats1.events == null) { - // If stats1 events are null, stats2 should be null or empty - if (stats2.events != null) { - assertEquals(stats2.events.size(), 0); - } - } else if (stats2.events == null) { - // If stats2 events are null, stats1 should be null or empty - assertEquals(stats1.events.size(), 0); - } else { - assertEquals(stats1.events.size(), stats2.events.size()); - for (int i = 0; i < stats1.events.size(); i++) { - compareUsageEvent(stats1.events.get(i), stats2.events.get(i), i, minVersion); - } + assertEquals(stats1.events.size(), stats2.events.size()); + for (int i = 0; i < stats1.events.size(); i++) { + compareUsageEvent(stats1.events.get(i), stats2.events.get(i), i, minVersion); } } @@ -418,7 +405,7 @@ public class UsageStatsDatabaseTest { // Clear non backed up data from expected IntervalStats mIntervalStats.activeConfiguration = null; mIntervalStats.configurations.clear(); - if (mIntervalStats.events != null) mIntervalStats.events.clear(); + mIntervalStats.events.clear(); // The written and read IntervalStats should match compareIntervalStats(mIntervalStats, stats.get(0), version); @@ -448,4 +435,45 @@ public class UsageStatsDatabaseTest { runBackupRestoreTest(0); runBackupRestoreTest(99999); } + + /** + * Test the pruning in indexFilesLocked() that only allow up to 100 daily files, 50 weekly files + * , 12 monthly files, 10 yearly files. + */ + @Test + public void testMaxFiles() throws IOException { + final File[] intervalDirs = new File[]{ + new File(mTestDir, "daily"), + new File(mTestDir, "weekly"), + new File(mTestDir, "monthly"), + new File(mTestDir, "yearly"), + }; + // Create 10 extra files under each interval dir. + final int extra = 10; + final int length = intervalDirs.length; + for (int i = 0; i < length; i++) { + final int numFiles = UsageStatsDatabase.MAX_FILES_PER_INTERVAL_TYPE[i] + extra; + for (int f = 0; f < numFiles; f++) { + final AtomicFile file = new AtomicFile(new File(intervalDirs[i], Long.toString(f))); + FileOutputStream fos = file.startWrite(); + fos.write(1); + file.finishWrite(fos); + } + } + // indexFilesLocked() list files under each interval dir, if number of files are more than + // the max allowed files for each interval type, it deletes the lowest numbered files. + mUsageStatsDatabase.forceIndexFiles(); + final int len = mUsageStatsDatabase.mSortedStatFiles.length; + for (int i = 0; i < len; i++) { + final TimeSparseArray<AtomicFile> files = mUsageStatsDatabase.mSortedStatFiles[i]; + // The stats file for each interval type equals to max allowed. + assertEquals(UsageStatsDatabase.MAX_FILES_PER_INTERVAL_TYPE[i], + files.size()); + // The highest numbered file, + assertEquals(UsageStatsDatabase.MAX_FILES_PER_INTERVAL_TYPE[i] + extra - 1, + files.keyAt(files.size() - 1)); + // The lowest numbered file: + assertEquals(extra, files.keyAt(0)); + } + } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index cc621387691a..31788ae9b194 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -99,7 +99,6 @@ import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.provider.MediaStore; -import android.provider.Settings.Secure; import android.service.notification.Adjustment; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationStats; @@ -2519,7 +2518,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(mListeners, times(1)).migrateToXml(); verify(mConditionProviders, times(1)).migrateToXml(); verify(mAssistants, times(1)).migrateToXml(); - verify(mAssistants, times(2)).ensureAssistant(); + verify(mAssistants, never()).ensureAssistant(); } @Test @@ -2539,7 +2538,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(mListeners, times(2)).migrateToXml(); verify(mConditionProviders, times(2)).migrateToXml(); verify(mAssistants, times(2)).migrateToXml(); - verify(mAssistants, times(2)).ensureAssistant(); + verify(mAssistants, never()).ensureAssistant(); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index a1db3e8d8ded..1319bad7a5ce 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -406,7 +406,7 @@ public class ActivityRecordTests extends ActivityTestsBase { } @Test - public void testFixedScreenConfigurationWhenMovingToDisplay() { + public void testSizeCompatMode_FixedScreenConfigurationWhenMovingToDisplay() { // Initialize different bounds on a new display. final ActivityDisplay newDisplay = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP); newDisplay.setBounds(0, 0, 1000, 2000); @@ -431,7 +431,7 @@ public class ActivityRecordTests extends ActivityTestsBase { } @Test - public void testFixedScreenBoundsWhenDisplaySizeChanged() { + public void testSizeCompatMode_FixedScreenBoundsWhenDisplaySizeChanged() { when(mActivity.mAppWindowToken.getOrientationIgnoreVisibility()).thenReturn( ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); mTask.getWindowConfiguration().setAppBounds(mStack.getDisplay().getBounds()); @@ -446,4 +446,28 @@ public class ActivityRecordTests extends ActivityTestsBase { assertEquals(originalBounds, mActivity.getBounds()); } + + @Test + public void testSizeCompatMode_FixedScreenLayoutSizeBits() { + final int fixedScreenLayout = Configuration.SCREENLAYOUT_LONG_NO + | Configuration.SCREENLAYOUT_SIZE_NORMAL; + mTask.getConfiguration().screenLayout = fixedScreenLayout + | Configuration.SCREENLAYOUT_LAYOUTDIR_LTR; + mTask.getWindowConfiguration().setAppBounds(mStack.getDisplay().getBounds()); + mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE; + mActivity.info.maxAspectRatio = 1.5f; + ensureActivityConfiguration(); + + // The initial configuration should inherit from parent. + assertEquals(mTask.getConfiguration().screenLayout, + mActivity.getConfiguration().screenLayout); + + mTask.getConfiguration().screenLayout = Configuration.SCREENLAYOUT_LAYOUTDIR_RTL + | Configuration.SCREENLAYOUT_LONG_YES | Configuration.SCREENLAYOUT_SIZE_LARGE; + mActivity.onConfigurationChanged(mTask.getConfiguration()); + + // The size and aspect ratio bits don't change, but the layout direction should be updated. + assertEquals(fixedScreenLayout | Configuration.SCREENLAYOUT_LAYOUTDIR_RTL, + mActivity.getConfiguration().screenLayout); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index 392b0106c8e5..606ab31f638e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -661,8 +661,8 @@ public class ActivityStarterTests extends ActivityTestsBase { doReturn(realCallingUidHasVisibleWindow).when(mService.mWindowManager.mRoot) .isAnyNonToastWindowVisibleForUid(realCallingUid); // process importance - doReturn(callingUidProcState).when(mService).getUidStateLocked(callingUid); - doReturn(realCallingUidProcState).when(mService).getUidStateLocked(realCallingUid); + doReturn(callingUidProcState).when(mService).getUidState(callingUid); + doReturn(realCallingUidProcState).when(mService).getUidState(realCallingUid); // foreground activities final IApplicationThread caller = mock(IApplicationThread.class); final ApplicationInfo ai = new ApplicationInfo(); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java index abc0bd64c0e0..afd4ec160aad 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java @@ -74,7 +74,6 @@ import com.android.server.uri.UriGrantsManagerInternal; import com.android.server.wm.utils.MockTracker; import org.junit.After; -import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Rule; @@ -89,8 +88,6 @@ import java.util.List; class ActivityTestsBase { private static int sNextDisplayId = DEFAULT_DISPLAY + 1; - private static MockTracker sMockTracker; - @Rule public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule = new DexmakerShareClassLoaderRule(); @@ -102,6 +99,8 @@ class ActivityTestsBase { RootActivityContainer mRootActivityContainer; ActivityStackSupervisor mSupervisor; + private MockTracker mMockTracker; + // Default package name static final String DEFAULT_COMPONENT_PACKAGE_NAME = "com.foo"; @@ -110,19 +109,13 @@ class ActivityTestsBase { @BeforeClass public static void setUpOnceBase() { - sMockTracker = new MockTracker(); - AttributeCache.init(getInstrumentation().getTargetContext()); } - @AfterClass - public static void tearDownOnceBase() { - sMockTracker.close(); - sMockTracker = null; - } - @Before public void setUpBase() { + mMockTracker = new MockTracker(); + mTestInjector.setUp(); mService = new TestActivityTaskManagerService(mContext); @@ -137,6 +130,9 @@ class ActivityTestsBase { mService.setWindowManager(null); mService = null; } + + mMockTracker.close(); + mMockTracker = null; } /** Creates a {@link TestActivityDisplay}. */ @@ -598,7 +594,7 @@ class ActivityTestsBase { doNothing().when(this).acquireLaunchWakelock(); doReturn(mKeyguardController).when(this).getKeyguardController(); - mLaunchingActivity = mock(PowerManager.WakeLock.class); + mLaunchingActivityWakeLock = mock(PowerManager.WakeLock.class); initialize(); } @@ -651,7 +647,10 @@ class ActivityTestsBase { @Override protected DisplayContent createDisplayContent() { - return WindowTestUtils.createTestDisplayContent(); + final DisplayContent displayContent = mock(DisplayContent.class); + DockedStackDividerController divider = mock(DockedStackDividerController.class); + doReturn(divider).when(displayContent).getDockedDividerController(); + return displayContent; } void removeAllTasks() { @@ -732,14 +731,13 @@ class ActivityTestsBase { @Override protected void createTaskStack(int displayId, boolean onTop, Rect outBounds) { - mTaskStack = WindowTestUtils.createMockTaskStack(); + mTaskStack = mock(TaskStack.class); // Primary pinned stacks require a non-empty out bounds to be set or else all tasks // will be moved to the full screen stack. if (getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { outBounds.set(0, 0, 100, 100); } - } @Override diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java index 2627ec762d7a..c072d4e28e81 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java @@ -59,8 +59,7 @@ public class AppChangeTransitionTests extends WindowTestsBase { public void setUpOnDisplay(DisplayContent dc) { mStack = createTaskStackOnDisplay(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, dc); mTask = createTaskInStack(mStack, 0 /* userId */); - mToken = WindowTestUtils.createTestAppWindowToken(dc); - mToken.mSkipOnParentChanged = false; + mToken = WindowTestUtils.createTestAppWindowToken(dc, false /* skipOnParentChanged */); mTask.addChild(mToken, 0); diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java index 123de2d227bc..b91f3ecd78c2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java @@ -24,14 +24,13 @@ import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE; import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; - -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static org.junit.Assert.assertEquals; -import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; @@ -39,9 +38,7 @@ import android.view.Display; import androidx.test.filters.SmallTest; -import org.junit.AfterClass; import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Test; /** @@ -54,33 +51,15 @@ import org.junit.Test; @Presubmit public class AppTransitionTests extends WindowTestsBase { - private static RootWindowContainer sOriginalRootWindowContainer; - private DisplayContent mDc; - @BeforeClass - public static void setUpRootWindowContainerMock() { - final WindowManagerService wm = TestSystemServices.getWindowManagerService(); - // For unit test, we don't need to test performSurfacePlacement to prevent some abnormal - // interaction with surfaceflinger native side. - sOriginalRootWindowContainer = wm.mRoot; - // Creating spied mock of RootWindowContainer shouldn't be done in @Before, since it will - // create unnecessary nested spied objects chain, because WindowManagerService object under - // test is a single instance shared among all tests that extend WindowTestsBase class. - // Instead it should be done once before running all tests in this test class. - wm.mRoot = spy(wm.mRoot); - doNothing().when(wm.mRoot).performSurfacePlacement(anyBoolean()); - } - - @AfterClass - public static void tearDownRootWindowContainerMock() { - final WindowManagerService wm = TestSystemServices.getWindowManagerService(); - wm.mRoot = sOriginalRootWindowContainer; - sOriginalRootWindowContainer = null; - } - @Before public void setUp() throws Exception { + synchronized (mWm.mGlobalLock) { + // Hold the lock to protect the stubbing from being accessed by other threads. + spyOn(mWm.mRoot); + doNothing().when(mWm.mRoot).performSurfacePlacement(anyBoolean()); + } mDc = mWm.getDefaultDisplayContentLocked(); } diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java index dd5f32d248b7..a98a6046890d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java @@ -18,7 +18,6 @@ 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.android.dx.mockito.inline.extended.ExtendedMockito.verify; @@ -27,6 +26,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; +import android.platform.test.annotations.Presubmit; import android.view.SurfaceControl; import androidx.test.filters.SmallTest; @@ -39,20 +39,20 @@ 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:AppWindowTokenAnimationTests + * atest AppWindowTokenAnimationTests */ @SmallTest +@Presubmit public class AppWindowTokenAnimationTests extends WindowTestsBase { private TestAppWindowToken mToken; @Mock - private Transaction mTransaction; - @Mock private AnimationAdapter mSpec; @Before @@ -60,8 +60,7 @@ public class AppWindowTokenAnimationTests extends WindowTestsBase { MockitoAnnotations.initMocks(this); mToken = createTestAppWindowToken(mDisplayContent, WINDOWING_MODE_FULLSCREEN, - ACTIVITY_TYPE_STANDARD); - mToken.setPendingTransaction(mTransaction); + ACTIVITY_TYPE_STANDARD, false /* skipOnParentChanged */); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java index 1dd72ec4fd71..68b40b92b9cc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java @@ -18,11 +18,11 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; -import static android.content.ActivityInfoProto.SCREEN_ORIENTATION_UNSPECIFIED; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD; @@ -42,12 +42,16 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.RETURNS_DEEP_STUBS; +import static org.mockito.Mockito.verify; import android.content.res.Configuration; import android.graphics.Point; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; +import android.view.Display; import android.view.Surface; import android.view.WindowManager; @@ -55,12 +59,13 @@ import androidx.test.filters.SmallTest; import org.junit.Before; import org.junit.Test; +import org.mockito.Mockito; /** * Tests for the {@link AppWindowToken} class. * * Build/Install/Run: - * atest FrameworksServicesTests:AppWindowTokenTests + * atest WmTests:AppWindowTokenTests */ @SmallTest @Presubmit @@ -76,9 +81,9 @@ public class AppWindowTokenTests extends WindowTestsBase { public void setUp() throws Exception { mStack = createTaskStackOnDisplay(mDisplayContent); mTask = createTaskInStack(mStack, 0 /* userId */); - mToken = WindowTestUtils.createTestAppWindowToken(mDisplayContent); + mToken = WindowTestUtils.createTestAppWindowToken(mDisplayContent, + false /* skipOnParentChanged */); - mToken.mSkipOnParentChanged = false; mTask.addChild(mToken, 0); } @@ -332,6 +337,46 @@ public class AppWindowTokenTests extends WindowTestsBase { } @Test + public void testReportOrientationChangeOnVisibilityChange() { + synchronized (mWm.mGlobalLock) { + mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); + + mDisplayContent.getDisplayRotation().setFixedToUserRotation( + DisplayRotation.FIXED_TO_USER_ROTATION_ENABLED); + + doReturn(Configuration.ORIENTATION_LANDSCAPE).when(mToken.mActivityRecord) + .getRequestedConfigurationOrientation(); + + mTask.mTaskRecord = Mockito.mock(TaskRecord.class, RETURNS_DEEP_STUBS); + mToken.commitVisibility(null, false /* visible */, TRANSIT_UNSET, + true /* performLayout */, false /* isVoiceInteraction */); + } + + verify(mTask.mTaskRecord).onConfigurationChanged(any(Configuration.class)); + } + + @Test + public void testReportOrientationChangeOnOpeningClosingAppChange() { + synchronized (mWm.mGlobalLock) { + mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); + + mDisplayContent.getDisplayRotation().setFixedToUserRotation( + DisplayRotation.FIXED_TO_USER_ROTATION_ENABLED); + mDisplayContent.getDisplayInfo().state = Display.STATE_ON; + mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_ACTIVITY_CLOSE, + false /* alwaysKeepCurrent */, 0 /* flags */, true /* forceOverride */); + + doReturn(Configuration.ORIENTATION_LANDSCAPE).when(mToken.mActivityRecord) + .getRequestedConfigurationOrientation(); + + mTask.mTaskRecord = Mockito.mock(TaskRecord.class, RETURNS_DEEP_STUBS); + mToken.setVisibility(false, false); + } + + verify(mTask.mTaskRecord).onConfigurationChanged(any(Configuration.class)); + } + + @Test public void testCreateRemoveStartingWindow() { mToken.addStartingWindow(mPackageName, android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index b1b8e8ca990c..b26aa050870a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -68,7 +68,6 @@ import android.view.Surface; import android.view.ViewRootImpl; import android.view.test.InsetsModeSession; -import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import com.android.dx.mockito.inline.extended.ExtendedMockito; @@ -97,7 +96,6 @@ import java.util.List; public class DisplayContentTests extends WindowTestsBase { @Test - @FlakyTest(detail = "Promote to presubmit when shown to be stable.") public void testForAllWindows() { final WindowState exitingAppWindow = createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "exiting app"); @@ -621,7 +619,8 @@ public class DisplayContentTests extends WindowTestsBase { @Test public void testOnDescendantOrientationRequestChanged_FrozenToUserRotation() { final DisplayContent dc = createNewDisplay(); - dc.getDisplayRotation().setFixedToUserRotation(true); + dc.getDisplayRotation().setFixedToUserRotation( + DisplayRotation.FIXED_TO_USER_ROTATION_ENABLED); mWm.mAtmService.mRootActivityContainer = mock(RootActivityContainer.class); final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE ? SCREEN_ORIENTATION_PORTRAIT diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java index 8349ac7fc62c..07dd93c948d1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java @@ -25,20 +25,27 @@ import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACK import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; +import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM; import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import android.graphics.PixelFormat; import android.platform.test.annotations.Presubmit; +import android.view.Surface; import android.view.WindowManager; import androidx.test.filters.SmallTest; @@ -196,4 +203,33 @@ public class DisplayPolicyTests extends WindowTestsBase { DisplayPolicy.updateLightNavigationBarLw(0, opaqueDarkNavBar, opaqueDarkNavBar, imeDrawLightNavBar, imeDrawLightNavBar)); } + + @Test + public void testShouldRotateSeamlessly() { + final DisplayPolicy policy = mDisplayContent.getDisplayPolicy(); + final WindowManager.LayoutParams attrs = mAppWindow.mAttrs; + attrs.x = attrs.y = 0; + attrs.height = attrs.width = WindowManager.LayoutParams.MATCH_PARENT; + attrs.rotationAnimation = ROTATION_ANIMATION_SEAMLESS; + final DisplayRotation displayRotation = mock(DisplayRotation.class); + doReturn(Surface.ROTATION_180).when(displayRotation).getUpsideDownRotation(); + + synchronized (mWm.mGlobalLock) { + policy.focusChangedLw(null /* lastFocus */, mAppWindow); + policy.applyPostLayoutPolicyLw( + mAppWindow, attrs, null /* attached */, null /* imeTarget */); + spyOn(policy); + doReturn(true).when(policy).navigationBarCanMove(); + // The focused fullscreen opaque window without override bounds should be able to be + // rotated seamlessly. + assertTrue(policy.shouldRotateSeamlessly( + displayRotation, Surface.ROTATION_0, Surface.ROTATION_90)); + + spyOn(mAppWindow.mAppToken); + doReturn(false).when(mAppWindow.mAppToken).matchParentBounds(); + // No seamless rotation if the window may be positioned with offset after rotation. + assertFalse(policy.shouldRotateSeamlessly( + displayRotation, Surface.ROTATION_0, Surface.ROTATION_90)); + } + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java index 7be331cc8a06..85b2f7b41e67 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java @@ -26,11 +26,11 @@ import static android.view.Surface.ROTATION_180; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; import static com.android.server.wm.utils.CoordinateTransforms.transformPhysicalToLogicalCoordinates; import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.when; import android.content.Context; import android.content.ContextWrapper; @@ -65,8 +65,7 @@ public class DisplayPolicyTestsBase extends WindowTestsBase { DisplayPolicy mDisplayPolicy; @Before - public void setUpBase() { - super.setUpBase(); + public void setUpDisplayPolicy() { mDisplayPolicy = spy(mDisplayContent.getDisplayPolicy()); final TestContextWrapper context = @@ -77,9 +76,9 @@ public class DisplayPolicyTestsBase extends WindowTestsBase { resources.addOverride(R.dimen.navigation_bar_height, NAV_BAR_HEIGHT); resources.addOverride(R.dimen.navigation_bar_height_landscape, NAV_BAR_HEIGHT); resources.addOverride(R.dimen.navigation_bar_width, NAV_BAR_HEIGHT); - when(mDisplayPolicy.getSystemUiContext()).thenReturn(context); - when(mDisplayPolicy.hasNavigationBar()).thenReturn(true); - when(mDisplayPolicy.hasStatusBar()).thenReturn(true); + doReturn(context).when(mDisplayPolicy).getSystemUiContext(); + doReturn(true).when(mDisplayPolicy).hasNavigationBar(); + doReturn(true).when(mDisplayPolicy).hasStatusBar(); final int shortSizeDp = Math.min(DISPLAY_WIDTH, DISPLAY_HEIGHT) * DENSITY_DEFAULT / DISPLAY_DENSITY; diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java index b15e99aaa8c2..1c10ffb01f8e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayRotationTests.java @@ -17,6 +17,7 @@ package com.android.server.wm; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; @@ -31,6 +32,9 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.same; import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; +import static com.android.server.wm.DisplayRotation.FIXED_TO_USER_ROTATION_DEFAULT; +import static com.android.server.wm.DisplayRotation.FIXED_TO_USER_ROTATION_DISABLED; +import static com.android.server.wm.DisplayRotation.FIXED_TO_USER_ROTATION_ENABLED; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -160,9 +164,7 @@ public class DisplayRotationTests { @Test public void testPersistsUserRotation_LockRotation_NonDefaultDisplay() throws Exception { - mBuilder.mIsDefaultDisplay = false; - - mBuilder.build(); + mBuilder.setIsDefaultDisplay(false).build(); freezeRotation(Surface.ROTATION_180); @@ -187,9 +189,7 @@ public class DisplayRotationTests { @Test public void testPersistsUserRotation_UnlockRotation_NonDefaultDisplay() throws Exception { - mBuilder.mIsDefaultDisplay = false; - - mBuilder.build(); + mBuilder.setIsDefaultDisplay(false).build(); thawRotation(); @@ -203,14 +203,22 @@ public class DisplayRotationTests { public void testPersistsFixedToUserRotation() throws Exception { mBuilder.build(); - mTarget.setFixedToUserRotation(true); + mTarget.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED); - verify(mMockDisplayWindowSettings).setFixedToUserRotation(mMockDisplayContent, true); + verify(mMockDisplayWindowSettings).setFixedToUserRotation(mMockDisplayContent, + FIXED_TO_USER_ROTATION_ENABLED); reset(mMockDisplayWindowSettings); - mTarget.setFixedToUserRotation(false); + mTarget.setFixedToUserRotation(FIXED_TO_USER_ROTATION_DISABLED); - verify(mMockDisplayWindowSettings).setFixedToUserRotation(mMockDisplayContent, false); + verify(mMockDisplayWindowSettings).setFixedToUserRotation(mMockDisplayContent, + FIXED_TO_USER_ROTATION_DISABLED); + + reset(mMockDisplayWindowSettings); + mTarget.setFixedToUserRotation(FIXED_TO_USER_ROTATION_DEFAULT); + + verify(mMockDisplayWindowSettings).setFixedToUserRotation(mMockDisplayContent, + FIXED_TO_USER_ROTATION_DEFAULT); } // ======================================== @@ -355,17 +363,15 @@ public class DisplayRotationTests { when(mMockDisplayPolicy.isAwake()).thenReturn(true); when(mMockDisplayPolicy.isKeyguardDrawComplete()).thenReturn(true); when(mMockDisplayPolicy.isWindowManagerDrawComplete()).thenReturn(true); - mTarget.setFixedToUserRotation(true); + mTarget.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED); mTarget.updateOrientationListener(); verifyOrientationListenerRegistration(0); } @Test - public void testNotEnablesSensor_ForceDefaultRotation() throws Exception { + public void testNotEnablesSensor_ForceDefaultRotation_Car() throws Exception { mBuilder.build(); - when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation)) - .thenReturn(true); - configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false); + configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, true, false); when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true); when(mMockDisplayPolicy.isAwake()).thenReturn(true); @@ -376,11 +382,9 @@ public class DisplayRotationTests { } @Test - public void testNotEnablesSensor_ForceDefaultRotation_Car() throws Exception { + public void testNotEnablesSensor_ForceDefaultRotation_Tv() throws Exception { mBuilder.build(); - when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation)) - .thenReturn(true); - configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, true, false); + configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, true); when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true); when(mMockDisplayPolicy.isAwake()).thenReturn(true); @@ -391,11 +395,9 @@ public class DisplayRotationTests { } @Test - public void testNotEnablesSensor_ForceDefaultRotation_Tv() throws Exception { + public void testNotEnablesSensor_ForceDefaultRotation_Squared() throws Exception { mBuilder.build(); - when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation)) - .thenReturn(true); - configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, true); + configureDisplayRotation(SCREEN_ORIENTATION_LOCKED, false, false); when(mMockDisplayPolicy.isScreenOnEarly()).thenReturn(true); when(mMockDisplayPolicy.isAwake()).thenReturn(true); @@ -513,33 +515,27 @@ public class DisplayRotationTests { // Tests for Policy based Rotation // ================================= @Test - public void testReturnsUserRotation_ForceDefaultRotation() throws Exception { + public void testReturnsUserRotation_ForceDefaultRotation_Car() throws Exception { mBuilder.build(); - when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation)) - .thenReturn(true); - configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, false); + configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, true, false); assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(SCREEN_ORIENTATION_PORTRAIT, Surface.ROTATION_180)); } @Test - public void testReturnsUserRotation_ForceDefaultRotation_Car() throws Exception { + public void testReturnsUserRotation_ForceDefaultRotation_Tv() throws Exception { mBuilder.build(); - when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation)) - .thenReturn(true); - configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, true, false); + configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, true); assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(SCREEN_ORIENTATION_PORTRAIT, Surface.ROTATION_180)); } @Test - public void testReturnsUserRotation_ForceDefaultRotation_Tv() throws Exception { + public void testReturnsUserRotation_ForceDefaultRotation_Squared() throws Exception { mBuilder.build(); - when(mMockRes.getBoolean(com.android.internal.R.bool.config_forceDefaultOrientation)) - .thenReturn(true); - configureDisplayRotation(SCREEN_ORIENTATION_LANDSCAPE, false, true); + configureDisplayRotation(SCREEN_ORIENTATION_LOCKED, false, false); assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(SCREEN_ORIENTATION_PORTRAIT, Surface.ROTATION_180)); @@ -591,7 +587,7 @@ public class DisplayRotationTests { mBuilder.build(); configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false); - mTarget.setFixedToUserRotation(true); + mTarget.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED); freezeRotation(Surface.ROTATION_180); @@ -625,7 +621,7 @@ public class DisplayRotationTests { @Test public void testNotRespectAppRequestedOrientation_FixedToUserRotation() throws Exception { mBuilder.build(); - mTarget.setFixedToUserRotation(true); + mTarget.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED); assertFalse("Display rotation shouldn't respect app requested orientation if" + " fixed to user rotation.", mTarget.respectAppRequestedOrientation()); @@ -647,9 +643,14 @@ public class DisplayRotationTests { width = 1080; height = 1920; break; + case SCREEN_ORIENTATION_LOCKED: + // We use locked for squared display. + width = 1080; + height = 1080; + break; default: - throw new IllegalArgumentException("displayOrientation needs to be either landscape" - + " or portrait, but we got " + throw new IllegalArgumentException("displayOrientation needs to be landscape, " + + "portrait or locked, but we got " + ActivityInfo.screenOrientationToString(displayOrientation)); } @@ -659,6 +660,10 @@ public class DisplayRotationTests { .thenReturn(isCar); when(mockPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) .thenReturn(isTv); + when(mMockDisplayPolicy.getNonDecorDisplayWidth(anyInt(), anyInt(), anyInt(), anyInt(), + any())).thenReturn(width); + when(mMockDisplayPolicy.getNonDecorDisplayHeight(anyInt(), anyInt(), anyInt(), anyInt(), + any())).thenReturn(height); final int shortSizeDp = (isCar || isTv) ? 540 : 720; final int longSizeDp = 960; @@ -807,7 +812,7 @@ public class DisplayRotationTests { private void build() throws Exception { mMockContext = mock(Context.class); - mMockDisplayContent = mock(WindowTestUtils.TestDisplayContent.class); + mMockDisplayContent = mock(DisplayContent.class); mMockDisplayContent.isDefaultDisplay = mIsDefaultDisplay; when(mMockDisplayContent.calculateDisplayCutoutForRotation(anyInt())) .thenReturn(WmDisplayCutout.NO_CUTOUT); @@ -826,6 +831,9 @@ public class DisplayRotationTests { .thenReturn(convertRotationToDegrees(mDeskDockRotation)); when(mMockRes.getInteger(com.android.internal.R.integer.config_undockedHdmiRotation)) .thenReturn(convertRotationToDegrees(mUndockedHdmiRotation)); + when(mMockRes.getFloat( + com.android.internal.R.dimen.config_closeToSquareDisplayMaxAspectRatio)) + .thenReturn(1.33f); mMockSensorManager = mock(SensorManager.class); when(mMockContext.getSystemService(Context.SENSOR_SERVICE)) diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java index 992d01766344..2dad18708499 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java @@ -26,6 +26,9 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.server.wm.DisplayRotation.FIXED_TO_USER_ROTATION_DEFAULT; +import static com.android.server.wm.DisplayRotation.FIXED_TO_USER_ROTATION_DISABLED; +import static com.android.server.wm.DisplayRotation.FIXED_TO_USER_ROTATION_ENABLED; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -407,7 +410,7 @@ public class DisplayWindowSettingsTests extends WindowTestsBase { } @Test - public void testNotFixedToUserRotationByDefault() { + public void testFixedToUserRotationDefault() { mTarget.setUserRotation(mPrimaryDisplay, WindowManagerPolicy.USER_ROTATION_LOCKED, Surface.ROTATION_0); @@ -419,13 +422,14 @@ public class DisplayWindowSettingsTests extends WindowTestsBase { mTarget.applySettingsToDisplayLocked(mPrimaryDisplay); - verify(displayRotation).restoreSettings(anyInt(), anyInt(), eq(false)); + verify(displayRotation).restoreSettings(anyInt(), anyInt(), + eq(FIXED_TO_USER_ROTATION_DEFAULT)); mockitoSession.finishMocking(); } @Test - public void testSetFixedToUserRotation() { - mTarget.setFixedToUserRotation(mPrimaryDisplay, true); + public void testSetFixedToUserRotationDisabled() { + mTarget.setFixedToUserRotation(mPrimaryDisplay, FIXED_TO_USER_ROTATION_DISABLED); final MockitoSession mockitoSession = ExtendedMockito.mockitoSession() .startMocking(); @@ -435,7 +439,25 @@ public class DisplayWindowSettingsTests extends WindowTestsBase { applySettingsToDisplayByNewInstance(mPrimaryDisplay); - verify(displayRotation).restoreSettings(anyInt(), anyInt(), eq(true)); + verify(displayRotation).restoreSettings(anyInt(), anyInt(), + eq(FIXED_TO_USER_ROTATION_DISABLED)); + mockitoSession.finishMocking(); + } + + @Test + public void testSetFixedToUserRotationEnabled() { + mTarget.setFixedToUserRotation(mPrimaryDisplay, FIXED_TO_USER_ROTATION_ENABLED); + + final MockitoSession mockitoSession = ExtendedMockito.mockitoSession() + .startMocking(); + final DisplayRotation displayRotation = mock(DisplayRotation.class); + spyOn(mPrimaryDisplay); + doReturn(displayRotation).when(mPrimaryDisplay).getDisplayRotation(); + + applySettingsToDisplayByNewInstance(mPrimaryDisplay); + + verify(displayRotation).restoreSettings(anyInt(), anyInt(), + eq(FIXED_TO_USER_ROTATION_ENABLED)); mockitoSession.finishMocking(); } diff --git a/services/tests/wmtests/src/com/android/server/wm/MockSurfaceControlBuilder.java b/services/tests/wmtests/src/com/android/server/wm/MockSurfaceControlBuilder.java new file mode 100644 index 000000000000..66139e6b483a --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/MockSurfaceControlBuilder.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static org.mockito.Mockito.mock; + +import android.view.SurfaceControl; + +/** + * Stubbed {@link SurfaceControl.Builder} class that returns a mocked SurfaceControl instance + * that can be used for unit testing. + */ +class MockSurfaceControlBuilder extends SurfaceControl.Builder { + @Override + public SurfaceControl.Builder setParent(SurfaceControl sc) { + return this; + } + + @Override + public SurfaceControl build() { + return mock(SurfaceControl.class); + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java index 3e025f6f36b5..fc1eb1c57198 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java @@ -30,7 +30,6 @@ import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.view.Display.DEFAULT_DISPLAY; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; @@ -96,6 +95,7 @@ public class RecentTasksTest extends ActivityTestsBase { private TestActivityTaskManagerService mTestService; private ActivityDisplay mDisplay; private ActivityDisplay mOtherDisplay; + private ActivityDisplay mSingleTaskDisplay; private ActivityStack mStack; private ActivityStack mHomeStack; private TestTaskPersister mTaskPersister; @@ -547,6 +547,41 @@ public class RecentTasksTest extends ActivityTestsBase { assertTrimmed(mTasks.get(0), mTasks.get(1)); } + /** + * Tests that tasks on singleTaskDisplay are not visible and not trimmed/removed. + */ + @Test + public void testVisibleTasks_singleTaskDisplay() { + mRecentTasks.setOnlyTestVisibleRange(); + mRecentTasks.setParameters(-1 /* min */, 3 /* max */, -1 /* ms */); + + ActivityStack singleTaskStack = mSingleTaskDisplay.createStack( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + + TaskRecord excludedTask1 = createTaskBuilder(".ExcludedTask1") + .setStack(singleTaskStack) + .build(); + + assertFalse("Tasks on singleTaskDisplay should not be visible recents", + mRecentTasks.isVisibleRecentTask(excludedTask1)); + + mRecentTasks.add(excludedTask1); + + // Add N+1 visible tasks. + mRecentTasks.add(mTasks.get(0)); + mRecentTasks.add(mTasks.get(1)); + mRecentTasks.add(mTasks.get(2)); + mRecentTasks.add(mTasks.get(3)); + + // excludedTask is not trimmed. + assertTrimmed(mTasks.get(0)); + + mRecentTasks.removeAllVisibleTasks(); + + // Only visible tasks removed. + assertTrimmed(mTasks.get(0), mTasks.get(1), mTasks.get(2), mTasks.get(3)); + } + @Test public void testBackStackTasks_expectNoTrim() { mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */); @@ -879,8 +914,12 @@ public class RecentTasksTest extends ActivityTestsBase { super.createDefaultDisplay(); mDisplay = mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY); mOtherDisplay = TestActivityDisplay.create(mTestStackSupervisor, DEFAULT_DISPLAY + 1); + mSingleTaskDisplay = TestActivityDisplay.create(mTestStackSupervisor, + DEFAULT_DISPLAY + 2); + mSingleTaskDisplay.setDisplayToSingleTaskInstance(); mRootActivityContainer.addChild(mOtherDisplay, ActivityDisplay.POSITION_TOP); mRootActivityContainer.addChild(mDisplay, ActivityDisplay.POSITION_TOP); + mRootActivityContainer.addChild(mSingleTaskDisplay, ActivityDisplay.POSITION_TOP); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java new file mode 100644 index 000000000000..d919fc80fe0b --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import android.graphics.Matrix; +import android.graphics.Rect; +import android.graphics.Region; +import android.os.IBinder; +import android.os.Parcel; +import android.view.InputWindowHandle; +import android.view.Surface; +import android.view.SurfaceControl; + +/** + * Stubbed {@link android.view.SurfaceControl.Transaction} class that can be used when unit + * testing to avoid calls to native code. + */ +public class StubTransaction extends SurfaceControl.Transaction { + @Override + public void apply() { + } + + @Override + public void close() { + } + + @Override + public void apply(boolean sync) { + } + + @Override + public SurfaceControl.Transaction setVisibility(SurfaceControl sc, boolean visible) { + return this; + } + + @Override + public SurfaceControl.Transaction show(SurfaceControl sc) { + return this; + } + + @Override + public SurfaceControl.Transaction hide(SurfaceControl sc) { + return this; + } + + @Override + public SurfaceControl.Transaction setPosition(SurfaceControl sc, float x, float y) { + return this; + } + + @Override + public SurfaceControl.Transaction setBufferSize(SurfaceControl sc, + int w, int h) { + return this; + } + + @Override + public SurfaceControl.Transaction setLayer(SurfaceControl sc, int z) { + return this; + } + + @Override + public SurfaceControl.Transaction setRelativeLayer(SurfaceControl sc, SurfaceControl relativeTo, + int z) { + return this; + } + + @Override + public SurfaceControl.Transaction setTransparentRegionHint(SurfaceControl sc, + Region transparentRegion) { + return this; + } + + @Override + public SurfaceControl.Transaction setAlpha(SurfaceControl sc, float alpha) { + return this; + } + + @Override + public SurfaceControl.Transaction setInputWindowInfo(SurfaceControl sc, + InputWindowHandle handle) { + return this; + } + + @Override + public SurfaceControl.Transaction transferTouchFocus(IBinder fromToken, IBinder toToken) { + return this; + } + + @Override + public SurfaceControl.Transaction setGeometry(SurfaceControl sc, Rect sourceCrop, + Rect destFrame, @Surface.Rotation int orientation) { + return this; + } + + @Override + public SurfaceControl.Transaction setMatrix(SurfaceControl sc, + float dsdx, float dtdx, float dtdy, float dsdy) { + return this; + } + + @Override + public SurfaceControl.Transaction setMatrix(SurfaceControl sc, Matrix matrix, float[] float9) { + return this; + } + + @Override + public SurfaceControl.Transaction setColorTransform(SurfaceControl sc, float[] matrix, + float[] translation) { + return this; + } + + @Override + public SurfaceControl.Transaction setWindowCrop(SurfaceControl sc, Rect crop) { + return this; + } + + @Override + public SurfaceControl.Transaction setWindowCrop(SurfaceControl sc, int width, int height) { + return this; + } + + @Override + public SurfaceControl.Transaction setCornerRadius(SurfaceControl sc, float cornerRadius) { + return this; + } + + @Override + public SurfaceControl.Transaction setLayerStack(SurfaceControl sc, int layerStack) { + return this; + } + + @Override + public SurfaceControl.Transaction deferTransactionUntil(SurfaceControl sc, IBinder handle, + long frameNumber) { + return this; + } + + @Override + public SurfaceControl.Transaction deferTransactionUntilSurface(SurfaceControl sc, + Surface barrierSurface, + long frameNumber) { + return this; + } + + @Override + public SurfaceControl.Transaction reparentChildren(SurfaceControl sc, IBinder newParentHandle) { + return this; + } + + @Override + public SurfaceControl.Transaction reparent(SurfaceControl sc, SurfaceControl newParent) { + return this; + } + + @Override + public SurfaceControl.Transaction detachChildren(SurfaceControl sc) { + return this; + } + + @Override + public SurfaceControl.Transaction setOverrideScalingMode(SurfaceControl sc, + int overrideScalingMode) { + return this; + } + + @Override + public SurfaceControl.Transaction setColor(SurfaceControl sc, float[] color) { + return this; + } + + @Override + public SurfaceControl.Transaction setGeometryAppliesWithResize(SurfaceControl sc) { + return this; + } + + @Override + public SurfaceControl.Transaction setSecure(SurfaceControl sc, boolean isSecure) { + return this; + } + + @Override + public SurfaceControl.Transaction setOpaque(SurfaceControl sc, boolean isOpaque) { + return this; + } + + @Override + public SurfaceControl.Transaction setDisplaySurface(IBinder displayToken, Surface surface) { + return this; + } + + @Override + public SurfaceControl.Transaction setDisplayLayerStack(IBinder displayToken, int layerStack) { + return this; + } + + @Override + public SurfaceControl.Transaction setDisplayProjection(IBinder displayToken, + int orientation, Rect layerStackRect, Rect displayRect) { + return this; + } + + @Override + public SurfaceControl.Transaction setDisplaySize(IBinder displayToken, int width, int height) { + return this; + } + + @Override + public SurfaceControl.Transaction setAnimationTransaction() { + return this; + } + + @Override + public SurfaceControl.Transaction setEarlyWakeup() { + return this; + } + + @Override + public SurfaceControl.Transaction setMetadata(int key, int data) { + return this; + } + + @Override + public SurfaceControl.Transaction setMetadata(int key, Parcel data) { + return this; + } + + @Override + public SurfaceControl.Transaction merge(SurfaceControl.Transaction other) { + return this; + } + + @Override + public SurfaceControl.Transaction remove(SurfaceControl sc) { + return this; + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java index 6cce9f088ee4..c48348992196 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java @@ -213,7 +213,8 @@ public class SurfaceAnimationRunnerTest extends WindowTestsBase { final Animation a = new TranslateAnimation(-10, 10, 0, 0); a.initialize(0, 0, 0, 0); a.setDuration(50); - return new WindowAnimationSpec(a, new Point(0, 0), false /* canSkipFirstFrame */); + return new WindowAnimationSpec(a, new Point(0, 0), false /* canSkipFirstFrame */, + 0 /* windowCornerRadius */); } /** diff --git a/services/tests/wmtests/src/com/android/server/wm/TestSystemServices.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java index e540b3a9db89..366aceafd7bf 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestSystemServices.java +++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java @@ -57,44 +57,61 @@ import com.android.server.LockGuard; import com.android.server.Watchdog; import com.android.server.input.InputManagerService; import com.android.server.policy.WindowManagerPolicy; +import com.android.server.wm.utils.MockTracker; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; import org.mockito.invocation.InvocationOnMock; import org.mockito.quality.Strictness; import java.util.concurrent.atomic.AtomicBoolean; /** - * A Test utility class to create a mock {@link WindowManagerService} instance for tests. + * JUnit test rule to create a mock {@link WindowManagerService} instance for tests. */ -class TestSystemServices { - private static StaticMockitoSession sMockitoSession; - private static WindowManagerService sService; - private static TestWindowManagerPolicy sPolicy; +public class SystemServicesTestRule implements TestRule { - static AtomicBoolean sCurrentMessagesProcessed = new AtomicBoolean(false); + private static final String TAG = SystemServicesTestRule.class.getSimpleName(); - static void setUpWindowManagerService() { - sMockitoSession = mockitoSession() - .spyStatic(LockGuard.class) - .spyStatic(Watchdog.class) - .strictness(Strictness.LENIENT) - .startMocking(); + private final AtomicBoolean mCurrentMessagesProcessed = new AtomicBoolean(false); - runWithDexmakerShareClassLoader(TestSystemServices::setUpTestWindowService); - } + private MockTracker mMockTracker; + private StaticMockitoSession mMockitoSession; + private WindowManagerService mWindowManagerService; + private TestWindowManagerPolicy mWindowManagerPolicy; - static void tearDownWindowManagerService() { - waitUntilWindowManagerHandlersIdle(); - removeLocalServices(); - sService = null; - sPolicy = null; + /** {@link MockTracker} to track mocks created by {@link SystemServicesTestRule}. */ + private static class Tracker extends MockTracker { + // This empty extended class is necessary since Mockito distinguishes a listener by it + // class. + } - sMockitoSession.finishMocking(); - sMockitoSession = null; + @Override + public Statement apply(Statement base, Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + try { + runWithDexmakerShareClassLoader(SystemServicesTestRule.this::setUp); + base.evaluate(); + } finally { + tearDown(); + } + } + }; } - private static void setUpTestWindowService() { - doReturn(null).when(() -> LockGuard.installLock(any(), anyInt())); + private void setUp() { + mMockTracker = new Tracker(); + + mMockitoSession = mockitoSession() + .spyStatic(LocalServices.class) + .mockStatic(LockGuard.class) + .mockStatic(Watchdog.class) + .strictness(Strictness.LENIENT) + .startMocking(); + doReturn(mock(Watchdog.class)).when(Watchdog::getInstance); final Context context = getInstrumentation().getTargetContext(); @@ -116,21 +133,18 @@ class TestSystemServices { doReturn(appOpsManager).when(context) .getSystemService(eq(Context.APP_OPS_SERVICE)); - removeLocalServices(); - final DisplayManagerInternal dmi = mock(DisplayManagerInternal.class); - LocalServices.addService(DisplayManagerInternal.class, dmi); + doReturn(dmi).when(() -> LocalServices.getService(eq(DisplayManagerInternal.class))); final PowerManagerInternal pmi = mock(PowerManagerInternal.class); - LocalServices.addService(PowerManagerInternal.class, pmi); final PowerSaveState state = new PowerSaveState.Builder().build(); doReturn(state).when(pmi).getLowPowerState(anyInt()); + doReturn(pmi).when(() -> LocalServices.getService(eq(PowerManagerInternal.class))); final ActivityManagerInternal ami = mock(ActivityManagerInternal.class); - LocalServices.addService(ActivityManagerInternal.class, ami); + doReturn(ami).when(() -> LocalServices.getService(eq(ActivityManagerInternal.class))); final ActivityTaskManagerInternal atmi = mock(ActivityTaskManagerInternal.class); - LocalServices.addService(ActivityTaskManagerInternal.class, atmi); doAnswer((InvocationOnMock invocationOnMock) -> { final Runnable runnable = invocationOnMock.getArgument(0); if (runnable != null) { @@ -138,6 +152,7 @@ class TestSystemServices { } return null; }).when(atmi).notifyKeyguardFlagsChanged(nullable(Runnable.class), anyInt()); + doReturn(atmi).when(() -> LocalServices.getService(eq(ActivityTaskManagerInternal.class))); final InputManagerService ims = mock(InputManagerService.class); // InputChannel is final and can't be mocked. @@ -150,31 +165,46 @@ class TestSystemServices { final WindowManagerGlobalLock wmLock = new WindowManagerGlobalLock(); doReturn(wmLock).when(atms).getGlobalLock(); - sPolicy = new TestWindowManagerPolicy(TestSystemServices::getWindowManagerService); - sService = WindowManagerService.main(context, ims, false, false, sPolicy, atms); + mWindowManagerPolicy = new TestWindowManagerPolicy(this::getWindowManagerService); + mWindowManagerService = WindowManagerService.main( + context, ims, false, false, mWindowManagerPolicy, atms, StubTransaction::new); - sService.onInitReady(); + mWindowManagerService.onInitReady(); - final Display display = sService.mDisplayManager.getDisplay(DEFAULT_DISPLAY); + final Display display = mWindowManagerService.mDisplayManager.getDisplay(DEFAULT_DISPLAY); // Display creation is driven by the ActivityManagerService via // ActivityStackSupervisor. We emulate those steps here. - sService.mRoot.createDisplayContent(display, mock(ActivityDisplay.class)); + mWindowManagerService.mRoot.createDisplayContent(display, mock(ActivityDisplay.class)); + + mMockTracker.stopTracking(); + } + + private void tearDown() { + waitUntilWindowManagerHandlersIdle(); + removeLocalServices(); + mWindowManagerService = null; + mWindowManagerPolicy = null; + if (mMockitoSession != null) { + mMockitoSession.finishMocking(); + mMockitoSession = null; + } + + if (mMockTracker != null) { + mMockTracker.close(); + mMockTracker = null; + } } private static void removeLocalServices() { - LocalServices.removeServiceForTest(DisplayManagerInternal.class); - LocalServices.removeServiceForTest(PowerManagerInternal.class); - LocalServices.removeServiceForTest(ActivityManagerInternal.class); - LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class); LocalServices.removeServiceForTest(WindowManagerInternal.class); LocalServices.removeServiceForTest(WindowManagerPolicy.class); } - static WindowManagerService getWindowManagerService() { - return sService; + WindowManagerService getWindowManagerService() { + return mWindowManagerService; } - static void cleanupWindowManagerHandlers() { + void cleanupWindowManagerHandlers() { final WindowManagerService wm = getWindowManagerService(); if (wm == null) { return; @@ -184,7 +214,7 @@ class TestSystemServices { SurfaceAnimationThread.getHandler().removeCallbacksAndMessages(null); } - static void waitUntilWindowManagerHandlersIdle() { + void waitUntilWindowManagerHandlersIdle() { final WindowManagerService wm = getWindowManagerService(); if (wm == null) { return; @@ -196,21 +226,21 @@ class TestSystemServices { waitHandlerIdle(SurfaceAnimationThread.getHandler()); } - private static void waitHandlerIdle(Handler handler) { - synchronized (sCurrentMessagesProcessed) { + private void waitHandlerIdle(Handler handler) { + synchronized (mCurrentMessagesProcessed) { // Add a message to the handler queue and make sure it is fully processed before we move // on. This makes sure all previous messages in the handler are fully processed vs. just // popping them from the message queue. - sCurrentMessagesProcessed.set(false); + mCurrentMessagesProcessed.set(false); handler.post(() -> { - synchronized (sCurrentMessagesProcessed) { - sCurrentMessagesProcessed.set(true); - sCurrentMessagesProcessed.notifyAll(); + synchronized (mCurrentMessagesProcessed) { + mCurrentMessagesProcessed.set(true); + mCurrentMessagesProcessed.notifyAll(); } }); - while (!sCurrentMessagesProcessed.get()) { + while (!mCurrentMessagesProcessed.get()) { try { - sCurrentMessagesProcessed.wait(); + mCurrentMessagesProcessed.wait(); } catch (InterruptedException e) { } } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java index ace179acdeb5..cdbb12109c58 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java @@ -29,13 +29,11 @@ import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.util.DisplayMetrics.DENSITY_DEFAULT; import static android.view.Display.DEFAULT_DISPLAY; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; import android.app.ActivityOptions; import android.content.pm.ActivityInfo; @@ -474,6 +472,22 @@ public class TaskLaunchParamsModifierTests extends ActivityTestsBase { } @Test + public void testUsesFullscreenWhenRequestedOnFreeformDisplay() { + final TestActivityDisplay freeformDisplay = createNewActivityDisplay( + WINDOWING_MODE_FREEFORM); + + final ActivityOptions options = ActivityOptions.makeBasic(); + options.setLaunchDisplayId(freeformDisplay.mDisplayId); + options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN); + + assertEquals(RESULT_CONTINUE, mTarget.onCalculate(/* task */ null, /* layout */ null, + mActivity, /* source */ null, options, mCurrent, mResult)); + + assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode, + WINDOWING_MODE_FREEFORM); + } + + @Test public void testUsesFreeformByDefaultForPostNApp() { final TestActivityDisplay freeformDisplay = createNewActivityDisplay( WINDOWING_MODE_FREEFORM); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java index 1e58e413dd1b..1b396f5ba389 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositioningControllerTests.java @@ -18,6 +18,8 @@ package com.android.server.wm; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; @@ -26,7 +28,6 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.anyInt; import android.platform.test.annotations.Presubmit; import android.view.InputChannel; @@ -63,8 +64,7 @@ public class TaskPositioningControllerTests extends WindowTestsBase { } spyOn(mDisplayContent); - InputMonitor inputMonitor = mock(InputMonitor.class); - when(mDisplayContent.getInputMonitor()).thenReturn(inputMonitor); + doReturn(mock(InputMonitor.class)).when(mDisplayContent).getInputMonitor(); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java index 388f98ffe4a3..5d0788881704 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java @@ -38,6 +38,7 @@ import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import android.app.ActivityManager; +import android.app.TaskInfo; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; @@ -109,8 +110,7 @@ public class TaskRecordTests extends ActivityTestsBase { public void testCopyBaseIntentForTaskInfo() { final TaskRecord task = createTaskRecord(1); task.lastTaskDescription = new ActivityManager.TaskDescription(); - final ActivityManager.RecentTaskInfo info = new ActivityManager.RecentTaskInfo(); - task.fillTaskInfo(info, new TaskRecord.TaskActivitiesReport()); + final TaskInfo info = task.getTaskInfo(); // The intent of info should be a copy so assert that they are different instances. assertThat(info.baseIntent, not(sameInstance(task.getBaseIntent()))); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java index 2554237cdbf5..70ed62a4a11e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java @@ -19,6 +19,9 @@ package com.android.server.wm; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; @@ -168,4 +171,26 @@ public class TaskStackTests extends WindowTestsBase { assertEquals(stack1PositionInParent, stack2PositionInParent + 1); assertTrue(task1.mOnDisplayChangedCalled); } + + @Test + public void testStackOutset() { + final TaskStack stack = createTaskStackOnDisplay(mDisplayContent); + final int stackOutset = 10; + // Clear the handler and hold the lock for mock, to prevent multi-thread issue. + waitUntilHandlersIdle(); + synchronized (mWm.mGlobalLock) { + spyOn(stack); + + doReturn(stackOutset).when(stack).getStackOutset(); + } + + final Rect stackBounds = new Rect(200, 200, 800, 1000); + // Update surface position and size by the given bounds. + stack.setBounds(stackBounds); + + assertEquals(stackBounds.width() + 2 * stackOutset, stack.getLastSurfaceSize().x); + assertEquals(stackBounds.height() + 2 * stackOutset, stack.getLastSurfaceSize().y); + assertEquals(stackBounds.left - stackOutset, stack.getLastSurfacePosition().x); + assertEquals(stackBounds.top - stackOutset, stack.getLastSurfacePosition().y); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java index 849772a4a26d..c3561f4bf6ab 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -313,8 +313,8 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { } @Override - public boolean performHapticFeedbackLw(WindowState win, int effectId, boolean always, - String reason) { + public boolean performHapticFeedback(int uid, String packageName, int effectId, + boolean always, String reason) { return false; } diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java index d07230ef2ca3..6249bde3cea6 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java @@ -21,11 +21,9 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; -import static junit.framework.TestCase.assertNotNull; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; -import static org.junit.Assert.assertNull; - -import android.graphics.Bitmap; import android.os.IBinder; import android.platform.test.annotations.Presubmit; @@ -37,7 +35,7 @@ import org.junit.Test; * Tests for the {@link WallpaperController} class. * * Build/Install/Run: - * atest FrameworksServicesTests:WallpaperControllerTests + * atest WmTests:WallpaperControllerTests */ @SmallTest @Presubmit @@ -49,34 +47,29 @@ public class WallpaperControllerTests extends WindowTestsBase { synchronized (mWm.mGlobalLock) { // No wallpaper final DisplayContent dc = createNewDisplay(); - Bitmap wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked(); - assertNull(wallpaperBitmap); + assertFalse(dc.mWallpaperController.canScreenshotWallpaper()); // No wallpaper WSA Surface WindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, mock(IBinder.class), true, dc, true /* ownerCanManageAppTokens */); WindowState wallpaperWindow = createWindow(null /* parent */, TYPE_WALLPAPER, wallpaperWindowToken, "wallpaperWindow"); - wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked(); - assertNull(wallpaperBitmap); + assertFalse(dc.mWallpaperController.canScreenshotWallpaper()); // Wallpaper with not visible WSA surface. wallpaperWindow.mWinAnimator.mSurfaceController = windowSurfaceController; wallpaperWindow.mWinAnimator.mLastAlpha = 1; - wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked(); - assertNull(wallpaperBitmap); + assertFalse(dc.mWallpaperController.canScreenshotWallpaper()); when(windowSurfaceController.getShown()).thenReturn(true); // Wallpaper with WSA alpha set to 0. wallpaperWindow.mWinAnimator.mLastAlpha = 0; - wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked(); - assertNull(wallpaperBitmap); + assertFalse(dc.mWallpaperController.canScreenshotWallpaper()); // Wallpaper window with WSA Surface wallpaperWindow.mWinAnimator.mLastAlpha = 1; - wallpaperBitmap = dc.mWallpaperController.screenshotWallpaperLocked(); - assertNotNull(wallpaperBitmap); + assertTrue(dc.mWallpaperController.canScreenshotWallpaper()); } } } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowAnimationSpecTest.java b/services/tests/wmtests/src/com/android/server/wm/WindowAnimationSpecTest.java index 9a825e068584..897f0a2c6e81 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowAnimationSpecTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowAnimationSpecTest.java @@ -24,6 +24,8 @@ import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.when; import android.graphics.Point; import android.graphics.Rect; @@ -56,7 +58,7 @@ public class WindowAnimationSpecTest { Animation a = createClipRectAnimation(windowCrop, windowCrop); WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(a, null, mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_NONE, - true /* isAppAnimation */); + true /* isAppAnimation */, 0 /* windowCornerRadius */); windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0); verify(mTransaction).setWindowCrop(eq(mSurfaceControl), argThat(rect -> rect.equals(windowCrop))); @@ -66,7 +68,7 @@ public class WindowAnimationSpecTest { public void testApply_clipAfter() { WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(mAnimation, null, mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_AFTER_ANIM, - true /* isAppAnimation */); + true /* isAppAnimation */, 0 /* windowCornerRadius */); windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0); verify(mTransaction).setWindowCrop(eq(mSurfaceControl), argThat(Rect::isEmpty)); } @@ -76,8 +78,7 @@ public class WindowAnimationSpecTest { // Stack bounds is (0, 0, 10, 10) position is (20, 40) WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(mAnimation, new Point(20, 40), mStackBounds, false /* canSkipFirstFrame */, - STACK_CLIP_AFTER_ANIM, - true /* isAppAnimation */); + STACK_CLIP_AFTER_ANIM, true /* isAppAnimation */, 0 /* windowCornerRadius */); windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0); verify(mTransaction).setWindowCrop(eq(mSurfaceControl), argThat(Rect::isEmpty)); } @@ -87,7 +88,7 @@ public class WindowAnimationSpecTest { // Stack bounds is (0, 0, 10, 10) animation clip is (0, 0, 0, 0) WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(mAnimation, null, mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_BEFORE_ANIM, - true /* isAppAnimation */); + true /* isAppAnimation */, 0 /* windowCornerRadius */); windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0); verify(mTransaction).setWindowCrop(eq(mSurfaceControl), argThat(rect -> rect.equals(mStackBounds))); @@ -101,19 +102,32 @@ public class WindowAnimationSpecTest { a.initialize(0, 0, 0, 0); WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(a, null, null, false /* canSkipFirstFrame */, STACK_CLIP_BEFORE_ANIM, - true /* isAppAnimation */); + true /* isAppAnimation */, 0 /* windowCornerRadius */); windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0); verify(mTransaction).setWindowCrop(eq(mSurfaceControl), argThat(Rect::isEmpty)); } @Test + public void testApply_setCornerRadius() { + final float windowCornerRadius = 30f; + WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(mAnimation, null, + mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_BEFORE_ANIM, + true /* isAppAnimation */, windowCornerRadius); + windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0); + verify(mTransaction, never()).setCornerRadius(eq(mSurfaceControl), eq(windowCornerRadius)); + when(mAnimation.hasRoundedCorners()).thenReturn(true); + windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0); + verify(mTransaction).setCornerRadius(eq(mSurfaceControl), eq(windowCornerRadius)); + } + + @Test public void testApply_clipBeforeSmallerAnimationClip() { // Stack bounds is (0, 0, 10, 10) animation clip is (0, 0, 5, 5) Rect windowCrop = new Rect(0, 0, 5, 5); Animation a = createClipRectAnimation(windowCrop, windowCrop); WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(a, null, mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_BEFORE_ANIM, - true /* isAppAnimation */); + true /* isAppAnimation */, 0 /* windowCornerRadius */); windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0); verify(mTransaction).setWindowCrop(eq(mSurfaceControl), argThat(rect -> rect.equals(windowCrop))); @@ -126,7 +140,7 @@ public class WindowAnimationSpecTest { Animation a = createClipRectAnimation(windowCrop, windowCrop); WindowAnimationSpec windowAnimationSpec = new WindowAnimationSpec(a, null, mStackBounds, false /* canSkipFirstFrame */, STACK_CLIP_BEFORE_ANIM, - true /* isAppAnimation */); + true /* isAppAnimation */, 0 /* windowCornerRadius */); windowAnimationSpec.apply(mTransaction, mSurfaceControl, 0); verify(mTransaction).setWindowCrop(eq(mSurfaceControl), argThat(rect -> rect.equals(mStackBounds))); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java index fb30f8b2b107..61c1d39e420f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java @@ -118,7 +118,7 @@ public class WindowFrameTests extends WindowTestsBase { public void setUp() throws Exception { mWindowToken = createAppWindowToken(mWm.getDefaultDisplayContentLocked(), WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); - mStubStack = WindowTestUtils.createMockTaskStack(); + mStubStack = mock(TaskStack.class); } // Do not use this function directly in the tests below. Instead, use more explicit function diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index b0e20b89b811..b03f63b9159f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -110,8 +110,11 @@ public class WindowStateTests extends WindowTestsBase { // TODO: Let the insets source with new mode keep the visibility control, and remove this // setup code. Now mTopFullscreenOpaqueWindowState will take back the control of insets // visibility. - spyOn(mDisplayContent); - doNothing().when(mDisplayContent).layoutAndAssignWindowLayersIfNeeded(); + // Hold the lock to protect the mock from accesssing by other threads. + synchronized (mWm.mGlobalLock) { + spyOn(mDisplayContent); + doNothing().when(mDisplayContent).layoutAndAssignWindowLayersIfNeeded(); + } } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java index 114eac911bd7..0dec8ee7776f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java @@ -20,97 +20,24 @@ import static android.app.AppOpsManager.OP_NONE; import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; import static com.android.server.wm.WindowContainer.POSITION_TOP; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - import android.app.ActivityManager; import android.content.ComponentName; -import android.content.Context; -import android.os.Binder; import android.os.Build; import android.os.IBinder; -import android.view.Display; import android.view.IApplicationToken; import android.view.IWindow; -import android.view.Surface; -import android.view.SurfaceControl.Transaction; import android.view.WindowManager; /** - * A collection of static functions that can be referenced by other test packages to provide access - * to WindowManager related test functionality. + * A collection of static functions that provide access to WindowManager related test functionality. */ -public class WindowTestUtils { +class WindowTestUtils { private static int sNextTaskId = 0; - /** An extension of {@link DisplayContent} to gain package scoped access. */ - public static class TestDisplayContent extends DisplayContent { - - private TestDisplayContent(Display display, WindowManagerService service, - ActivityDisplay activityDisplay) { - super(display, service, activityDisplay); - } - - /** - * Stubbing method of non-public parent class isn't supported, so here explicitly overrides. - */ - @Override - public DisplayRotation getDisplayRotation() { - return null; - } - - /** - * Stubbing method of non-public parent class isn't supported, so here explicitly overrides. - */ - @Override - DockedStackDividerController getDockedDividerController() { - return null; - } - - /** Create a mocked default {@link DisplayContent}. */ - public static TestDisplayContent create(Context context) { - final TestDisplayContent displayContent = mock(TestDisplayContent.class); - displayContent.isDefaultDisplay = true; - - final DisplayPolicy displayPolicy = mock(DisplayPolicy.class); - when(displayPolicy.navigationBarCanMove()).thenReturn(true); - when(displayPolicy.hasNavigationBar()).thenReturn(true); - - final DisplayRotation displayRotation = new DisplayRotation( - mock(WindowManagerService.class), displayContent, displayPolicy, - mock(DisplayWindowSettings.class), context, new Object()); - displayRotation.mPortraitRotation = Surface.ROTATION_0; - displayRotation.mLandscapeRotation = Surface.ROTATION_90; - displayRotation.mUpsideDownRotation = Surface.ROTATION_180; - displayRotation.mSeascapeRotation = Surface.ROTATION_270; - - when(displayContent.getDisplayRotation()).thenReturn(displayRotation); - - return displayContent; - } - } - - /** Create a mocked default {@link DisplayContent}. */ - public static TestDisplayContent createTestDisplayContent() { - final TestDisplayContent displayContent = mock(TestDisplayContent.class); - DockedStackDividerController divider = mock(DockedStackDividerController.class); - when(displayContent.getDockedDividerController()).thenReturn(divider); - - return displayContent; - } - - /** - * Creates a mock instance of {@link TestTaskStack}. - */ - public static TaskStack createMockTaskStack() { - return mock(TestTaskStack.class); - } - /** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */ - public static Task createTaskInStack(WindowManagerService service, TaskStack stack, + static Task createTaskInStack(WindowManagerService service, TaskStack stack, int userId) { synchronized (service.mGlobalLock) { final Task newTask = new Task(sNextTaskId++, stack, userId, service, 0, false, @@ -121,41 +48,32 @@ public class WindowTestUtils { } /** Creates an {@link AppWindowToken} and adds it to the specified {@link Task}. */ - public static TestAppWindowToken createAppWindowTokenInTask(DisplayContent dc, Task task) { + static TestAppWindowToken createAppWindowTokenInTask(DisplayContent dc, Task task) { final TestAppWindowToken newToken = createTestAppWindowToken(dc); task.addChild(newToken, POSITION_TOP); return newToken; } - /** - * An extension of {@link TestTaskStack}, which overrides package scoped methods that would not - * normally be mocked out. - */ - public static class TestTaskStack extends TaskStack { - TestTaskStack(WindowManagerService service, int stackId) { - super(service, stackId, null); - } - - @Override - void addTask(Task task, int position, boolean showForAllUsers, boolean moveParents) { - // Do nothing. + static TestAppWindowToken createTestAppWindowToken(DisplayContent dc) { + synchronized (dc.mWmService.mGlobalLock) { + return new TestAppWindowToken(dc, true /* skipOnParentChanged */); } } - static TestAppWindowToken createTestAppWindowToken(DisplayContent dc) { + static TestAppWindowToken createTestAppWindowToken(DisplayContent dc, + boolean skipOnParentChanged) { synchronized (dc.mWmService.mGlobalLock) { - return new TestAppWindowToken(dc); + return new TestAppWindowToken(dc, skipOnParentChanged); } } /** Used so we can gain access to some protected members of the {@link AppWindowToken} class. */ - public static class TestAppWindowToken extends AppWindowToken { + static class TestAppWindowToken extends AppWindowToken { boolean mOnTop = false; private boolean mSkipPrepareSurfaces; - private Transaction mPendingTransactionOverride; boolean mSkipOnParentChanged = true; - private TestAppWindowToken(DisplayContent dc) { + private TestAppWindowToken(DisplayContent dc, boolean skipOnParentChanged) { super(dc.mWmService, new IApplicationToken.Stub() { @Override public String getName() { @@ -163,17 +81,9 @@ public class WindowTestUtils { } }, new ComponentName("", ""), false, dc, true /* fillsParent */); mTargetSdk = Build.VERSION_CODES.CUR_DEVELOPMENT; - } - - TestAppWindowToken(WindowManagerService service, IApplicationToken token, - ComponentName activityComponent, boolean voiceInteraction, DisplayContent dc, - long inputDispatchingTimeoutNanos, boolean fullscreen, boolean showForAllUsers, - int targetSdk, int orientation, int rotationAnimationHint, int configChanges, - boolean launchTaskBehind, boolean alwaysFocusable, ActivityRecord activityRecord) { - super(service, token, activityComponent, voiceInteraction, dc, - inputDispatchingTimeoutNanos, fullscreen, showForAllUsers, targetSdk, - orientation, rotationAnimationHint, configChanges, launchTaskBehind, - alwaysFocusable, activityRecord); + mSkipOnParentChanged = skipOnParentChanged; + mActivityRecord = mock(ActivityRecord.class); + mActivityRecord.app = mock(WindowProcessController.class); } int getWindowsCount() { @@ -192,14 +102,6 @@ public class WindowTestUtils { return mChildren.peekLast(); } - int positionInParent() { - return getParent().mChildren.indexOf(this); - } - - void setIsOnTop(boolean onTop) { - mOnTop = onTop; - } - @Override void onParentChanged() { if (!mSkipOnParentChanged) { @@ -224,17 +126,6 @@ public class WindowTestUtils { void setSkipPrepareSurfaces(boolean ignore) { mSkipPrepareSurfaces = ignore; } - - void setPendingTransaction(Transaction transaction) { - mPendingTransactionOverride = transaction; - } - - @Override - public Transaction getPendingTransaction() { - return mPendingTransactionOverride == null - ? super.getPendingTransaction() - : mPendingTransactionOverride; - } } /** @@ -264,7 +155,7 @@ public class WindowTestUtils { } /* Used so we can gain access to some protected members of the {@link WindowToken} class */ - public static class TestWindowToken extends WindowToken { + static class TestWindowToken extends WindowToken { private TestWindowToken(int type, DisplayContent dc, boolean persistOnEmpty) { super(dc.mWmService, mock(IBinder.class), type, persistOnEmpty, dc, @@ -281,7 +172,7 @@ public class WindowTestUtils { } /* Used so we can gain access to some protected members of the {@link Task} class */ - public static class TestTask extends Task { + static class TestTask extends Task { boolean mShouldDeferRemoval = false; boolean mOnDisplayChangedCalled = false; private boolean mIsAnimating = false; @@ -318,26 +209,13 @@ public class WindowTestUtils { } } - public static TestTask createTestTask(TaskStack stack) { + static TestTask createTestTask(TaskStack stack) { return new TestTask(sNextTaskId++, stack, 0, stack.mWmService, RESIZE_MODE_UNRESIZEABLE, false, mock(TaskRecord.class)); } - public static class TestIApplicationToken implements IApplicationToken { - - private final Binder mBinder = new Binder(); - @Override - public IBinder asBinder() { - return mBinder; - } - @Override - public String getName() { - return null; - } - } - /** Used to track resize reports. */ - public static class TestWindowState extends WindowState { + static class TestWindowState extends WindowState { boolean mResizeReported; TestWindowState(WindowManagerService service, Session session, IWindow window, diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index a83bf2af613e..d202e16a5ff4 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -37,7 +37,10 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; + +import static org.mockito.Mockito.mock; + import android.content.Context; import android.content.res.Configuration; @@ -47,6 +50,8 @@ import android.util.Log; import android.view.Display; import android.view.DisplayInfo; import android.view.IWindow; +import android.view.Surface; +import android.view.SurfaceControl.Transaction; import android.view.WindowManager; import com.android.server.AttributeCache; @@ -58,6 +63,7 @@ import org.junit.Before; import org.junit.BeforeClass; import org.junit.Rule; +import java.io.IOException; import java.util.HashSet; import java.util.LinkedList; @@ -78,8 +84,6 @@ class WindowTestsBase { private static int sNextDisplayId = DEFAULT_DISPLAY + 1; static int sNextStackId = 1000; - private static MockTracker sMockTracker; - /** Non-default display. */ DisplayContent mDisplayContent; DisplayInfo mDisplayInfo = new DisplayInfo(); @@ -94,15 +98,18 @@ class WindowTestsBase { WindowState mChildAppWindowBelow; HashSet<WindowState> mCommonWindows; + private MockTracker mMockTracker; + /** - * To restore the original SurfaceControl.Transaction factory if any tests changed - * {@link WindowManagerService#mTransactionFactory}. + * Spied {@link Transaction} class than can be used to verify calls. */ - private TransactionFactory mOriginalTransactionFactory; + Transaction mTransaction; @Rule public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule = new DexmakerShareClassLoaderRule(); + @Rule + public final SystemServicesTestRule mSystemServicesTestRule = new SystemServicesTestRule(); static WindowState.PowerManagerWrapper sPowerManagerWrapper; @@ -110,34 +117,37 @@ class WindowTestsBase { public static void setUpOnceBase() { AttributeCache.init(getInstrumentation().getTargetContext()); - TestSystemServices.setUpWindowManagerService(); - - // MockTracker needs to be initialized after TestSystemServices because we don't want to - // track static mocks. - sMockTracker = new MockTracker(); - sPowerManagerWrapper = mock(WindowState.PowerManagerWrapper.class); } @AfterClass - public static void tearDownOnceBase() { - sMockTracker.close(); - sMockTracker = null; - - TestSystemServices.tearDownWindowManagerService(); + public static void tearDownOnceBase() throws IOException { + sPowerManagerWrapper = null; } @Before public void setUpBase() { + mMockTracker = new MockTracker(); + // If @Before throws an exception, the error isn't logged. This will make sure any failures // in the set up are clear. This can be removed when b/37850063 is fixed. try { mMockSession = mock(Session.class); + mTransaction = spy(StubTransaction.class); final Context context = getInstrumentation().getTargetContext(); - mWm = TestSystemServices.getWindowManagerService(); - mOriginalTransactionFactory = mWm.mTransactionFactory; + mWm = mSystemServicesTestRule.getWindowManagerService(); + + // Setup factory classes to prevent calls to native code. + + // Return a spied Transaction class than can be used to verify calls. + mWm.mTransactionFactory = () -> mTransaction; + // Return a SurfaceControl.Builder class that creates mocked SurfaceControl instances. + mWm.mSurfaceBuilderFactory = (unused) -> new MockSurfaceControlBuilder(); + // Return mocked Surface instances. + mWm.mSurfaceFactory = () -> mock(Surface.class); + beforeCreateDisplay(); context.getDisplay().getDisplayInfo(mDisplayInfo); @@ -187,7 +197,6 @@ class WindowTestsBase { // stable state to clean up for consistency. waitUntilHandlersIdle(); - mWm.mTransactionFactory = mOriginalTransactionFactory; final LinkedList<WindowState> nonCommonWindows = new LinkedList<>(); synchronized (mWm.mGlobalLock) { @@ -216,11 +225,14 @@ class WindowTestsBase { } // Cleaned up everything in Handler. - TestSystemServices.cleanupWindowManagerHandlers(); + mSystemServicesTestRule.cleanupWindowManagerHandlers(); } catch (Exception e) { Log.e(TAG, "Failed to tear down test", e); throw e; } + + mMockTracker.close(); + mMockTracker = null; } private WindowState createCommonWindow(WindowState parent, int type, String name) { @@ -237,7 +249,7 @@ class WindowTestsBase { * Waits until the main handler for WM has processed all messages. */ void waitUntilHandlersIdle() { - TestSystemServices.waitUntilWindowManagerHandlersIdle(); + mSystemServicesTestRule.waitUntilWindowManagerHandlersIdle(); } private WindowToken createWindowToken( @@ -257,10 +269,16 @@ class WindowTestsBase { WindowTestUtils.TestAppWindowToken createTestAppWindowToken(DisplayContent dc, int windowingMode, int activityType) { + return createTestAppWindowToken(dc, windowingMode, activityType, + true /*skipOnParentChanged */); + } + + WindowTestUtils.TestAppWindowToken createTestAppWindowToken(DisplayContent dc, int + windowingMode, int activityType, boolean skipOnParentChanged) { final TaskStack stack = createTaskStackOnDisplay(windowingMode, activityType, dc); final Task task = createTaskInStack(stack, 0 /* userId */); final WindowTestUtils.TestAppWindowToken appWindowToken = - WindowTestUtils.createTestAppWindowToken(dc); + WindowTestUtils.createTestAppWindowToken(dc, skipOnParentChanged); task.addChild(appWindowToken, 0); return appWindowToken; } diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java index f3b8a6265eb3..2907021d9d63 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java @@ -23,15 +23,20 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; +import static com.android.server.wm.WindowStateAnimator.PRESERVED_SURFACE_LAYER; + import static com.google.common.truth.Truth.assertThat; import android.platform.test.annotations.Presubmit; @@ -44,6 +49,7 @@ import androidx.test.filters.SmallTest; import org.junit.After; import org.junit.Test; +import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; @@ -404,6 +410,28 @@ public class ZOrderingTests extends WindowTestsBase { assertWindowHigher(mediaOverlayChild, child); } + @Test + public void testAssignWindowLayers_ForPostivelyZOrderedSubtype() { + final WindowState anyWindow = createWindow("anyWindow"); + final ArrayList<WindowState> childList = new ArrayList<>(); + childList.add(createWindow(anyWindow, TYPE_APPLICATION_PANEL, mDisplayContent, + "TypeApplicationPanelChild")); + childList.add(createWindow(anyWindow, TYPE_APPLICATION_SUB_PANEL, mDisplayContent, + "TypeApplicationSubPanelChild")); + childList.add(createWindow(anyWindow, TYPE_APPLICATION_ATTACHED_DIALOG, mDisplayContent, + "TypeApplicationAttachedDialogChild")); + childList.add(createWindow(anyWindow, TYPE_APPLICATION_ABOVE_SUB_PANEL, mDisplayContent, + "TypeApplicationAboveSubPanelPanelChild")); + + final LayerRecordingTransaction t = mTransaction; + mDisplayContent.assignChildLayers(t); + + for (int i = childList.size() - 1; i >= 0; i--) { + assertThat(t.getLayer(childList.get(i).getSurfaceControl())) + .isGreaterThan(PRESERVED_SURFACE_LAYER); + } + } + @FlakyTest(bugId = 124088319) @Test public void testDockedDividerPosition() { diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/MockTracker.java b/services/tests/wmtests/src/com/android/server/wm/utils/MockTracker.java index 1ce463bbbd9e..a6e675ab5f7e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/utils/MockTracker.java +++ b/services/tests/wmtests/src/com/android/server/wm/utils/MockTracker.java @@ -21,6 +21,7 @@ import android.util.Log; import org.mockito.Mockito; import org.mockito.MockitoFramework; import org.mockito.internal.creation.settings.CreationSettings; +import org.mockito.internal.util.MockUtil; import org.mockito.listeners.MockCreationListener; import org.mockito.mock.MockCreationSettings; @@ -33,7 +34,7 @@ import java.util.IdentityHashMap; * same type registered. */ public class MockTracker implements MockCreationListener, AutoCloseable { - private static final String TAG = "MockTracker"; + private static final String TAG = MockTracker.class.getSimpleName(); private static final Field SPIED_INSTANCE_FIELD; @@ -54,6 +55,10 @@ public class MockTracker implements MockCreationListener, AutoCloseable { mMockitoFramework.addListener(this); } + public void stopTracking() { + mMockitoFramework.removeListener(this); + } + @Override public void onMockCreated(Object mock, MockCreationSettings settings) { mMocks.put(mock, null); @@ -83,10 +88,8 @@ public class MockTracker implements MockCreationListener, AutoCloseable { mMockitoFramework.removeListener(this); for (final Object mock : mMocks.keySet()) { - try { + if (MockUtil.isMock(mock)) { Mockito.reset(mock); - } catch (Exception e) { - Log.e(TAG, "Failed to reset " + mock, e); } } mMocks.clear(); diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java index 0e15947abf52..4756fb4d4948 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java +++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java @@ -43,8 +43,8 @@ import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.FilenameFilter; -import java.io.InputStream; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.StandardCopyOption; @@ -84,6 +84,9 @@ public class UsageStatsDatabase { @VisibleForTesting public static final int BACKUP_VERSION = 4; + @VisibleForTesting + static final int[] MAX_FILES_PER_INTERVAL_TYPE = new int[]{100, 50, 12, 10}; + // Key under which the payload blob is stored // same as UsageStatsBackupHelper.KEY_USAGE_STATS static final String KEY_USAGE_STATS = "usage_stats"; @@ -104,7 +107,8 @@ public class UsageStatsDatabase { private final Object mLock = new Object(); private final File[] mIntervalDirs; - private final TimeSparseArray<AtomicFile>[] mSortedStatFiles; + @VisibleForTesting + final TimeSparseArray<AtomicFile>[] mSortedStatFiles; private final UnixCalendar mCal; private final File mVersionFile; private final File mBackupsDir; @@ -126,10 +130,10 @@ public class UsageStatsDatabase { @VisibleForTesting public UsageStatsDatabase(File dir, int version) { mIntervalDirs = new File[]{ - new File(dir, "daily"), - new File(dir, "weekly"), - new File(dir, "monthly"), - new File(dir, "yearly"), + new File(dir, "daily"), + new File(dir, "weekly"), + new File(dir, "monthly"), + new File(dir, "yearly"), }; mCurrentVersion = version; mVersionFile = new File(dir, "version"); @@ -248,6 +252,14 @@ public class UsageStatsDatabase { return true; } + /** @hide */ + @VisibleForTesting + void forceIndexFiles() { + synchronized (mLock) { + indexFilesLocked(); + } + } + private void indexFilesLocked() { final FilenameFilter backupFileFilter = new FilenameFilter() { @Override @@ -255,7 +267,6 @@ public class UsageStatsDatabase { return !name.endsWith(BAK_SUFFIX); } }; - // Index the available usage stat files on disk. for (int i = 0; i < mSortedStatFiles.length; i++) { if (mSortedStatFiles[i] == null) { @@ -268,8 +279,9 @@ public class UsageStatsDatabase { if (DEBUG) { Slog.d(TAG, "Found " + files.length + " stat files for interval " + i); } - - for (File f : files) { + final int len = files.length; + for (int j = 0; j < len; j++) { + final File f = files[j]; final AtomicFile af = new AtomicFile(f); try { mSortedStatFiles[i].put(parseBeginTime(af), af); @@ -277,6 +289,16 @@ public class UsageStatsDatabase { Slog.e(TAG, "failed to index file: " + f, e); } } + + // only keep the max allowed number of files for each interval type. + final int toDelete = mSortedStatFiles[i].size() - MAX_FILES_PER_INTERVAL_TYPE[i]; + if (toDelete > 0) { + for (int j = 0; j < toDelete; j++) { + mSortedStatFiles[i].valueAt(0).delete(); + mSortedStatFiles[i].removeAt(0); + } + Slog.d(TAG, "Deleted " + toDelete + " stat files for interval " + i); + } } } } @@ -908,39 +930,37 @@ public class UsageStatsDatabase { } } - if (statsOut.events != null) { - final int eventSize = statsOut.events.size(); - for (int i = 0; i < eventSize; i++) { - final UsageEvents.Event event = statsOut.events.get(i); - if (event.mPackage.isEmpty()) { - if (failures++ < failureLogLimit) { - sb.append("\nUnexpected empty empty package name loaded"); - } + final int eventSize = statsOut.events.size(); + for (int i = 0; i < eventSize; i++) { + final UsageEvents.Event event = statsOut.events.get(i); + if (event.mPackage.isEmpty()) { + if (failures++ < failureLogLimit) { + sb.append("\nUnexpected empty empty package name loaded"); } - if (event.mTimeStamp < statsOut.beginTime || event.mTimeStamp > statsOut.endTime) { - if (failures++ < failureLogLimit) { - sb.append("\nUnexpected event timestamp "); - sb.append(event.mTimeStamp); - sb.append(" loaded (beginTime : "); - sb.append(statsOut.beginTime); - sb.append(", endTime : "); - sb.append(statsOut.endTime); - sb.append(")"); - } + } + if (event.mTimeStamp < statsOut.beginTime || event.mTimeStamp > statsOut.endTime) { + if (failures++ < failureLogLimit) { + sb.append("\nUnexpected event timestamp "); + sb.append(event.mTimeStamp); + sb.append(" loaded (beginTime : "); + sb.append(statsOut.beginTime); + sb.append(", endTime : "); + sb.append(statsOut.endTime); + sb.append(")"); } - if (event.mEventType < 0 || event.mEventType > UsageEvents.Event.MAX_EVENT_TYPE) { - if (failures++ < failureLogLimit) { - sb.append("\nUnexpected event type "); - sb.append(event.mEventType); - sb.append(" loaded"); - } + } + if (event.mEventType < 0 || event.mEventType > UsageEvents.Event.MAX_EVENT_TYPE) { + if (failures++ < failureLogLimit) { + sb.append("\nUnexpected event type "); + sb.append(event.mEventType); + sb.append(" loaded"); } - if ((event.mFlags & ~UsageEvents.Event.VALID_FLAG_BITS) != 0) { - if (failures++ < failureLogLimit) { - sb.append("\nUnexpected event flag bit 0b"); - sb.append(Integer.toBinaryString(event.mFlags)); - sb.append(" loaded"); - } + } + if ((event.mFlags & ~UsageEvents.Event.VALID_FLAG_BITS) != 0) { + if (failures++ < failureLogLimit) { + sb.append("\nUnexpected event flag bit 0b"); + sb.append(Integer.toBinaryString(event.mFlags)); + sb.append(" loaded"); } } } @@ -1176,7 +1196,7 @@ public class UsageStatsDatabase { if (stats == null) return; stats.activeConfiguration = null; stats.configurations.clear(); - if (stats.events != null) stats.events.clear(); + stats.events.clear(); } private byte[] serializeIntervalStats(IntervalStats stats, int version) { diff --git a/services/usage/java/com/android/server/usage/UsageStatsProto.java b/services/usage/java/com/android/server/usage/UsageStatsProto.java index 11d49eb40bc0..63bf7e7629a9 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsProto.java +++ b/services/usage/java/com/android/server/usage/UsageStatsProto.java @@ -16,7 +16,6 @@ package com.android.server.usage; import android.app.usage.ConfigurationStats; -import android.app.usage.EventList; import android.app.usage.UsageEvents; import android.app.usage.UsageStats; import android.content.res.Configuration; @@ -303,10 +302,6 @@ final class UsageStatsProto { if (event.mPackage == null) { throw new ProtocolException("no package field present"); } - - if (statsOut.events == null) { - statsOut.events = new EventList(); - } statsOut.events.insert(event); } @@ -514,10 +509,7 @@ final class UsageStatsProto { statsOut.packageStats.clear(); statsOut.configurations.clear(); statsOut.activeConfiguration = null; - - if (statsOut.events != null) { - statsOut.events.clear(); - } + statsOut.events.clear(); while (true) { switch (proto.nextField()) { @@ -607,7 +599,7 @@ final class UsageStatsProto { writeConfigStats(proto, IntervalStatsProto.CONFIGURATIONS, stats, stats.configurations.valueAt(i), active); } - final int eventCount = stats.events != null ? stats.events.size() : 0; + final int eventCount = stats.events.size(); for (int i = 0; i < eventCount; i++) { writeEvent(proto, IntervalStatsProto.EVENT_LOG, stats, stats.events.get(i)); } diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java index eddf8f9b7254..e39cd87f2314 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java +++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java @@ -384,10 +384,7 @@ final class UsageStatsXmlV1 { statsOut.packageStats.clear(); statsOut.configurations.clear(); statsOut.activeConfiguration = null; - - if (statsOut.events != null) { - statsOut.events.clear(); - } + statsOut.events.clear(); statsOut.endTime = statsOut.beginTime + XmlUtils.readLongAttribute(parser, END_TIME_ATTR); try { @@ -481,7 +478,7 @@ final class UsageStatsXmlV1 { xml.endTag(null, CONFIGURATIONS_TAG); xml.startTag(null, EVENT_LOG_TAG); - final int eventCount = stats.events != null ? stats.events.size() : 0; + final int eventCount = stats.events.size(); for (int i = 0; i < eventCount; i++) { writeEvent(xml, stats, stats.events.get(i)); } diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java index 3cb22162e36c..ebd8e36aa07d 100644 --- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java @@ -595,8 +595,8 @@ class UserUsageStatsService { private void loadActiveStats(final long currentTimeMillis) { for (int intervalType = 0; intervalType < mCurrentStats.length; intervalType++) { final IntervalStats stats = mDatabase.getLatestUsageStats(intervalType); - if (stats != null && currentTimeMillis - 500 >= stats.endTime && - currentTimeMillis < stats.beginTime + INTERVAL_LENGTH[intervalType]) { + if (stats != null + && currentTimeMillis < stats.beginTime + INTERVAL_LENGTH[intervalType]) { if (DEBUG) { Slog.d(TAG, mLogPrefix + "Loading existing stats @ " + sDateFormat.format(stats.beginTime) + "(" + stats.beginTime + diff --git a/telecomm/java/android/telecom/CallIdentification.java b/telecomm/java/android/telecom/CallIdentification.java index 87834fd5109d..cde7f608fa6a 100644 --- a/telecomm/java/android/telecom/CallIdentification.java +++ b/telecomm/java/android/telecom/CallIdentification.java @@ -45,13 +45,13 @@ public final class CallIdentification implements Parcelable { * {@link CallIdentification} for a screened call. */ public static class Builder { - private String mName; - private String mDescription; - private String mDetails; + private CharSequence mName; + private CharSequence mDescription; + private CharSequence mDetails; private Icon mPhoto; private int mNuisanceConfidence = CallIdentification.CONFIDENCE_UNKNOWN; private String mPackageName; - private String mAppName; + private CharSequence mAppName; /** * Default builder constructor. @@ -67,7 +67,7 @@ public final class CallIdentification implements Parcelable { * @param callIdAppName The app name. * @hide */ - public Builder(String callIdPackageName, String callIdAppName) { + public Builder(String callIdPackageName, CharSequence callIdAppName) { mPackageName = callIdPackageName; mAppName = callIdAppName; } @@ -80,7 +80,7 @@ public final class CallIdentification implements Parcelable { * @param name The name associated with the call, or {@code null} if none is provided. * @return Builder instance. */ - public Builder setName(@Nullable String name) { + public Builder setName(@Nullable CharSequence name) { mName = name; return this; } @@ -97,7 +97,7 @@ public final class CallIdentification implements Parcelable { * @param description The call description, or {@code null} if none is provided. * @return Builder instance. */ - public Builder setDescription(@Nullable String description) { + public Builder setDescription(@Nullable CharSequence description) { mDescription = description; return this; } @@ -114,7 +114,7 @@ public final class CallIdentification implements Parcelable { * @param details The call details, or {@code null} if none is provided. * @return Builder instance. */ - public Builder setDetails(@Nullable String details) { + public Builder setDetails(@Nullable CharSequence details) { mDetails = details; return this; } @@ -241,10 +241,10 @@ public final class CallIdentification implements Parcelable { * call identification. * @hide */ - private CallIdentification(@Nullable String name, @Nullable String description, - @Nullable String details, @Nullable Icon photo, + private CallIdentification(@Nullable CharSequence name, @Nullable CharSequence description, + @Nullable CharSequence details, @Nullable Icon photo, @NuisanceConfidence int nuisanceConfidence, @NonNull String callScreeningPackageName, - @NonNull String callScreeningAppName) { + @NonNull CharSequence callScreeningAppName) { mName = name; mDescription = description; mDetails = details; @@ -254,13 +254,13 @@ public final class CallIdentification implements Parcelable { mCallScreeningPackageName = callScreeningPackageName; } - private String mName; - private String mDescription; - private String mDetails; + private CharSequence mName; + private CharSequence mDescription; + private CharSequence mDetails; private Icon mPhoto; private int mNuisanceConfidence; private String mCallScreeningPackageName; - private String mCallScreeningAppName; + private CharSequence mCallScreeningAppName; @Override public int describeContents() { @@ -269,13 +269,13 @@ public final class CallIdentification implements Parcelable { @Override public void writeToParcel(Parcel parcel, int i) { - parcel.writeString(mName); - parcel.writeString(mDescription); - parcel.writeString(mDetails); + parcel.writeCharSequence(mName); + parcel.writeCharSequence(mDescription); + parcel.writeCharSequence(mDetails); parcel.writeParcelable(mPhoto, 0); parcel.writeInt(mNuisanceConfidence); parcel.writeString(mCallScreeningPackageName); - parcel.writeString(mCallScreeningAppName); + parcel.writeCharSequence(mCallScreeningAppName); } /** @@ -286,13 +286,13 @@ public final class CallIdentification implements Parcelable { @Override public CallIdentification createFromParcel(Parcel source) { - String name = source.readString(); - String description = source.readString(); - String details = source.readString(); + CharSequence name = source.readCharSequence(); + CharSequence description = source.readCharSequence(); + CharSequence details = source.readCharSequence(); Icon photo = source.readParcelable(ClassLoader.getSystemClassLoader()); int nuisanceConfidence = source.readInt(); String callScreeningPackageName = source.readString(); - String callScreeningAppName = source.readString(); + CharSequence callScreeningAppName = source.readCharSequence(); return new CallIdentification(name, description, details, photo, nuisanceConfidence, callScreeningPackageName, callScreeningAppName); } @@ -311,7 +311,7 @@ public final class CallIdentification implements Parcelable { * * @return The name associated with the number, or {@code null} if none was provided. */ - public final @Nullable String getName() { + public final @Nullable CharSequence getName() { return mName; } @@ -325,7 +325,7 @@ public final class CallIdentification implements Parcelable { * * @return The call description, or {@code null} if none was provided. */ - public final @Nullable String getDescription() { + public final @Nullable CharSequence getDescription() { return mDescription; } @@ -340,7 +340,7 @@ public final class CallIdentification implements Parcelable { * * @return The call details, or {@code null} if none was provided. */ - public final @Nullable String getDetails() { + public final @Nullable CharSequence getDetails() { return mDetails; } @@ -363,8 +363,7 @@ public final class CallIdentification implements Parcelable { * * @return The nuisance confidence. */ - public final @NuisanceConfidence - int getNuisanceConfidence() { + public final @NuisanceConfidence int getNuisanceConfidence() { return mNuisanceConfidence; } @@ -387,7 +386,7 @@ public final class CallIdentification implements Parcelable { * * @return The name of the app. */ - public final @NonNull String getCallScreeningAppName() { + public final @NonNull CharSequence getCallScreeningAppName() { return mCallScreeningAppName; } @@ -407,7 +406,7 @@ public final class CallIdentification implements Parcelable { * @param callScreeningAppName The app name. * @hide */ - public void setCallScreeningAppName(@NonNull String callScreeningAppName) { + public void setCallScreeningAppName(@NonNull CharSequence callScreeningAppName) { mCallScreeningAppName = callScreeningAppName; } diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl index 5030f90afd3e..93eea56f6490 100644 --- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl +++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl @@ -182,6 +182,7 @@ interface ITelecomService { /** * @see TelecomServiceImpl#getCallState */ + @UnsupportedAppUsage int getCallState(); /** diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index ad98e3676733..c0444bb17ac7 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -2992,9 +2992,9 @@ public class CarrierConfigManager { /* Default value is 1024 kbps */ sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_BANDWIDTH_INT, 1024); /* Default value is 10 seconds */ - sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_OR_EXIT_HYSTERESIS_TIME_LONG, 10000); + sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_ENTRY_OR_EXIT_HYSTERESIS_TIME_LONG, 10000); /* Default value is 10 seconds. */ - sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_DATA_SWITCH_HYSTERESIS_TIME_LONG, 10000); + sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_DATA_SWITCH_HYSTERESIS_TIME_LONG, 10000); sDefaults.putAll(Gps.getDefaults()); sDefaults.putIntArray(KEY_CDMA_ENHANCED_ROAMING_INDICATOR_FOR_HOME_NETWORK_INT_ARRAY, new int[] { diff --git a/telephony/java/android/telephony/CellSignalStrength.java b/telephony/java/android/telephony/CellSignalStrength.java index e6182eda852a..740b970b8e7c 100644 --- a/telephony/java/android/telephony/CellSignalStrength.java +++ b/telephony/java/android/telephony/CellSignalStrength.java @@ -119,7 +119,7 @@ public abstract class CellSignalStrength { /** @hide */ protected static final int getAsuFromRssiDbm(int dbm) { if (dbm == CellInfo.UNAVAILABLE) return 99; - return (dbm / 2) + 113; + return (dbm + 113) / 2; } // Range for RSCP in ASU (0-96, 255) as defined in TS 27.007 8.69 diff --git a/telephony/java/android/telephony/DataSpecificRegistrationStates.java b/telephony/java/android/telephony/DataSpecificRegistrationStates.java index d6a8065feabe..c3387f3f112d 100644 --- a/telephony/java/android/telephony/DataSpecificRegistrationStates.java +++ b/telephony/java/android/telephony/DataSpecificRegistrationStates.java @@ -1,5 +1,7 @@ package android.telephony; +import android.annotation.NonNull; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -10,14 +12,17 @@ import java.util.Objects; * Class that stores information specific to data network registration. * @hide */ -public class DataSpecificRegistrationStates implements Parcelable{ +@SystemApi +public final class DataSpecificRegistrationStates implements Parcelable{ /** + * @hide * The maximum number of simultaneous Data Calls that * must be established using setupDataCall(). */ public final int maxDataCalls; /** + * @hide * Indicates if the use of dual connectivity with NR is restricted. * Reference: 3GPP TS 24.301 v15.03 section 9.3.3.12A. */ @@ -25,7 +30,7 @@ public class DataSpecificRegistrationStates implements Parcelable{ /** * Indicates if NR is supported by the selected PLMN. - * + * @hide * {@code true} if the bit N is in the PLMN-InfoList-r15 is true and the selected PLMN is * present in plmn-IdentityList at position N. * Reference: 3GPP TS 36.331 v15.2.2 section 6.3.1 PLMN-InfoList-r15. @@ -34,6 +39,7 @@ public class DataSpecificRegistrationStates implements Parcelable{ public final boolean isNrAvailable; /** + * @hide * Indicates that if E-UTRA-NR Dual Connectivity (EN-DC) is supported by the primary serving * cell. * @@ -47,8 +53,11 @@ public class DataSpecificRegistrationStates implements Parcelable{ /** * Provides network support info for LTE VoPS and LTE Emergency bearer support */ - public final LteVopsSupportInfo lteVopsSupportInfo; + private final LteVopsSupportInfo lteVopsSupportInfo; + /** + * @hide + */ DataSpecificRegistrationStates( int maxDataCalls, boolean isDcNrRestricted, boolean isNrAvailable, boolean isEnDcAvailable, LteVopsSupportInfo lteVops) { @@ -126,4 +135,12 @@ public class DataSpecificRegistrationStates implements Parcelable{ return new DataSpecificRegistrationStates[size]; } }; + + /** + * @return LteVopsSupportInfo + */ + @NonNull + public LteVopsSupportInfo getLteVopsSupportInfo() { + return lteVopsSupportInfo; + } } diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java index f53cb8224706..6e839ab0ad6b 100644 --- a/telephony/java/android/telephony/DisconnectCause.java +++ b/telephony/java/android/telephony/DisconnectCause.java @@ -26,7 +26,7 @@ import android.annotation.UnsupportedAppUsage; * @hide */ @SystemApi -public class DisconnectCause { +public final class DisconnectCause { /** The disconnect cause is not valid (Not received a disconnect cause) */ public static final int NOT_VALID = -1; diff --git a/telephony/java/android/telephony/NetworkRegistrationState.java b/telephony/java/android/telephony/NetworkRegistrationState.java index 6e6d59e62148..84d6628d47d2 100644 --- a/telephony/java/android/telephony/NetworkRegistrationState.java +++ b/telephony/java/android/telephony/NetworkRegistrationState.java @@ -349,7 +349,7 @@ public class NetworkRegistrationState implements Parcelable { } /** - * @hide + * @return Data registration related info */ @Nullable public DataSpecificRegistrationStates getDataSpecificStates() { diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java index ffebc04c39e6..bb0673f921e3 100644 --- a/telephony/java/android/telephony/PhoneStateListener.java +++ b/telephony/java/android/telephony/PhoneStateListener.java @@ -176,26 +176,21 @@ public class PhoneStateListener { /** * Listen for {@link PreciseCallState.State} of ringing, background and foreground calls. - * {@more} - * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE - * READ_PRECISE_PHONE_STATE} * * @hide */ + @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) @SystemApi public static final int LISTEN_PRECISE_CALL_STATE = 0x00000800; /** * Listen for {@link PreciseDataConnectionState} on the data connection (cellular). * - * {@more} - * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE - * READ_PRECISE_PHONE_STATE} - * * @see #onPreciseDataConnectionStateChanged * * @hide */ + @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) @SystemApi public static final int LISTEN_PRECISE_DATA_CONNECTION_STATE = 0x00001000; @@ -331,12 +326,10 @@ public class PhoneStateListener { /** * Listen for call disconnect causes which contains {@link DisconnectCause} and * {@link PreciseDisconnectCause}. - * {@more} - * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE - * READ_PRECISE_PHONE_STATE} * * @hide */ + @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) @SystemApi public static final int LISTEN_CALL_DISCONNECT_CAUSES = 0x02000000; @@ -356,13 +349,10 @@ public class PhoneStateListener { * Listen for IMS call disconnect causes which contains * {@link android.telephony.ims.ImsReasonInfo} * - * {@more} - * Requires Permission: {@link android.Manifest.permission#READ_PRECISE_PHONE_STATE - * READ_PRECISE_PHONE_STATE} - * * @see #onImsCallDisconnectCauseChanged(ImsReasonInfo) * @hide */ + @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) @SystemApi public static final int LISTEN_IMS_CALL_DISCONNECT_CAUSES = 0x08000000; @@ -579,8 +569,9 @@ public class PhoneStateListener { * @param callState {@link PreciseCallState} * @hide */ + @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) @SystemApi - public void onPreciseCallStateChanged(PreciseCallState callState) { + public void onPreciseCallStateChanged(@NonNull PreciseCallState callState) { // default implementation empty } @@ -591,6 +582,7 @@ public class PhoneStateListener { * * @hide */ + @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) @SystemApi public void onCallDisconnectCauseChanged(int disconnectCause, int preciseDisconnectCause) { // default implementation empty @@ -602,6 +594,7 @@ public class PhoneStateListener { * * @hide */ + @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) @SystemApi public void onImsCallDisconnectCauseChanged(@NonNull ImsReasonInfo imsReasonInfo) { // default implementation empty @@ -613,6 +606,7 @@ public class PhoneStateListener { * * @hide */ + @RequiresPermission((android.Manifest.permission.READ_PRECISE_PHONE_STATE)) @SystemApi public void onPreciseDataConnectionStateChanged( PreciseDataConnectionState dataConnectionState) { diff --git a/telephony/java/android/telephony/PreciseDisconnectCause.java b/telephony/java/android/telephony/PreciseDisconnectCause.java index af88748af9e6..54980a29c0a6 100644 --- a/telephony/java/android/telephony/PreciseDisconnectCause.java +++ b/telephony/java/android/telephony/PreciseDisconnectCause.java @@ -23,7 +23,7 @@ import android.annotation.SystemApi; * @hide */ @SystemApi -public class PreciseDisconnectCause { +public final class PreciseDisconnectCause { /** The disconnect cause is not valid (Not received a disconnect cause).*/ public static final int NOT_VALID = -1; diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index a1aee6d8217f..3dc119950a59 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -1569,6 +1569,17 @@ public class ServiceState implements Parcelable { /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) public @TelephonyManager.NetworkType int getDataNetworkType() { + final NetworkRegistrationState iwlanRegState = getNetworkRegistrationState( + NetworkRegistrationState.DOMAIN_PS, AccessNetworkConstants.TransportType.WLAN); + if (iwlanRegState != null + && iwlanRegState.getRegState() == NetworkRegistrationState.REG_STATE_HOME) { + // If the device is on IWLAN, return IWLAN as the network type. This is to simulate the + // behavior of legacy mode device. In the future caller should use + // getNetworkRegistrationState() to retrieve the actual data network type on cellular + // or on IWLAN. + return iwlanRegState.getAccessNetworkTechnology(); + } + final NetworkRegistrationState regState = getNetworkRegistrationState( NetworkRegistrationState.DOMAIN_PS, AccessNetworkConstants.TransportType.WWAN); if (regState != null) { diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java index d2ae106b5545..d461bd0fee8a 100644 --- a/telephony/java/android/telephony/SignalStrength.java +++ b/telephony/java/android/telephony/SignalStrength.java @@ -215,13 +215,52 @@ public class SignalStrength implements Parcelable { * @see android.telephony#CellSignalStrengthGsm */ public @NonNull List<CellSignalStrength> getCellSignalStrengths() { - List<CellSignalStrength> cssList = new ArrayList<>(2); // Usually have 2 or fewer elems - if (mLte.isValid()) cssList.add(mLte); - if (mCdma.isValid()) cssList.add(mCdma); - if (mTdscdma.isValid()) cssList.add(mTdscdma); - if (mWcdma.isValid()) cssList.add(mWcdma); - if (mGsm.isValid()) cssList.add(mGsm); - if (mNr.isValid()) cssList.add(mNr); + return getCellSignalStrengths(CellSignalStrength.class); + } + + /** + * Returns a List of CellSignalStrength Components of this SignalStrength Report. + * + * Use this API to access underlying + * {@link android.telephony#CellSignalStrength CellSignalStrength} objects that provide more + * granular information about the SignalStrength report. Only valid (non-empty) + * CellSignalStrengths will be returned. The order of any returned elements is not guaranteed, + * and the list may contain more than one instance of a CellSignalStrength type. + * + * @param clazz a class type that extends + * {@link android.telephony.CellSignalStrength CellSignalStrength} to filter possible + * return values. + * @return a List of CellSignalStrength or an empty List if there are no valid measurements. + * + * @see android.telephony#CellSignalStrength + * @see android.telephony#CellSignalStrengthNr + * @see android.telephony#CellSignalStrengthLte + * @see android.telephony#CellSignalStrengthTdscdma + * @see android.telephony#CellSignalStrengthWcdma + * @see android.telephony#CellSignalStrengthCdma + * @see android.telephony#CellSignalStrengthGsm + */ + public <T extends CellSignalStrength> @NonNull List<T> getCellSignalStrengths( + @NonNull Class<T> clazz) { + List<T> cssList = new ArrayList<>(2); // Usually have 2 or fewer elems + if (mLte.isValid() && clazz.isAssignableFrom(CellSignalStrengthLte.class)) { + cssList.add((T) mLte); + } + if (mCdma.isValid() && clazz.isAssignableFrom(CellSignalStrengthCdma.class)) { + cssList.add((T) mCdma); + } + if (mTdscdma.isValid() && clazz.isAssignableFrom(CellSignalStrengthTdscdma.class)) { + cssList.add((T) mTdscdma); + } + if (mWcdma.isValid() && clazz.isAssignableFrom(CellSignalStrengthWcdma.class)) { + cssList.add((T) mWcdma); + } + if (mGsm.isValid() && clazz.isAssignableFrom(CellSignalStrengthGsm.class)) { + cssList.add((T) mGsm); + } + if (mNr.isValid() && clazz.isAssignableFrom(CellSignalStrengthNr.class)) { + cssList.add((T) mNr); + } return cssList; } diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java index 17acf2bcdd1d..108af617fccc 100644 --- a/telephony/java/android/telephony/SubscriptionInfo.java +++ b/telephony/java/android/telephony/SubscriptionInfo.java @@ -467,7 +467,7 @@ public class SubscriptionInfo implements Parcelable { * @return group UUID a String of group UUID if it belongs to a group. Otherwise * it will return null. */ - public String getGroupUuid() { + public @Nullable String getGroupUuid() { return mGroupUUID; } diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 94f26a8a8d61..d6d3a891484c 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -63,14 +63,17 @@ import com.android.internal.telephony.ISetOpportunisticDataCallback; import com.android.internal.telephony.ISub; import com.android.internal.telephony.ITelephonyRegistry; import com.android.internal.telephony.PhoneConstants; +import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; @@ -1066,7 +1069,8 @@ public class SubscriptionManager { * @param listener that is to be unregistered. */ public void removeOnOpportunisticSubscriptionsChangedListener( - OnOpportunisticSubscriptionsChangedListener listener) { + @NonNull OnOpportunisticSubscriptionsChangedListener listener) { + Preconditions.checkNotNull(listener, "listener cannot be null"); String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>"; if (DBG) { logd("unregister OnOpportunisticSubscriptionsChangedListener pkgForDebug=" @@ -2081,7 +2085,7 @@ public class SubscriptionManager { try { ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub")); if (iSub != null) { - subId = iSub.getActiveSubIdList(); + subId = iSub.getActiveSubIdList(/*visibleOnly*/true); } } catch (RemoteException ex) { // ignore it @@ -2682,7 +2686,8 @@ public class SubscriptionManager { * @param callbackIntent pending intent that will be sent after operation is done. */ @RequiresPermission(android.Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS) - public void switchToSubscription(int subId, PendingIntent callbackIntent) { + public void switchToSubscription(int subId, @NonNull PendingIntent callbackIntent) { + Preconditions.checkNotNull(callbackIntent, "callbackIntent cannot be null"); EuiccManager euiccManager = new EuiccManager(mContext); euiccManager.switchToSubscription(subId, callbackIntent); } @@ -2866,7 +2871,7 @@ public class SubscriptionManager { * * @hide */ - private boolean shouldHideSubscription(SubscriptionInfo info) { + public boolean shouldHideSubscription(SubscriptionInfo info) { if (info == null) return false; // If hasCarrierPrivileges or canManageSubscription returns true, it means caller @@ -2874,8 +2879,14 @@ public class SubscriptionManager { boolean hasCarrierPrivilegePermission = (info.isEmbedded() && canManageSubscription(info)) || TelephonyManager.from(mContext).hasCarrierPrivileges(info.getSubscriptionId()); - return (!TextUtils.isEmpty(info.getGroupUuid()) && info.isOpportunistic() - && !hasCarrierPrivilegePermission); + return isInvisibleSubscription(info) && !hasCarrierPrivilegePermission; + } + + /** + * @hide + */ + public static boolean isInvisibleSubscription(SubscriptionInfo info) { + return info != null && !TextUtils.isEmpty(info.getGroupUuid()) && info.isOpportunistic(); } /** @@ -2894,8 +2905,33 @@ public class SubscriptionManager { if (availableList == null) { return null; } else { - return availableList.stream().filter(subInfo -> !shouldHideSubscription(subInfo)) - .collect(Collectors.toList()); + // Multiple subscriptions in a group should only have one representative. + // It should be the current active primary subscription if any, or any + // primary subscription. + List<SubscriptionInfo> selectableList = new ArrayList<>(); + Map<String, SubscriptionInfo> groupMap = new HashMap<>(); + + for (SubscriptionInfo info : availableList) { + // Opportunistic subscriptions are considered invisible + // to users so they should never be returned. + if (isInvisibleSubscription(info)) continue; + + String groupUuid = info.getGroupUuid(); + if (groupUuid == null) { + // Doesn't belong to any group. Add in the list. + selectableList.add(info); + } else if (!groupMap.containsKey(groupUuid) + || (groupMap.get(groupUuid).getSimSlotIndex() == INVALID_SIM_SLOT_INDEX + && info.getSimSlotIndex() != INVALID_SIM_SLOT_INDEX)) { + // If it belongs to a group that has never been recorded or it's the current + // active subscription, add it in the list. + selectableList.remove(groupMap.get(groupUuid)); + selectableList.add(info); + groupMap.put(groupUuid, info); + } + + } + return selectableList; } } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 0b72679cdf2a..31d8ddbbd35f 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -1366,6 +1366,26 @@ public class TelephonyManager { * Intent sent when an error occurs that debug tools should log and possibly take further * action such as capturing vendor-specific logs. * + * A privileged application that reads these events should take appropriate vendor-specific + * action to record the event and collect further information to assist in analysis, debugging, + * and resolution of any associated issue. + * + * <p>This event should not be used for generic logging or diagnostic monitoring purposes and + * should generally be sent at a low rate. Instead, this mechanism should be used for the + * framework to notify a debugging application that an event (such as a bug) has occured + * within the framework if that event should trigger the collection and preservation of other + * more detailed device state for debugging. + * + * <p>At most one application can receive these events and should register a receiver in + * in the application manifest. For performance reasons, if no application to receive these + * events is detected at boot, then these events will not be sent. + * + * <p>Each event will include an {@link EXTRA_DEBUG_EVENT_ID} that will uniquely identify the + * event that has occurred. Each event will be sent to the diagnostic monitor only once per + * boot cycle (as another optimization). + * + * @see #EXTRA_DEBUG_EVENT_ID + * @see #EXTRA_DEBUG_EVENT_DESCRIPTION * @hide */ @SystemApi @@ -1373,21 +1393,23 @@ public class TelephonyManager { public static final String ACTION_DEBUG_EVENT = "android.telephony.action.DEBUG_EVENT"; /** - * An arbitrary ParcelUuid which should be consistent for each occurrence of the same event. + * An arbitrary ParcelUuid which should be consistent for each occurrence of a DebugEvent. * - * This field must be included in all events. + * This field must be included in all {@link ACTION_DEBUG_EVENT} events. * + * @see #ACTION_DEBUG_EVENT * @hide */ @SystemApi public static final String EXTRA_DEBUG_EVENT_ID = "android.telephony.extra.DEBUG_EVENT_ID"; /** - * A freeform string description of the event. + * A freeform string description of the DebugEvent. * - * This field is optional for all events and as a guideline should not exceed 80 characters - * and should be as short as possible to convey the essence of the event. + * This field is optional for all {@link ACTION_DEBUG_EVENT}s, as a guideline should not + * exceed 80 characters, and should be as short as possible to convey the essence of the event. * + * @see #ACTION_DEBUG_EVENT * @hide */ @SystemApi @@ -3227,6 +3249,7 @@ public class TelephonyManager { * the caller does not have adequate permissions for that card. */ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @NonNull public List<UiccCardInfo> getUiccCardsInfo() { try { ITelephony telephony = getITelephony(); @@ -8665,24 +8688,26 @@ public class TelephonyManager { /** - * Returns a well-formed IETF BCP 47 language tag representing the locale from the SIM, e.g, - * en-US. Returns {@code null} if no locale could be derived from subscriptions. + * Returns a locale based on the country and language from the SIM. Returns {@code null} if + * no locale could be derived from subscriptions. * * <p>Requires Permission: * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} * * @see Locale#toLanguageTag() - * @see Locale#forLanguageTag(String) * * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - @Nullable public String getSimLocale() { + @Nullable public Locale getSimLocale() { try { final ITelephony telephony = getITelephony(); if (telephony != null) { - return telephony.getSimLocaleForSubscriber(getSubId()); + String languageTag = telephony.getSimLocaleForSubscriber(getSubId()); + if (!TextUtils.isEmpty(languageTag)) { + return Locale.forLanguageTag(languageTag); + } } } catch (RemoteException ex) { } @@ -10306,24 +10331,25 @@ public class TelephonyManager { /** * Returns if the usage of multiple SIM cards at the same time to register on the network - * (e.g. Dual Standby or Dual Active) is restricted. + * (e.g. Dual Standby or Dual Active) is supported by the device and by the carrier. * - * @return true if usage of multiple SIMs is restricted, false otherwise. + * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). * - * @hide + * @return true if usage of multiple SIMs is supported, false otherwise. */ - @SystemApi - @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - public boolean isMultisimCarrierRestricted() { + @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges + @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) + public boolean isMultisimSupported() { try { ITelephony service = getITelephony(); if (service != null) { - return service.isMultisimCarrierRestricted(); + return service.isMultisimSupported(getOpPackageName()); } } catch (RemoteException e) { - Log.e(TAG, "isMultisimCarrierRestricted RemoteException", e); + Log.e(TAG, "isMultisimSupported RemoteException", e); } - return true; + return false; } /** @@ -10338,8 +10364,8 @@ public class TelephonyManager { @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void switchMultiSimConfig(int numOfSims) { //only proceed if multi-sim is not restricted - if (isMultisimCarrierRestricted()) { - Rlog.e(TAG, "switchMultiSimConfig not possible. It is restricted."); + if (!isMultisimSupported()) { + Rlog.e(TAG, "switchMultiSimConfig not possible. It is restricted or not supported."); return; } diff --git a/telephony/java/android/telephony/UiccCardInfo.java b/telephony/java/android/telephony/UiccCardInfo.java index 19f357a14687..d95a4992f808 100644 --- a/telephony/java/android/telephony/UiccCardInfo.java +++ b/telephony/java/android/telephony/UiccCardInfo.java @@ -15,6 +15,8 @@ */ package android.telephony; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; @@ -30,6 +32,7 @@ public final class UiccCardInfo implements Parcelable { private final String mEid; private final String mIccId; private final int mSlotIndex; + private final boolean mIsRemovable; public static final Creator<UiccCardInfo> CREATOR = new Creator<UiccCardInfo>() { @Override @@ -49,6 +52,7 @@ public final class UiccCardInfo implements Parcelable { mEid = in.readString(); mIccId = in.readString(); mSlotIndex = in.readInt(); + mIsRemovable = in.readByte() != 0; } @Override @@ -58,6 +62,7 @@ public final class UiccCardInfo implements Parcelable { dest.writeString(mEid); dest.writeString(mIccId); dest.writeInt(mSlotIndex); + dest.writeByte((byte) (mIsRemovable ? 1 : 0)); } @Override @@ -65,16 +70,21 @@ public final class UiccCardInfo implements Parcelable { return 0; } - public UiccCardInfo(boolean isEuicc, int cardId, String eid, String iccId, int slotIndex) { + /** + * @hide + */ + public UiccCardInfo(boolean isEuicc, int cardId, String eid, String iccId, int slotIndex, + boolean isRemovable) { this.mIsEuicc = isEuicc; this.mCardId = cardId; this.mEid = eid; this.mIccId = iccId; this.mSlotIndex = slotIndex; + this.mIsRemovable = isRemovable; } /** - * Return whether the UiccCardInfo is an eUICC. + * Return whether the UICC is an eUICC. * @return true if the UICC is an eUICC. */ public boolean isEuicc() { @@ -96,6 +106,7 @@ public final class UiccCardInfo implements Parcelable { * Note that this field may be omitted if the caller does not have the correct permissions * (see {@link TelephonyManager#getUiccCardsInfo()}). */ + @Nullable public String getEid() { if (!mIsEuicc) { return null; @@ -109,6 +120,7 @@ public final class UiccCardInfo implements Parcelable { * Note that this field may be omitted if the caller does not have the correct permissions * (see {@link TelephonyManager#getUiccCardsInfo()}). */ + @Nullable public String getIccId() { return mIccId; } @@ -121,13 +133,24 @@ public final class UiccCardInfo implements Parcelable { } /** - * Returns a copy of the UiccCardinfo with the clears the EID and ICCID set to null. These - * values are generally private and require carrier privileges to view. + * Returns a copy of the UiccCardinfo with the EID and ICCID set to null. These values are + * generally private and require carrier privileges to view. * * @hide */ + @NonNull public UiccCardInfo getUnprivileged() { - return new UiccCardInfo(mIsEuicc, mCardId, null, null, mSlotIndex); + return new UiccCardInfo(mIsEuicc, mCardId, null, null, mSlotIndex, mIsRemovable); + } + + /** + * Return whether the UICC or eUICC is removable. + * <p> + * UICCs are generally removable, but eUICCs may be removable or built in to the device. + * @return true if the UICC or eUICC is removable + */ + public boolean isRemovable() { + return mIsRemovable; } @Override @@ -144,12 +167,13 @@ public final class UiccCardInfo implements Parcelable { && (mCardId == that.mCardId) && (Objects.equals(mEid, that.mEid)) && (Objects.equals(mIccId, that.mIccId)) - && (mSlotIndex == that.mSlotIndex)); + && (mSlotIndex == that.mSlotIndex) + && (mIsRemovable == that.mIsRemovable)); } @Override public int hashCode() { - return Objects.hash(mIsEuicc, mCardId, mEid, mIccId, mSlotIndex); + return Objects.hash(mIsEuicc, mCardId, mEid, mIccId, mSlotIndex, mIsRemovable); } @Override @@ -164,6 +188,8 @@ public final class UiccCardInfo implements Parcelable { + mIccId + ", mSlotIndex=" + mSlotIndex + + ", mIsRemovable=" + + mIsRemovable + ")"; } } diff --git a/telephony/java/android/telephony/UiccSlotInfo.java b/telephony/java/android/telephony/UiccSlotInfo.java index a39992b17f8e..93a7da04c56e 100644 --- a/telephony/java/android/telephony/UiccSlotInfo.java +++ b/telephony/java/android/telephony/UiccSlotInfo.java @@ -15,15 +15,15 @@ */ package android.telephony; +import android.annotation.IntDef; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Objects; -import android.annotation.IntDef; - /** * Class for the information of a UICC slot. * @hide @@ -61,6 +61,7 @@ public class UiccSlotInfo implements Parcelable { private final @CardStateInfo int mCardStateInfo; private final int mLogicalSlotIdx; private final boolean mIsExtendedApduSupported; + private final boolean mIsRemovable; public static final Creator<UiccSlotInfo> CREATOR = new Creator<UiccSlotInfo>() { @Override @@ -81,6 +82,7 @@ public class UiccSlotInfo implements Parcelable { mCardStateInfo = in.readInt(); mLogicalSlotIdx = in.readInt(); mIsExtendedApduSupported = in.readByte() != 0; + mIsRemovable = in.readByte() != 0; } @Override @@ -91,6 +93,7 @@ public class UiccSlotInfo implements Parcelable { dest.writeInt(mCardStateInfo); dest.writeInt(mLogicalSlotIdx); dest.writeByte((byte) (mIsExtendedApduSupported ? 1 : 0)); + dest.writeByte((byte) (mIsRemovable ? 1 : 0)); } @Override @@ -98,6 +101,11 @@ public class UiccSlotInfo implements Parcelable { return 0; } + /** + * Construct a UiccSlotInfo. + * @deprecated apps should not be constructing UiccSlotInfo objects + */ + @Deprecated public UiccSlotInfo(boolean isActive, boolean isEuicc, String cardId, @CardStateInfo int cardStateInfo, int logicalSlotIdx, boolean isExtendedApduSupported) { this.mIsActive = isActive; @@ -106,6 +114,22 @@ public class UiccSlotInfo implements Parcelable { this.mCardStateInfo = cardStateInfo; this.mLogicalSlotIdx = logicalSlotIdx; this.mIsExtendedApduSupported = isExtendedApduSupported; + this.mIsRemovable = false; + } + + /** + * @hide + */ + public UiccSlotInfo(boolean isActive, boolean isEuicc, String cardId, + @CardStateInfo int cardStateInfo, int logicalSlotIdx, boolean isExtendedApduSupported, + boolean isRemovable) { + this.mIsActive = isActive; + this.mIsEuicc = isEuicc; + this.mCardId = cardId; + this.mCardStateInfo = cardStateInfo; + this.mLogicalSlotIdx = logicalSlotIdx; + this.mIsExtendedApduSupported = isExtendedApduSupported; + this.mIsRemovable = isRemovable; } public boolean getIsActive() { @@ -136,6 +160,16 @@ public class UiccSlotInfo implements Parcelable { return mIsExtendedApduSupported; } + /** + * Return whether the UICC slot is for a removable UICC. + * <p> + * UICCs are generally removable, but eUICCs may be removable or built in to the device. + * @return true if the slot is for removable UICCs + */ + public boolean isRemovable() { + return mIsRemovable; + } + @Override public boolean equals(Object obj) { if (this == obj) { @@ -151,7 +185,8 @@ public class UiccSlotInfo implements Parcelable { && (Objects.equals(mCardId, that.mCardId)) && (mCardStateInfo == that.mCardStateInfo) && (mLogicalSlotIdx == that.mLogicalSlotIdx) - && (mIsExtendedApduSupported == that.mIsExtendedApduSupported); + && (mIsExtendedApduSupported == that.mIsExtendedApduSupported) + && (mIsRemovable == that.mIsRemovable); } @Override @@ -163,6 +198,7 @@ public class UiccSlotInfo implements Parcelable { result = 31 * result + mCardStateInfo; result = 31 * result + mLogicalSlotIdx; result = 31 * result + (mIsExtendedApduSupported ? 1 : 0); + result = 31 * result + (mIsRemovable ? 1 : 0); return result; } @@ -180,6 +216,8 @@ public class UiccSlotInfo implements Parcelable { + mLogicalSlotIdx + ", mIsExtendedApduSupported=" + mIsExtendedApduSupported + + ", mIsRemovable=" + + mIsRemovable + ")"; } } diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java index eb99d5dcaaeb..bb85be16eb3a 100644 --- a/telephony/java/android/telephony/ims/ImsMmTelManager.java +++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java @@ -139,7 +139,7 @@ public class ImsMmTelManager { if (mLocalCallback == null) return; Binder.withCleanCallingIdentity(() -> - mExecutor.execute(() -> mLocalCallback.onDeregistered(info))); + mExecutor.execute(() -> mLocalCallback.onUnregistered(info))); } @Override @@ -199,7 +199,7 @@ public class ImsMmTelManager { * * @param info the {@link ImsReasonInfo} associated with why registration was disconnected. */ - public void onDeregistered(ImsReasonInfo info) { + public void onUnregistered(ImsReasonInfo info) { } /** @@ -485,7 +485,7 @@ public class ImsMmTelManager { * @see android.telephony.CarrierConfigManager#KEY_HIDE_ENHANCED_4G_LTE_BOOL * @see android.telephony.CarrierConfigManager#KEY_ENHANCED_4G_LTE_ON_BY_DEFAULT_BOOL * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VOLTE_AVAILABLE_BOOL - * @see #setAdvancedCallingSetting(boolean) + * @see #setAdvancedCallingSettingEnabled(boolean) * @return true if the user's setting for advanced calling is enabled, false otherwise. */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @@ -519,9 +519,9 @@ public class ImsMmTelManager { * @see #isAdvancedCallingSettingEnabled() */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) - public void setAdvancedCallingSetting(boolean isEnabled) { + public void setAdvancedCallingSettingEnabled(boolean isEnabled) { try { - getITelephony().setAdvancedCallingSetting(mSubId, isEnabled); + getITelephony().setAdvancedCallingSettingEnabled(mSubId, isEnabled); return; } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); @@ -589,7 +589,7 @@ public class ImsMmTelManager { /** * The user's setting for whether or not they have enabled the "Video Calling" setting. * @return true if the user’s “Video Calling” setting is currently enabled. - * @see #setVtSetting(boolean) + * @see #setVtSettingEnabled(boolean) */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isVtSettingEnabled() { @@ -605,9 +605,9 @@ public class ImsMmTelManager { * @see #isVtSettingEnabled() */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) - public void setVtSetting(boolean isEnabled) { + public void setVtSettingEnabled(boolean isEnabled) { try { - getITelephony().setVtSetting(mSubId, isEnabled); + getITelephony().setVtSettingEnabled(mSubId, isEnabled); return; } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); @@ -616,7 +616,7 @@ public class ImsMmTelManager { /** * @return true if the user's setting for Voice over WiFi is enabled and false if it is not. - * @see #setVoWiFiSetting(boolean) + * @see #setVoWiFiSettingEnabled(boolean) */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isVoWiFiSettingEnabled() { @@ -633,9 +633,9 @@ public class ImsMmTelManager { * @see #isVoWiFiSettingEnabled() */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) - public void setVoWiFiSetting(boolean isEnabled) { + public void setVoWiFiSettingEnabled(boolean isEnabled) { try { - getITelephony().setVoWiFiSetting(mSubId, isEnabled); + getITelephony().setVoWiFiSettingEnabled(mSubId, isEnabled); return; } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); @@ -645,7 +645,7 @@ public class ImsMmTelManager { /** * @return true if the user's setting for Voice over WiFi while roaming is enabled, false * if disabled. - * @see #setVoWiFiRoamingSetting(boolean) + * @see #setVoWiFiRoamingSettingEnabled(boolean) */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isVoWiFiRoamingSettingEnabled() { @@ -663,9 +663,9 @@ public class ImsMmTelManager { * @see #isVoWiFiRoamingSettingEnabled() */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) - public void setVoWiFiRoamingSetting(boolean isEnabled) { + public void setVoWiFiRoamingSettingEnabled(boolean isEnabled) { try { - getITelephony().setVoWiFiRoamingSetting(mSubId, isEnabled); + getITelephony().setVoWiFiRoamingSettingEnabled(mSubId, isEnabled); return; } catch (RemoteException e) { throw e.rethrowAsRuntimeException(); @@ -682,7 +682,7 @@ public class ImsMmTelManager { * - {@link #WIFI_MODE_WIFI_ONLY} * - {@link #WIFI_MODE_CELLULAR_PREFERRED} * - {@link #WIFI_MODE_WIFI_PREFERRED} - * @see #setVoWiFiSetting(boolean) + * @see #setVoWiFiSettingEnabled(boolean) */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiNonPersistent(boolean isCapable, int mode) { @@ -700,7 +700,7 @@ public class ImsMmTelManager { * - {@link #WIFI_MODE_WIFI_ONLY} * - {@link #WIFI_MODE_CELLULAR_PREFERRED} * - {@link #WIFI_MODE_WIFI_PREFERRED} - * @see #setVoWiFiSetting(boolean) + * @see #setVoWiFiSettingEnabled(boolean) */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public @WiFiCallingMode int getVoWiFiModeSetting() { @@ -739,7 +739,7 @@ public class ImsMmTelManager { * - {@link #WIFI_MODE_WIFI_ONLY} * - {@link #WIFI_MODE_CELLULAR_PREFERRED} * - {@link #WIFI_MODE_WIFI_PREFERRED} - * @see #setVoWiFiRoamingSetting(boolean) + * @see #setVoWiFiRoamingSettingEnabled(boolean) */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public @WiFiCallingMode int getVoWiFiRoamingModeSetting() { diff --git a/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java b/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java index 837ef54a2f24..d11a0de24fb5 100644 --- a/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java +++ b/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java @@ -99,7 +99,7 @@ public final class ImsStreamMediaProfile implements Parcelable { public int mRttMode; // RTT Audio Speech Indicator /** @hide */ - public boolean mHasRttAudioSpeech = false; + public boolean mIsReceivingRttAudio = false; /** @hide */ public ImsStreamMediaProfile(Parcel in) { @@ -201,7 +201,7 @@ public final class ImsStreamMediaProfile implements Parcelable { ", videoQuality=" + mVideoQuality + ", videoDirection=" + mVideoDirection + ", rttMode=" + mRttMode + - ", hasRttAudioSpeech=" + mHasRttAudioSpeech + " }"; + ", hasRttAudioSpeech=" + mIsReceivingRttAudio + " }"; } @Override @@ -216,7 +216,7 @@ public final class ImsStreamMediaProfile implements Parcelable { out.writeInt(mVideoQuality); out.writeInt(mVideoDirection); out.writeInt(mRttMode); - out.writeBoolean(mHasRttAudioSpeech); + out.writeBoolean(mIsReceivingRttAudio); } private void readFromParcel(Parcel in) { @@ -225,7 +225,7 @@ public final class ImsStreamMediaProfile implements Parcelable { mVideoQuality = in.readInt(); mVideoDirection = in.readInt(); mRttMode = in.readInt(); - mHasRttAudioSpeech = in.readBoolean(); + mIsReceivingRttAudio = in.readBoolean(); } public static final Creator<ImsStreamMediaProfile> CREATOR = @@ -256,8 +256,12 @@ public final class ImsStreamMediaProfile implements Parcelable { mRttMode = rttMode; } - public void setRttAudioSpeech(boolean audioOn) { - mHasRttAudioSpeech = audioOn; + /** + * Sets whether the remote party is transmitting audio over the RTT call. + * @param audioOn true if audio is being received, false otherwise. + */ + public void setReceivingRttAudio(boolean audioOn) { + mIsReceivingRttAudio = audioOn; } public int getAudioQuality() { @@ -280,7 +284,10 @@ public final class ImsStreamMediaProfile implements Parcelable { return mRttMode; } - public boolean getRttAudioSpeech() { - return mHasRttAudioSpeech; + /** + * @return true if remote party is transmitting audio, false otherwise. + */ + public boolean isReceivingRttAudio() { + return mIsReceivingRttAudio; } } diff --git a/telephony/java/android/telephony/ims/Rcs1To1Thread.java b/telephony/java/android/telephony/ims/Rcs1To1Thread.java index d4a78ffb77db..0bb1b4379679 100644 --- a/telephony/java/android/telephony/ims/Rcs1To1Thread.java +++ b/telephony/java/android/telephony/ims/Rcs1To1Thread.java @@ -22,6 +22,8 @@ import android.annotation.WorkerThread; * Rcs1To1Thread represents a single RCS conversation thread with a total of two * {@link RcsParticipant}s. Please see Section 5 (1-to-1 Messaging) - GSMA RCC.71 (RCS Universal * Profile Service Definition Document) + * + * @hide */ public class Rcs1To1Thread extends RcsThread { private int mThreadId; diff --git a/telephony/java/android/telephony/ims/RcsEvent.java b/telephony/java/android/telephony/ims/RcsEvent.java index a547c5c00141..994b27ab7405 100644 --- a/telephony/java/android/telephony/ims/RcsEvent.java +++ b/telephony/java/android/telephony/ims/RcsEvent.java @@ -17,6 +17,8 @@ package android.telephony.ims; /** * The base class for events that can happen on {@link RcsParticipant}s and {@link RcsThread}s. + * + * @hide */ public abstract class RcsEvent { private final long mTimestamp; diff --git a/telephony/java/android/telephony/ims/RcsEventQueryParams.java b/telephony/java/android/telephony/ims/RcsEventQueryParams.java index 9dbfe4393213..5f8fa8003751 100644 --- a/telephony/java/android/telephony/ims/RcsEventQueryParams.java +++ b/telephony/java/android/telephony/ims/RcsEventQueryParams.java @@ -37,6 +37,8 @@ import java.security.InvalidParameterException; * The parameters to pass into * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} in order to select a * subset of {@link RcsEvent}s present in the message store. + * + * @hide */ public final class RcsEventQueryParams implements Parcelable { /** diff --git a/telephony/java/android/telephony/ims/RcsEventQueryResult.java b/telephony/java/android/telephony/ims/RcsEventQueryResult.java index 92bda813f93e..d6347e3ec693 100644 --- a/telephony/java/android/telephony/ims/RcsEventQueryResult.java +++ b/telephony/java/android/telephony/ims/RcsEventQueryResult.java @@ -22,6 +22,8 @@ import java.util.List; * The result of a {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} * call. This class allows getting the token for querying the next batch of events in order to * prevent handling large amounts of data at once. + * + * @hide */ public class RcsEventQueryResult { private RcsQueryContinuationToken mContinuationToken; diff --git a/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.java b/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.java index 14af8ea63a67..4742ba2730b6 100644 --- a/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.java +++ b/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.java @@ -24,6 +24,8 @@ import android.os.Parcelable; * Pass an instance of this class to * {@link RcsMessage#insertFileTransfer(RcsFileTransferCreationParams)} create an * {@link RcsFileTransferPart} and save it into storage. + * + * @hide */ public final class RcsFileTransferCreationParams implements Parcelable { private String mRcsFileTransferSessionId; diff --git a/telephony/java/android/telephony/ims/RcsFileTransferPart.java b/telephony/java/android/telephony/ims/RcsFileTransferPart.java index 9531c2e2f981..3816cd413722 100644 --- a/telephony/java/android/telephony/ims/RcsFileTransferPart.java +++ b/telephony/java/android/telephony/ims/RcsFileTransferPart.java @@ -26,6 +26,8 @@ import java.lang.annotation.RetentionPolicy; /** * A part of a composite {@link RcsMessage} that holds a file transfer. Please see Section 7 * (File Transfer) - GSMA RCC.71 (RCS Universal Profile Service Definition Document) + * + * @hide */ public class RcsFileTransferPart { /** diff --git a/telephony/java/android/telephony/ims/RcsGroupThread.java b/telephony/java/android/telephony/ims/RcsGroupThread.java index 6e17bc2a685f..8cd633ba1edf 100644 --- a/telephony/java/android/telephony/ims/RcsGroupThread.java +++ b/telephony/java/android/telephony/ims/RcsGroupThread.java @@ -29,6 +29,8 @@ import java.util.Set; * RcsGroupThread represents a single RCS conversation thread where {@link RcsParticipant}s can join * or leave. Please see Section 6 (Group Chat) - GSMA RCC.71 (RCS Universal Profile Service * Definition Document) + * + * @hide */ public class RcsGroupThread extends RcsThread { /** diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadEvent.java index 99086aaef676..4a6b963a143a 100644 --- a/telephony/java/android/telephony/ims/RcsGroupThreadEvent.java +++ b/telephony/java/android/telephony/ims/RcsGroupThreadEvent.java @@ -19,6 +19,8 @@ import android.annotation.NonNull; /** * An event that happened on an {@link RcsGroupThread}. + * + * @hide */ public abstract class RcsGroupThreadEvent extends RcsEvent { private final int mRcsGroupThreadId; diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java index cbd762d3032b..3c6c74fac8e2 100644 --- a/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java +++ b/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java @@ -22,6 +22,8 @@ import android.net.Uri; /** * An event that indicates an {@link RcsGroupThread}'s icon was changed. Please see R6-2-5 - GSMA * RCC.71 (RCS Universal Profile Service Definition Document) + * + * @hide */ public final class RcsGroupThreadIconChangedEvent extends RcsGroupThreadEvent { private final Uri mNewIcon; diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java index a2a4fab4f3d2..54032536601e 100644 --- a/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java +++ b/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java @@ -21,6 +21,8 @@ import android.annotation.Nullable; /** * An event that indicates an {@link RcsGroupThread}'s name was changed. Please see R6-2-5 - GSMA * RCC.71 (RCS Universal Profile Service Definition Document) + * + * @hide */ public final class RcsGroupThreadNameChangedEvent extends RcsGroupThreadEvent { private final String mNewName; diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java index 183cd9a81431..48be479a1ac6 100644 --- a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java +++ b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java @@ -20,6 +20,8 @@ import android.annotation.NonNull; /** * An event that indicates an RCS participant has joined an {@link RcsThread}. Please see US6-3 - * GSMA RCC.71 (RCS Universal Profile Service Definition Document) + * + * @hide */ public final class RcsGroupThreadParticipantJoinedEvent extends RcsGroupThreadEvent { private final RcsParticipant mJoinedParticipantId; diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java index c12549b5acbf..b724a3f2159f 100644 --- a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java +++ b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java @@ -20,6 +20,8 @@ import android.annotation.NonNull; /** * An event that indicates an RCS participant has left an {@link RcsThread}. Please see US6-23 - * GSMA RCC.71 (RCS Universal Profile Service Definition Document) + * + * @hide */ public final class RcsGroupThreadParticipantLeftEvent extends RcsGroupThreadEvent { private RcsParticipant mLeavingParticipant; diff --git a/telephony/java/android/telephony/ims/RcsIncomingMessage.java b/telephony/java/android/telephony/ims/RcsIncomingMessage.java index 61911abd00c5..06e2a41accee 100644 --- a/telephony/java/android/telephony/ims/RcsIncomingMessage.java +++ b/telephony/java/android/telephony/ims/RcsIncomingMessage.java @@ -19,6 +19,8 @@ import android.annotation.WorkerThread; /** * This is a single instance of a message received over RCS. + * + * @hide */ public class RcsIncomingMessage extends RcsMessage { /** diff --git a/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.java b/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.java index 61dedbc1578a..58dc1bc70424 100644 --- a/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.java +++ b/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.java @@ -24,6 +24,8 @@ import android.os.Parcelable; * {@link RcsIncomingMessageCreationParams} is a collection of parameters that should be passed * into {@link RcsThread#addIncomingMessage(RcsIncomingMessageCreationParams)} to generate an * {@link RcsIncomingMessage} on that {@link RcsThread} + * + * @hide */ public final class RcsIncomingMessageCreationParams extends RcsMessageCreationParams implements Parcelable { diff --git a/telephony/java/android/telephony/ims/RcsManager.java b/telephony/java/android/telephony/ims/RcsManager.java index 22e4b2249c36..63dc1ac568bf 100644 --- a/telephony/java/android/telephony/ims/RcsManager.java +++ b/telephony/java/android/telephony/ims/RcsManager.java @@ -20,6 +20,8 @@ import android.content.Context; /** * The manager class for RCS related utilities. + * + * @hide */ @SystemService(Context.TELEPHONY_RCS_SERVICE) public class RcsManager { diff --git a/telephony/java/android/telephony/ims/RcsMessage.java b/telephony/java/android/telephony/ims/RcsMessage.java index 32274131a5ad..b0d0d5a6a9bb 100644 --- a/telephony/java/android/telephony/ims/RcsMessage.java +++ b/telephony/java/android/telephony/ims/RcsMessage.java @@ -27,6 +27,8 @@ import java.util.Set; /** * This is a single instance of a message sent or received over RCS. + * + * @hide */ public abstract class RcsMessage { /** diff --git a/telephony/java/android/telephony/ims/RcsMessageCreationParams.java b/telephony/java/android/telephony/ims/RcsMessageCreationParams.java index c46c605d861d..f0eea88ac8a9 100644 --- a/telephony/java/android/telephony/ims/RcsMessageCreationParams.java +++ b/telephony/java/android/telephony/ims/RcsMessageCreationParams.java @@ -27,6 +27,8 @@ import android.os.Parcel; * {@link RcsThread#addIncomingMessage(RcsIncomingMessageCreationParams)} and * {@link RcsThread#addOutgoingMessage(RcsOutgoingMessageCreationParams)} to create and persist * {@link RcsMessage}s on an {@link RcsThread} + * + * @hide */ public class RcsMessageCreationParams { // The globally unique id of the RcsMessage to be created. diff --git a/telephony/java/android/telephony/ims/RcsMessageQueryParams.java b/telephony/java/android/telephony/ims/RcsMessageQueryParams.java index 535a597f5e1e..6491ac9fad5f 100644 --- a/telephony/java/android/telephony/ims/RcsMessageQueryParams.java +++ b/telephony/java/android/telephony/ims/RcsMessageQueryParams.java @@ -31,6 +31,8 @@ import java.security.InvalidParameterException; * The parameters to pass into * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} in order to select a * subset of {@link RcsMessage}s present in the message store. + * + * @hide */ public final class RcsMessageQueryParams implements Parcelable { /** diff --git a/telephony/java/android/telephony/ims/RcsMessageQueryResult.java b/telephony/java/android/telephony/ims/RcsMessageQueryResult.java index 3514b48e80a1..e4020c185fa3 100644 --- a/telephony/java/android/telephony/ims/RcsMessageQueryResult.java +++ b/telephony/java/android/telephony/ims/RcsMessageQueryResult.java @@ -32,6 +32,8 @@ import java.util.List; * The result of a {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} * call. This class allows getting the token for querying the next batch of messages in order to * prevent handling large amounts of data at once. + * + * @hide */ public final class RcsMessageQueryResult implements Parcelable { // The token to continue the query to get the next batch of results diff --git a/telephony/java/android/telephony/ims/RcsMessageSnippet.java b/telephony/java/android/telephony/ims/RcsMessageSnippet.java index b0b930c56e91..9064251f5021 100644 --- a/telephony/java/android/telephony/ims/RcsMessageSnippet.java +++ b/telephony/java/android/telephony/ims/RcsMessageSnippet.java @@ -23,6 +23,8 @@ import android.telephony.ims.RcsMessage.RcsMessageStatus; /** * An immutable summary of the latest {@link RcsMessage} on an {@link RcsThread} + * + * @hide */ public final class RcsMessageSnippet implements Parcelable { private final String mText; diff --git a/telephony/java/android/telephony/ims/RcsMessageStore.java b/telephony/java/android/telephony/ims/RcsMessageStore.java index 31f2983dcf93..311165232158 100644 --- a/telephony/java/android/telephony/ims/RcsMessageStore.java +++ b/telephony/java/android/telephony/ims/RcsMessageStore.java @@ -26,6 +26,8 @@ import java.util.List; /** * RcsMessageStore is the application interface to RcsProvider and provides access methods to * RCS related database tables. + * + * @hide */ public class RcsMessageStore { /** diff --git a/telephony/java/android/telephony/ims/RcsMessageStoreException.java b/telephony/java/android/telephony/ims/RcsMessageStoreException.java index f25bb173be37..3b3fcf21dd7a 100644 --- a/telephony/java/android/telephony/ims/RcsMessageStoreException.java +++ b/telephony/java/android/telephony/ims/RcsMessageStoreException.java @@ -19,6 +19,8 @@ package android.telephony.ims; /** * An exception that happened on {@link RcsMessageStore} or one of the derived storage classes in * {@link android.telephony.ims} + * + * @hide */ public class RcsMessageStoreException extends Exception { diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessage.java b/telephony/java/android/telephony/ims/RcsOutgoingMessage.java index 06fb83268afb..1b4bfe576ac6 100644 --- a/telephony/java/android/telephony/ims/RcsOutgoingMessage.java +++ b/telephony/java/android/telephony/ims/RcsOutgoingMessage.java @@ -23,6 +23,8 @@ import java.util.List; /** * This is a single instance of a message sent over RCS. + * + * @hide */ public class RcsOutgoingMessage extends RcsMessage { RcsOutgoingMessage(int id) { diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.java b/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.java index 979634a069df..81e3244d57e8 100644 --- a/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.java +++ b/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.java @@ -23,6 +23,8 @@ import android.os.Parcelable; * {@link RcsOutgoingMessageCreationParams} is a collection of parameters that should be passed * into {@link RcsThread#addOutgoingMessage(RcsOutgoingMessageCreationParams)} to generate an * {@link RcsOutgoingMessage} on that {@link RcsThread} + * + * @hide */ public final class RcsOutgoingMessageCreationParams extends RcsMessageCreationParams implements Parcelable { diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java b/telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java index 1c87b13f0dfb..2db49c6d0dce 100644 --- a/telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java +++ b/telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java @@ -21,6 +21,8 @@ import android.annotation.WorkerThread; /** * This class holds the delivery information of an {@link RcsOutgoingMessage} for each * {@link RcsParticipant} that the message was intended for. + * + * @hide */ public class RcsOutgoingMessageDelivery { // The participant that this delivery is intended for diff --git a/telephony/java/android/telephony/ims/RcsParticipant.java b/telephony/java/android/telephony/ims/RcsParticipant.java index 7ba5d8e65f76..bcf134a71ea3 100644 --- a/telephony/java/android/telephony/ims/RcsParticipant.java +++ b/telephony/java/android/telephony/ims/RcsParticipant.java @@ -20,6 +20,8 @@ import android.annotation.WorkerThread; /** * RcsParticipant is an RCS capable contact that can participate in {@link RcsThread}s. + * + * @hide */ public class RcsParticipant { // The row ID of this participant in the database diff --git a/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java b/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java index cc2613f9e684..61801f3fbf2c 100644 --- a/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java +++ b/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java @@ -21,6 +21,8 @@ import android.annotation.Nullable; /** * An event that indicates an {@link RcsParticipant}'s alias was changed. Please see US18-2 - GSMA * RCC.71 (RCS Universal Profile Service Definition Document) + * + * @hide */ public final class RcsParticipantAliasChangedEvent extends RcsEvent { // The participant that changed their alias diff --git a/telephony/java/android/telephony/ims/RcsParticipantQueryParams.java b/telephony/java/android/telephony/ims/RcsParticipantQueryParams.java index d24d079d7038..ada9b8ae9d1a 100644 --- a/telephony/java/android/telephony/ims/RcsParticipantQueryParams.java +++ b/telephony/java/android/telephony/ims/RcsParticipantQueryParams.java @@ -30,6 +30,8 @@ import java.security.InvalidParameterException; * The parameters to pass into * {@link RcsMessageStore#getRcsParticipants(RcsParticipantQueryParams)} in order to select a * subset of {@link RcsThread}s present in the message store. + * + * @hide */ public final class RcsParticipantQueryParams implements Parcelable { /** diff --git a/telephony/java/android/telephony/ims/RcsParticipantQueryResult.java b/telephony/java/android/telephony/ims/RcsParticipantQueryResult.java index 505f1a55d1f0..92e2fa78526a 100644 --- a/telephony/java/android/telephony/ims/RcsParticipantQueryResult.java +++ b/telephony/java/android/telephony/ims/RcsParticipantQueryResult.java @@ -28,6 +28,8 @@ import java.util.List; * The result of a {@link RcsMessageStore#getRcsParticipants(RcsParticipantQueryParams)} * call. This class allows getting the token for querying the next batch of participants in order to * prevent handling large amounts of data at once. + * + * @hide */ public final class RcsParticipantQueryResult implements Parcelable { // A token for the caller to continue their query for the next batch of results diff --git a/telephony/java/android/telephony/ims/RcsQueryContinuationToken.java b/telephony/java/android/telephony/ims/RcsQueryContinuationToken.java index 08643de51d40..970c11078772 100644 --- a/telephony/java/android/telephony/ims/RcsQueryContinuationToken.java +++ b/telephony/java/android/telephony/ims/RcsQueryContinuationToken.java @@ -31,6 +31,8 @@ import java.lang.annotation.RetentionPolicy; * @see RcsMessageQueryResult#getContinuationToken() * @see RcsParticipantQueryResult#getContinuationToken() * @see RcsThreadQueryResult#getContinuationToken() + * + * @hide */ public final class RcsQueryContinuationToken implements Parcelable { /** diff --git a/telephony/java/android/telephony/ims/RcsThread.java b/telephony/java/android/telephony/ims/RcsThread.java index e015dd3e9c0a..cf1dc76fedfb 100644 --- a/telephony/java/android/telephony/ims/RcsThread.java +++ b/telephony/java/android/telephony/ims/RcsThread.java @@ -27,6 +27,8 @@ import com.android.internal.annotations.VisibleForTesting; /** * RcsThread represents a single RCS conversation thread. It holds messages that were sent and * received and events that occurred on that thread. + * + * @hide */ public abstract class RcsThread { /** diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryParams.java b/telephony/java/android/telephony/ims/RcsThreadQueryParams.java index 05a5a3917691..81eee4081ace 100644 --- a/telephony/java/android/telephony/ims/RcsThreadQueryParams.java +++ b/telephony/java/android/telephony/ims/RcsThreadQueryParams.java @@ -35,6 +35,8 @@ import java.util.Set; /** * The parameters to pass into {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)} in * order to select a subset of {@link RcsThread}s present in the message store. + * + * @hide */ public final class RcsThreadQueryParams implements Parcelable { /** diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryResult.java b/telephony/java/android/telephony/ims/RcsThreadQueryResult.java index 1cac61d1aa64..9f2fba5caab4 100644 --- a/telephony/java/android/telephony/ims/RcsThreadQueryResult.java +++ b/telephony/java/android/telephony/ims/RcsThreadQueryResult.java @@ -32,6 +32,8 @@ import java.util.List; * The result of a {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)} * call. This class allows getting the token for querying the next batch of threads in order to * prevent handling large amounts of data at once. + * + * @hide */ public final class RcsThreadQueryResult implements Parcelable { // A token for the caller to continue their query for the next batch of results diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl index c90ffc7726e4..c140127237d4 100755 --- a/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl +++ b/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl @@ -26,13 +26,17 @@ import android.telephony.mbms.StreamingServiceInfo; */ interface IMbmsStreamingService { + @UnsupportedAppUsage int initialize(IMbmsStreamingSessionCallback callback, int subId); + @UnsupportedAppUsage int requestUpdateStreamingServices(int subId, in List<String> serviceClasses); + @UnsupportedAppUsage int startStreaming(int subId, String serviceId, IStreamingServiceCallback callback); + @UnsupportedAppUsage Uri getPlaybackUri(int subId, String serviceId); void stopStreaming(int subId, String serviceId); diff --git a/telephony/java/com/android/ims/ImsConfigListener.aidl b/telephony/java/com/android/ims/ImsConfigListener.aidl index 64a501552550..4f229df252a6 100644 --- a/telephony/java/com/android/ims/ImsConfigListener.aidl +++ b/telephony/java/com/android/ims/ImsConfigListener.aidl @@ -47,6 +47,7 @@ oneway interface ImsConfigListener { * * @return void. */ + @UnsupportedAppUsage void onSetFeatureResponse(int feature, int network, int value, int status); /** diff --git a/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl b/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl index 579369f4b549..b33a9f1ad23b 100644 --- a/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl +++ b/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl @@ -34,33 +34,47 @@ oneway interface IImsCallSessionListener { /** * Notifies the result of the basic session operation (setup / terminate). */ + @UnsupportedAppUsage void callSessionProgressing(in IImsCallSession session, in ImsStreamMediaProfile profile); + @UnsupportedAppUsage void callSessionStarted(in IImsCallSession session, in ImsCallProfile profile); + @UnsupportedAppUsage void callSessionStartFailed(in IImsCallSession session, in ImsReasonInfo reasonInfo); + @UnsupportedAppUsage void callSessionTerminated(in IImsCallSession session, in ImsReasonInfo reasonInfo); /** * Notifies the result of the call hold/resume operation. */ + @UnsupportedAppUsage void callSessionHeld(in IImsCallSession session, in ImsCallProfile profile); + @UnsupportedAppUsage void callSessionHoldFailed(in IImsCallSession session, in ImsReasonInfo reasonInfo); + @UnsupportedAppUsage void callSessionHoldReceived(in IImsCallSession session, in ImsCallProfile profile); + @UnsupportedAppUsage void callSessionResumed(in IImsCallSession session, in ImsCallProfile profile); + @UnsupportedAppUsage void callSessionResumeFailed(in IImsCallSession session, in ImsReasonInfo reasonInfo); + @UnsupportedAppUsage void callSessionResumeReceived(in IImsCallSession session, in ImsCallProfile profile); /** * Notifies the result of call merge operation. */ + @UnsupportedAppUsage void callSessionMergeStarted(in IImsCallSession session, in IImsCallSession newSession, in ImsCallProfile profile); + @UnsupportedAppUsage void callSessionMergeComplete(in IImsCallSession session); + @UnsupportedAppUsage void callSessionMergeFailed(in IImsCallSession session, in ImsReasonInfo reasonInfo); /** * Notifies the result of call upgrade / downgrade or any other call updates. */ + @UnsupportedAppUsage void callSessionUpdated(in IImsCallSession session, in ImsCallProfile profile); void callSessionUpdateFailed(in IImsCallSession session, @@ -81,7 +95,9 @@ oneway interface IImsCallSessionListener { /** * Notifies the result of the participant invitation / removal to/from the conference session. */ + @UnsupportedAppUsage void callSessionInviteParticipantsRequestDelivered(in IImsCallSession session); + @UnsupportedAppUsage void callSessionInviteParticipantsRequestFailed(in IImsCallSession session, in ImsReasonInfo reasonInfo); void callSessionRemoveParticipantsRequestDelivered(in IImsCallSession session); @@ -91,6 +107,7 @@ oneway interface IImsCallSessionListener { /** * Notifies the changes of the conference info. in the conference session. */ + @UnsupportedAppUsage void callSessionConferenceStateUpdated(in IImsCallSession session, in ImsConferenceState state); @@ -103,8 +120,10 @@ oneway interface IImsCallSessionListener { /** * Notifies of handover information for this call */ + @UnsupportedAppUsage void callSessionHandover(in IImsCallSession session, in int srcAccessTech, in int targetAccessTech, in ImsReasonInfo reasonInfo); + @UnsupportedAppUsage void callSessionHandoverFailed(in IImsCallSession session, in int srcAccessTech, in int targetAccessTech, in ImsReasonInfo reasonInfo); void callSessionMayHandover(in IImsCallSession session, @@ -118,6 +137,7 @@ oneway interface IImsCallSessionListener { * - {@link com.android.internal.telephony.Phone#TTY_MODE_HCO} * - {@link com.android.internal.telephony.Phone#TTY_MODE_VCO} */ + @UnsupportedAppUsage void callSessionTtyModeReceived(in IImsCallSession session, in int mode); /** @@ -126,11 +146,13 @@ oneway interface IImsCallSessionListener { * @param session The call session. * @param isMultiParty {@code true} if the session became multiparty, {@code false} otherwise. */ + @UnsupportedAppUsage void callSessionMultipartyStateChanged(in IImsCallSession session, in boolean isMultiParty); /** * Notifies the supplementary service information for the current session. */ + @UnsupportedAppUsage void callSessionSuppServiceReceived(in IImsCallSession session, in ImsSuppServiceNotification suppSrvNotification); diff --git a/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl b/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl index 2212109c8a67..a7a62a625478 100644 --- a/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl +++ b/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl @@ -32,6 +32,7 @@ oneway interface IImsRegistrationListener { * * @deprecated see {@link registrationConnectedWithRadioTech} */ + @UnsupportedAppUsage void registrationConnected(); /** @@ -47,6 +48,7 @@ oneway interface IImsRegistrationListener { * @param imsRadioTech the radio access technology. Valid values are {@code * RIL_RADIO_TECHNOLOGY_*} defined in {@link ServiceState}. */ + @UnsupportedAppUsage void registrationConnectedWithRadioTech(int imsRadioTech); /** @@ -55,12 +57,14 @@ oneway interface IImsRegistrationListener { * @param imsRadioTech the radio access technology. Valid values are {@code * RIL_RADIO_TECHNOLOGY_*} defined in {@link ServiceState}. */ + @UnsupportedAppUsage void registrationProgressingWithRadioTech(int imsRadioTech); /** * Notifies the application when the device is disconnected from the IMS network. */ + @UnsupportedAppUsage void registrationDisconnected(in ImsReasonInfo imsReasonInfo); /** @@ -94,6 +98,7 @@ oneway interface IImsRegistrationListener { * @param enabledFeatures features enabled as defined in com.android.ims.ImsConfig#FeatureConstants. * @param disabledFeatures features disabled as defined in com.android.ims.ImsConfig#FeatureConstants. */ + @UnsupportedAppUsage void registrationFeatureCapabilityChanged(int serviceClass, in int[] enabledFeatures, in int[] disabledFeatures); @@ -101,11 +106,13 @@ oneway interface IImsRegistrationListener { * Updates the application with the waiting voice message count. * @param count The number of waiting voice messages. */ + @UnsupportedAppUsage void voiceMessageCountUpdate(int count); /** * Notifies the application when the list of URIs associated with IMS client is updated. */ + @UnsupportedAppUsage void registrationAssociatedUriChanged(in Uri[] uris); /** @@ -116,5 +123,6 @@ oneway interface IImsRegistrationListener { * attempted. * @param imsReasonInfo Reason for the failure. */ + @UnsupportedAppUsage void registrationChangeFailed(in int targetAccessTech, in ImsReasonInfo imsReasonInfo); } diff --git a/telephony/java/com/android/ims/internal/IImsUtListener.aidl b/telephony/java/com/android/ims/internal/IImsUtListener.aidl index a603cd34dfcd..fcb9fb1f8773 100644 --- a/telephony/java/com/android/ims/internal/IImsUtListener.aidl +++ b/telephony/java/com/android/ims/internal/IImsUtListener.aidl @@ -31,30 +31,37 @@ oneway interface IImsUtListener { /** * Notifies the result of the supplementary service configuration udpate. */ + @UnsupportedAppUsage void utConfigurationUpdated(in IImsUt ut, int id); + @UnsupportedAppUsage void utConfigurationUpdateFailed(in IImsUt ut, int id, in ImsReasonInfo error); /** * Notifies the result of the supplementary service configuration query. */ + @UnsupportedAppUsage void utConfigurationQueried(in IImsUt ut, int id, in Bundle ssInfo); + @UnsupportedAppUsage void utConfigurationQueryFailed(in IImsUt ut, int id, in ImsReasonInfo error); /** * Notifies the status of the call barring supplementary service. */ + @UnsupportedAppUsage void utConfigurationCallBarringQueried(in IImsUt ut, int id, in ImsSsInfo[] cbInfo); /** * Notifies the status of the call forwarding supplementary service. */ + @UnsupportedAppUsage void utConfigurationCallForwardQueried(in IImsUt ut, int id, in ImsCallForwardInfo[] cfInfo); /** * Notifies the status of the call waiting supplementary service. */ + @UnsupportedAppUsage void utConfigurationCallWaitingQueried(in IImsUt ut, int id, in ImsSsInfo[] cwInfo); diff --git a/telephony/java/com/android/ims/internal/IImsVideoCallCallback.aidl b/telephony/java/com/android/ims/internal/IImsVideoCallCallback.aidl index 9499c9f5dde9..cf8d63794546 100644 --- a/telephony/java/com/android/ims/internal/IImsVideoCallCallback.aidl +++ b/telephony/java/com/android/ims/internal/IImsVideoCallCallback.aidl @@ -31,18 +31,25 @@ import android.telecom.VideoProfile; * {@hide} */ oneway interface IImsVideoCallCallback { + @UnsupportedAppUsage void receiveSessionModifyRequest(in VideoProfile videoProfile); + @UnsupportedAppUsage void receiveSessionModifyResponse(int status, in VideoProfile requestedProfile, in VideoProfile responseProfile); + @UnsupportedAppUsage void handleCallSessionEvent(int event); + @UnsupportedAppUsage void changePeerDimensions(int width, int height); + @UnsupportedAppUsage void changeCallDataUsage(long dataUsage); + @UnsupportedAppUsage void changeCameraCapabilities(in VideoProfile.CameraCapabilities cameraCapabilities); + @UnsupportedAppUsage void changeVideoQuality(int videoQuality); } diff --git a/telephony/java/com/android/ims/internal/IImsVideoCallProvider.aidl b/telephony/java/com/android/ims/internal/IImsVideoCallProvider.aidl index 0da27e163df1..4d20bd675628 100644 --- a/telephony/java/com/android/ims/internal/IImsVideoCallProvider.aidl +++ b/telephony/java/com/android/ims/internal/IImsVideoCallProvider.aidl @@ -41,6 +41,7 @@ import com.android.ims.internal.IImsVideoCallCallback; * @hide */ oneway interface IImsVideoCallProvider { + @UnsupportedAppUsage void setCallback(IImsVideoCallCallback callback); void setCamera(String cameraId, int uid); diff --git a/telephony/java/com/android/ims/internal/uce/options/IOptionsListener.aidl b/telephony/java/com/android/ims/internal/uce/options/IOptionsListener.aidl index 8cb1153c48bb..c69d5a94f760 100644 --- a/telephony/java/com/android/ims/internal/uce/options/IOptionsListener.aidl +++ b/telephony/java/com/android/ims/internal/uce/options/IOptionsListener.aidl @@ -29,6 +29,7 @@ interface IOptionsListener * @param version, version information of the service. * @hide */ + @UnsupportedAppUsage void getVersionCb(in String version ); /** @@ -37,6 +38,7 @@ interface IOptionsListener * @param statusCode, UCE_SUCCESS as service availability. * @hide */ + @UnsupportedAppUsage void serviceAvailable(in StatusCode statusCode); /** @@ -45,6 +47,7 @@ interface IOptionsListener * @param statusCode, UCE_SUCCESS as service unavailability. * @hide */ + @UnsupportedAppUsage void serviceUnavailable(in StatusCode statusCode); /** @@ -55,6 +58,7 @@ interface IOptionsListener * @param capInfo, capabilities of the remote entity received. * @hide */ + @UnsupportedAppUsage void sipResponseReceived( String uri, in OptionsSipResponse sipResponse, in OptionsCapInfo capInfo); @@ -63,6 +67,7 @@ interface IOptionsListener * @param cmdStatus, command status of the request placed. * @hide */ + @UnsupportedAppUsage void cmdStatus(in OptionsCmdStatus cmdStatus); /** @@ -73,6 +78,7 @@ interface IOptionsListener * @param tID, transation of the request received from network. * @hide */ + @UnsupportedAppUsage void incomingOptions( String uri, in OptionsCapInfo capInfo, in int tID); } diff --git a/telephony/java/com/android/ims/internal/uce/options/IOptionsService.aidl b/telephony/java/com/android/ims/internal/uce/options/IOptionsService.aidl index 839bb5574d33..2e49082988c3 100644 --- a/telephony/java/com/android/ims/internal/uce/options/IOptionsService.aidl +++ b/telephony/java/com/android/ims/internal/uce/options/IOptionsService.aidl @@ -33,6 +33,7 @@ interface IOptionsService * @return StatusCode, status of the request placed. * @hide */ + @UnsupportedAppUsage StatusCode getVersion(int optionsServiceHandle); /** @@ -44,6 +45,7 @@ interface IOptionsService * The service will fill UceLong.mUceLong with optionsServiceListenerHdl * @return StatusCode, status of the request placed. */ + @UnsupportedAppUsage StatusCode addListener(int optionsServiceHandle, IOptionsListener optionsListener, inout UceLong optionsServiceListenerHdl); @@ -54,6 +56,7 @@ interface IOptionsService * @param optionsServiceListenerHdl provided in createOptionsService() or Addlistener(). * @return StatusCode, status of the request placed. */ + @UnsupportedAppUsage StatusCode removeListener(int optionsServiceHandle, in UceLong optionsServiceListenerHdl); /** @@ -66,6 +69,7 @@ interface IOptionsService * with original request. * @return StatusCode, status of the request placed. */ + @UnsupportedAppUsage StatusCode setMyInfo(int optionsServiceHandle , in CapInfo capInfo, int reqUserData); @@ -78,6 +82,7 @@ interface IOptionsService * with original request. * @return StatusCode, status of the request placed. */ + @UnsupportedAppUsage StatusCode getMyInfo(int optionsServiceHandle , int reqUserdata); /** @@ -90,6 +95,7 @@ interface IOptionsService * with original request. * @return StatusCode, status of the request placed. */ + @UnsupportedAppUsage StatusCode getContactCap(int optionsServiceHandle , String remoteURI, int reqUserData); @@ -103,6 +109,7 @@ interface IOptionsService * with original request. * @return StatusCode, status of the request placed. */ + @UnsupportedAppUsage StatusCode getContactListCap(int optionsServiceHandle, in String[] remoteURIList, int reqUserData); @@ -119,6 +126,7 @@ interface IOptionsService * @param bContactInBL, true if the contact is blacklisted, else false. * @return StatusCode, status of the request placed. */ + @UnsupportedAppUsage StatusCode responseIncomingOptions(int optionsServiceHandle, int tId, int sipResponseCode, String reasonPhrase, in OptionsCapInfo capInfo, in boolean bContactInBL); diff --git a/telephony/java/com/android/ims/internal/uce/presence/IPresenceListener.aidl b/telephony/java/com/android/ims/internal/uce/presence/IPresenceListener.aidl index 2ae424f4af8e..65e7fc9756bb 100644 --- a/telephony/java/com/android/ims/internal/uce/presence/IPresenceListener.aidl +++ b/telephony/java/com/android/ims/internal/uce/presence/IPresenceListener.aidl @@ -36,6 +36,7 @@ interface IPresenceListener * Gets the version of the presence listener implementation. * @param version, version information. */ + @UnsupportedAppUsage void getVersionCb(in String version ); /** @@ -43,6 +44,7 @@ interface IPresenceListener * availability. * @param statusCode, UCE_SUCCESS as service availability. */ + @UnsupportedAppUsage void serviceAvailable(in StatusCode statusCode); /** @@ -50,6 +52,7 @@ interface IPresenceListener * unavailability. * @param statusCode, UCE_SUCCESS as service unAvailability. */ + @UnsupportedAppUsage void serviceUnAvailable(in StatusCode statusCode); /** @@ -57,12 +60,14 @@ interface IPresenceListener * publish request. * @param publishTrigger, Publish trigger for the network being supported. */ + @UnsupportedAppUsage void publishTriggering(in PresPublishTriggerType publishTrigger); /** * Callback function to be invoked to inform the client of the status of an asynchronous call. * @param cmdStatus, command status of the request placed. */ + @UnsupportedAppUsage void cmdStatus( in PresCmdStatus cmdStatus); /** @@ -70,6 +75,7 @@ interface IPresenceListener * such as PUBLISH or SUBSCRIBE, has been received. * @param sipResponse, network response received for the request placed. */ + @UnsupportedAppUsage void sipResponseReceived(in PresSipResponse sipResponse); /** @@ -78,6 +84,7 @@ interface IPresenceListener * @param presentityURI, URI of the remote entity the request was placed. * @param tupleInfo, array of capability information remote entity supports. */ + @UnsupportedAppUsage void capInfoReceived(in String presentityURI, in PresTupleInfo [] tupleInfo); @@ -87,6 +94,7 @@ interface IPresenceListener * @param rlmiInfo, resource infomation received from network. * @param resInfo, array of capabilities received from network for the list of remore URI. */ + @UnsupportedAppUsage void listCapInfoReceived(in PresRlmiInfo rlmiInfo, in PresResInfo [] resInfo); @@ -94,6 +102,7 @@ interface IPresenceListener * Callback function to be invoked to inform the client when Unpublish message * is sent to network. */ + @UnsupportedAppUsage void unpublishMessageSent(); }
\ No newline at end of file diff --git a/telephony/java/com/android/ims/internal/uce/presence/IPresenceService.aidl b/telephony/java/com/android/ims/internal/uce/presence/IPresenceService.aidl index fdea6d35c195..26a3e83efcfa 100644 --- a/telephony/java/com/android/ims/internal/uce/presence/IPresenceService.aidl +++ b/telephony/java/com/android/ims/internal/uce/presence/IPresenceService.aidl @@ -33,6 +33,7 @@ interface IPresenceService * @param presenceServiceHdl returned in createPresenceService(). * @return StatusCode, status of the request placed. */ + @UnsupportedAppUsage StatusCode getVersion(int presenceServiceHdl); /** @@ -45,6 +46,7 @@ interface IPresenceService * * @return StatusCode, status of the request placed */ + @UnsupportedAppUsage StatusCode addListener(int presenceServiceHdl, IPresenceListener presenceServiceListener, inout UceLong presenceServiceListenerHdl); @@ -54,6 +56,7 @@ interface IPresenceService * @param presenceServiceListenerHdl provided in createPresenceService() or Addlistener(). * @return StatusCode, status of the request placed. */ + @UnsupportedAppUsage StatusCode removeListener(int presenceServiceHdl, in UceLong presenceServiceListenerHdl); /** @@ -69,6 +72,7 @@ interface IPresenceService * with original request. * @return StatusCode, status of the request placed. */ + @UnsupportedAppUsage StatusCode reenableService(int presenceServiceHdl, int userData); /** @@ -81,6 +85,7 @@ interface IPresenceService * with original request. * @return StatusCode, status of the request placed. */ + @UnsupportedAppUsage StatusCode publishMyCap(int presenceServiceHdl, in PresCapInfo myCapInfo , int userData); /** @@ -94,6 +99,7 @@ interface IPresenceService * with original request. * @return StatusCode, status of the request placed. */ + @UnsupportedAppUsage StatusCode getContactCap(int presenceServiceHdl , String remoteUri, int userData); /** @@ -107,6 +113,7 @@ interface IPresenceService * with original request. * @return StatusCode, status of the request placed. */ + @UnsupportedAppUsage StatusCode getContactListCap(int presenceServiceHdl, in String[] remoteUriList, int userData); /** @@ -122,6 +129,7 @@ interface IPresenceService * with original request. * @return StatusCode, status of the request placed. */ + @UnsupportedAppUsage StatusCode setNewFeatureTag(int presenceServiceHdl, String featureTag, in PresServiceInfo serviceInfo, int userData); diff --git a/telephony/java/com/android/ims/internal/uce/uceservice/IUceListener.aidl b/telephony/java/com/android/ims/internal/uce/uceservice/IUceListener.aidl index 13707a16a0c1..41abf7d1a1f0 100644 --- a/telephony/java/com/android/ims/internal/uce/uceservice/IUceListener.aidl +++ b/telephony/java/com/android/ims/internal/uce/uceservice/IUceListener.aidl @@ -25,5 +25,6 @@ interface IUceListener * @param serviceStatusValue defined in ImsUceManager * @hide */ + @UnsupportedAppUsage void setStatus(int serviceStatusValue); } diff --git a/telephony/java/com/android/ims/internal/uce/uceservice/IUceService.aidl b/telephony/java/com/android/ims/internal/uce/uceservice/IUceService.aidl index 1fb8513d410a..ec45371689cf 100644 --- a/telephony/java/com/android/ims/internal/uce/uceservice/IUceService.aidl +++ b/telephony/java/com/android/ims/internal/uce/uceservice/IUceService.aidl @@ -38,6 +38,7 @@ interface IUceService * Service status is returned in setStatus callback in IUceListener. * @hide */ + @UnsupportedAppUsage boolean startService(IUceListener uceListener); /** @@ -45,6 +46,7 @@ interface IUceService * @return boolean true if the service stop request is processed successfully, FALSE otherwise. * @hide */ + @UnsupportedAppUsage boolean stopService(); @@ -54,6 +56,7 @@ interface IUceService * @return boolean true if service started else false. * @hide */ + @UnsupportedAppUsage boolean isServiceStarted(); /** @@ -71,6 +74,7 @@ interface IUceService * * @deprecated This is replaced with new API createOptionsServiceForSubscription() */ + @UnsupportedAppUsage int createOptionsService(IOptionsListener optionsListener, inout UceLong optionsServiceListenerHdl); /** @@ -97,6 +101,7 @@ interface IUceService * in IOptionsListener * @hide */ + @UnsupportedAppUsage void destroyOptionsService(int optionsServiceHandle); /** @@ -114,6 +119,7 @@ interface IUceService * * @deprecated This is replaced with new API createPresenceServiceForSubscription() */ + @UnsupportedAppUsage int createPresenceService(IPresenceListener presenceServiceListener, inout UceLong presenceServiceListenerHdl); /** @@ -141,6 +147,7 @@ interface IUceService * * @hide */ + @UnsupportedAppUsage void destroyPresenceService(int presenceServiceHdl); @@ -152,6 +159,7 @@ interface IUceService * * @hide */ + @UnsupportedAppUsage boolean getServiceStatus(); /** @@ -163,6 +171,7 @@ interface IUceService * * @deprecated use API getPresenceServiceForSubscription() */ + @UnsupportedAppUsage IPresenceService getPresenceService(); /** @@ -185,6 +194,7 @@ interface IUceService * * @hide */ + @UnsupportedAppUsage IOptionsService getOptionsService(); /** diff --git a/telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl b/telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl index 5cd67d977ad5..8e50a8f9d7d5 100644 --- a/telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl +++ b/telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl @@ -23,6 +23,7 @@ import android.os.PersistableBundle; */ interface ICarrierConfigLoader { + @UnsupportedAppUsage PersistableBundle getConfigForSubId(int subId, String callingPackage); void overrideConfig(int subId, in PersistableBundle overrides); diff --git a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl index abcb15ae4d50..5b509b7260ba 100644 --- a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl +++ b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl @@ -60,6 +60,7 @@ interface IPhoneSubInfo { /** * Retrieves the unique sbuscriber ID, e.g., IMSI for GSM phones. */ + @UnsupportedAppUsage String getSubscriberId(String callingPackage); /** @@ -75,6 +76,7 @@ interface IPhoneSubInfo { /** * Retrieves the serial number of the ICC, if applicable. */ + @UnsupportedAppUsage String getIccSerialNumber(String callingPackage); /** diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl index 75a4d8227e23..79e0aa15b6ab 100755 --- a/telephony/java/com/android/internal/telephony/ISub.aidl +++ b/telephony/java/com/android/internal/telephony/ISub.aidl @@ -275,7 +275,7 @@ interface ISub { void clearDefaultsForInactiveSubIds(); - int[] getActiveSubIdList(); + int[] getActiveSubIdList(boolean visibleOnly); int setSubscriptionProperty(int subId, String propKey, String propValue); diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index c54a60629ecb..5c5d44a8f42e 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -76,6 +76,7 @@ interface ITelephony { * @param number the number to be dialed. If null, this * would display the Dialer screen with no number pre-filled. */ + @UnsupportedAppUsage void dial(String number); /** @@ -83,6 +84,7 @@ interface ITelephony { * @param callingPackage The package making the call. * @param number the number to be called. */ + @UnsupportedAppUsage void call(String callingPackage, String number); /** @@ -98,6 +100,7 @@ interface ITelephony { * @param callingPackage the name of the package making the call. * @return returns true if the radio is on. */ + @UnsupportedAppUsage boolean isRadioOnForSubscriber(int subId, String callingPackage); /** @@ -105,6 +108,7 @@ interface ITelephony { * @param pin The pin to check. * @return whether the operation was a success. */ + @UnsupportedAppUsage boolean supplyPin(String pin); /** @@ -182,6 +186,7 @@ interface ITelephony { * @param dialString the MMI command to be executed. * @return true if MMI command is executed. */ + @UnsupportedAppUsage boolean handlePinMmi(String dialString); @@ -202,11 +207,13 @@ interface ITelephony { * @param subId user preferred subId. * @return true if MMI command is executed. */ + @UnsupportedAppUsage boolean handlePinMmiForSubscriber(int subId, String dialString); /** * Toggles the radio on or off. */ + @UnsupportedAppUsage void toggleRadioOnOff(); /** @@ -218,6 +225,7 @@ interface ITelephony { /** * Set the radio to on or off */ + @UnsupportedAppUsage boolean setRadio(boolean turnOn); /** @@ -234,6 +242,7 @@ interface ITelephony { /** * Request to update location information in service state */ + @UnsupportedAppUsage void updateServiceLocation(); /** @@ -245,6 +254,7 @@ interface ITelephony { /** * Enable location update notifications. */ + @UnsupportedAppUsage void enableLocationUpdates(); /** @@ -256,6 +266,7 @@ interface ITelephony { /** * Disable location update notifications. */ + @UnsupportedAppUsage void disableLocationUpdates(); /** @@ -267,11 +278,13 @@ interface ITelephony { /** * Allow mobile data connections. */ + @UnsupportedAppUsage boolean enableDataConnectivity(); /** * Disallow mobile data connections. */ + @UnsupportedAppUsage boolean disableDataConnectivity(); /** @@ -293,6 +306,7 @@ interface ITelephony { */ List<NeighboringCellInfo> getNeighboringCellInfo(String callingPkg); + @UnsupportedAppUsage int getCallState(); /** @@ -300,7 +314,9 @@ interface ITelephony { */ int getCallStateForSlot(int slotIndex); + @UnsupportedAppUsage int getDataActivity(); + @UnsupportedAppUsage int getDataState(); /** @@ -308,6 +324,7 @@ interface ITelephony { * Returns TelephonyManager.PHONE_TYPE_CDMA if RILConstants.CDMA_PHONE * and TelephonyManager.PHONE_TYPE_GSM if RILConstants.GSM_PHONE */ + @UnsupportedAppUsage int getActivePhoneType(); /** @@ -444,6 +461,7 @@ interface ITelephony { * Returns the network type for data transmission * Legacy call, permission-free */ + @UnsupportedAppUsage int getNetworkType(); /** @@ -477,6 +495,7 @@ interface ITelephony { /** * Return true if an ICC card is present */ + @UnsupportedAppUsage boolean hasIccCard(); /** @@ -557,6 +576,7 @@ interface ITelephony { * successful iccOpenLogicalChannel. * @return true if the channel was closed successfully. */ + @UnsupportedAppUsage boolean iccCloseLogicalChannel(int subId, int channel); /** @@ -577,6 +597,7 @@ interface ITelephony { * @return The APDU response from the ICC card with the status appended at * the end. */ + @UnsupportedAppUsage String iccTransmitApduLogicalChannel(int subId, int channel, int cla, int instruction, int p1, int p2, int p3, String data); @@ -829,6 +850,7 @@ interface ITelephony { * * @return true on enabled */ + @UnsupportedAppUsage boolean getDataEnabled(int subId); /** @@ -1679,7 +1701,7 @@ interface ITelephony { /** * Modify the user's setting for whether or not 4G LTE is enabled. */ - void setAdvancedCallingSetting(int subId, boolean isEnabled); + void setAdvancedCallingSettingEnabled(int subId, boolean isEnabled); /** * return true if the user's setting for VT is enabled for the subscription. @@ -1689,7 +1711,7 @@ interface ITelephony { /** * Modify the user's setting for whether or not VT is available for the subscrption specified. */ - void setVtSetting(int subId, boolean isEnabled); + void setVtSettingEnabled(int subId, boolean isEnabled); /** * return true if the user's setting for whether or not Voice over WiFi is currently enabled. @@ -1699,7 +1721,7 @@ interface ITelephony { /** * sets the user's setting for Voice over WiFi enabled state. */ - void setVoWiFiSetting(int subId, boolean isEnabled); + void setVoWiFiSettingEnabled(int subId, boolean isEnabled); /** * return true if the user's setting for Voice over WiFi while roaming is enabled. @@ -1710,7 +1732,7 @@ interface ITelephony { * Sets the user's preference for whether or not Voice over WiFi is enabled for the current * subscription while roaming. */ - void setVoWiFiRoamingSetting(int subId, boolean isEnabled); + void setVoWiFiRoamingSettingEnabled(int subId, boolean isEnabled); /** * Set the Voice over WiFi enabled state, but do not persist the setting. @@ -1834,10 +1856,12 @@ interface ITelephony { void setMultisimCarrierRestriction(boolean isMultisimCarrierRestricted); /** - * Returns if the usage of multiple SIM cards at the same time is restricted. - * @hide + * Returns if the usage of multiple SIM cards at the same time is supported. + * + * @param callingPackage The package making the call. + * @return true if multisim is supported, false otherwise. */ - boolean isMultisimCarrierRestricted(); + boolean isMultisimSupported(String callingPackage); /** * Switch configs to enable multi-sim or switch back to single-sim diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl index a922ab601442..0610c5d106c3 100644 --- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl @@ -38,9 +38,11 @@ interface ITelephonyRegistry { IOnSubscriptionsChangedListener callback); void removeOnSubscriptionsChangedListener(String pkg, IOnSubscriptionsChangedListener callback); + @UnsupportedAppUsage void listen(String pkg, IPhoneStateListener callback, int events, boolean notifyNow); void listenForSubscriber(in int subId, String pkg, IPhoneStateListener callback, int events, boolean notifyNow); + @UnsupportedAppUsage void notifyCallState(int state, String incomingNumber); void notifyCallStateForPhoneId(in int phoneId, in int subId, int state, String incomingNumber); void notifyServiceStateForPhoneId(in int phoneId, in int subId, in ServiceState state); @@ -57,11 +59,13 @@ interface ITelephonyRegistry { void notifyDataConnectionForSubscriber(int subId, int state, boolean isDataConnectivityPossible, String apn, String apnType, in LinkProperties linkProperties, in NetworkCapabilities networkCapabilities, int networkType, boolean roaming); + @UnsupportedAppUsage void notifyDataConnectionFailed(String apnType); void notifyDataConnectionFailedForSubscriber(int subId, String apnType); void notifyCellLocation(in Bundle cellLocation); void notifyCellLocationForSubscriber(in int subId, in Bundle cellLocation); void notifyOtaspChanged(in int otaspMode); + @UnsupportedAppUsage void notifyCellInfo(in List<CellInfo> cellInfo); void notifyPhysicalChannelConfiguration(in List<PhysicalChannelConfig> configs); void notifyPhysicalChannelConfigurationForSubscriber(in int subId, @@ -84,6 +88,6 @@ interface ITelephonyRegistry { void notifyActiveDataSubIdChanged(int activeDataSubId); void notifyRadioPowerStateChanged(in int state); void notifyEmergencyNumberList(); - void notifyCallQualityChanged(in CallQuality callQuality, int phoneId); + void notifyCallQualityChanged(in CallQuality callQuality, int phoneId, int callNetworkType); void notifyImsDisconnectCause(int subId, in ImsReasonInfo imsReasonInfo); } diff --git a/telephony/java/com/android/internal/telephony/IWapPushManager.aidl b/telephony/java/com/android/internal/telephony/IWapPushManager.aidl index d5ecb940c8d1..1c3df65336f8 100644 --- a/telephony/java/com/android/internal/telephony/IWapPushManager.aidl +++ b/telephony/java/com/android/internal/telephony/IWapPushManager.aidl @@ -30,6 +30,7 @@ interface IWapPushManager { * Returns true if inserting the information is successfull. Inserting the duplicated * record in the application ID table is not allowed. Use update/delete method. */ + @UnsupportedAppUsage boolean addPackage(String x_app_id, String content_type, String package_name, String class_name, int app_type, boolean need_signature, boolean further_processing); @@ -38,6 +39,7 @@ interface IWapPushManager { * Updates receiver application that is last added. * Returns true if updating the information is successfull. */ + @UnsupportedAppUsage boolean updatePackage(String x_app_id, String content_type, String package_name, String class_name, int app_type, boolean need_signature, boolean further_processing); @@ -46,6 +48,7 @@ interface IWapPushManager { * Delites receiver application information. * Returns true if deleting is successfull. */ + @UnsupportedAppUsage boolean deletePackage(String x_app_id, String content_type, String package_name, String class_name); } diff --git a/telephony/java/com/android/internal/telephony/TelephonyProperties.java b/telephony/java/com/android/internal/telephony/TelephonyProperties.java index 603c4c2870d7..030c3f495d0f 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyProperties.java +++ b/telephony/java/com/android/internal/telephony/TelephonyProperties.java @@ -188,18 +188,17 @@ public interface TelephonyProperties */ static final String PROPERTY_IGNORE_NITZ = "telephony.test.ignore.nitz"; - /** + /** * Property to set multi sim feature. * Type: String(dsds, dsda) */ static final String PROPERTY_MULTI_SIM_CONFIG = "persist.radio.multisim.config"; - /** + /** * Property to indicate if reboot is required when changing modems configurations * Type: String(true, false) default is false; most devices don't need reboot */ - String PROPERTY_REBOOT_REQUIRED_ON_MODEM_CHANGE = - "persist.radio.reboot_on_modem_change"; + String PROPERTY_REBOOT_REQUIRED_ON_MODEM_CHANGE = "persist.radio.reboot_on_modem_change"; /** * Property to store default subscription. diff --git a/test-base/Android.bp b/test-base/Android.bp index 157609cec09c..8aa0aaf363dd 100644 --- a/test-base/Android.bp +++ b/test-base/Android.bp @@ -44,7 +44,7 @@ java_sdk_library { // ========================================== // This is only intended for inclusion in the android.test.runner-minus-junit, // robolectric_android-all-stub and repackaged.android.test.* libraries. -// Must not be used elewhere. +// Must not be used elsewhere. java_library_static { name: "android.test.base_static", installable: false, @@ -61,19 +61,6 @@ java_library_static { sdk_version: "current", } -// Build the legacy-test library -// ============================= -// This contains the junit.framework and android.test classes that were in -// Android API level 25 excluding those from android.test.runner. -// Also contains the com.android.internal.util.Predicate[s] classes. -java_library { - name: "legacy-test", - installable: true, - - sdk_version: "current", - static_libs: ["android.test.base_static"], -} - // Build the repackaged.android.test.base library // ============================================== // This contains repackaged versions of the classes from @@ -93,8 +80,8 @@ java_library_static { // =============================================== // This contains the android.test classes from android.test.base plus // the com.android.internal.util.Predicate[s] classes. This is only -// intended for inclusion in the android.test.legacy and -// legacy-android-test static libraries and must not be used elsewhere. +// intended for inclusion in android.test.legacy and must not be used +// elsewhere. java_library_static { name: "android.test.base-minus-junit", diff --git a/test-legacy/Android.bp b/test-legacy/Android.bp deleted file mode 100644 index a69f422b3bdb..000000000000 --- a/test-legacy/Android.bp +++ /dev/null @@ -1,36 +0,0 @@ -// -// Copyright (C) 2018 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -// Build the legacy-android-test library -// ===================================== -// This contains the android.test classes that were in Android API level 25, -// including those from android.test.runner. -// Also contains the com.android.internal.util.Predicate[s] classes. -java_library_static { - name: "legacy-android-test", - - static_libs: [ - "android.test.base-minus-junit", - "android.test.runner-minus-junit", - "android.test.mock_static", - ], - - no_framework_libs: true, - libs: [ - "framework", - "junit", - ], -} diff --git a/test-legacy/Android.mk b/test-legacy/Android.mk index da47de0a3d35..af26c5b80717 100644 --- a/test-legacy/Android.mk +++ b/test-legacy/Android.mk @@ -24,35 +24,16 @@ ifeq (,$(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK))) # Built against the SDK so that it can be statically included in APKs # without breaking link type checks. # -# This builds directly from the source rather than simply statically -# including the android.test.base-minus-junit and -# android.test.runner-minus-junit libraries because the latter library -# cannot itself be built against the SDK. That is because it uses on -# an internal method (setTestContext) on the AndroidTestCase class. -# That class is provided by both the android.test.base-minus-junit and -# the current SDK and as the latter is first on the classpath its -# version is used. Unfortunately, it does not provide the internal -# method and so compilation fails. -# -# Building from source avoids that because the compiler will use the -# source version of AndroidTestCase instead of the one from the current -# SDK. -# -# The use of the internal method does not prevent this from being -# statically included because the class that provides the method is -# also included in this library. include $(CLEAR_VARS) LOCAL_MODULE := android.test.legacy -LOCAL_SRC_FILES := \ - $(call all-java-files-under, ../test-base/src/android) \ - $(call all-java-files-under, ../test-base/src/com) \ - $(call all-java-files-under, ../test-runner/src/android) \ - LOCAL_SDK_VERSION := current LOCAL_JAVA_LIBRARIES := junit android.test.mock.stubs +LOCAL_STATIC_JAVA_LIBRARIES := \ + android.test.base-minus-junit \ + android.test.runner-minus-junit \ include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/test-mock/Android.bp b/test-mock/Android.bp index 43b765d8b783..e1d6e01d6d06 100644 --- a/test-mock/Android.bp +++ b/test-mock/Android.bp @@ -30,19 +30,3 @@ java_sdk_library { srcs_lib_whitelist_pkgs: ["android"], compile_dex: true, } - -// Build the android.test.mock_static library -// ========================================== -// This is only intended for inclusion in the legacy-android-test. -// Must not be used elewhere. -java_library_static { - name: "android.test.mock_static", - - java_version: "1.8", - srcs: ["src/**/*.java"], - - no_framework_libs: true, - libs: [ - "framework", - ], -} diff --git a/test-mock/api/test-current.txt b/test-mock/api/test-current.txt index 0cb8f22d8070..6765316a304b 100644 --- a/test-mock/api/test-current.txt +++ b/test-mock/api/test-current.txt @@ -1,6 +1,10 @@ // Signature format: 2.0 package android.test.mock { + public class MockContext extends android.content.Context { + method public android.view.Display getDisplay(); + } + @Deprecated public class MockPackageManager extends android.content.pm.PackageManager { method public boolean arePermissionsIndividuallyControlled(); method public String getDefaultBrowserPackageNameAsUser(int); diff --git a/test-runner/Android.bp b/test-runner/Android.bp index db5053eeb903..35212020be7b 100644 --- a/test-runner/Android.bp +++ b/test-runner/Android.bp @@ -45,7 +45,7 @@ java_sdk_library { // Build the android.test.runner-minus-junit library // ================================================= -// This is only intended for inclusion in the legacy-android-test static +// This is only intended for inclusion in the android.test.legacy static // library and must not be used elsewhere. java_library { name: "android.test.runner-minus-junit", diff --git a/tests/DexLoggerIntegrationTests/Android.mk b/tests/DynamicCodeLoggerIntegrationTests/Android.mk index ee02a72b6819..f324eb10a7b0 100644 --- a/tests/DexLoggerIntegrationTests/Android.mk +++ b/tests/DynamicCodeLoggerIntegrationTests/Android.mk @@ -21,12 +21,12 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests -LOCAL_MODULE := DexLoggerTestLibrary +LOCAL_MODULE := DynamicCodeLoggerTestLibrary LOCAL_SRC_FILES := $(call all-java-files-under, src/com/android/dcl) include $(BUILD_JAVA_LIBRARY) -dexloggertest_jar := $(LOCAL_BUILT_MODULE) +dynamiccodeloggertest_jar := $(LOCAL_BUILT_MODULE) # Also build a native library that the test app can dynamically load @@ -34,7 +34,7 @@ dexloggertest_jar := $(LOCAL_BUILT_MODULE) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests -LOCAL_MODULE := DexLoggerNativeTestLibrary +LOCAL_MODULE := DynamicCodeLoggerNativeTestLibrary LOCAL_SRC_FILES := src/cpp/com_android_dcl_Jni.cpp LOCAL_C_INCLUDES += \ $(JNI_H_INCLUDE) @@ -48,19 +48,19 @@ include $(BUILD_SHARED_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests -LOCAL_MODULE := DexLoggerNativeExecutable +LOCAL_MODULE := DynamicCodeLoggerNativeExecutable LOCAL_SRC_FILES := src/cpp/test_executable.cpp include $(BUILD_EXECUTABLE) -dexloggertest_executable := $(LOCAL_BUILT_MODULE) +dynamiccodeloggertest_executable := $(LOCAL_BUILT_MODULE) # Build the test app itself include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests -LOCAL_PACKAGE_NAME := DexLoggerIntegrationTests +LOCAL_PACKAGE_NAME := DynamicCodeLoggerIntegrationTests LOCAL_SDK_VERSION := current LOCAL_COMPATIBILITY_SUITE := device-tests LOCAL_CERTIFICATE := shared @@ -73,12 +73,12 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ # Include both versions of the .so if we have 2 arch LOCAL_MULTILIB := both LOCAL_JNI_SHARED_LIBRARIES := \ - DexLoggerNativeTestLibrary \ + DynamicCodeLoggerNativeTestLibrary \ -# This gets us the javalib.jar built by DexLoggerTestLibrary above as well as the various +# This gets us the javalib.jar built by DynamicCodeLoggerTestLibrary above as well as the various # native binaries. LOCAL_JAVA_RESOURCE_FILES := \ - $(dexloggertest_jar) \ - $(dexloggertest_executable) \ + $(dynamiccodeloggertest_jar) \ + $(dynamiccodeloggertest_executable) \ include $(BUILD_PACKAGE) diff --git a/tests/DexLoggerIntegrationTests/AndroidManifest.xml b/tests/DynamicCodeLoggerIntegrationTests/AndroidManifest.xml index a9f01edbdc41..4327da2db3dd 100644 --- a/tests/DexLoggerIntegrationTests/AndroidManifest.xml +++ b/tests/DynamicCodeLoggerIntegrationTests/AndroidManifest.xml @@ -15,7 +15,7 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.frameworks.dexloggertest"> + package="com.android.frameworks.dynamiccodeloggertest"> <!-- Tests feature introduced in P (28) --> <uses-sdk @@ -30,6 +30,6 @@ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner" - android:targetPackage="com.android.frameworks.dexloggertest" - android:label="Integration test for DexLogger" /> + android:targetPackage="com.android.frameworks.dynamiccodeloggertest" + android:label="Integration test for DynamicCodeLogger" /> </manifest> diff --git a/tests/DexLoggerIntegrationTests/AndroidTest.xml b/tests/DynamicCodeLoggerIntegrationTests/AndroidTest.xml index fb1bef6da08a..f70b9c8cb357 100644 --- a/tests/DexLoggerIntegrationTests/AndroidTest.xml +++ b/tests/DynamicCodeLoggerIntegrationTests/AndroidTest.xml @@ -13,17 +13,17 @@ See the License for the specific language governing permissions and limitations under the License. --> -<configuration description="Runs DexLogger Integration Tests"> +<configuration description="Runs DynamicLogger Integration Tests"> <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> - <option name="test-file-name" value="DexLoggerIntegrationTests.apk"/> + <option name="test-file-name" value="DynamicCodeLoggerIntegrationTests.apk"/> <option name="cleanup-apks" value="true"/> </target_preparer> <option name="test-suite-tag" value="apct"/> - <option name="test-tag" value="DexLoggerIntegrationTests"/> + <option name="test-tag" value="DynamicCodeLoggerIntegrationTests"/> <test class="com.android.tradefed.testtype.AndroidJUnitTest"> - <option name="package" value="com.android.frameworks.dexloggertest"/> + <option name="package" value="com.android.frameworks.dynamiccodeloggertest"/> <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/> <option name="hidden-api-checks" value="false"/> </test> diff --git a/tests/DexLoggerIntegrationTests/src/com/android/dcl/Simple.java b/tests/DynamicCodeLoggerIntegrationTests/src/com/android/dcl/Simple.java index e995a26ea5c9..e995a26ea5c9 100644 --- a/tests/DexLoggerIntegrationTests/src/com/android/dcl/Simple.java +++ b/tests/DynamicCodeLoggerIntegrationTests/src/com/android/dcl/Simple.java diff --git a/tests/DexLoggerIntegrationTests/src/com/android/server/pm/dex/DexLoggerIntegrationTests.java b/tests/DynamicCodeLoggerIntegrationTests/src/com/android/server/pm/dex/DynamicCodeLoggerIntegrationTests.java index 99d1f5ff4bb3..4f9aeea5bdb4 100644 --- a/tests/DexLoggerIntegrationTests/src/com/android/server/pm/dex/DexLoggerIntegrationTests.java +++ b/tests/DynamicCodeLoggerIntegrationTests/src/com/android/server/pm/dex/DynamicCodeLoggerIntegrationTests.java @@ -49,7 +49,7 @@ import java.util.List; import java.util.concurrent.TimeUnit; /** - * Integration tests for {@link DynamicCodeLogger}, formerly known as {@code DexLogger}. + * Integration tests for {@link DynamicCodeLogger}. * * The setup for the test dynamically loads code in a jar extracted * from our assets (a secondary dex file). @@ -59,11 +59,11 @@ import java.util.concurrent.TimeUnit; * file's name and content. We verify that this message appears in * the event log. * - * Run with "atest DexLoggerIntegrationTests". + * Run with "atest DynamicCodeLoggerIntegrationTests". */ @LargeTest @RunWith(JUnit4.class) -public final class DexLoggerIntegrationTests { +public final class DynamicCodeLoggerIntegrationTests { private static final String SHA_256 = "SHA-256"; @@ -162,7 +162,7 @@ public final class DexLoggerIntegrationTests { File privateCopyFile = privateFile(privateCopyName); mExpectedNameHash = hashOf(privateCopyName); mExpectedContentHash = copyAndHashResource( - libraryPath("DexLoggerNativeTestLibrary.so"), privateCopyFile); + libraryPath("DynamicCodeLoggerNativeTestLibrary.so"), privateCopyFile); System.load(privateCopyFile.toString()); } @@ -180,7 +180,7 @@ public final class DexLoggerIntegrationTests { File privateCopyFile = privateFile(privateCopyName); mExpectedNameHash = hashOf(privateCopyName); mExpectedContentHash = copyAndHashResource( - libraryPath("DexLoggerNativeTestLibrary.so"), privateCopyFile); + libraryPath("DynamicCodeLoggerNativeTestLibrary.so"), privateCopyFile); System.load(privateCopyFile.toString()); } @@ -196,7 +196,7 @@ public final class DexLoggerIntegrationTests { File privateCopyFile = privateFile(privateCopyName); mExpectedNameHash = hashOf(privateCopyName); mExpectedContentHash = copyAndHashResource( - "/DexLoggerNativeExecutable", privateCopyFile); + "/DynamicCodeLoggerNativeExecutable", privateCopyFile); assertThat(privateCopyFile.setExecutable(true)).isTrue(); Process process = Runtime.getRuntime().exec(privateCopyFile.toString()); @@ -211,7 +211,7 @@ public final class DexLoggerIntegrationTests { File privateCopyFile = privateFile("spoofed"); String expectedContentHash = copyAndHashResource( - "/DexLoggerNativeExecutable", privateCopyFile); + "/DynamicCodeLoggerNativeExecutable", privateCopyFile); EventLog.writeEvent(EventLog.getTagCode("auditd"), "type=1400 avc: granted { execute_no_trans } " @@ -235,6 +235,34 @@ public final class DexLoggerIntegrationTests { } @Test + public void testGeneratesEvents_spoofed_validFile_untrustedApp() throws Exception { + File privateCopyFile = privateFile("spoofed2"); + + String expectedContentHash = copyAndHashResource( + "/DynamicCodeLoggerNativeExecutable", privateCopyFile); + + EventLog.writeEvent(EventLog.getTagCode("auditd"), + "type=1400 avc: granted { execute_no_trans } " + + "path=\"" + privateCopyFile + "\" " + + "scontext=u:r:untrusted_app: " + + "tcontext=u:object_r:app_data_file: " + + "tclass=file "); + + String expectedNameHash = + "3E57AA59249154C391316FDCF07C1D499C26A564E4D305833CCD9A98ED895AC9"; + + // Run the job to scan generated audit log entries + runDynamicCodeLoggingJob(AUDIT_WATCHING_JOB_ID); + + // And then make sure we log events about it + long previousEventNanos = mostRecentEventTimeNanos(); + runDynamicCodeLoggingJob(IDLE_LOGGING_JOB_ID); + + assertDclLoggedSince(previousEventNanos, DCL_NATIVE_SUBTAG, + expectedNameHash, expectedContentHash); + } + + @Test public void testGeneratesEvents_spoofed_pathTraversal() throws Exception { File privateDir = privateFile("x").getParentFile(); @@ -275,7 +303,7 @@ public final class DexLoggerIntegrationTests { public void testGeneratesEvents_spoofed_otherAppFile() throws Exception { File ourPath = sContext.getDatabasePath("android_pay"); File targetPath = new File(ourPath.toString() - .replace("com.android.frameworks.dexloggertest", "com.google.android.gms")); + .replace("com.android.frameworks.dynamiccodeloggertest", "com.google.android.gms")); assertWithMessage("Expected " + targetPath + " to not be readable") .that(targetPath.canRead()).isFalse(); @@ -348,7 +376,7 @@ public final class DexLoggerIntegrationTests { MessageDigest hasher = MessageDigest.getInstance(SHA_256); // Copy the jar from our Java resources to a private data directory - Class<?> thisClass = DexLoggerIntegrationTests.class; + Class<?> thisClass = DynamicCodeLoggerIntegrationTests.class; try (InputStream input = thisClass.getResourceAsStream(resourcePath); OutputStream output = new FileOutputStream(copyTo)) { byte[] buffer = new byte[1024]; diff --git a/tests/DexLoggerIntegrationTests/src/cpp/com_android_dcl_Jni.cpp b/tests/DynamicCodeLoggerIntegrationTests/src/cpp/com_android_dcl_Jni.cpp index 060888310b51..060888310b51 100644 --- a/tests/DexLoggerIntegrationTests/src/cpp/com_android_dcl_Jni.cpp +++ b/tests/DynamicCodeLoggerIntegrationTests/src/cpp/com_android_dcl_Jni.cpp diff --git a/tests/DexLoggerIntegrationTests/src/cpp/test_executable.cpp b/tests/DynamicCodeLoggerIntegrationTests/src/cpp/test_executable.cpp index ad025e696dec..ad025e696dec 100644 --- a/tests/DexLoggerIntegrationTests/src/cpp/test_executable.cpp +++ b/tests/DynamicCodeLoggerIntegrationTests/src/cpp/test_executable.cpp diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/CustomRenderer.java b/tests/HwAccelerationTest/src/com/android/test/hwui/CustomRenderer.java index 60bd60f0bfd1..fece8babb400 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/CustomRenderer.java +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/CustomRenderer.java @@ -49,7 +49,7 @@ public class CustomRenderer extends Activity { @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { mContent.setLeftTopRightBottom(0, 0, width, height); - RecordingCanvas canvas = mContent.startRecording(); + RecordingCanvas canvas = mContent.beginRecording(); canvas.drawColor(Color.WHITE); Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setColor(Color.BLACK); diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/MyLittleTextureView.java b/tests/HwAccelerationTest/src/com/android/test/hwui/MyLittleTextureView.java index 8bd7d797aea3..08d5d4fff50a 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/MyLittleTextureView.java +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/MyLittleTextureView.java @@ -60,14 +60,14 @@ public class MyLittleTextureView extends Activity { outline.setAlpha(1f); childNode.setOutline(outline); { - Canvas canvas = childNode.startRecording(); + Canvas canvas = childNode.beginRecording(); canvas.drawColor(Color.BLUE); } childNode.endRecording(); childNode.setElevation(20f); { - Canvas canvas = mContent.startRecording(); + Canvas canvas = mContent.beginRecording(); canvas.drawColor(Color.WHITE); canvas.enableZ(); canvas.drawRenderNode(childNode); diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/PositionListenerActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/PositionListenerActivity.java index 316aad343d19..818d899413de 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/PositionListenerActivity.java +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/PositionListenerActivity.java @@ -53,7 +53,7 @@ public class PositionListenerActivity extends Activity { MyPositionReporter(Context c) { super(c); mNode = new RenderNode("positionListener"); - mNode.requestPositionUpdates(this); + mNode.addPositionUpdateListener(this); setTextAlignment(TEXT_ALIGNMENT_VIEW_START); } diff --git a/tests/RollbackTest/Android.mk b/tests/RollbackTest/Android.mk index 0967ad3767c6..db9376b844f9 100644 --- a/tests/RollbackTest/Android.mk +++ b/tests/RollbackTest/Android.mk @@ -21,6 +21,7 @@ LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS) LOCAL_SDK_VERSION := current LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src) LOCAL_MANIFEST_FILE := TestApp/Av1.xml +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/TestApp/res_v1 LOCAL_PACKAGE_NAME := RollbackTestAppAv1 include $(BUILD_PACKAGE) ROLLBACK_TEST_APP_AV1 := $(LOCAL_INSTALLED_MODULE) @@ -32,6 +33,7 @@ LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS) LOCAL_SDK_VERSION := current LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src) LOCAL_MANIFEST_FILE := TestApp/Av2.xml +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/TestApp/res_v2 LOCAL_PACKAGE_NAME := RollbackTestAppAv2 include $(BUILD_PACKAGE) ROLLBACK_TEST_APP_AV2 := $(LOCAL_INSTALLED_MODULE) @@ -43,6 +45,7 @@ LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS) LOCAL_SDK_VERSION := current LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src) LOCAL_MANIFEST_FILE := TestApp/ACrashingV2.xml +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/TestApp/res_v2 LOCAL_PACKAGE_NAME := RollbackTestAppACrashingV2 include $(BUILD_PACKAGE) ROLLBACK_TEST_APP_A_CRASHING_V2 := $(LOCAL_INSTALLED_MODULE) @@ -54,6 +57,7 @@ LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS) LOCAL_SDK_VERSION := current LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src) LOCAL_MANIFEST_FILE := TestApp/Bv1.xml +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/TestApp/res_v1 LOCAL_PACKAGE_NAME := RollbackTestAppBv1 include $(BUILD_PACKAGE) ROLLBACK_TEST_APP_BV1 := $(LOCAL_INSTALLED_MODULE) @@ -65,10 +69,39 @@ LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS) LOCAL_SDK_VERSION := current LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src) LOCAL_MANIFEST_FILE := TestApp/Bv2.xml +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/TestApp/res_v2 LOCAL_PACKAGE_NAME := RollbackTestAppBv2 include $(BUILD_PACKAGE) ROLLBACK_TEST_APP_BV2 := $(LOCAL_INSTALLED_MODULE) +# RollbackTestAppASplitV1.apk +include $(CLEAR_VARS) +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS) +LOCAL_SDK_VERSION := current +LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src) +LOCAL_MANIFEST_FILE := TestApp/Av1.xml +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/TestApp/res_v1 +LOCAL_PACKAGE_NAME := RollbackTestAppASplitV1 +LOCAL_PACKAGE_SPLITS := anydpi +include $(BUILD_PACKAGE) +ROLLBACK_TEST_APP_A_SPLIT_V1 := $(LOCAL_INSTALLED_MODULE) +ROLLBACK_TEST_APP_A_SPLIT_V1_SPLIT := $(installed_apk_splits) + +# RollbackTestAppASplitV2.apk +include $(CLEAR_VARS) +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS) +LOCAL_SDK_VERSION := current +LOCAL_SRC_FILES := $(call all-java-files-under, TestApp/src) +LOCAL_MANIFEST_FILE := TestApp/Av2.xml +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/TestApp/res_v2 +LOCAL_PACKAGE_NAME := RollbackTestAppASplitV2 +LOCAL_PACKAGE_SPLITS := anydpi +include $(BUILD_PACKAGE) +ROLLBACK_TEST_APP_A_SPLIT_V2 := $(LOCAL_INSTALLED_MODULE) +ROLLBACK_TEST_APP_A_SPLIT_V2_SPLIT := $(installed_apk_splits) + # RollbackTest include $(CLEAR_VARS) LOCAL_SRC_FILES := $(call all-java-files-under, RollbackTest/src) @@ -76,13 +109,17 @@ LOCAL_PACKAGE_NAME := RollbackTest LOCAL_MODULE_TAGS := tests LOCAL_STATIC_JAVA_LIBRARIES := android-support-test LOCAL_COMPATIBILITY_SUITE := general-tests -LOCAL_COMPATIBILITY_SUPPORT_FILES := $(ROLLBACK_TEST_APEX_V1) LOCAL_JAVA_RESOURCE_FILES := \ $(ROLLBACK_TEST_APP_AV1) \ $(ROLLBACK_TEST_APP_AV2) \ $(ROLLBACK_TEST_APP_A_CRASHING_V2) \ $(ROLLBACK_TEST_APP_BV1) \ $(ROLLBACK_TEST_APP_BV2) \ + $(ROLLBACK_TEST_APP_A_SPLIT_V1) \ + $(ROLLBACK_TEST_APP_A_SPLIT_V1_SPLIT) \ + $(ROLLBACK_TEST_APP_A_SPLIT_V2) \ + $(ROLLBACK_TEST_APP_A_SPLIT_V2_SPLIT) \ + $(ROLLBACK_TEST_APEX_V1) \ $(ROLLBACK_TEST_APEX_V2) LOCAL_MANIFEST_FILE := RollbackTest/AndroidManifest.xml LOCAL_SDK_VERSION := system_current @@ -103,5 +140,9 @@ include $(BUILD_HOST_JAVA_LIBRARY) ROLLBACK_TEST_APP_AV1 := ROLLBACK_TEST_APP_AV2 := ROLLBACK_TEST_APP_A_CRASHING_V2 := +ROLLBACK_TEST_APP_A_SPLIT_V1 := +ROLLBACK_TEST_APP_A_SPLIT_V1_SPLIT := +ROLLBACK_TEST_APP_A_SPLIT_V2 := +ROLLBACK_TEST_APP_A_SPLIT_V2_SPLIT := ROLLBACK_TEST_APP_BV1 := ROLLBACK_TEST_APP_BV2 := diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java index f3edf09c5e95..7be83edd91b9 100644 --- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java +++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java @@ -32,6 +32,7 @@ import android.content.rollback.RollbackInfo; import android.content.rollback.RollbackManager; import android.os.Handler; import android.os.HandlerThread; +import android.provider.DeviceConfig; import android.support.test.InstrumentationRegistry; import android.util.Log; @@ -331,6 +332,64 @@ public class RollbackTest { } /** + * Test the scheduling aspect of rollback expiration. + */ + @Test + public void testRollbackExpiresAfterLifetime() throws Exception { + long expirationTime = TimeUnit.SECONDS.toMillis(30); + long defaultExpirationTime = TimeUnit.HOURS.toMillis(48); + RollbackManager rm = RollbackTestUtils.getRollbackManager(); + + try { + RollbackTestUtils.adoptShellPermissionIdentity( + Manifest.permission.INSTALL_PACKAGES, + Manifest.permission.DELETE_PACKAGES, + Manifest.permission.MANAGE_ROLLBACKS, + Manifest.permission.WRITE_DEVICE_CONFIG); + + DeviceConfig.setProperty(DeviceConfig.Rollback.BOOT_NAMESPACE, + DeviceConfig.Rollback.ROLLBACK_LIFETIME_IN_MILLIS, + Long.toString(expirationTime), false /* makeDefault*/); + + // Pull the new expiration time from DeviceConfig + rm.reloadPersistedData(); + + // Uninstall TEST_APP_A + RollbackTestUtils.uninstall(TEST_APP_A); + assertEquals(-1, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); + + // Install v1 of the app (without rollbacks enabled). + RollbackTestUtils.install("RollbackTestAppAv1.apk", false); + assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); + + // Upgrade from v1 to v2, with rollbacks enabled. + RollbackTestUtils.install("RollbackTestAppAv2.apk", true); + assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); + + // Check that the rollback data has not expired + Thread.sleep(1000); + RollbackInfo rollback = getUniqueRollbackInfoForPackage( + rm.getAvailableRollbacks(), TEST_APP_A); + assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback); + + // Give it a little more time, but still not the long enough to expire + Thread.sleep(expirationTime / 2); + rollback = getUniqueRollbackInfoForPackage( + rm.getAvailableRollbacks(), TEST_APP_A); + assertRollbackInfoEquals(TEST_APP_A, 2, 1, rollback); + + // Check that the data has expired after the expiration time (with a buffer of 1 second) + Thread.sleep(expirationTime / 2); + assertNull(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(), TEST_APP_A)); + } finally { + DeviceConfig.setProperty(DeviceConfig.Rollback.BOOT_NAMESPACE, + DeviceConfig.Rollback.ROLLBACK_LIFETIME_IN_MILLIS, + Long.toString(defaultExpirationTime), false /* makeDefault*/); + RollbackTestUtils.dropShellPermissionIdentity(); + } + } + + /** * Test explicit expiration of rollbacks. * Does not test the scheduling aspects of rollback expiration. */ @@ -442,6 +501,39 @@ public class RollbackTest { } /** + * Test rollback of apks involving splits. + */ + @Test + public void testRollbackWithSplits() throws Exception { + try { + RollbackTestUtils.adoptShellPermissionIdentity( + Manifest.permission.INSTALL_PACKAGES, + Manifest.permission.DELETE_PACKAGES, + Manifest.permission.MANAGE_ROLLBACKS); + + RollbackTestUtils.uninstall(TEST_APP_A); + RollbackTestUtils.installSplit(false, + "RollbackTestAppASplitV1.apk", + "RollbackTestAppASplitV1_anydpi.apk"); + processUserData(TEST_APP_A); + + RollbackTestUtils.installSplit(true, + "RollbackTestAppASplitV2.apk", + "RollbackTestAppASplitV2_anydpi.apk"); + processUserData(TEST_APP_A); + + RollbackManager rm = RollbackTestUtils.getRollbackManager(); + RollbackInfo rollback = getUniqueRollbackInfoForPackage( + rm.getAvailableRollbacks(), TEST_APP_A); + assertNotNull(rollback); + RollbackTestUtils.rollback(rollback.getRollbackId()); + processUserData(TEST_APP_A); + } finally { + RollbackTestUtils.dropShellPermissionIdentity(); + } + } + + /** * Test restrictions on rollback broadcast sender. * A random app should not be able to send a ROLLBACK_COMMITTED broadcast. */ @@ -587,9 +679,13 @@ public class RollbackTest { RollbackTestUtils.installMultiPackage(false, "RollbackTestAppAv1.apk", "RollbackTestAppBv1.apk"); + processUserData(TEST_APP_A); + processUserData(TEST_APP_B); RollbackTestUtils.installMultiPackage(true, "RollbackTestAppAv2.apk", "RollbackTestAppBv2.apk"); + processUserData(TEST_APP_A); + processUserData(TEST_APP_B); assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_B)); @@ -614,6 +710,9 @@ public class RollbackTest { assertRollbackInfoForAandB(rollbackB); assertEquals(rollbackA.getRollbackId(), rollbackB.getRollbackId()); + + processUserData(TEST_APP_A); + processUserData(TEST_APP_B); } finally { RollbackTestUtils.dropShellPermissionIdentity(); } diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java index 280ee1d1c576..f28714c87125 100644 --- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java +++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTestUtils.java @@ -66,7 +66,7 @@ class RollbackTestUtils { Context context = InstrumentationRegistry.getContext(); PackageManager pm = context.getPackageManager(); try { - PackageInfo info = pm.getPackageInfo(packageName, 0); + PackageInfo info = pm.getPackageInfo(packageName, PackageManager.MATCH_APEX); return info.getLongVersionCode(); } catch (PackageManager.NameNotFoundException e) { return -1; @@ -130,6 +130,19 @@ class RollbackTestUtils { */ static void install(String resourceName, boolean enableRollback) throws InterruptedException, IOException { + installSplit(enableRollback, resourceName); + } + + /** + * Installs the apk with the given name and its splits. + * + * @param enableRollback if rollback should be enabled. + * @param resourceNames names of class loader resources for the apk and + * its splits to install. + * @throws AssertionError if the installation fails. + */ + static void installSplit(boolean enableRollback, String... resourceNames) + throws InterruptedException, IOException { Context context = InstrumentationRegistry.getContext(); PackageInstaller.Session session = null; PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller(); @@ -142,12 +155,14 @@ class RollbackTestUtils { session = packageInstaller.openSession(sessionId); ClassLoader loader = RollbackTest.class.getClassLoader(); - try (OutputStream packageInSession = session.openWrite("package", 0, -1); - InputStream is = loader.getResourceAsStream(resourceName);) { - byte[] buffer = new byte[4096]; - int n; - while ((n = is.read(buffer)) >= 0) { - packageInSession.write(buffer, 0, n); + for (String resourceName : resourceNames) { + try (OutputStream packageInSession = session.openWrite(resourceName, 0, -1); + InputStream is = loader.getResourceAsStream(resourceName);) { + byte[] buffer = new byte[4096]; + int n; + while ((n = is.read(buffer)) >= 0) { + packageInSession.write(buffer, 0, n); + } } } @@ -168,7 +183,8 @@ class RollbackTestUtils { } /** - * Installs the apks with the given resource names as an atomic set. + * Installs the APKs or APEXs with the given resource names as an atomic + * set. A resource is assumed to be an APEX if it has the .apex extension. * <p> * In case of staged installs, this function will return succesfully after * the staged install has been committed and is ready for the device to @@ -206,6 +222,9 @@ class RollbackTestUtils { if (staged) { params.setStaged(); } + if (resourceName.endsWith(".apex")) { + params.setInstallAsApex(); + } if (enableRollback) { params.setEnableRollback(); } @@ -213,7 +232,7 @@ class RollbackTestUtils { session = packageInstaller.openSession(sessionId); ClassLoader loader = RollbackTest.class.getClassLoader(); - try (OutputStream packageInSession = session.openWrite("package", 0, -1); + try (OutputStream packageInSession = session.openWrite(resourceName, 0, -1); InputStream is = loader.getResourceAsStream(resourceName);) { byte[] buffer = new byte[4096]; int n; @@ -247,7 +266,9 @@ class RollbackTestUtils { } /** - * Installs the apks with the given resource names as a staged atomic set. + * Installs the APKs or APEXs with the given resource names as a staged + * atomic set. A resource is assumed to be an APEX if it has the .apex + * extension. * * @param enableRollback if rollback should be enabled. * @param resourceNames names of the class loader resource for the apks to @@ -339,7 +360,7 @@ class RollbackTestUtils { PackageInstaller.SessionInfo info = intent.getParcelableExtra(PackageInstaller.EXTRA_SESSION); if (info != null && info.getSessionId() == sessionId) { - if (info.isSessionReady() || info.isSessionFailed()) { + if (info.isStagedSessionReady() || info.isStagedSessionFailed()) { try { sessionStatus.put(info); } catch (InterruptedException e) { @@ -359,13 +380,13 @@ class RollbackTestUtils { PackageInstaller.SessionInfo info = installer.getSessionInfo(sessionId); try { - if (info.isSessionReady() || info.isSessionFailed()) { + if (info.isStagedSessionReady() || info.isStagedSessionFailed()) { sessionStatus.put(info); } info = sessionStatus.take(); context.unregisterReceiver(sessionUpdatedReceiver); - if (info.isSessionFailed()) { + if (info.isStagedSessionFailed()) { throw new AssertionError(info.getStagedSessionErrorMessage()); } } catch (InterruptedException e) { diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java index 297bf869278f..b65917b2ae5b 100644 --- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java +++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java @@ -25,6 +25,7 @@ import android.content.rollback.RollbackManager; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import org.junit.After; @@ -46,6 +47,11 @@ public class StagedRollbackTest { private static final String TAG = "RollbackTest"; private static final String TEST_APP_A = "com.android.tests.rollback.testapp.A"; + private static final String TEST_APEX_PKG = "com.android.tests.rollback.testapex"; + private static final String TEST_APEX_V1 = + "com.android.tests.rollback.testapex.RollbackTestApexV1.apex"; + private static final String TEST_APEX_V2 = + "com.android.tests.rollback.testapex.RollbackTestApexV2.apex"; /** * Adopts common shell permissions needed for rollback tests. @@ -68,10 +74,11 @@ public class StagedRollbackTest { /** - * Test basic rollbacks. Enable rollback phase. + * Test rollbacks of staged installs involving only apks. + * Enable rollback phase. */ @Test - public void testBasicEnableRollback() throws Exception { + public void testApkOnlyEnableRollback() throws Exception { RollbackTestUtils.uninstall(TEST_APP_A); assertEquals(-1, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); @@ -81,14 +88,15 @@ public class StagedRollbackTest { RollbackTestUtils.installStaged(true, "RollbackTestAppAv2.apk"); // At this point, the host test driver will reboot the device and run - // testBasicCommitRollback(). + // testApkOnlyCommitRollback(). } /** - * Test basic rollbacks. Commit rollback phase. + * Test rollbacks of staged installs involving only apks. + * Commit rollback phase. */ @Test - public void testBasicCommitRollback() throws Exception { + public void testApkOnlyCommitRollback() throws Exception { assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); RollbackManager rm = RollbackTestUtils.getRollbackManager(); @@ -111,14 +119,15 @@ public class StagedRollbackTest { assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); // At this point, the host test driver will reboot the device and run - // testBasicConfirmRollback(). + // testApkOnlyConfirmRollback(). } /** - * Test basic rollbacks. Confirm rollback phase. + * Test rollbacks of staged installs involving only apks. + * Confirm rollback phase. */ @Test - public void testBasicConfirmRollback() throws Exception { + public void testApkOnlyConfirmRollback() throws Exception { assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APP_A)); RollbackManager rm = RollbackTestUtils.getRollbackManager(); @@ -128,4 +137,83 @@ public class StagedRollbackTest { assertTrue(rollback.isStaged()); assertNotEquals(-1, rollback.getCommittedSessionId()); } + + /** + * Test rollbacks of staged installs involving only apex. + * Prepare apex phase. + */ + @Test + public void testApexOnlyPrepareApex() throws Exception { + // Note: We can't uninstall the apex if it is already on device, + // because that isn't supported yet (b/123667725). As long as nothing + // is failing, this should be fine because we don't expect the tests + // to leave the device with v2 of the apex installed. + RollbackTestUtils.installStaged(false, TEST_APEX_V1); + + // At this point, the host test driver will reboot the device and run + // testApexOnlyEnableRollback(). + } + + /** + * Test rollbacks of staged installs involving only apex. + * Enable rollback phase. + */ + @Test + public void testApexOnlyEnableRollback() throws Exception { + assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APEX_PKG)); + RollbackTestUtils.installStaged(true, TEST_APEX_V2); + + // At this point, the host test driver will reboot the device and run + // testApexOnlyCommitRollback(). + } + + /** + * Test rollbacks of staged installs involving only apks. + * Commit rollback phase. + */ + @Test + public void testApexOnlyCommitRollback() throws Exception { + assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APEX_PKG)); + + RollbackManager rm = RollbackTestUtils.getRollbackManager(); + RollbackInfo rollback = getUniqueRollbackInfoForPackage( + rm.getAvailableRollbacks(), TEST_APEX_PKG); + assertRollbackInfoEquals(TEST_APEX_PKG, 2, 1, rollback); + assertTrue(rollback.isStaged()); + + RollbackTestUtils.rollback(rollback.getRollbackId()); + + // Note: We can't use getUniqueRollbackInfoForPackage for the apex, + // because we can't uninstall the apex (b/123667725), which means + // there's no way to clear info about rollbacks from previous tests + // run on the device. Look up the info by rollback id instead. + RollbackInfo committed = null; + for (RollbackInfo info : rm.getRecentlyCommittedRollbacks()) { + if (info.getRollbackId() == rollback.getRollbackId()) { + assertNull(committed); + committed = info; + break; + } + } + assertRollbackInfoEquals(TEST_APEX_PKG, 2, 1, committed); + assertTrue(committed.isStaged()); + assertNotEquals(-1, committed.getCommittedSessionId()); + + RollbackTestUtils.waitForSessionReady(committed.getCommittedSessionId()); + + // The apex should not be rolled back until after reboot. + assertEquals(2, RollbackTestUtils.getInstalledVersion(TEST_APEX_PKG)); + + // At this point, the host test driver will reboot the device and run + // testApexOnlyConfirmRollback(). + } + + /** + * Test rollbacks of staged installs involving only apks. + * Confirm rollback phase. + */ + @Test + public void testApexOnlyConfirmRollback() throws Exception { + assertEquals(1, RollbackTestUtils.getInstalledVersion(TEST_APEX_PKG)); + } } diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java index 6cb0dd091392..24a51dc5a060 100644 --- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java +++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java @@ -34,7 +34,7 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { * Runs the given phase of a test by calling into the device. * Throws an exception if the test phase fails. * <p> - * For example, <code>runPhase("testBasicEnableRollback");</code> + * For example, <code>runPhase("testApkOnlyEnableRollback");</code> */ private void runPhase(String phase) throws Exception { assertTrue(runDeviceTests("com.android.tests.rollback", @@ -43,14 +43,28 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { } /** - * Tests staged rollbacks. + * Tests staged rollbacks involving only apks. */ @Test - public void testBasic() throws Exception { - runPhase("testBasicEnableRollback"); + public void testApkOnly() throws Exception { + runPhase("testApkOnlyEnableRollback"); getDevice().reboot(); - runPhase("testBasicCommitRollback"); + runPhase("testApkOnlyCommitRollback"); getDevice().reboot(); - runPhase("testBasicConfirmRollback"); + runPhase("testApkOnlyConfirmRollback"); + } + + /** + * Tests staged rollbacks involving only apex. + */ + @Test + public void testApexOnly() throws Exception { + runPhase("testApexOnlyPrepareApex"); + getDevice().reboot(); + runPhase("testApexOnlyEnableRollback"); + getDevice().reboot(); + runPhase("testApexOnlyCommitRollback"); + getDevice().reboot(); + runPhase("testApexOnlyConfirmRollback"); } } diff --git a/tests/RollbackTest/TestApp/ACrashingV2.xml b/tests/RollbackTest/TestApp/ACrashingV2.xml index 5708d2385f01..77bfd4e0f9a0 100644 --- a/tests/RollbackTest/TestApp/ACrashingV2.xml +++ b/tests/RollbackTest/TestApp/ACrashingV2.xml @@ -23,7 +23,6 @@ <uses-sdk android:minSdkVersion="19" /> <application android:label="Rollback Test App A v2"> - <meta-data android:name="version" android:value="2" /> <receiver android:name="com.android.tests.rollback.testapp.ProcessUserData" android:exported="true" /> <activity android:name="com.android.tests.rollback.testapp.CrashingMainActivity"> diff --git a/tests/RollbackTest/TestApp/Av1.xml b/tests/RollbackTest/TestApp/Av1.xml index 996d831013a8..63729fbaaf28 100644 --- a/tests/RollbackTest/TestApp/Av1.xml +++ b/tests/RollbackTest/TestApp/Av1.xml @@ -23,7 +23,6 @@ <uses-sdk android:minSdkVersion="19" /> <application android:label="Rollback Test App A v1"> - <meta-data android:name="version" android:value="1" /> <receiver android:name="com.android.tests.rollback.testapp.ProcessUserData" android:exported="true" /> <activity android:name="com.android.tests.rollback.testapp.MainActivity"> diff --git a/tests/RollbackTest/TestApp/Av2.xml b/tests/RollbackTest/TestApp/Av2.xml index 21c7260b570b..f0e909feabf3 100644 --- a/tests/RollbackTest/TestApp/Av2.xml +++ b/tests/RollbackTest/TestApp/Av2.xml @@ -23,7 +23,6 @@ <uses-sdk android:minSdkVersion="19" /> <application android:label="Rollback Test App A v2"> - <meta-data android:name="version" android:value="2" /> <receiver android:name="com.android.tests.rollback.testapp.ProcessUserData" android:exported="true" /> <activity android:name="com.android.tests.rollback.testapp.MainActivity"> diff --git a/tests/RollbackTest/TestApp/Bv1.xml b/tests/RollbackTest/TestApp/Bv1.xml index de0fd0d6d616..ca9c2ec47a20 100644 --- a/tests/RollbackTest/TestApp/Bv1.xml +++ b/tests/RollbackTest/TestApp/Bv1.xml @@ -23,7 +23,6 @@ <uses-sdk android:minSdkVersion="19" /> <application android:label="Rollback Test App B v1"> - <meta-data android:name="version" android:value="1" /> <receiver android:name="com.android.tests.rollback.testapp.ProcessUserData" android:exported="true" /> <activity android:name="com.android.tests.rollback.testapp.MainActivity"> diff --git a/tests/RollbackTest/TestApp/Bv2.xml b/tests/RollbackTest/TestApp/Bv2.xml index 6c2e66a3220c..bd3e6133f6f6 100644 --- a/tests/RollbackTest/TestApp/Bv2.xml +++ b/tests/RollbackTest/TestApp/Bv2.xml @@ -23,7 +23,6 @@ <uses-sdk android:minSdkVersion="19" /> <application android:label="Rollback Test App B v2"> - <meta-data android:name="version" android:value="2" /> <receiver android:name="com.android.tests.rollback.testapp.ProcessUserData" android:exported="true" /> <activity android:name="com.android.tests.rollback.testapp.MainActivity"> diff --git a/tests/RollbackTest/TestApp/res_v1/values-anydpi/values.xml b/tests/RollbackTest/TestApp/res_v1/values-anydpi/values.xml new file mode 100644 index 000000000000..90d3da2565cc --- /dev/null +++ b/tests/RollbackTest/TestApp/res_v1/values-anydpi/values.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources> + <integer name="split_version">1</integer> +</resources> diff --git a/tests/RollbackTest/TestApp/res_v1/values/values.xml b/tests/RollbackTest/TestApp/res_v1/values/values.xml new file mode 100644 index 000000000000..0447c74a79a6 --- /dev/null +++ b/tests/RollbackTest/TestApp/res_v1/values/values.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources> + <integer name="app_version">1</integer> + <integer name="split_version">0</integer> +</resources> diff --git a/tests/RollbackTest/TestApp/res_v2/values-anydpi/values.xml b/tests/RollbackTest/TestApp/res_v2/values-anydpi/values.xml new file mode 100644 index 000000000000..9a1aa7fd8461 --- /dev/null +++ b/tests/RollbackTest/TestApp/res_v2/values-anydpi/values.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources> + <integer name="split_version">2</integer> +</resources> diff --git a/tests/RollbackTest/TestApp/res_v2/values/values.xml b/tests/RollbackTest/TestApp/res_v2/values/values.xml new file mode 100644 index 000000000000..fd988f597f61 --- /dev/null +++ b/tests/RollbackTest/TestApp/res_v2/values/values.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources> + <integer name="app_version">2</integer> + <integer name="split_version">0</integer> +</resources> diff --git a/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/ProcessUserData.java b/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/ProcessUserData.java index fde6a83b6e56..38c658e795aa 100644 --- a/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/ProcessUserData.java +++ b/tests/RollbackTest/TestApp/src/com/android/tests/rollback/testapp/ProcessUserData.java @@ -19,9 +19,7 @@ package com.android.tests.rollback.testapp; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.os.Bundle; +import android.content.res.Resources; import java.io.File; import java.io.FileNotFoundException; @@ -35,6 +33,8 @@ import java.util.Scanner; */ public class ProcessUserData extends BroadcastReceiver { + private static final String TAG = "RollbackTestApp"; + /** * Exception thrown in case of issue with user data. */ @@ -66,14 +66,19 @@ public class ProcessUserData extends BroadcastReceiver { * @throws UserDataException in case of problems with app user data. */ public void processUserData(Context context) throws UserDataException { - int appVersion = 0; - try { - ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo( - context.getPackageName(), PackageManager.GET_META_DATA); - Bundle bundle = appInfo.metaData; - appVersion = bundle.getInt("version"); - } catch (PackageManager.NameNotFoundException e) { - throw new UserDataException("Unable to get app version info", e); + Resources res = context.getResources(); + String packageName = context.getPackageName(); + + int appVersionId = res.getIdentifier("app_version", "integer", packageName); + int appVersion = res.getInteger(appVersionId); + + int splitVersionId = res.getIdentifier("split_version", "integer", packageName); + int splitVersion = res.getInteger(splitVersionId); + + // Make sure the app version and split versions are compatible. + if (appVersion != splitVersion) { + throw new UserDataException("Split version " + splitVersion + + " does not match app version " + appVersion); } // Read the version of the app's user data and ensure it is compatible diff --git a/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java b/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java index 7a5e7325ad22..e2a4c26606bf 100644 --- a/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java +++ b/tests/UsageStatsPerfTests/src/com/android/frameworks/perftests/usage/tests/UsageStatsDatabasePerfTest.java @@ -18,7 +18,6 @@ package com.android.frameworks.perftests.usage.tests; import static junit.framework.Assert.assertEquals; -import android.app.usage.EventList; import android.app.usage.UsageEvents; import android.app.usage.UsageStatsManager; import android.content.Context; @@ -84,9 +83,6 @@ public class UsageStatsDatabasePerfTest { private static void populateIntervalStats(IntervalStats intervalStats, int packageCount, int eventsPerPackage) { - if (intervalStats.events == null) { - intervalStats.events = new EventList(); - } for (int pkg = 0; pkg < packageCount; pkg++) { UsageEvents.Event event = new UsageEvents.Event(); event.mPackage = "fake.package.name" + pkg; diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index a10fb4ee1305..129416f5f04b 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -17,10 +17,10 @@ package com.android.server; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; +import static android.net.ConnectivityManager.NETID_UNSET; import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF; import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC; import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME; -import static android.net.ConnectivityManager.NETID_UNSET; import static android.net.ConnectivityManager.TYPE_ETHERNET; import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA; @@ -154,6 +154,7 @@ import android.test.mock.MockContentResolver; import android.text.TextUtils; import android.util.ArraySet; import android.util.Log; +import android.util.SparseArray; import com.android.internal.net.VpnConfig; import com.android.internal.util.ArrayUtils; @@ -748,6 +749,10 @@ public class ConnectivityServiceTest { // mExpectations is non-empty. private boolean mExpectingAdditions; + // Used to collect the networks requests managed by this factory. This is a duplicate of + // the internal information stored in the NetworkFactory (which is private). + private SparseArray<NetworkRequest> mNetworkRequests = new SparseArray<>(); + public MockNetworkFactory(Looper looper, Context context, String logTag, NetworkCapabilities filter) { super(looper, context, logTag, filter); @@ -800,6 +805,7 @@ public class ConnectivityServiceTest { } // Add the request. + mNetworkRequests.put(request.requestId, request); super.handleAddRequest(request, score, factorySerialNumber); mExpectations.notify(); } @@ -817,11 +823,17 @@ public class ConnectivityServiceTest { } // Remove the request. + mNetworkRequests.remove(request.requestId); super.handleRemoveRequest(request); mExpectations.notify(); } } + // Trigger releasing the request as unfulfillable + public void triggerUnfulfillable(NetworkRequest r) { + super.releaseRequestAsUnfulfillableByAnyFactory(r); + } + private void assertNoExpectations() { if (mExpectations.size() != 0) { fail("Can't add expectation, " + mExpectations.size() + " already pending"); @@ -861,9 +873,11 @@ public class ConnectivityServiceTest { assertEquals(msg, 0, count); } - public void waitForNetworkRequests(final int count) throws InterruptedException { + public SparseArray<NetworkRequest> waitForNetworkRequests(final int count) + throws InterruptedException { waitForRequests(); assertEquals(count, getMyRequestCount()); + return mNetworkRequests; } } @@ -1814,6 +1828,12 @@ public class ConnectivityServiceTest { fn.test((NetworkCapabilities) cbi.arg)); } + void expectLinkPropertiesLike(Predicate<LinkProperties> fn, MockNetworkAgent agent) { + CallbackInfo cbi = expectCallback(CallbackState.LINK_PROPERTIES, agent); + assertTrue("Received LinkProperties don't match expectations : " + cbi.arg, + fn.test((LinkProperties) cbi.arg)); + } + void expectBlockedStatusCallback(boolean expectBlocked, MockNetworkAgent agent) { CallbackInfo cbi = expectCallback(CallbackState.BLOCKED_STATUS, agent); boolean actualBlocked = (boolean) cbi.arg; @@ -3533,6 +3553,55 @@ public class ConnectivityServiceTest { networkCallback.assertNoCallback(); } + /** + * Validate the callback flow for a factory releasing a request as unfulfillable. + */ + @Test + public void testUnfulfillableNetworkRequest() throws Exception { + NetworkRequest nr = new NetworkRequest.Builder().addTransportType( + NetworkCapabilities.TRANSPORT_WIFI).build(); + final TestNetworkCallback networkCallback = new TestNetworkCallback(); + + final HandlerThread handlerThread = new HandlerThread("testUnfulfillableNetworkRequest"); + handlerThread.start(); + NetworkCapabilities filter = new NetworkCapabilities() + .addTransportType(TRANSPORT_WIFI) + .addCapability(NET_CAPABILITY_INTERNET); + final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(), + mServiceContext, "testFactory", filter); + testFactory.setScoreFilter(40); + + // Register the factory and expect it to receive the default request. + testFactory.expectAddRequestsWithScores(0); + testFactory.register(); + SparseArray<NetworkRequest> requests = testFactory.waitForNetworkRequests(1); + + assertEquals(1, requests.size()); // have 1 request at this point + int origRequestId = requests.valueAt(0).requestId; + + // Now file the test request and expect it. + testFactory.expectAddRequestsWithScores(0); + mCm.requestNetwork(nr, networkCallback); + requests = testFactory.waitForNetworkRequests(2); // have 2 requests at this point + + int newRequestId = 0; + for (int i = 0; i < requests.size(); ++i) { + if (requests.valueAt(i).requestId != origRequestId) { + newRequestId = requests.valueAt(i).requestId; + break; + } + } + + // Simulate the factory releasing the request as unfulfillable and expect onUnavailable! + testFactory.expectRemoveRequests(1); + testFactory.triggerUnfulfillable(requests.get(newRequestId)); + networkCallback.expectCallback(CallbackState.UNAVAILABLE, null); + testFactory.waitForRequests(); + + testFactory.unregister(); + handlerThread.quit(); + } + private static class TestKeepaliveCallback extends PacketKeepaliveCallback { public static enum CallbackType { ON_STARTED, ON_STOPPED, ON_ERROR }; @@ -5113,6 +5182,9 @@ public class ConnectivityServiceTest { public void testStackedLinkProperties() throws UnknownHostException, RemoteException { final LinkAddress myIpv4 = new LinkAddress("1.2.3.4/24"); final LinkAddress myIpv6 = new LinkAddress("2001:db8:1::1/64"); + final String kNat64PrefixString = "2001:db8:64:64:64:64::"; + final IpPrefix kNat64Prefix = new IpPrefix(InetAddress.getByName(kNat64PrefixString), 96); + final NetworkRequest networkRequest = new NetworkRequest.Builder() .addTransportType(TRANSPORT_CELLULAR) .addCapability(NET_CAPABILITY_INTERNET) @@ -5120,8 +5192,9 @@ public class ConnectivityServiceTest { final TestNetworkCallback networkCallback = new TestNetworkCallback(); mCm.registerNetworkCallback(networkRequest, networkCallback); - // Prepare ipv6 only link properties and connect. + // Prepare ipv6 only link properties. mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + final int cellNetId = mCellNetworkAgent.getNetwork().netId; final LinkProperties cellLp = new LinkProperties(); cellLp.setInterfaceName(MOBILE_IFNAME); cellLp.addLinkAddress(myIpv6); @@ -5131,15 +5204,44 @@ public class ConnectivityServiceTest { when(mNetworkManagementService.getInterfaceConfig(CLAT_PREFIX + MOBILE_IFNAME)) .thenReturn(getClatInterfaceConfig(myIpv4)); - // Connect with ipv6 link properties, then expect clat setup ipv4 and update link - // properties properly. + // Connect with ipv6 link properties. Expect prefix discovery to be started. mCellNetworkAgent.sendLinkProperties(cellLp); mCellNetworkAgent.connect(true); networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); - verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME); - Nat464Xlat clat = mService.getNat464Xlat(mCellNetworkAgent); + verify(mMockNetd, times(1)).resolverStartPrefix64Discovery(cellNetId); + + // Switching default network updates TCP buffer sizes. + verifyTcpBufferSizeChange(ConnectivityService.DEFAULT_TCP_BUFFER_SIZES); + + // Add an IPv4 address. Expect prefix discovery to be stopped. Netd doesn't tell us that + // the NAT64 prefix was removed because one was never discovered. + cellLp.addLinkAddress(myIpv4); + mCellNetworkAgent.sendLinkProperties(cellLp); + networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent); + verify(mMockNetd, times(1)).resolverStopPrefix64Discovery(cellNetId); + + verifyNoMoreInteractions(mMockNetd); + reset(mMockNetd); - // Clat iface up, expect stack link updated. + // Remove IPv4 address. Expect prefix discovery to be started again. + cellLp.removeLinkAddress(myIpv4); + cellLp.removeRoute(new RouteInfo(myIpv4, null, MOBILE_IFNAME)); + mCellNetworkAgent.sendLinkProperties(cellLp); + networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent); + verify(mMockNetd, times(1)).resolverStartPrefix64Discovery(cellNetId); + + // When NAT64 prefix discovery succeeds, LinkProperties are updated and clatd is started. + Nat464Xlat clat = mService.getNat464Xlat(mCellNetworkAgent); + assertNull(mCm.getLinkProperties(mCellNetworkAgent.getNetwork()).getNat64Prefix()); + mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, true /* added */, + kNat64PrefixString, 96); + LinkProperties lpBeforeClat = (LinkProperties) networkCallback.expectCallback( + CallbackState.LINK_PROPERTIES, mCellNetworkAgent).arg; + assertEquals(0, lpBeforeClat.getStackedLinks().size()); + assertEquals(kNat64Prefix, lpBeforeClat.getNat64Prefix()); + verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME, kNat64Prefix.toString()); + + // Clat iface comes up. Expect stacked link to be added. clat.interfaceLinkStateChanged(CLAT_PREFIX + MOBILE_IFNAME, true); networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent); List<LinkProperties> stackedLps = mCm.getLinkProperties(mCellNetworkAgent.getNetwork()) @@ -5156,20 +5258,66 @@ public class ConnectivityServiceTest { assertNotEquals(stackedLpsAfterChange, Collections.EMPTY_LIST); assertEquals(makeClatLinkProperties(myIpv4), stackedLpsAfterChange.get(0)); - // Add ipv4 address, expect stacked linkproperties be cleaned up + // Add ipv4 address, expect that clatd and prefix discovery are stopped and stacked + // linkproperties are cleaned up. cellLp.addLinkAddress(myIpv4); cellLp.addRoute(new RouteInfo(myIpv4, null, MOBILE_IFNAME)); mCellNetworkAgent.sendLinkProperties(cellLp); networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent); verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME); + verify(mMockNetd, times(1)).resolverStopPrefix64Discovery(cellNetId); - // Clat iface removed, expect linkproperties revert to original one - clat.interfaceRemoved(CLAT_PREFIX + MOBILE_IFNAME); + // As soon as stop is called, the linkproperties lose the stacked interface. networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent); LinkProperties actualLpAfterIpv4 = mCm.getLinkProperties(mCellNetworkAgent.getNetwork()); - assertEquals(cellLp, actualLpAfterIpv4); + LinkProperties expected = new LinkProperties(cellLp); + expected.setNat64Prefix(kNat64Prefix); + assertEquals(expected, actualLpAfterIpv4); + assertEquals(0, actualLpAfterIpv4.getStackedLinks().size()); - // Clean up + // The interface removed callback happens but has no effect after stop is called. + clat.interfaceRemoved(CLAT_PREFIX + MOBILE_IFNAME); + networkCallback.assertNoCallback(); + + verifyNoMoreInteractions(mMockNetd); + reset(mMockNetd); + + // Stopping prefix discovery causes netd to tell us that the NAT64 prefix is gone. + mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, false /* added */, + kNat64PrefixString, 96); + networkCallback.expectLinkPropertiesLike((lp) -> lp.getNat64Prefix() == null, + mCellNetworkAgent); + + // Remove IPv4 address and expect prefix discovery and clatd to be started again. + cellLp.removeLinkAddress(myIpv4); + cellLp.removeRoute(new RouteInfo(myIpv4, null, MOBILE_IFNAME)); + cellLp.removeDnsServer(InetAddress.getByName("8.8.8.8")); + mCellNetworkAgent.sendLinkProperties(cellLp); + networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent); + verify(mMockNetd, times(1)).resolverStartPrefix64Discovery(cellNetId); + mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, true /* added */, + kNat64PrefixString, 96); + networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent); + verify(mMockNetd, times(1)).clatdStart(MOBILE_IFNAME, kNat64Prefix.toString()); + + + // Clat iface comes up. Expect stacked link to be added. + clat.interfaceLinkStateChanged(CLAT_PREFIX + MOBILE_IFNAME, true); + networkCallback.expectLinkPropertiesLike( + (lp) -> lp.getStackedLinks().size() == 1 && lp.getNat64Prefix() != null, + mCellNetworkAgent); + + // NAT64 prefix is removed. Expect that clat is stopped. + mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, false /* added */, + kNat64PrefixString, 96); + networkCallback.expectLinkPropertiesLike( + (lp) -> lp.getStackedLinks().size() == 0 && lp.getNat64Prefix() == null, + mCellNetworkAgent); + verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME); + networkCallback.expectLinkPropertiesLike((lp) -> lp.getStackedLinks().size() == 0, + mCellNetworkAgent); + + // Clean up. mCellNetworkAgent.disconnect(); networkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent); networkCallback.assertNoCallback(); @@ -5250,30 +5398,34 @@ public class ConnectivityServiceTest { mCm.unregisterNetworkCallback(networkCallback); } - private static final String TEST_TCP_BUFFER_SIZES = "1,2,3,4,5,6"; - - private void verifyTcpBufferSizeChange(String tcpBufferSizes) throws Exception { + private void verifyTcpBufferSizeChange(String tcpBufferSizes) { String[] values = tcpBufferSizes.split(","); String rmemValues = String.join(" ", values[0], values[1], values[2]); String wmemValues = String.join(" ", values[3], values[4], values[5]); waitForIdle(); - verify(mMockNetd, atLeastOnce()).setTcpRWmemorySize(rmemValues, wmemValues); + try { + verify(mMockNetd, atLeastOnce()).setTcpRWmemorySize(rmemValues, wmemValues); + } catch (RemoteException e) { + fail("mMockNetd should never throw RemoteException"); + } reset(mMockNetd); } @Test - public void testTcpBufferReset() throws Exception { + public void testTcpBufferReset() { + final String testTcpBufferSizes = "1,2,3,4,5,6"; + mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); reset(mMockNetd); - // Simple connection should have updated tcp buffer size. + // Switching default network updates TCP buffer sizes. mCellNetworkAgent.connect(false); verifyTcpBufferSizeChange(ConnectivityService.DEFAULT_TCP_BUFFER_SIZES); // Change link Properties should have updated tcp buffer size. LinkProperties lp = new LinkProperties(); - lp.setTcpBufferSizes(TEST_TCP_BUFFER_SIZES); + lp.setTcpBufferSizes(testTcpBufferSizes); mCellNetworkAgent.sendLinkProperties(lp); - verifyTcpBufferSizeChange(TEST_TCP_BUFFER_SIZES); + verifyTcpBufferSizeChange(testTcpBufferSizes); } @Test diff --git a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java index 07b1d057c882..37c0df80f3e3 100644 --- a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java +++ b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java @@ -16,9 +16,11 @@ package com.android.server.connectivity; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -27,6 +29,7 @@ import static org.mockito.Mockito.when; import android.net.ConnectivityManager; import android.net.INetd; import android.net.InterfaceConfiguration; +import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.NetworkInfo; @@ -43,6 +46,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -53,6 +57,8 @@ public class Nat464XlatTest { static final String BASE_IFACE = "test0"; static final String STACKED_IFACE = "v4-test0"; static final LinkAddress ADDR = new LinkAddress("192.0.2.5/29"); + static final String NAT64_PREFIX = "64:ff9b::/96"; + static final int NETID = 42; @Mock ConnectivityService mConnectivity; @Mock NetworkMisc mMisc; @@ -65,7 +71,11 @@ public class Nat464XlatTest { Handler mHandler; Nat464Xlat makeNat464Xlat() { - return new Nat464Xlat(mNai, mNetd, mNms); + return new Nat464Xlat(mNai, mNetd, mNms) { + @Override protected int getNetId() { + return NETID; + } + }; } @Before @@ -87,6 +97,24 @@ public class Nat464XlatTest { when(mConfig.getLinkAddress()).thenReturn(ADDR); } + private void assertRequiresClat(boolean expected, NetworkAgentInfo nai) { + String msg = String.format("requiresClat expected %b for type=%d state=%s skip=%b " + + "nat64Prefix=%s addresses=%s", expected, nai.networkInfo.getType(), + nai.networkInfo.getDetailedState(), + mMisc.skip464xlat, nai.linkProperties.getNat64Prefix(), + nai.linkProperties.getLinkAddresses()); + assertEquals(msg, expected, Nat464Xlat.requiresClat(nai)); + } + + private void assertShouldStartClat(boolean expected, NetworkAgentInfo nai) { + String msg = String.format("shouldStartClat expected %b for type=%d state=%s skip=%b " + + "nat64Prefix=%s addresses=%s", expected, nai.networkInfo.getType(), + nai.networkInfo.getDetailedState(), + mMisc.skip464xlat, nai.linkProperties.getNat64Prefix(), + nai.linkProperties.getLinkAddresses()); + assertEquals(msg, expected, Nat464Xlat.shouldStartClat(nai)); + } + @Test public void testRequiresClat() throws Exception { final int[] supportedTypes = { @@ -102,20 +130,45 @@ public class Nat464XlatTest { NetworkInfo.DetailedState.SUSPENDED, }; + LinkProperties oldLp = new LinkProperties(mNai.linkProperties); for (int type : supportedTypes) { mNai.networkInfo.setType(type); for (NetworkInfo.DetailedState state : supportedDetailedStates) { mNai.networkInfo.setDetailedState(state, "reason", "extraInfo"); - String msg = String.format("requiresClat expected for type=%d state=%s", - type, state); + + mNai.linkProperties.setNat64Prefix(new IpPrefix("2001:db8:0:64::/96")); + assertRequiresClat(false, mNai); + assertShouldStartClat(false, mNai); + + mNai.linkProperties.addLinkAddress(new LinkAddress("fc00::1/64")); + assertRequiresClat(false, mNai); + assertShouldStartClat(false, mNai); + + mNai.linkProperties.addLinkAddress(new LinkAddress("2001:db8::1/64")); + assertRequiresClat(true, mNai); + assertShouldStartClat(true, mNai); mMisc.skip464xlat = true; - String errorMsg = msg + String.format(" skip464xlat=%b", mMisc.skip464xlat); - assertFalse(errorMsg, Nat464Xlat.requiresClat(mNai)); + assertRequiresClat(false, mNai); + assertShouldStartClat(false, mNai); mMisc.skip464xlat = false; - errorMsg = msg + String.format(" skip464xlat=%b", mMisc.skip464xlat); - assertTrue(errorMsg, Nat464Xlat.requiresClat(mNai)); + assertRequiresClat(true, mNai); + assertShouldStartClat(true, mNai); + + mNai.linkProperties.addLinkAddress(new LinkAddress("192.0.2.2/24")); + assertRequiresClat(false, mNai); + assertShouldStartClat(false, mNai); + + mNai.linkProperties.removeLinkAddress(new LinkAddress("192.0.2.2/24")); + assertRequiresClat(true, mNai); + assertShouldStartClat(true, mNai); + + mNai.linkProperties.setNat64Prefix(null); + assertRequiresClat(true, mNai); + assertShouldStartClat(false, mNai); + + mNai.linkProperties = new LinkProperties(oldLp); } } } @@ -125,11 +178,13 @@ public class Nat464XlatTest { Nat464Xlat nat = makeNat464Xlat(); ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class); - // ConnectivityService starts clat. + nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX)); + + // Start clat. nat.start(); verify(mNms).registerObserver(eq(nat)); - verify(mNetd).clatdStart(eq(BASE_IFACE)); + verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX)); // Stacked interface up notification arrives. nat.interfaceLinkStateChanged(STACKED_IFACE, true); @@ -141,22 +196,109 @@ public class Nat464XlatTest { assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); assertRunning(nat); - // ConnectivityService stops clat (Network disconnects, IPv4 addr appears, ...). + // Stop clat (Network disconnects, IPv4 addr appears, ...). nat.stop(); verify(mNetd).clatdStop(eq(BASE_IFACE)); + verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture()); + verify(mNms).unregisterObserver(eq(nat)); + assertTrue(c.getValue().getStackedLinks().isEmpty()); + assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); + verify(mNetd).resolverStopPrefix64Discovery(eq(NETID)); + assertIdle(nat); - // Stacked interface removed notification arrives. + // Stacked interface removed notification arrives and is ignored. nat.interfaceRemoved(STACKED_IFACE); mLooper.dispatchNext(); - verify(mNms).unregisterObserver(eq(nat)); - verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture()); + verifyNoMoreInteractions(mNetd, mNms, mConnectivity); + } + + private void checkStartStopStart(boolean interfaceRemovedFirst) throws Exception { + Nat464Xlat nat = makeNat464Xlat(); + ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class); + InOrder inOrder = inOrder(mNetd, mConnectivity); + + nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX)); + + nat.start(); + + inOrder.verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX)); + + // Stacked interface up notification arrives. + nat.interfaceLinkStateChanged(STACKED_IFACE, true); + mLooper.dispatchNext(); + + inOrder.verify(mConnectivity).handleUpdateLinkProperties(eq(mNai), c.capture()); + assertFalse(c.getValue().getStackedLinks().isEmpty()); + assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); + assertRunning(nat); + + // ConnectivityService stops clat (Network disconnects, IPv4 addr appears, ...). + nat.stop(); + + inOrder.verify(mNetd).clatdStop(eq(BASE_IFACE)); + + inOrder.verify(mConnectivity, times(1)).handleUpdateLinkProperties(eq(mNai), c.capture()); assertTrue(c.getValue().getStackedLinks().isEmpty()); assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); assertIdle(nat); - verifyNoMoreInteractions(mNetd, mNms, mConnectivity); + if (interfaceRemovedFirst) { + // Stacked interface removed notification arrives and is ignored. + nat.interfaceRemoved(STACKED_IFACE); + mLooper.dispatchNext(); + nat.interfaceLinkStateChanged(STACKED_IFACE, false); + mLooper.dispatchNext(); + } + + assertTrue(c.getValue().getStackedLinks().isEmpty()); + assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); + assertIdle(nat); + inOrder.verifyNoMoreInteractions(); + + nat.start(); + + inOrder.verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX)); + + if (!interfaceRemovedFirst) { + // Stacked interface removed notification arrives and is ignored. + nat.interfaceRemoved(STACKED_IFACE); + mLooper.dispatchNext(); + nat.interfaceLinkStateChanged(STACKED_IFACE, false); + mLooper.dispatchNext(); + } + + // Stacked interface up notification arrives. + nat.interfaceLinkStateChanged(STACKED_IFACE, true); + mLooper.dispatchNext(); + + inOrder.verify(mConnectivity).handleUpdateLinkProperties(eq(mNai), c.capture()); + assertFalse(c.getValue().getStackedLinks().isEmpty()); + assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); + assertRunning(nat); + + // ConnectivityService stops clat again. + nat.stop(); + + inOrder.verify(mNetd).clatdStop(eq(BASE_IFACE)); + + inOrder.verify(mConnectivity, times(1)).handleUpdateLinkProperties(eq(mNai), c.capture()); + assertTrue(c.getValue().getStackedLinks().isEmpty()); + assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); + assertIdle(nat); + + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testStartStopStart() throws Exception { + checkStartStopStart(true); + } + + @Test + public void testStartStopStartBeforeInterfaceRemoved() throws Exception { + checkStartStopStart(false); } @Test @@ -164,11 +306,12 @@ public class Nat464XlatTest { Nat464Xlat nat = makeNat464Xlat(); ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class); - // ConnectivityService starts clat. + nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX)); + nat.start(); verify(mNms).registerObserver(eq(nat)); - verify(mNetd).clatdStart(eq(BASE_IFACE)); + verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX)); // Stacked interface up notification arrives. nat.interfaceLinkStateChanged(STACKED_IFACE, true); @@ -184,9 +327,10 @@ public class Nat464XlatTest { nat.interfaceRemoved(STACKED_IFACE); mLooper.dispatchNext(); - verify(mNms).unregisterObserver(eq(nat)); verify(mNetd).clatdStop(eq(BASE_IFACE)); verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture()); + verify(mNms).unregisterObserver(eq(nat)); + verify(mNetd).resolverStopPrefix64Discovery(eq(NETID)); assertTrue(c.getValue().getStackedLinks().isEmpty()); assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE)); assertIdle(nat); @@ -201,24 +345,25 @@ public class Nat464XlatTest { public void testStopBeforeClatdStarts() throws Exception { Nat464Xlat nat = makeNat464Xlat(); - // ConnectivityService starts clat. + nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX)); + nat.start(); verify(mNms).registerObserver(eq(nat)); - verify(mNetd).clatdStart(eq(BASE_IFACE)); + verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX)); // ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...) nat.stop(); - verify(mNms).unregisterObserver(eq(nat)); verify(mNetd).clatdStop(eq(BASE_IFACE)); + verify(mNms).unregisterObserver(eq(nat)); + verify(mNetd).resolverStopPrefix64Discovery(eq(NETID)); assertIdle(nat); // In-flight interface up notification arrives: no-op nat.interfaceLinkStateChanged(STACKED_IFACE, true); mLooper.dispatchNext(); - // Interface removed notification arrives after stopClatd() takes effect: no-op. nat.interfaceRemoved(STACKED_IFACE); mLooper.dispatchNext(); @@ -232,17 +377,19 @@ public class Nat464XlatTest { public void testStopAndClatdNeverStarts() throws Exception { Nat464Xlat nat = makeNat464Xlat(); - // ConnectivityService starts clat. + nat.setNat64Prefix(new IpPrefix(NAT64_PREFIX)); + nat.start(); verify(mNms).registerObserver(eq(nat)); - verify(mNetd).clatdStart(eq(BASE_IFACE)); + verify(mNetd).clatdStart(eq(BASE_IFACE), eq(NAT64_PREFIX)); // ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...) nat.stop(); - verify(mNms).unregisterObserver(eq(nat)); verify(mNetd).clatdStop(eq(BASE_IFACE)); + verify(mNms).unregisterObserver(eq(nat)); + verify(mNetd).resolverStopPrefix64Discovery(eq(NETID)); assertIdle(nat); verifyNoMoreInteractions(mNetd, mNms, mConnectivity); diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp index 0032960ff93e..ffc1a92a88b0 100644 --- a/tools/aapt2/ResourceUtils.cpp +++ b/tools/aapt2/ResourceUtils.cpp @@ -44,6 +44,7 @@ constexpr int32_t kNonBreakingSpace = 0xa0; Maybe<ResourceName> ToResourceName( const android::ResTable::resource_name& name_in) { + // TODO: Remove this when ResTable and AssetManager(1) are removed from AAPT2 ResourceName name_out; if (!name_in.package) { return {}; @@ -79,6 +80,41 @@ Maybe<ResourceName> ToResourceName( return name_out; } +Maybe<ResourceName> ToResourceName(const android::AssetManager2::ResourceName& name_in) { + ResourceName name_out; + if (!name_in.package) { + return {}; + } + + name_out.package = std::string(name_in.package, name_in.package_len); + + const ResourceType* type; + if (name_in.type16) { + type = ParseResourceType( + util::Utf16ToUtf8(StringPiece16(name_in.type16, name_in.type_len))); + } else if (name_in.type) { + type = ParseResourceType(StringPiece(name_in.type, name_in.type_len)); + } else { + return {}; + } + + if (!type) { + return {}; + } + + name_out.type = *type; + + if (name_in.entry16) { + name_out.entry = + util::Utf16ToUtf8(StringPiece16(name_in.entry16, name_in.entry_len)); + } else if (name_in.entry) { + name_out.entry = std::string(name_in.entry, name_in.entry_len); + } else { + return {}; + } + return name_out; +} + bool ParseResourceName(const StringPiece& str, ResourceNameRef* out_ref, bool* out_private) { if (str.empty()) { diff --git a/tools/aapt2/ResourceUtils.h b/tools/aapt2/ResourceUtils.h index e282fd58d261..a8a312005e4e 100644 --- a/tools/aapt2/ResourceUtils.h +++ b/tools/aapt2/ResourceUtils.h @@ -20,6 +20,7 @@ #include <functional> #include <memory> +#include "androidfw/AssetManager2.h" #include "androidfw/ConfigDescription.h" #include "androidfw/ResourceTypes.h" #include "androidfw/StringPiece.h" @@ -78,6 +79,12 @@ Maybe<ResourceName> ToResourceName( const android::ResTable::resource_name& name); /** + * Convert an android::AssetManager2::ResourceName to an aapt::ResourceName struct. + */ +Maybe<ResourceName> ToResourceName( + const android::AssetManager2::ResourceName& name_in); + +/** * Returns a boolean value if the string is equal to TRUE, true, True, FALSE, * false, or False. */ diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp index 0512bdc5bf72..bec6c6973613 100644 --- a/tools/aapt2/cmd/Compile.cpp +++ b/tools/aapt2/cmd/Compile.cpp @@ -769,7 +769,10 @@ int CompileCommand::Action(const std::vector<std::string>& args) { auto collection = util::make_unique<io::FileCollection>(); // Collect data from the path for each input file. - for (const std::string& arg : args) { + std::vector<std::string> sorted_args = args; + std::sort(sorted_args.begin(), sorted_args.end()); + + for (const std::string& arg : sorted_args) { collection->InsertFile(arg); } diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp index 22edd2f2055a..a7b8d2535e79 100644 --- a/tools/aapt2/cmd/Link.cpp +++ b/tools/aapt2/cmd/Link.cpp @@ -717,28 +717,20 @@ static bool LoadStableIdMap(IDiagnostics* diag, const std::string& path, return true; } -static int32_t FindFrameworkAssetManagerCookie(const android::AssetManager& assets) { +static android::ApkAssetsCookie FindFrameworkAssetManagerCookie( + const android::AssetManager2& assets) { using namespace android; // Find the system package (0x01). AAPT always generates attributes with the type 0x01, so // we're looking for the first attribute resource in the system package. - const ResTable& table = assets.getResources(true); - Res_value val; - ssize_t idx = table.getResource(0x01010000, &val, true); - if (idx != NO_ERROR) { - // Try as a bag. - const ResTable::bag_entry* entry; - ssize_t cnt = table.lockBag(0x01010000, &entry); - if (cnt >= 0) { - idx = entry->stringBlock; - } - table.unlockBag(entry); - } - - if (idx < 0) { - return 0; - } - return table.getTableCookie(idx); + Res_value val{}; + ResTable_config config{}; + uint32_t type_spec_flags; + ApkAssetsCookie idx = assets.GetResource(0x01010000, true /** may_be_bag */, + 0 /** density_override */, &val, &config, + &type_spec_flags); + + return idx; } class Linker { @@ -750,17 +742,17 @@ class Linker { file_collection_(util::make_unique<io::FileCollection>()) { } - void ExtractCompileSdkVersions(android::AssetManager* assets) { + void ExtractCompileSdkVersions(android::AssetManager2* assets) { using namespace android; - int32_t cookie = FindFrameworkAssetManagerCookie(*assets); - if (cookie == 0) { + android::ApkAssetsCookie cookie = FindFrameworkAssetManagerCookie(*assets); + if (cookie == android::kInvalidCookie) { // No Framework assets loaded. Not a failure. return; } std::unique_ptr<Asset> manifest( - assets->openNonAsset(cookie, kAndroidManifestPath, Asset::AccessMode::ACCESS_BUFFER)); + assets->OpenNonAsset(kAndroidManifestPath, cookie, Asset::AccessMode::ACCESS_BUFFER)); if (manifest == nullptr) { // No errors. return; diff --git a/tools/aapt2/io/FileSystem.cpp b/tools/aapt2/io/FileSystem.cpp index 51cc9032fb3e..e15f935cad27 100644 --- a/tools/aapt2/io/FileSystem.cpp +++ b/tools/aapt2/io/FileSystem.cpp @@ -79,6 +79,7 @@ std::unique_ptr<FileCollection> FileCollection::Create(const android::StringPiec return nullptr; } + std::vector<std::string> sorted_files; while (struct dirent *entry = readdir(d.get())) { std::string prefix_path = root.to_string(); file::AppendPath(&prefix_path, entry->d_name); @@ -105,10 +106,15 @@ std::unique_ptr<FileCollection> FileCollection::Create(const android::StringPiec continue; } - collection->InsertFile(full_path); + sorted_files.push_back(full_path); } } + std::sort(sorted_files.begin(), sorted_files.end()); + for (const std::string& full_path : sorted_files) { + collection->InsertFile(full_path); + } + return collection; } diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp index a844a43698e5..78e00746f6cb 100644 --- a/tools/aapt2/process/SymbolTable.cpp +++ b/tools/aapt2/process/SymbolTable.cpp @@ -20,9 +20,11 @@ #include "android-base/logging.h" #include "android-base/stringprintf.h" -#include "androidfw/AssetManager.h" +#include "androidfw/Asset.h" +#include "androidfw/AssetManager2.h" #include "androidfw/ConfigDescription.h" #include "androidfw/ResourceTypes.h" +#include "androidfw/ResourceUtils.h" #include "NameMangler.h" #include "Resource.h" @@ -30,6 +32,7 @@ #include "ValueVisitor.h" #include "util/Util.h" +using ::android::ApkAssets; using ::android::ConfigDescription; using ::android::StringPiece; using ::android::StringPiece16; @@ -214,51 +217,75 @@ std::unique_ptr<SymbolTable::Symbol> ResourceTableSymbolSource::FindByName( } bool AssetManagerSymbolSource::AddAssetPath(const StringPiece& path) { - int32_t cookie = 0; - return assets_.addAssetPath(android::String8(path.data(), path.size()), &cookie); + if (std::unique_ptr<const ApkAssets> apk = ApkAssets::Load(path.data())) { + apk_assets_.push_back(std::move(apk)); + + std::vector<const ApkAssets*> apk_assets; + for (const std::unique_ptr<const ApkAssets>& apk_asset : apk_assets_) { + apk_assets.push_back(apk_asset.get()); + } + + asset_manager_.SetApkAssets(apk_assets, true /* invalidate_caches */, + false /* filter_incompatible_configs */); + return true; + } + return false; } std::map<size_t, std::string> AssetManagerSymbolSource::GetAssignedPackageIds() const { std::map<size_t, std::string> package_map; - const android::ResTable& table = assets_.getResources(false); - const size_t package_count = table.getBasePackageCount(); - for (size_t i = 0; i < package_count; i++) { - package_map[table.getBasePackageId(i)] = - util::Utf16ToUtf8(android::StringPiece16(table.getBasePackageName(i).string())); - } + asset_manager_.ForEachPackage([&package_map](const std::string& name, uint8_t id) -> bool { + package_map.insert(std::make_pair(id, name)); + return true; + }); + return package_map; } bool AssetManagerSymbolSource::IsPackageDynamic(uint32_t packageId) const { - return assets_.getResources(false).isPackageDynamic(packageId); + if (packageId == 0) { + return true; + } + + for (const std::unique_ptr<const ApkAssets>& assets : apk_assets_) { + for (const std::unique_ptr<const android::LoadedPackage>& loaded_package + : assets->GetLoadedArsc()->GetPackages()) { + if (packageId == loaded_package->GetPackageId() && loaded_package->IsDynamic()) { + return true; + } + } + } + + return false; } static std::unique_ptr<SymbolTable::Symbol> LookupAttributeInTable( - const android::ResTable& table, ResourceId id) { - // Try as a bag. - const android::ResTable::bag_entry* entry; - ssize_t count = table.lockBag(id.id, &entry); - if (count < 0) { - table.unlockBag(entry); + android::AssetManager2& am, ResourceId id) { + if (am.GetApkAssets().empty()) { + return {}; + } + + const android::ResolvedBag* bag = am.GetBag(id.id); + if (bag == nullptr) { return nullptr; } // We found a resource. std::unique_ptr<SymbolTable::Symbol> s = util::make_unique<SymbolTable::Symbol>(id); - // Check to see if it is an attribute. - for (size_t i = 0; i < (size_t)count; i++) { - if (entry[i].map.name.ident == android::ResTable_map::ATTR_TYPE) { - s->attribute = std::make_shared<Attribute>(entry[i].map.value.data); + const size_t count = bag->entry_count; + for (uint32_t i = 0; i < count; i++) { + if (bag->entries[i].key == android::ResTable_map::ATTR_TYPE) { + s->attribute = std::make_shared<Attribute>(bag->entries[i].value.data); break; } } if (s->attribute) { - for (size_t i = 0; i < (size_t)count; i++) { - const android::ResTable_map& map_entry = entry[i].map; - if (Res_INTERNALID(map_entry.name.ident)) { - switch (map_entry.name.ident) { + for (size_t i = 0; i < count; i++) { + const android::ResolvedBag::Entry& map_entry = bag->entries[i]; + if (Res_INTERNALID(map_entry.key)) { + switch (map_entry.key) { case android::ResTable_map::ATTR_MIN: s->attribute->min_int = static_cast<int32_t>(map_entry.value.data); break; @@ -269,74 +296,65 @@ static std::unique_ptr<SymbolTable::Symbol> LookupAttributeInTable( continue; } - android::ResTable::resource_name entry_name; - if (!table.getResourceName(map_entry.name.ident, false, &entry_name)) { - table.unlockBag(entry); + android::AssetManager2::ResourceName name; + if (!am.GetResourceName(map_entry.key, &name)) { return nullptr; } - Maybe<ResourceName> parsed_name = ResourceUtils::ToResourceName(entry_name); + Maybe<ResourceName> parsed_name = ResourceUtils::ToResourceName(name); if (!parsed_name) { return nullptr; } Attribute::Symbol symbol; symbol.symbol.name = parsed_name.value(); - symbol.symbol.id = ResourceId(map_entry.name.ident); + symbol.symbol.id = ResourceId(map_entry.key); symbol.value = map_entry.value.data; s->attribute->symbols.push_back(std::move(symbol)); } } - table.unlockBag(entry); + return s; } std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindByName( const ResourceName& name) { - const android::ResTable& table = assets_.getResources(false); - - const std::u16string package16 = util::Utf8ToUtf16(name.package); - const std::u16string type16 = util::Utf8ToUtf16(to_string(name.type)); - const std::u16string entry16 = util::Utf8ToUtf16(name.entry); - const std::u16string mangled_entry16 = - util::Utf8ToUtf16(NameMangler::MangleEntry(name.package, name.entry)); + const std::string mangled_entry = NameMangler::MangleEntry(name.package, name.entry); + bool found = false; + ResourceId res_id = 0; uint32_t type_spec_flags; - ResourceId res_id; // There can be mangled resources embedded within other packages. Here we will // look into each package and look-up the mangled name until we find the resource. - const size_t count = table.getBasePackageCount(); - for (size_t i = 0; i < count; i++) { - const android::String16 package_name = table.getBasePackageName(i); - StringPiece16 real_package16 = package16; - StringPiece16 real_entry16 = entry16; - std::u16string scratch_entry16; - if (StringPiece16(package_name) != package16) { - real_entry16 = mangled_entry16; - real_package16 = package_name.string(); + asset_manager_.ForEachPackage([&](const std::string& package_name, uint8_t id) -> bool { + ResourceName real_name(name.package, name.type, name.entry); + + if (package_name != name.package) { + real_name.entry = mangled_entry; + real_name.package = package_name; } - type_spec_flags = 0; - res_id = table.identifierForName(real_entry16.data(), real_entry16.size(), type16.data(), - type16.size(), real_package16.data(), real_package16.size(), - &type_spec_flags); - if (res_id.is_valid()) { - break; + res_id = asset_manager_.GetResourceId(real_name.to_string()); + if (res_id.is_valid() && asset_manager_.GetResourceFlags(res_id.id, &type_spec_flags)) { + found = true; + return false; } - } - if (!res_id.is_valid()) { + return true; + }); + + if (!found) { return {}; } std::unique_ptr<SymbolTable::Symbol> s; if (name.type == ResourceType::kAttr) { - s = LookupAttributeInTable(table, res_id); + s = LookupAttributeInTable(asset_manager_, res_id); } else { s = util::make_unique<SymbolTable::Symbol>(); s->id = res_id; - s->is_dynamic = table.isResourceDynamic(res_id.id); + s->is_dynamic = IsPackageDynamic(ResourceId(res_id).package_id()); } if (s) { @@ -346,13 +364,13 @@ std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindByName( return {}; } -static Maybe<ResourceName> GetResourceName(const android::ResTable& table, +static Maybe<ResourceName> GetResourceName(android::AssetManager2& am, ResourceId id) { - android::ResTable::resource_name res_name = {}; - if (!table.getResourceName(id.id, true, &res_name)) { + android::AssetManager2::ResourceName name; + if (!am.GetResourceName(id.id, &name)) { return {}; } - return ResourceUtils::ToResourceName(res_name); + return ResourceUtils::ToResourceName(name); } std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindById( @@ -361,22 +379,30 @@ std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindById( // Exit early and avoid the error logs from AssetManager. return {}; } - const android::ResTable& table = assets_.getResources(false); - Maybe<ResourceName> maybe_name = GetResourceName(table, id); + + if (apk_assets_.empty()) { + return {}; + } + + Maybe<ResourceName> maybe_name = GetResourceName(asset_manager_, id); if (!maybe_name) { return {}; } + uint32_t type_spec_flags = 0; - table.getResourceFlags(id.id, &type_spec_flags); + if (!asset_manager_.GetResourceFlags(id.id, &type_spec_flags)) { + return {}; + } + ResourceName& name = maybe_name.value(); std::unique_ptr<SymbolTable::Symbol> s; - if (maybe_name.value().type == ResourceType::kAttr) { - s = LookupAttributeInTable(table, id); + if (name.type == ResourceType::kAttr) { + s = LookupAttributeInTable(asset_manager_, id); } else { s = util::make_unique<SymbolTable::Symbol>(); s->id = id; - s->is_dynamic = table.isResourceDynamic(id.id); + s->is_dynamic = IsPackageDynamic(ResourceId(id).package_id()); } if (s) { diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h index 2d8bd02a3799..6997cd6714a8 100644 --- a/tools/aapt2/process/SymbolTable.h +++ b/tools/aapt2/process/SymbolTable.h @@ -22,7 +22,8 @@ #include <vector> #include "android-base/macros.h" -#include "androidfw/AssetManager.h" +#include "androidfw/Asset.h" +#include "androidfw/AssetManager2.h" #include "utils/JenkinsHash.h" #include "utils/LruCache.h" @@ -201,12 +202,13 @@ class AssetManagerSymbolSource : public ISymbolSource { std::unique_ptr<SymbolTable::Symbol> FindByReference( const Reference& ref) override; - android::AssetManager* GetAssetManager() { - return &assets_; + android::AssetManager2* GetAssetManager() { + return &asset_manager_; } private: - android::AssetManager assets_; + android::AssetManager2 asset_manager_; + std::vector<std::unique_ptr<const android::ApkAssets>> apk_assets_; DISALLOW_COPY_AND_ASSIGN(AssetManagerSymbolSource); }; diff --git a/tools/aapt2/process/SymbolTable_test.cpp b/tools/aapt2/process/SymbolTable_test.cpp index 1f59d7034300..ddc210189793 100644 --- a/tools/aapt2/process/SymbolTable_test.cpp +++ b/tools/aapt2/process/SymbolTable_test.cpp @@ -76,40 +76,54 @@ TEST(SymbolTableTest, FindByName) { EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("com.android.lib:id/foo")), NotNull()); } -TEST(SymbolTableTest, FindByNameWhenSymbolIsMangledInResTable) { +using SymbolTableTestFixture = CommandTestFixture; +TEST_F(SymbolTableTestFixture, FindByNameWhenSymbolIsMangledInResTable) { using namespace android; - - std::unique_ptr<IAaptContext> context = - test::ContextBuilder() - .SetCompilationPackage("com.android.app") - .SetPackageId(0x7f) - .SetPackageType(PackageType::kApp) - .SetMinSdkVersion(SDK_LOLLIPOP_MR1) - .SetNameManglerPolicy(NameManglerPolicy{"com.android.app"}) - .Build(); - - // Create a ResourceTable with a mangled resource, simulating a static library being merged into - // the main application package. - std::unique_ptr<ResourceTable> table = - test::ResourceTableBuilder() - .AddSimple("com.android.app:id/" + NameMangler::MangleEntry("com.android.lib", "foo"), - ResourceId(0x7f020000)) - .AddSimple("com.android.app:id/bar", ResourceId(0x7f020001)) - .Build(); - - BigBuffer buffer(1024u); - TableFlattener flattener({}, &buffer); - ASSERT_TRUE(flattener.Consume(context.get(), table.get())); - - std::unique_ptr<uint8_t[]> data = util::Copy(buffer); + StdErrDiagnostics diag; + + // Create a static library. + const std::string static_lib_compiled_files_dir = GetTestPath("static-lib-compiled"); + ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), + R"(<?xml version="1.0" encoding="utf-8"?> + <resources> + <item type="id" name="foo"/> + </resources>)", + static_lib_compiled_files_dir, &diag)); + + const std::string static_lib_apk = GetTestPath("static_lib.apk"); + std::vector<std::string> link_args = { + "--manifest", GetDefaultManifest("com.android.lib"), + "--min-sdk-version", "22", + "--static-lib", + "-o", static_lib_apk, + }; + ASSERT_TRUE(Link(link_args, static_lib_compiled_files_dir, &diag)); + + // Merge the static library into the main application package. The static library resources will + // be mangled with the library package name. + const std::string app_compiled_files_dir = GetTestPath("app-compiled"); + ASSERT_TRUE(CompileFile(GetTestPath("res/values/values.xml"), + R"(<?xml version="1.0" encoding="utf-8"?> + <resources> + <item type="id" name="bar"/> + </resources>)", + app_compiled_files_dir, &diag)); + + const std::string out_apk = GetTestPath("out.apk"); + link_args = { + "--manifest", GetDefaultManifest("com.android.app"), + "--min-sdk-version", "22", + "-o", out_apk, + static_lib_apk + }; + ASSERT_TRUE(Link(link_args, app_compiled_files_dir, &diag)); // Construct the test AssetManager. auto asset_manager_source = util::make_unique<AssetManagerSymbolSource>(); - ResTable& res_table = const_cast<ResTable&>( - asset_manager_source->GetAssetManager()->getResources(false /*required*/)); - ASSERT_THAT(res_table.add(data.get(), buffer.size()), Eq(NO_ERROR)); + asset_manager_source->AddAssetPath(out_apk); - SymbolTable symbol_table(context->GetNameMangler()); + NameMangler name_mangler(NameManglerPolicy{"com.android.app"}); + SymbolTable symbol_table(&name_mangler); symbol_table.AppendSource(std::move(asset_manager_source)); EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("com.android.lib:id/foo")), NotNull()); diff --git a/tools/aapt2/test/Fixture.cpp b/tools/aapt2/test/Fixture.cpp index 3fcdfb70a524..a51b4a4649f1 100644 --- a/tools/aapt2/test/Fixture.cpp +++ b/tools/aapt2/test/Fixture.cpp @@ -37,6 +37,8 @@ using testing::Ne; namespace aapt { +const char* CommandTestFixture::kDefaultPackageName = "com.aapt.command.test"; + void ClearDirectory(const android::StringPiece& path) { const std::string root_dir = path.to_string(); std::unique_ptr<DIR, decltype(closedir)*> dir(opendir(root_dir.data()), closedir); @@ -124,12 +126,12 @@ bool CommandTestFixture::Link(const std::vector<std::string>& args, return LinkCommand(diag).Execute(link_args, &std::cerr) == 0; } -std::string CommandTestFixture::GetDefaultManifest() { +std::string CommandTestFixture::GetDefaultManifest(const char* package_name) { const std::string manifest_file = GetTestPath("AndroidManifest.xml"); - CHECK(WriteFile(manifest_file, R"( + CHECK(WriteFile(manifest_file, android::base::StringPrintf(R"( <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.aapt.command.test"> - </manifest>)")); + package="%s"> + </manifest>)", package_name))); return manifest_file; } diff --git a/tools/aapt2/test/Fixture.h b/tools/aapt2/test/Fixture.h index 3079c757f61a..fce2aebfecaa 100644 --- a/tools/aapt2/test/Fixture.h +++ b/tools/aapt2/test/Fixture.h @@ -81,7 +81,7 @@ class CommandTestFixture : public TestDirectoryFixture { IDiagnostics* diag); // Creates a minimal android manifest within the test directory and returns the file path. - std::string GetDefaultManifest(); + std::string GetDefaultManifest(const char* package_name = kDefaultPackageName); // Returns pointer to data inside APK files std::unique_ptr<io::IData> OpenFileAsData(LoadedApk* apk, @@ -91,6 +91,7 @@ class CommandTestFixture : public TestDirectoryFixture { void AssertLoadXml(LoadedApk* apk, const io::IData* data, android::ResXMLTree* out_tree); + static const char* kDefaultPackageName; private: DISALLOW_COPY_AND_ASSIGN(CommandTestFixture); }; diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp index 604b2575daa1..5d57de6a9fb1 100644 --- a/tools/aapt2/util/Files.cpp +++ b/tools/aapt2/util/Files.cpp @@ -102,12 +102,21 @@ FileType GetFileType(const std::string& path) { #endif bool mkdirs(const std::string& path) { - #ifdef _WIN32 - // Start after the drive path if present. Calling mkdir with only the drive will cause an error. - size_t current_pos = 1u; - if (path.size() >= 3 && path[1] == ':' && - (path[2] == '\\' || path[2] == '/')) { - current_pos = 3u; + #ifdef _WIN32 + // Start after the long path prefix if present. + bool require_drive = false; + size_t current_pos = 0u; + if (util::StartsWith(path, R"(\\?\)")) { + require_drive = true; + current_pos = 4u; + } + + // Start after the drive path if present. + if (path.size() >= 3 && path[current_pos + 1] == ':' && + (path[current_pos + 2] == '\\' || path[current_pos + 2] == '/')) { + current_pos += 3u; + } else if (require_drive) { + return false; } #else // Start after the first character so that we don't consume the root '/'. diff --git a/tools/aapt2/util/Files_test.cpp b/tools/aapt2/util/Files_test.cpp index 202cc261ad89..6c380808c0df 100644 --- a/tools/aapt2/util/Files_test.cpp +++ b/tools/aapt2/util/Files_test.cpp @@ -19,6 +19,7 @@ #include <sstream> #include "android-base/stringprintf.h" +#include "android-base/utf8.h" #include "test/Test.h" @@ -65,5 +66,40 @@ TEST_F(FilesTest, AppendPathWithLeadingOrTrailingSeparators) { EXPECT_EQ(expected_path_, base); } +#ifdef _WIN32 +TEST_F(FilesTest, WindowsMkdirsLongPath) { + // Creating directory paths longer than the Windows maximum path length (260 charatcers) should + // succeed. + const std::string kDirName = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + const size_t kRecursiveDepth = 10u; + + // Recursively create the test file path and clean up the created directories after the files have + // been created. + std::function<void(std::string, size_t)> CreateResursiveDirs = + [&kDirName, &CreateResursiveDirs](std::string current_path, const size_t n) -> void { + AppendPath(¤t_path, kDirName); + + if (n == 0) { + ASSERT_TRUE(file::mkdirs(current_path)) << "Failed to create path " << current_path; + } else { + CreateResursiveDirs(current_path, n - 1); + } + + // Clean up the created directories. + _rmdir(current_path.data()); + }; + + CreateResursiveDirs( + android::base::StringPrintf(R"(\\?\%s)", android::base::GetExecutableDirectory().data()), + kRecursiveDepth); +} + +TEST_F(FilesTest, WindowsMkdirsLongPathMissingDrive) { + ASSERT_FALSE(file::mkdirs(R"(\\?\local\path\to\file)")); + ASSERT_FALSE(file::mkdirs(R"(\\?\:local\path\to\file)")); + ASSERT_FALSE(file::mkdirs(R"(\\?\\local\path\to\file)")); +} +#endif + } // namespace files } // namespace aapt diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py index 59e89f515e82..295e3de544ee 100644 --- a/tools/apilint/apilint.py +++ b/tools/apilint/apilint.py @@ -79,6 +79,7 @@ class Field(): self.value = raw[3].strip(';"') else: self.value = None + self.annotations = [] self.ident = "-".join((self.typ, self.name, self.value or "")) @@ -88,6 +89,18 @@ class Field(): def __repr__(self): return self.raw + +class Argument(object): + + __slots__ = ["type", "annotations", "name", "default"] + + def __init__(self, type): + self.type = type + self.annotations = [] + self.name = None + self.default = None + + class Method(): def __init__(self, clazz, line, raw, blame, sig_format = 1): self.clazz = clazz @@ -118,21 +131,24 @@ class Method(): self.name = raw[1] # parse args - self.args = [] + self.detailed_args = [] for arg in re.split(",\s*", raw_args): arg = re.split("\s", arg) # ignore annotations for now arg = [ a for a in arg if not a.startswith("@") ] if len(arg[0]) > 0: - self.args.append(arg[0]) + self.detailed_args.append(Argument(arg[0])) # parse throws self.throws = [] for throw in re.split(",\s*", raw_throws): self.throws.append(throw) + + self.annotations = [] else: raise ValueError("Unknown signature format: " + sig_format) + self.args = map(lambda a: a.type, self.detailed_args) self.ident = "-".join((self.typ, self.name, "-".join(self.args))) def sig_matches(self, typ, name, args): @@ -208,13 +224,14 @@ class Class(): class Package(): + NAME = re.compile("package(?: .*)? ([A-Za-z.]+)") + def __init__(self, line, raw, blame): self.line = line self.raw = raw.strip(" {;") self.blame = blame - raw = raw.split() - self.name = raw[raw.index("package")+1] + self.name = Package.NAME.match(raw).group(1) self.name_path = self.name.split(".") def __repr__(self): @@ -311,10 +328,10 @@ class V2LineParser(object): method.split = [] kind = self.parse_one_of("ctor", "method") method.split.append(kind) - annotations = self.parse_annotations() + method.annotations = self.parse_annotations() method.split.extend(self.parse_modifiers()) self.parse_matching_paren("<", ">") - if "@Deprecated" in annotations: + if "@Deprecated" in method.annotations: method.split.append("deprecated") if kind == "ctor": method.typ = "ctor" @@ -324,7 +341,7 @@ class V2LineParser(object): method.name = self.parse_name() method.split.append(method.name) self.parse_token("(") - method.args = self.parse_args() + method.detailed_args = self.parse_args() self.parse_token(")") method.throws = self.parse_throws() if "@interface" in method.clazz.split: @@ -359,8 +376,8 @@ class V2LineParser(object): def parse_into_field(self, field): kind = self.parse_one_of(*V2LineParser.FIELD_KINDS) field.split = [kind] - annotations = self.parse_annotations() - if "@Deprecated" in annotations: + field.annotations = self.parse_annotations() + if "@Deprecated" in field.annotations: field.split.append("deprecated") field.split.extend(self.parse_modifiers()) field.typ = self.parse_type() @@ -487,15 +504,16 @@ class V2LineParser(object): def parse_arg(self): self.parse_if("vararg") # kotlin vararg - self.parse_annotations() - type = self.parse_arg_type() + annotations = self.parse_annotations() + arg = Argument(self.parse_arg_type()) + arg.annotations = annotations l = self.lookahead() if l != "," and l != ")": if self.lookahead() != '=': - self.parse_token() # kotlin argument name + arg.name = self.parse_token() # kotlin argument name if self.parse_if('='): # kotlin default value - self.parse_expression() - return type + arg.default = self.parse_expression() + return arg def parse_expression(self): while not self.lookahead() in [')', ',', ';']: @@ -592,7 +610,7 @@ def _parse_stream_to_generator(f): blame = None sig_format = 1 - re_blame = re.compile("^([a-z0-9]{7,}) \(<([^>]+)>.+?\) (.+?)$") + re_blame = re.compile(r"^(\^?[a-z0-9]{7,}) \(<([^>]+)>.+?\) (.+?)$") field_prefixes = map(lambda kind: " %s" % (kind,), V2LineParser.FIELD_KINDS) def startsWithFieldPrefix(raw): @@ -607,11 +625,13 @@ def _parse_stream_to_generator(f): match = re_blame.match(raw) if match is not None: blame = match.groups()[0:2] + if blame[0].startswith("^"): # Outside of blame range + blame = None raw = match.groups()[2] else: blame = None - if line == 1 and raw.startswith("// Signature format: "): + if line == 1 and V2Tokenizer.SIGNATURE_PREFIX in raw: sig_format_string = raw[len(V2Tokenizer.SIGNATURE_PREFIX):] if sig_format_string in ["2.0", "3.0"]: sig_format = 2 @@ -1108,6 +1128,9 @@ def verify_builder(clazz): if not has_build: warn(clazz, None, None, "Missing build() method") + if "final" not in clazz.split: + error(clazz, None, None, "Builder should be final") + def verify_aidl(clazz): """Catch people exposing raw AIDL.""" @@ -1867,6 +1890,35 @@ def verify_numbers(clazz): if arg in discouraged: warn(clazz, m, "FW12", "Should avoid odd sized primitives; use int instead") +PRIMITIVES = {"void", "int", "float", "boolean", "short", "char", "byte", "long", "double"} + +def verify_nullability(clazz): + """Catches missing nullability annotations""" + + for f in clazz.fields: + if f.value is not None and 'static' in f.split and 'final' in f.split: + continue # Nullability of constants can be inferred. + if f.typ not in PRIMITIVES and not has_nullability(f.annotations): + error(clazz, f, "M12", "Field must be marked either @NonNull or @Nullable") + + for c in clazz.ctors: + verify_nullability_args(clazz, c) + + for m in clazz.methods: + if m.name == "writeToParcel" or m.name == "onReceive": + continue # Parcelable.writeToParcel() and BroadcastReceiver.onReceive() are not yet annotated + + if m.typ not in PRIMITIVES and not has_nullability(m.annotations): + error(clazz, m, "M12", "Return value must be marked either @NonNull or @Nullable") + verify_nullability_args(clazz, m) + +def verify_nullability_args(clazz, m): + for i, arg in enumerate(m.detailed_args): + if arg.type not in PRIMITIVES and not has_nullability(arg.annotations): + error(clazz, m, "M12", "Argument %d must be marked either @NonNull or @Nullable" % (i+1,)) + +def has_nullability(annotations): + return "@NonNull" in annotations or "@Nullable" in annotations def verify_singleton(clazz): """Catch singleton objects with constructors.""" @@ -1955,6 +2007,7 @@ def examine_clazz(clazz): verify_pfd(clazz) verify_numbers(clazz) verify_singleton(clazz) + verify_nullability(clazz) def examine_stream(stream, base_stream=None, in_classes_with_base=[], out_classes_with_base=None): diff --git a/tools/apilint/apilint_test.py b/tools/apilint/apilint_test.py index 3716bf9f5d6f..f34492d644ce 100644 --- a/tools/apilint/apilint_test.py +++ b/tools/apilint/apilint_test.py @@ -88,20 +88,22 @@ class UtilTests(unittest.TestCase): faulty_current_txt = """ +// Signature format: 2.0 package android.app { public final class Activity { } public final class WallpaperColors implements android.os.Parcelable { - ctor public WallpaperColors(android.os.Parcel); + ctor public WallpaperColors(@NonNull android.os.Parcel); method public int describeContents(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR; + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR; } } -""".split('\n') +""".strip().split('\n') ok_current_txt = """ +// Signature format: 2.0 package android.app { public final class Activity { } @@ -109,19 +111,20 @@ package android.app { public final class WallpaperColors implements android.os.Parcelable { ctor public WallpaperColors(); method public int describeContents(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR; + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR; } } -""".split('\n') +""".strip().split('\n') system_current_txt = """ +// Signature format: 2.0 package android.app { public final class WallpaperColors implements android.os.Parcelable { method public int getSomething(); } } -""".split('\n') +""".strip().split('\n') @@ -369,5 +372,21 @@ class V2ParserTests(unittest.TestCase): m = self._method('method @NonNull public @NonNull String @NonNull [] split(@NonNull String, int);') self.assertEquals('java.lang.String[]', m.typ) +class PackageTests(unittest.TestCase): + def _package(self, raw): + return apilint.Package(123, raw, "blame") + + def test_regular_package(self): + p = self._package("package an.pref.int {") + self.assertEquals('an.pref.int', p.name) + + def test_annotation_package(self): + p = self._package("package @RestrictTo(a.b.C) an.pref.int {") + self.assertEquals('an.pref.int', p.name) + + def test_multi_annotation_package(self): + p = self._package("package @Rt(a.b.L_G_P) @RestrictTo(a.b.C) an.pref.int {") + self.assertEquals('an.pref.int', p.name) + if __name__ == "__main__": unittest.main() diff --git a/tools/bit/main.cpp b/tools/bit/main.cpp index 860094aef1f4..082ccf38299b 100644 --- a/tools/bit/main.cpp +++ b/tools/bit/main.cpp @@ -33,6 +33,8 @@ using namespace std; +#define NATIVE_TESTS "NATIVE_TESTS" + /** * An entry from the command line for something that will be built, installed, * and/or tested. @@ -132,6 +134,32 @@ InstallApk::InstallApk(const string& filename, bool always) { } +struct PushedFile +{ + TrackedFile file; + string dest; + + PushedFile(); + PushedFile(const PushedFile& that); + PushedFile(const string& filename, const string& dest); + ~PushedFile() {}; +}; + +PushedFile::PushedFile() +{ +} + +PushedFile::PushedFile(const PushedFile& that) + :file(that.file), + dest(that.dest) +{ +} + +PushedFile::PushedFile(const string& f, const string& d) + :file(f), + dest(d) +{ +} /** * Record for an test that is going to be launched. @@ -658,12 +686,14 @@ run_phases(vector<Target*> targets, const Options& options) } // Figure out whether we need to sync the system and which apks to install - string systemPath = buildOut + "/target/product/" + buildDevice + "/system/"; - string dataPath = buildOut + "/target/product/" + buildDevice + "/data/"; + string deviceTargetPath = buildOut + "/target/product/" + buildDevice; + string systemPath = deviceTargetPath + "/system/"; + string dataPath = deviceTargetPath + "/data/"; bool syncSystem = false; bool alwaysSyncSystem = false; vector<string> systemFiles; vector<InstallApk> installApks; + vector<PushedFile> pushedFiles; for (size_t i=0; i<targets.size(); i++) { Target* target = targets[i]; if (target->install) { @@ -687,6 +717,11 @@ run_phases(vector<Target*> targets, const Options& options) installApks.push_back(InstallApk(file, !target->build)); continue; } + // If it's a native test module, push it. + if (target->module.HasClass(NATIVE_TESTS) && starts_with(file, dataPath)) { + string installedPath(file.c_str() + deviceTargetPath.length()); + pushedFiles.push_back(PushedFile(file, installedPath)); + } } } } @@ -701,6 +736,13 @@ run_phases(vector<Target*> targets, const Options& options) printf(" %s\n", systemFiles[i].c_str()); } } + if (pushedFiles.size() > 0){ + print_info("Files to push:"); + for (size_t i=0; i<pushedFiles.size(); i++) { + printf(" %s\n", pushedFiles[i].file.filename.c_str()); + printf(" --> %s\n", pushedFiles[i].dest.c_str()); + } + } if (installApks.size() > 0){ print_info("APKs to install:"); for (size_t i=0; i<installApks.size(); i++) { @@ -784,6 +826,25 @@ run_phases(vector<Target*> targets, const Options& options) } } + // Push files + if (pushedFiles.size() > 0) { + print_status("Pushing files"); + for (size_t i=0; i<pushedFiles.size(); i++) { + const PushedFile& pushed = pushedFiles[i]; + string dir = dirname(pushed.dest); + if (dir.length() == 0 || dir == "/") { + // This isn't really a file inside the data directory. Just skip it. + continue; + } + // TODO: if (!apk.file.fileInfo.exists || apk.file.HasChanged()) + err = run_adb("shell", "mkdir", "-p", dir.c_str(), NULL); + check_error(err); + err = run_adb("push", pushed.file.filename.c_str(), pushed.dest.c_str()); + check_error(err); + // pushed.installed = true; + } + } + // Install APKs if (installApks.size() > 0) { print_status("Installing APKs"); @@ -804,6 +865,74 @@ run_phases(vector<Target*> targets, const Options& options) // Actions // + // Whether there have been any tests run, so we can print a summary. + bool testsRun = false; + + // Run the native tests. + // TODO: We don't have a good way of running these and capturing the output of + // them live. It'll take some work. On the other hand, if they're gtest tests, + // the output of gtest is not completely insane like the text output of the + // instrumentation tests. So for now, we'll just live with that. + for (size_t i=0; i<targets.size(); i++) { + Target* target = targets[i]; + if (target->test && target->module.HasClass(NATIVE_TESTS)) { + // We don't have a clear signal from the build system which of the installed + // files is actually the test, so we guess by looking for one with the same + // leaf name as the module that is executable. + for (size_t j=0; j<target->module.installed.size(); j++) { + string filename = target->module.installed[j]; + if (!starts_with(filename, dataPath)) { + // Native tests go into the data directory. + continue; + } + if (leafname(filename) != target->module.name) { + // This isn't the test executable. + continue; + } + if (!is_executable(filename)) { + continue; + } + string installedPath(filename.c_str() + deviceTargetPath.length()); + printf("the magic one is: %s\n", filename.c_str()); + printf(" and it's installed at: %s\n", installedPath.c_str()); + + // Convert bit-style actions to gtest test filter arguments + if (target->actions.size() > 0) { + testsRun = true; + target->testActionCount++; + bool runAll = false; + string filterArg("--gtest_filter="); + for (size_t k=0; k<target->actions.size(); k++) { + string actionString = target->actions[k]; + if (actionString == "*") { + runAll = true; + } else { + filterArg += actionString; + if (k != target->actions.size()-1) { + // We would otherwise have to worry about this condition + // being true, and appending an extra ':', but we know that + // if the extra action is "*", then we'll just run all and + // won't use filterArg anyway, so just keep this condition + // simple. + filterArg += ':'; + } + } + } + if (runAll) { + err = run_adb("shell", installedPath.c_str(), NULL); + } else { + err = run_adb("shell", installedPath.c_str(), filterArg.c_str(), NULL); + } + if (err == 0) { + target->testPassCount++; + } else { + target->testFailCount++; + } + } + } + } + } + // Inspect the apks, and figure out what is an activity and what needs a test runner bool printedInspecting = false; vector<TestAction> testActions; @@ -872,6 +1001,7 @@ run_phases(vector<Target*> targets, const Options& options) TestResults testResults; if (testActions.size() > 0) { print_status("Running tests"); + testsRun = true; for (size_t i=0; i<testActions.size(); i++) { TestAction& action = testActions[i]; testResults.SetCurrentAction(&action); @@ -969,7 +1099,7 @@ run_phases(vector<Target*> targets, const Options& options) // Tests bool hasErrors = false; - if (testActions.size() > 0) { + if (testsRun) { printf("%sRan tests:%s\n", g_escapeBold, g_escapeEndColor); size_t maxNameLength = 0; for (size_t i=0; i<targets.size(); i++) { diff --git a/tools/bit/make.cpp b/tools/bit/make.cpp index 627091321b2e..df64a801e213 100644 --- a/tools/bit/make.cpp +++ b/tools/bit/make.cpp @@ -51,6 +51,18 @@ make_cache_filename(const string& outDir) return filename + "/.bit_cache"; } +bool +Module::HasClass(const string& cl) +{ + for (vector<string>::const_iterator c = classes.begin(); c != classes.end(); c++) { + if (*c == cl) { + return true; + } + } + return false; +} + + BuildVars::BuildVars(const string& outDir, const string& buildProduct, const string& buildVariant, const string& buildType) :m_filename(), diff --git a/tools/bit/make.h b/tools/bit/make.h index db0b69f88a0e..785912a6594d 100644 --- a/tools/bit/make.h +++ b/tools/bit/make.h @@ -29,6 +29,8 @@ struct Module vector<string> classes; vector<string> paths; vector<string> installed; + + bool HasClass(const string& cl); }; /** diff --git a/tools/bit/util.cpp b/tools/bit/util.cpp index a502a9dbe736..63399d69a166 100644 --- a/tools/bit/util.cpp +++ b/tools/bit/util.cpp @@ -254,4 +254,42 @@ read_file(const string& filename) return result; } +bool +is_executable(const string& filename) +{ + int err; + struct stat st; + + err = stat(filename.c_str(), &st); + if (err != 0) { + return false; + } + + return (st.st_mode & S_IXUSR) != 0; +} + +string +dirname(const string& filename) +{ + size_t slash = filename.rfind('/'); + if (slash == string::npos) { + return ""; + } else if (slash == 0) { + return "/"; + } else { + return string(filename, 0, slash); + } +} +string +leafname(const string& filename) +{ + size_t slash = filename.rfind('/'); + if (slash == string::npos) { + return filename; + } else if (slash == filename.length() - 1) { + return ""; + } else { + return string(filename, slash + 1); + } +} diff --git a/tools/bit/util.h b/tools/bit/util.h index 718f1474a969..7ccdab103d9a 100644 --- a/tools/bit/util.h +++ b/tools/bit/util.h @@ -79,5 +79,10 @@ void split_lines(vector<string>* result, const string& str); string read_file(const string& filename); +bool is_executable(const string& filename); + +string dirname(const string& filename); +string leafname(const string& filename); + #endif // UTIL_H diff --git a/tools/validatekeymaps/Main.cpp b/tools/validatekeymaps/Main.cpp index f31f771004bd..56a242f1daaf 100644 --- a/tools/validatekeymaps/Main.cpp +++ b/tools/validatekeymaps/Main.cpp @@ -128,13 +128,11 @@ static bool validateFile(const char* filename) { } case FILETYPE_VIRTUALKEYDEFINITION: { - VirtualKeyMap* map; - status_t status = VirtualKeyMap::load(filename, &map); - if (status) { - error("Error %d parsing virtual key definition file.\n\n", status); + std::unique_ptr<VirtualKeyMap> map = VirtualKeyMap::load(filename); + if (!map) { + error("Error while parsing virtual key definition file.\n\n"); return false; } - delete map; break; } } diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl index d5497990aefd..17948e7e1bc0 100644 --- a/wifi/java/android/net/wifi/IWifiManager.aidl +++ b/wifi/java/android/net/wifi/IWifiManager.aidl @@ -147,8 +147,10 @@ interface IWifiManager void stopWatchLocalOnlyHotspot(); + @UnsupportedAppUsage int getWifiApEnabledState(); + @UnsupportedAppUsage WifiConfiguration getWifiApConfiguration(); boolean setWifiApConfiguration(in WifiConfiguration wifiConfig, String packageName); @@ -173,6 +175,7 @@ interface IWifiManager void factoryReset(String packageName); + @UnsupportedAppUsage Network getCurrentNetwork(); byte[] retrieveBackupData(); diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index 3881e9e68eca..4d6ff4816621 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -16,6 +16,7 @@ package android.net.wifi; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SystemApi; import android.annotation.UnsupportedAppUsage; @@ -41,6 +42,8 @@ import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.BitSet; import java.util.HashMap; @@ -339,6 +342,90 @@ public class WifiConfiguration implements Parcelable { public static final String[] strings = { "current", "disabled", "enabled" }; } + /** + * Security types we support. + */ + /** @hide */ + public static final int SECURITY_TYPE_OPEN = 0; + /** @hide */ + public static final int SECURITY_TYPE_WEP = 1; + /** @hide */ + public static final int SECURITY_TYPE_PSK = 2; + /** @hide */ + public static final int SECURITY_TYPE_EAP = 3; + /** @hide */ + public static final int SECURITY_TYPE_SAE = 4; + /** @hide */ + public static final int SECURITY_TYPE_EAP_SUITE_B = 5; + /** @hide */ + public static final int SECURITY_TYPE_OWE = 6; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "SECURITY_TYPE_" }, value = { + SECURITY_TYPE_OPEN, + SECURITY_TYPE_WEP, + SECURITY_TYPE_PSK, + SECURITY_TYPE_EAP, + SECURITY_TYPE_SAE, + SECURITY_TYPE_EAP_SUITE_B, + SECURITY_TYPE_OWE + }) + public @interface SecurityType {} + + /** + * @hide + * Set security params (sets the various bitsets exposed in WifiConfiguration). + * + * @param securityType One of the security types from {@link SecurityType}. + */ + public void setSecurityParams(@SecurityType int securityType) { + // Clear all the bitsets. + allowedKeyManagement.clear(); + allowedProtocols.clear(); + allowedAuthAlgorithms.clear(); + allowedPairwiseCiphers.clear(); + allowedGroupCiphers.clear(); + allowedGroupManagementCiphers.clear(); + allowedSuiteBCiphers.clear(); + + switch (securityType) { + case SECURITY_TYPE_OPEN: + allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); + break; + case SECURITY_TYPE_WEP: + allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); + allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN); + allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED); + break; + case SECURITY_TYPE_PSK: + allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); + break; + case SECURITY_TYPE_EAP: + allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP); + allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X); + break; + case SECURITY_TYPE_SAE: + allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SAE); + requirePMF = true; + break; + case SECURITY_TYPE_EAP_SUITE_B: + allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SUITE_B_192); + allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256); + allowedGroupManagementCiphers.set(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256); + allowedSuiteBCiphers.set(WifiConfiguration.SuiteBCipher.ECDHE_ECDSA); + allowedSuiteBCiphers.set(WifiConfiguration.SuiteBCipher.ECDHE_RSA); + requirePMF = true; + break; + case SECURITY_TYPE_OWE: + allowedKeyManagement.set(WifiConfiguration.KeyMgmt.OWE); + requirePMF = true; + break; + default: + throw new IllegalArgumentException("unknown security type " + securityType); + } + } + /** @hide */ public static final int UNKNOWN_UID = -1; diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java index af9fdfbd364d..c7180c1b1c57 100644 --- a/wifi/java/android/net/wifi/WifiInfo.java +++ b/wifi/java/android/net/wifi/WifiInfo.java @@ -16,6 +16,7 @@ package android.net.wifi; +import android.annotation.IntRange; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.UnsupportedAppUsage; @@ -101,6 +102,11 @@ public class WifiInfo implements Parcelable { private int mLinkSpeed; /** + * Constant for unknown link speed. + */ + public static final int LINK_SPEED_UNKNOWN = -1; + + /** * Tx(transmit) Link speed in Mbps */ private int mTxLinkSpeed; @@ -137,6 +143,16 @@ public class WifiInfo implements Parcelable { private boolean mOsuAp; /** + * Fully qualified domain name of a Passpoint configuration + */ + private String mFqdn; + + /** + * Name of Passpoint credential provider + */ + private String mProviderFriendlyName; + + /** * If connected to a network suggestion or specifier, store the package name of the app, * else null. */ @@ -204,7 +220,7 @@ public class WifiInfo implements Parcelable { mNetworkId = -1; mSupplicantState = SupplicantState.UNINITIALIZED; mRssi = INVALID_RSSI; - mLinkSpeed = -1; + mLinkSpeed = LINK_SPEED_UNKNOWN; mFrequency = -1; } @@ -215,14 +231,16 @@ public class WifiInfo implements Parcelable { setSSID(null); setNetworkId(-1); setRssi(INVALID_RSSI); - setLinkSpeed(-1); - setTxLinkSpeedMbps(-1); - setRxLinkSpeedMbps(-1); + setLinkSpeed(LINK_SPEED_UNKNOWN); + setTxLinkSpeedMbps(LINK_SPEED_UNKNOWN); + setRxLinkSpeedMbps(LINK_SPEED_UNKNOWN); setFrequency(-1); setMeteredHint(false); setEphemeral(false); setOsuAp(false); setNetworkSuggestionOrSpecifierPackageName(null); + setFQDN(null); + setProviderFriendlyName(null); txBad = 0; txSuccess = 0; rxSuccess = 0; @@ -257,6 +275,8 @@ public class WifiInfo implements Parcelable { mNetworkSuggestionOrSpecifierPackageName = source.mNetworkSuggestionOrSpecifierPackageName; mOsuAp = source.mOsuAp; + mFqdn = source.mFqdn; + mProviderFriendlyName = source.mProviderFriendlyName; txBad = source.txBad; txRetries = source.txRetries; txSuccess = source.txSuccess; @@ -355,8 +375,9 @@ public class WifiInfo implements Parcelable { /** * Returns the current link speed in {@link #LINK_SPEED_UNITS}. - * @return the link speed or -1 if there is no valid value. + * @return the link speed or {@link #LINK_SPEED_UNKNOWN} if link speed is unknown. * @see #LINK_SPEED_UNITS + * @see #LINK_SPEED_UNKNOWN */ public int getLinkSpeed() { return mLinkSpeed; @@ -370,8 +391,10 @@ public class WifiInfo implements Parcelable { /** * Returns the current transmit link speed in Mbps. - * @return the Tx link speed or -1 if there is no valid value. + * @return the Tx link speed or {@link #LINK_SPEED_UNKNOWN} if link speed is unknown. + * @see #LINK_SPEED_UNKNOWN */ + @IntRange(from = -1) public int getTxLinkSpeedMbps() { return mTxLinkSpeed; } @@ -386,8 +409,10 @@ public class WifiInfo implements Parcelable { /** * Returns the current receive link speed in Mbps. - * @return the Rx link speed or -1 if there is no valid value. + * @return the Rx link speed or {@link #LINK_SPEED_UNKNOWN} if link speed is unknown. + * @see #LINK_SPEED_UNKNOWN */ + @IntRange(from = -1) public int getRxLinkSpeedMbps() { return mRxLinkSpeed; } @@ -504,6 +529,34 @@ public class WifiInfo implements Parcelable { } /** {@hide} */ + @SystemApi + public boolean isPasspointAp() { + return mFqdn != null && mProviderFriendlyName != null; + } + + /** {@hide} */ + public void setFQDN(@Nullable String fqdn) { + mFqdn = fqdn; + } + + /** {@hide} */ + @SystemApi + public @Nullable String getFqdn() { + return mFqdn; + } + + /** {@hide} */ + public void setProviderFriendlyName(@Nullable String providerFriendlyName) { + mProviderFriendlyName = providerFriendlyName; + } + + /** {@hide} */ + @SystemApi + public @Nullable String getProviderFriendlyName() { + return mProviderFriendlyName; + } + + /** {@hide} */ public void setNetworkSuggestionOrSpecifierPackageName(@Nullable String packageName) { mNetworkSuggestionOrSpecifierPackageName = packageName; } @@ -677,6 +730,8 @@ public class WifiInfo implements Parcelable { mSupplicantState.writeToParcel(dest, flags); dest.writeInt(mOsuAp ? 1 : 0); dest.writeString(mNetworkSuggestionOrSpecifierPackageName); + dest.writeString(mFqdn); + dest.writeString(mProviderFriendlyName); } /** Implement the Parcelable interface {@hide} */ @@ -716,6 +771,8 @@ public class WifiInfo implements Parcelable { info.mSupplicantState = SupplicantState.CREATOR.createFromParcel(in); info.mOsuAp = in.readInt() != 0; info.mNetworkSuggestionOrSpecifierPackageName = in.readString(); + info.mFqdn = in.readString(); + info.mProviderFriendlyName = in.readString(); return info; } diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 7caace6ec854..5ca30fcbf0db 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -1738,10 +1738,7 @@ public class WifiManager { * @deprecated This is no longer supported. */ @Deprecated - @RequiresPermission(anyOf = { - android.Manifest.permission.NETWORK_SETTINGS, - android.Manifest.permission.NETWORK_SETUP_WIZARD - }) + @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void removePasspointConfiguration(String fqdn) { try { if (!mService.removePasspointConfiguration(fqdn, mContext.getOpPackageName())) { @@ -4578,7 +4575,8 @@ public class WifiManager { public static final int DEVICE_MOBILITY_STATE_UNKNOWN = 0; /** - * High movement device mobility state + * High movement device mobility state. + * e.g. on a bike, in a motor vehicle * * @see #setDeviceMobilityState(int) * @@ -4588,7 +4586,8 @@ public class WifiManager { public static final int DEVICE_MOBILITY_STATE_HIGH_MVMT = 1; /** - * Low movement device mobility state + * Low movement device mobility state. + * e.g. walking, running * * @see #setDeviceMobilityState(int) * @@ -4610,6 +4609,8 @@ public class WifiManager { /** * Updates the device mobility state. Wifi uses this information to adjust the interval between * Wifi scans in order to balance power consumption with scan accuracy. + * The default mobility state when the device boots is {@link #DEVICE_MOBILITY_STATE_UNKNOWN}. + * This API should be called whenever there is a change in the mobility state. * @param state the updated device mobility state * @hide */ diff --git a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java index 333b82ccd146..c99bd2e45dad 100644 --- a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java +++ b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java @@ -269,58 +269,26 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc } - /** - * Set defaults for the various low level credential type fields in the newly created - * WifiConfiguration object. - * - * See {@link com.android.server.wifi.WifiConfigManager#setDefaultsInWifiConfiguration( - * WifiConfiguration)}. - * - * @param configuration provided WifiConfiguration object. - */ - private static void setDefaultsInWifiConfiguration( - @NonNull WifiConfiguration configuration) { - configuration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN); - configuration.allowedProtocols.set(WifiConfiguration.Protocol.RSN); - configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP); - configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP); - configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP); - } - private void setSecurityParamsInWifiConfiguration( @NonNull WifiConfiguration configuration) { if (!TextUtils.isEmpty(mWpa2PskPassphrase)) { // WPA-PSK network. - configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); + configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK); // WifiConfiguration.preSharedKey needs quotes around ASCII password. configuration.preSharedKey = "\"" + mWpa2PskPassphrase + "\""; } else if (!TextUtils.isEmpty(mWpa3SaePassphrase)) { // WPA3-SAE network. - configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SAE); - // PMF mandatory for SAE. - configuration.requirePMF = true; + configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE); // WifiConfiguration.preSharedKey needs quotes around ASCII password. configuration.preSharedKey = "\"" + mWpa3SaePassphrase + "\""; } else if (mWpa2EnterpriseConfig != null) { // WPA-EAP network - configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP); - configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X); + configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP); configuration.enterpriseConfig = mWpa2EnterpriseConfig; } else if (mWpa3EnterpriseConfig != null) { // WPA3-SuiteB network - configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SUITE_B_192); - configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256); - // TODO (b/113878056): Verify these params once we verify SuiteB configuration. - configuration.allowedGroupManagementCiphers.set( - WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256); - configuration.allowedSuiteBCiphers.set( - WifiConfiguration.SuiteBCipher.ECDHE_ECDSA); - configuration.allowedSuiteBCiphers.set( - WifiConfiguration.SuiteBCipher.ECDHE_RSA); - configuration.requirePMF = true; + configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B); configuration.enterpriseConfig = mWpa3EnterpriseConfig; } else if (mIsEnhancedOpen) { // OWE network - configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.OWE); - // PMF mandatory. - configuration.requirePMF = true; + configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE); } else { // Open network - configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); + configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OPEN); } } @@ -330,7 +298,6 @@ public final class WifiNetworkSpecifier extends NetworkSpecifier implements Parc */ private WifiConfiguration buildWifiConfiguration() { final WifiConfiguration wifiConfiguration = new WifiConfiguration(); - setDefaultsInWifiConfiguration(wifiConfiguration); // WifiConfiguration.SSID needs quotes around unicode SSID. if (mSsidPatternMatcher.getType() == PatternMatcher.PATTERN_LITERAL) { wifiConfiguration.SSID = "\"" + mSsidPatternMatcher.getPath() + "\""; diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java index 233fa2cb4fff..f02404fc1940 100644 --- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java +++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java @@ -303,58 +303,26 @@ public final class WifiNetworkSuggestion implements Parcelable { return this; } - /** - * Set defaults for the various low level credential type fields in the newly created - * WifiConfiguration object. - * - * See {@link com.android.server.wifi.WifiConfigManager#setDefaultsInWifiConfiguration( - * WifiConfiguration)}. - * - * @param configuration provided WifiConfiguration object. - */ - private static void setDefaultsInWifiConfiguration( - @NonNull WifiConfiguration configuration) { - configuration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN); - configuration.allowedProtocols.set(WifiConfiguration.Protocol.RSN); - configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP); - configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP); - configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP); - } - private void setSecurityParamsInWifiConfiguration( @NonNull WifiConfiguration configuration) { if (!TextUtils.isEmpty(mWpa2PskPassphrase)) { // WPA-PSK network. - configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK); + configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK); // WifiConfiguration.preSharedKey needs quotes around ASCII password. configuration.preSharedKey = "\"" + mWpa2PskPassphrase + "\""; } else if (!TextUtils.isEmpty(mWpa3SaePassphrase)) { // WPA3-SAE network. - configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SAE); - // PMF mandatory for SAE. - configuration.requirePMF = true; + configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_SAE); // WifiConfiguration.preSharedKey needs quotes around ASCII password. configuration.preSharedKey = "\"" + mWpa3SaePassphrase + "\""; } else if (mWpa2EnterpriseConfig != null) { // WPA-EAP network - configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP); - configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X); + configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP); configuration.enterpriseConfig = mWpa2EnterpriseConfig; } else if (mWpa3EnterpriseConfig != null) { // WPA3-SuiteB network - configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SUITE_B_192); - configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.GCMP_256); - // TODO (b/113878056): Verify these params once we verify SuiteB configuration. - configuration.allowedGroupManagementCiphers.set( - WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256); - configuration.allowedSuiteBCiphers.set( - WifiConfiguration.SuiteBCipher.ECDHE_ECDSA); - configuration.allowedSuiteBCiphers.set( - WifiConfiguration.SuiteBCipher.ECDHE_RSA); - configuration.requirePMF = true; + configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_EAP_SUITE_B); configuration.enterpriseConfig = mWpa3EnterpriseConfig; } else if (mIsEnhancedOpen) { // OWE network - configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.OWE); - // PMF mandatory. - configuration.requirePMF = true; + configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OWE); } else { // Open network - configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE); + configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_OPEN); } } @@ -364,7 +332,6 @@ public final class WifiNetworkSuggestion implements Parcelable { */ private WifiConfiguration buildWifiConfiguration() { final WifiConfiguration wifiConfiguration = new WifiConfiguration(); - setDefaultsInWifiConfiguration(wifiConfiguration); // WifiConfiguration.SSID needs quotes around unicode SSID. wifiConfiguration.SSID = "\"" + mSsid + "\""; if (mBssid != null) { diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java index acc0518dbca4..b73551fc62e0 100644 --- a/wifi/java/android/net/wifi/WifiScanner.java +++ b/wifi/java/android/net/wifi/WifiScanner.java @@ -259,8 +259,17 @@ public class WifiScanner { * {@hide} */ @SystemApi - @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public boolean ignoreLocationSettings; + /** + * This scan request will be hidden from app-ops noting for location information. This + * should only be used by FLP/NLP module on the device which is using the scan results to + * compute results for behalf on their clients. FLP/NLP module using this flag should ensure + * that they note in app-ops the eventual delivery of location information computed using + * these results to their client . + * {@hide} + */ + @SystemApi + public boolean hideFromAppOps; /** Implement the Parcelable interface {@hide} */ public int describeContents() { @@ -279,6 +288,7 @@ public class WifiScanner { dest.writeInt(isPnoScan ? 1 : 0); dest.writeInt(type); dest.writeInt(ignoreLocationSettings ? 1 : 0); + dest.writeInt(hideFromAppOps ? 1 : 0); if (channels != null) { dest.writeInt(channels.length); for (int i = 0; i < channels.length; i++) { @@ -314,6 +324,7 @@ public class WifiScanner { settings.isPnoScan = in.readInt() == 1; settings.type = in.readInt(); settings.ignoreLocationSettings = in.readInt() == 1; + settings.hideFromAppOps = in.readInt() == 1; int num_channels = in.readInt(); settings.channels = new ChannelSpec[num_channels]; for (int i = 0; i < num_channels; i++) { diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java index f931ad2bdf6d..479adbcb1a00 100644 --- a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java +++ b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java @@ -26,6 +26,7 @@ import android.text.TextUtils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.regex.PatternSyntaxException; /** * A class representing a Wi-Fi P2p configuration for setting up a connection @@ -252,7 +253,12 @@ public class WifiP2pConfig implements Parcelable { * Specify the network name, a.k.a. group name, * for creating or joining a group. * <p> - * Must be called - an empty network name is not valid. + * A network name shall begin with "DIRECT-xy". x and y are selected + * from the following character set: upper case letters, lower case + * letters and numbers. + * <p> + * Must be called - an empty network name or an network name + * not conforming to the P2P Group ID naming rule is not valid. * * @param networkName network name of a group. * @return The builder to facilitate chaining @@ -263,6 +269,14 @@ public class WifiP2pConfig implements Parcelable { throw new IllegalArgumentException( "network name must be non-empty."); } + try { + if (!networkName.matches("^DIRECT-[a-zA-Z0-9]{2}.*")) { + throw new IllegalArgumentException( + "network name must starts with the prefix DIRECT-xy."); + } + } catch (PatternSyntaxException e) { + // can never happen (fixed pattern) + } mNetworkName = networkName; return this; } diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java index 22dc2ed32083..ab7bb688f5ea 100644 --- a/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java +++ b/wifi/java/android/net/wifi/p2p/WifiP2pDevice.java @@ -17,14 +17,13 @@ package android.net.wifi.p2p; import android.annotation.UnsupportedAppUsage; -import android.os.Parcelable; import android.os.Parcel; +import android.os.Parcelable; import android.util.Log; import java.util.Objects; - -import java.util.regex.Pattern; import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * A class representing a Wi-Fi p2p device @@ -360,7 +359,9 @@ public class WifiP2pDevice implements Parcelable { deviceCapability = source.deviceCapability; groupCapability = source.groupCapability; status = source.status; - wfdInfo = new WifiP2pWfdInfo(source.wfdInfo); + if (source.wfdInfo != null) { + wfdInfo = new WifiP2pWfdInfo(source.wfdInfo); + } } } diff --git a/wifi/tests/src/android/net/wifi/WifiInfoTest.java b/wifi/tests/src/android/net/wifi/WifiInfoTest.java index 948dcfa47f59..b3034965b5fc 100644 --- a/wifi/tests/src/android/net/wifi/WifiInfoTest.java +++ b/wifi/tests/src/android/net/wifi/WifiInfoTest.java @@ -36,6 +36,8 @@ public class WifiInfoTest { private static final long TEST_TX_BAD = 3; private static final long TEST_RX_SUCCESS = 4; private static final String TEST_PACKAGE_NAME = "com.test.example"; + private static final String TEST_FQDN = "test.com"; + private static final String TEST_PROVIDER_NAME = "test"; /** * Verify parcel write/read with WifiInfo. @@ -49,6 +51,8 @@ public class WifiInfoTest { writeWifiInfo.rxSuccess = TEST_RX_SUCCESS; writeWifiInfo.setTrusted(true); writeWifiInfo.setOsuAp(true); + writeWifiInfo.setFQDN(TEST_FQDN); + writeWifiInfo.setProviderFriendlyName(TEST_PROVIDER_NAME); writeWifiInfo.setNetworkSuggestionOrSpecifierPackageName(TEST_PACKAGE_NAME); Parcel parcel = Parcel.obtain(); @@ -64,6 +68,9 @@ public class WifiInfoTest { assertEquals(TEST_RX_SUCCESS, readWifiInfo.rxSuccess); assertTrue(readWifiInfo.isTrusted()); assertTrue(readWifiInfo.isOsuAp()); + assertTrue(readWifiInfo.isPasspointAp()); assertEquals(TEST_PACKAGE_NAME, readWifiInfo.getNetworkSuggestionOrSpecifierPackageName()); + assertEquals(TEST_FQDN, readWifiInfo.getFqdn()); + assertEquals(TEST_PROVIDER_NAME, readWifiInfo.getProviderFriendlyName()); } } diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java index bef33b769a75..feac0e598127 100644 --- a/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java +++ b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java @@ -69,16 +69,6 @@ public class WifiNetworkSpecifierTest { assertEquals(MacAddress.ALL_ZEROS_ADDRESS, wifiNetworkSpecifier.bssidPatternMatcher.second); assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedKeyManagement .get(WifiConfiguration.KeyMgmt.NONE)); - assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedProtocols - .get(WifiConfiguration.Protocol.RSN)); - assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedAuthAlgorithms - .get(WifiConfiguration.AuthAlgorithm.OPEN)); - assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedPairwiseCiphers - .get(WifiConfiguration.PairwiseCipher.CCMP)); - assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers - .get(WifiConfiguration.GroupCipher.CCMP)); - assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers - .get(WifiConfiguration.GroupCipher.TKIP)); } /** @@ -105,16 +95,6 @@ public class WifiNetworkSpecifierTest { wifiNetworkSpecifier.bssidPatternMatcher.second); assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedKeyManagement .get(WifiConfiguration.KeyMgmt.WPA_PSK)); - assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedProtocols - .get(WifiConfiguration.Protocol.RSN)); - assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedAuthAlgorithms - .get(WifiConfiguration.AuthAlgorithm.OPEN)); - assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedPairwiseCiphers - .get(WifiConfiguration.PairwiseCipher.CCMP)); - assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers - .get(WifiConfiguration.GroupCipher.CCMP)); - assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers - .get(WifiConfiguration.GroupCipher.TKIP)); assertEquals("\"" + TEST_PRESHARED_KEY + "\"", wifiNetworkSpecifier.wifiConfiguration.preSharedKey); } @@ -150,16 +130,6 @@ public class WifiNetworkSpecifierTest { .get(WifiConfiguration.KeyMgmt.WPA_EAP)); assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedKeyManagement .get(WifiConfiguration.KeyMgmt.IEEE8021X)); - assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedProtocols - .get(WifiConfiguration.Protocol.RSN)); - assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedAuthAlgorithms - .get(WifiConfiguration.AuthAlgorithm.OPEN)); - assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedPairwiseCiphers - .get(WifiConfiguration.PairwiseCipher.CCMP)); - assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers - .get(WifiConfiguration.GroupCipher.CCMP)); - assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedGroupCiphers - .get(WifiConfiguration.GroupCipher.TKIP)); assertTrue(wifiNetworkSpecifier.wifiConfiguration.hiddenSSID); assertEquals(enterpriseConfig.getEapMethod(), wifiNetworkSpecifier.wifiConfiguration.enterpriseConfig.getEapMethod()); diff --git a/wifi/tests/src/android/net/wifi/p2p/WifiP2pConfigTest.java b/wifi/tests/src/android/net/wifi/p2p/WifiP2pConfigTest.java new file mode 100644 index 000000000000..560c88ee1d07 --- /dev/null +++ b/wifi/tests/src/android/net/wifi/p2p/WifiP2pConfigTest.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi.p2p; + +import static org.junit.Assert.fail; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; + +/** + * Unit test harness for {@link android.net.wifi.p2p.WifiP2pConfig} + */ +@SmallTest +public class WifiP2pConfigTest { + /** + * Check network name setter + */ + @Test + public void testBuilderInvalidNetworkName() throws Exception { + WifiP2pConfig.Builder b = new WifiP2pConfig.Builder(); + + // sunny case + try { + b.setNetworkName("DIRECT-ab-Hello"); + } catch (IllegalArgumentException e) { + fail("Unexpected IllegalArgumentException"); + } + + // sunny case, no trailing string + try { + b.setNetworkName("DIRECT-WR"); + } catch (IllegalArgumentException e) { + fail("Unexpected IllegalArgumentException"); + } + + // less than 9 characters. + try { + b.setNetworkName("DIRECT-z"); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { } + + // not starts with DIRECT-xy. + try { + b.setNetworkName("ABCDEFGHIJK"); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { } + + // not starts with uppercase DIRECT-xy + try { + b.setNetworkName("direct-ab"); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { } + + // x and y are not selected from upper case letters, lower case letters or + // numbers. + try { + b.setNetworkName("direct-a?"); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { } + } +} diff --git a/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java b/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java index f61e6b759085..17ee75594c2f 100644 --- a/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java +++ b/wifi/tests/src/android/net/wifi/p2p/WifiP2pDeviceTest.java @@ -30,6 +30,31 @@ import org.junit.Test; public class WifiP2pDeviceTest { /** + * Compare two p2p devices. + * + * @param devA is the first device to be compared + * @param devB is the second device to be compared + */ + private void compareWifiP2pDevices(WifiP2pDevice devA, WifiP2pDevice devB) { + assertEquals(devA.deviceName, devB.deviceName); + assertEquals(devA.deviceAddress, devB.deviceAddress); + assertEquals(devA.primaryDeviceType, devB.primaryDeviceType); + assertEquals(devA.secondaryDeviceType, devB.secondaryDeviceType); + assertEquals(devA.wpsConfigMethodsSupported, devB.wpsConfigMethodsSupported); + assertEquals(devA.deviceCapability, devB.deviceCapability); + assertEquals(devA.groupCapability, devB.groupCapability); + assertEquals(devA.status, devB.status); + if (devA.wfdInfo != null) { + assertEquals(devA.wfdInfo.isWfdEnabled(), devB.wfdInfo.isWfdEnabled()); + assertEquals(devA.wfdInfo.getDeviceInfoHex(), devB.wfdInfo.getDeviceInfoHex()); + assertEquals(devA.wfdInfo.getControlPort(), devB.wfdInfo.getControlPort()); + assertEquals(devA.wfdInfo.getMaxThroughput(), devB.wfdInfo.getMaxThroughput()); + } else { + assertEquals(devA.wfdInfo, devB.wfdInfo); + } + } + + /** * Check equals and hashCode consistency */ @Test @@ -42,4 +67,52 @@ public class WifiP2pDeviceTest { assertTrue(dev_a.equals(dev_b)); assertEquals(dev_a.hashCode(), dev_b.hashCode()); } + + /** + * Check the copy constructor with default values. + */ + @Test + public void testCopyConstructorWithDefaultValues() throws Exception { + WifiP2pDevice device = new WifiP2pDevice(); + WifiP2pDevice copy = new WifiP2pDevice(device); + compareWifiP2pDevices(device, copy); + } + + /** + * Check the copy constructor with updated values. + */ + @Test + public void testCopyConstructorWithUpdatedValues() throws Exception { + WifiP2pDevice device = new WifiP2pDevice(); + device.deviceName = "deviceName"; + device.deviceAddress = "11:22:33:44:55:66"; + device.primaryDeviceType = "primaryDeviceType"; + device.secondaryDeviceType = "secondaryDeviceType"; + device.wpsConfigMethodsSupported = 0x0008; + device.deviceCapability = 1; + device.groupCapability = 1; + device.status = WifiP2pDevice.CONNECTED; + device.wfdInfo = new WifiP2pWfdInfo(); + WifiP2pDevice copy = new WifiP2pDevice(device); + compareWifiP2pDevices(device, copy); + } + + /** + * Check the copy constructor when the wfdInfo of the source object is null. + */ + @Test + public void testCopyConstructorWithNullWfdInfo() throws Exception { + WifiP2pDevice device = new WifiP2pDevice(); + device.deviceName = "deviceName"; + device.deviceAddress = "11:22:33:44:55:66"; + device.primaryDeviceType = "primaryDeviceType"; + device.secondaryDeviceType = "secondaryDeviceType"; + device.wpsConfigMethodsSupported = 0x0008; + device.deviceCapability = 1; + device.groupCapability = 1; + device.status = WifiP2pDevice.CONNECTED; + device.wfdInfo = null; + WifiP2pDevice copy = new WifiP2pDevice(device); + compareWifiP2pDevices(device, copy); + } } |