diff options
108 files changed, 3816 insertions, 841 deletions
diff --git a/Android.bp b/Android.bp index cb7f7a2317d3..ca1c614e6c02 100644 --- a/Android.bp +++ b/Android.bp @@ -1084,107 +1084,95 @@ framework_docs_args = "-android -manifest $(location core/res/AndroidManifest.xm "-federate SupportLib https://developer.android.com " + "-federationapi SupportLib $(location current/support-api.txt) " -doc_defaults { - name: "api-stubs-default", +framework_docs_only_args = " -android -manifest $(location core/res/AndroidManifest.xml) " + + "-overview $(location core/java/overview.html) " + + // Federate Support Library references against local API file. + "-federate SupportLib https://developer.android.com " + + "-federationapi SupportLib $(location current/support-api.txt) " + +framework_docs_only_libs = [ + "conscrypt", + "bouncycastle", + "voip-common", + "android.test.mock", + "android-support-annotations", + "android-support-compat", + "android-support-core-ui", + "android-support-core-utils", + "android-support-customtabs", + "android-support-design", + "android-support-dynamic-animation", + "android-support-exifinterface", + "android-support-fragment", + "android-support-media-compat", + "android-support-percent", + "android-support-recommendation", + "android-support-transition", + "android-support-tv-provider", + "android-support-v7-cardview", + "android-support-v7-gridlayout", + "android-support-v7-mediarouter", + "android-support-v7-palette", + "android-support-v7-preference", + "android-support-v13", + "android-support-v14-preference", + "android-support-v17-leanback", + "android-support-v17-preference-leanback", + "android-support-wear", + "android-support-vectordrawable", + "android-support-animatedvectordrawable", + "android-support-v7-appcompat", + "android-support-v7-recyclerview", + "android-support-emoji", + "android-support-emoji-appcompat", + "android-support-emoji-bundled", + "android-support-v8-renderscript", + "android-support-multidex", + "android-support-multidex-instrumentation", +] + +metalava_framework_docs_args = "--manifest $(location core/res/AndroidManifest.xml) " + + "--hide-package com.android.okhttp " + + "--hide-package com.android.org.conscrypt --hide-package com.android.server " + + "--hide RequiresPermission " + + "--hide MissingPermission --hide BroadcastBehavior " + + "--hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol " + + "--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo" + +stubs_defaults { + name: "framework-doc-stubs-default", srcs: [ + "test-base/src/**/*.java", ":opt-telephony-srcs", ":opt-net-voip-srcs", ":openjdk_javadoc_files", ":non_openjdk_javadoc_files", ":android_icu4j_src_files_for_docs", + "test-mock/src/**/*.java", + "test-runner/src/**/*.java", ], srcs_lib: "framework", srcs_lib_whitelist_dirs: frameworks_base_subdirs, srcs_lib_whitelist_pkgs: packages_to_document, - libs: [ - "core-oj", - "core-libart", - "conscrypt", - "bouncycastle", - "okhttp", - "ext", - "framework", - "voip-common", - "android.test.mock.impl", - ], + libs: framework_docs_only_libs, local_sourcepaths: frameworks_base_subdirs, - html_dirs: [ - "docs/html", - ], - knowntags: [ - "docs/knowntags.txt", - ":known-oj-tags", + create_doc_stubs: true, + annotations_enabled: true, + api_levels_annotations_enabled: true, + api_levels_annotations_dirs: [ + "sdk-dir", + "api-versions-jars-dir", ], - custom_template: "droiddoc-templates-sdk", - hdf: [ - "dac true", - "sdk.codename O", - "sdk.preview.version 1", - "sdk.version 7.0", - "sdk.rel.id 1", - "sdk.preview 0", + previous_api: ":last-released-public-api", + merge_annotations_dirs: [ + "metalava-manual", + "ojluni-annotated-stubs", ], - resourcesdir: "docs/html/reference/images", - resourcesoutdir: "reference/android/images", - installable: false, } doc_defaults { name: "framework-docs-default", - srcs: [ - "test-base/src/**/*.java", - ":opt-telephony-srcs", - ":opt-net-voip-srcs", - ":openjdk_javadoc_files", - ":non_openjdk_javadoc_files", - ":android_icu4j_src_files_for_docs", - "test-mock/src/**/*.java", - "test-runner/src/**/*.java", - ], - srcs_lib: "framework", - srcs_lib_whitelist_dirs: frameworks_base_subdirs, - srcs_lib_whitelist_pkgs: packages_to_document, - libs: [ - "conscrypt", - "bouncycastle", - "voip-common", - "android.test.mock", - "android-support-annotations", - "android-support-compat", - "android-support-core-ui", - "android-support-core-utils", - "android-support-customtabs", - "android-support-design", - "android-support-dynamic-animation", - "android-support-exifinterface", - "android-support-fragment", - "android-support-media-compat", - "android-support-percent", - "android-support-recommendation", - "android-support-transition", - "android-support-tv-provider", - "android-support-v7-cardview", - "android-support-v7-gridlayout", - "android-support-v7-mediarouter", - "android-support-v7-palette", - "android-support-v7-preference", - "android-support-v13", - "android-support-v14-preference", - "android-support-v17-leanback", - "android-support-v17-preference-leanback", - "android-support-wear", - "android-support-vectordrawable", - "android-support-animatedvectordrawable", - "android-support-v7-appcompat", - "android-support-v7-recyclerview", - "android-support-emoji", - "android-support-emoji-appcompat", - "android-support-emoji-bundled", - "android-support-v8-renderscript", - "android-support-multidex", - "android-support-multidex-instrumentation", - ], - local_sourcepaths: frameworks_base_subdirs, + libs: framework_docs_only_libs, html_dirs: [ "docs/html", ], @@ -1205,22 +1193,12 @@ doc_defaults { ], arg_files: [ "core/res/AndroidManifest.xml", - ":api-version-xml", "core/java/overview.html", ":current-support-api", - "api/current.txt", ], create_stubs: false, } -metalava_framework_docs_args = "--manifest $(location core/res/AndroidManifest.xml) " + - "--hide-package com.android.okhttp " + - "--hide-package com.android.org.conscrypt --hide-package com.android.server " + - "--hide RequiresPermission " + - "--hide MissingPermission --hide BroadcastBehavior " + - "--hide HiddenSuperclass --hide DeprecationMismatch --hide UnavailableSymbol " + - "--hide SdkConstant --hide HiddenTypeParameter --hide Todo --hide Typo" - stubs_defaults { name: "metalava-api-stubs-default", srcs: [ @@ -1254,21 +1232,45 @@ stubs_defaults { ], } +droidstubs { + name: "framework-doc-stubs", + defaults: ["framework-doc-stubs-default"], + arg_files: [ + "core/res/AndroidManifest.xml", + ], + args: metalava_framework_docs_args, +} + +droidstubs { + name: "framework-doc-system-stubs", + defaults: ["framework-doc-stubs-default"], + arg_files: [ + "core/res/AndroidManifest.xml", + ], + args: metalava_framework_docs_args + " --show-annotation android.annotation.SystemApi ", +} + droiddoc { name: "doc-comment-check-docs", defaults: ["framework-docs-default"], - args: framework_docs_args + " -referenceonly -parsecomments", + srcs: [ + ":framework-doc-stubs", + ], + args: framework_docs_only_args + " -referenceonly -parsecomments", installable: false, } droiddoc { name: "offline-sdk-docs", defaults: ["framework-docs-default"], + srcs: [ + ":framework-doc-stubs", + ], hdf: [ "android.whichdoc offline", ], proofread_file: "offline-sdk-docs-proofrerad.txt", - args: framework_docs_args + " -offlinemode -title \"Android SDK\"", + args: framework_docs_only_args + " -offlinemode -title \"Android SDK\"", write_sdk_values: true, static_doc_index_redirect: "docs/docs-preview-index.html", } @@ -1276,11 +1278,14 @@ droiddoc { droiddoc { name: "offline-sdk-referenceonly-docs", defaults: ["framework-docs-default"], + srcs: [ + ":framework-doc-stubs", + ], hdf: [ "android.whichdoc offline", ], proofread_file: "offline-sdk-referenceonly-docs-proofrerad.txt", - args: framework_docs_args + " -offlinemode -title \"Android SDK\" -referenceonly", + args: framework_docs_only_args + " -offlinemode -title \"Android SDK\" -referenceonly", write_sdk_values: true, static_doc_index_redirect: "docs/docs-documentation-redirect.html", static_doc_properties: "docs/source.properties", @@ -1289,13 +1294,15 @@ droiddoc { droiddoc { name: "offline-system-sdk-referenceonly-docs", defaults: ["framework-docs-default"], + srcs: [ + ":framework-doc-system-stubs", + ], hdf: [ "android.whichdoc offline", ], proofread_file: "offline-system-sdk-referenceonly-docs-proofrerad.txt", - args: framework_docs_args + " -hide 101 -hide 104 -hide 108" + - " -showAnnotation android.annotation.SystemApi " + - " -offlinemode -title \"Android System SDK\" -referenceonly", + args: framework_docs_only_args + " -hide 101 -hide 104 -hide 108" + + " -offlinemode -title \"Android System SDK\" -referenceonly", write_sdk_values: true, static_doc_index_redirect: "docs/docs-documentation-redirect.html", static_doc_properties: "docs/source.properties", @@ -1304,12 +1311,15 @@ droiddoc { droiddoc { name: "online-sdk-docs", defaults: ["framework-docs-default"], + srcs: [ + ":framework-doc-stubs", + ], hdf: [ "android.whichdoc online", "android.hasSamples true", ], proofread_file: "online-sdk-docs-proofrerad.txt", - args: framework_docs_args + + args: framework_docs_only_args + " -toroot / -samplegroup Admin " + " -samplegroup Background " + " -samplegroup Connectivity " + @@ -1330,14 +1340,16 @@ droiddoc { droiddoc { name: "online-system-api-sdk-docs", defaults: ["framework-docs-default"], + srcs: [ + ":framework-doc-system-stubs", + ], hdf: [ "android.whichdoc online", "android.hasSamples true", ], proofread_file: "online-system-api-sdk-docs-proofrerad.txt", - args: framework_docs_args + + args: framework_docs_only_args + " -referenceonly " + - " -showAnnotation android.annotation.SystemApi " + " -title \"Android SDK - Including system APIs.\" " + " -hide 101 " + " -hide 104 " + @@ -1363,12 +1375,15 @@ droiddoc { droiddoc { name: "ds-docs", defaults: ["framework-docs-default"], + srcs: [ + ":framework-doc-stubs", + ], hdf: [ "android.whichdoc online", "android.hasSamples true", ], proofread_file: "ds-docs-proofrerad.txt", - args: framework_docs_args + + args: framework_docs_only_args + " -toroot / -samplegroup Admin " + " -samplegroup Background " + " -samplegroup Connectivity " + @@ -1389,11 +1404,14 @@ droiddoc { droiddoc { name: "ds-static-docs", defaults: ["framework-docs-default"], + srcs: [ + ":framework-doc-stubs", + ], hdf: [ "android.whichdoc online", ], proofread_file: "ds-static-docs-proofrerad.txt", - args: framework_docs_args + + args: framework_docs_only_args + " -staticonly " + " -toroot / " + " -devsite " + @@ -1403,11 +1421,14 @@ droiddoc { droiddoc { name: "ds-ref-navtree-docs", defaults: ["framework-docs-default"], + srcs: [ + ":framework-doc-stubs", + ], hdf: [ "android.whichdoc online", ], proofread_file: "ds-ref-navtree-docs-proofrerad.txt", - args: framework_docs_args + + args: framework_docs_only_args + " -toroot / " + " -atLinksNavtree " + " -navtreeonly ", @@ -1416,12 +1437,15 @@ droiddoc { droiddoc { name: "online-sdk-dev-docs", defaults: ["framework-docs-default"], + srcs: [ + ":framework-doc-stubs", + ], hdf: [ "android.whichdoc online", "android.hasSamples true", ], proofread_file: "online-sdk-dev-docs-proofrerad.txt", - args: framework_docs_args + + args: framework_docs_only_args + " -toroot / -samplegroup Admin " + " -samplegroup Background " + " -samplegroup Connectivity " + @@ -1442,13 +1466,16 @@ droiddoc { droiddoc { name: "hidden-docs", defaults: ["framework-docs-default"], + srcs: [ + ":framework-doc-stubs", + ], proofread_file: "hidden-docs-proofrerad.txt", - args: framework_docs_args + + args: framework_docs_only_args + " -referenceonly " + " -title \"Android SDK - Including hidden APIs.\"", } -droiddoc { +droidstubs { name: "hwbinder-stubs-docs", srcs: [ "core/java/android/os/HidlSupport.java", @@ -1466,10 +1493,15 @@ droiddoc { "core/java/android/os/RemoteException.java", "core/java/android/util/AndroidException.java", ], - custom_template: "droiddoc-templates-sdk", installable: false, no_framework_libs: true, - args: "-showAnnotation android.annotation.SystemApi -nodocs -stubsourceonly", + annotations_enabled: true, + previous_api: ":last-released-public-api", + merge_annotations_dirs: [ + "metalava-manual", + "ojluni-annotated-stubs", + ], + args: " --show-annotation android.annotation.SystemApi", } java_library_static { @@ -1496,23 +1528,17 @@ droidstubs { } -droiddoc { +droidstubs { name: "hiddenapi-mappings", - defaults: ["api-stubs-default"], + defaults: ["metalava-api-stubs-default"], arg_files: [ "core/res/AndroidManifest.xml", - ":api-version-xml", - "core/java/overview.html", - ":current-support-api", - "api/current.txt", ], dex_mapping_filename: "dex-mapping.txt", - args: framework_docs_args + - " -referenceonly" + - " -nodocs" + - " -showUnannotated" + - " -showAnnotation android.annotation.SystemApi" + - " -showAnnotation android.annotation.TestApi", + args: metalava_framework_docs_args + + " --show-unannotated " + + " --show-annotation android.annotation.SystemApi " + + " --show-annotation android.annotation.TestApi " } filegroup { diff --git a/api/current.txt b/api/current.txt index 2c2bc5f4d21c..ea21c2dd10b1 100755 --- a/api/current.txt +++ b/api/current.txt @@ -4385,6 +4385,7 @@ package android.app { public final class AutomaticZenRule implements android.os.Parcelable { ctor public AutomaticZenRule(java.lang.String, android.content.ComponentName, android.net.Uri, int, boolean); + ctor public AutomaticZenRule(java.lang.String, android.content.ComponentName, android.net.Uri, android.service.notification.ZenPolicy, boolean); ctor public AutomaticZenRule(android.os.Parcel); method public int describeContents(); method public android.net.Uri getConditionId(); @@ -4392,11 +4393,13 @@ package android.app { method public int getInterruptionFilter(); method public java.lang.String getName(); method public android.content.ComponentName getOwner(); + method public android.service.notification.ZenPolicy getZenPolicy(); method public boolean isEnabled(); method public void setConditionId(android.net.Uri); method public void setEnabled(boolean); method public void setInterruptionFilter(int); method public void setName(java.lang.String); + method public void setZenPolicy(android.service.notification.ZenPolicy); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.app.AutomaticZenRule> CREATOR; } @@ -9340,6 +9343,7 @@ package android.content { ctor public ContentUris(); method public static android.net.Uri.Builder appendId(android.net.Uri.Builder, long); method public static long parseId(android.net.Uri); + method public static android.net.Uri removeId(android.net.Uri); method public static android.net.Uri withAppendedId(android.net.Uri, long); } @@ -11928,6 +11932,7 @@ package android.content.res { method public android.graphics.drawable.Drawable getDrawable(int, android.content.res.Resources.Theme) throws android.content.res.Resources.NotFoundException; method public deprecated android.graphics.drawable.Drawable getDrawableForDensity(int, int) throws android.content.res.Resources.NotFoundException; method public android.graphics.drawable.Drawable getDrawableForDensity(int, int, android.content.res.Resources.Theme); + method public float getFloat(int); method public android.graphics.Typeface getFont(int) throws android.content.res.Resources.NotFoundException; method public float getFraction(int, int, int); method public int getIdentifier(java.lang.String, java.lang.String, java.lang.String); @@ -12655,7 +12660,7 @@ package android.database.sqlite { ctor public SQLiteMisuseException(java.lang.String); } - public abstract class SQLiteOpenHelper { + public abstract class SQLiteOpenHelper implements java.lang.AutoCloseable { ctor public SQLiteOpenHelper(android.content.Context, java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory, int); ctor public SQLiteOpenHelper(android.content.Context, java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory, int, android.database.DatabaseErrorHandler); ctor public SQLiteOpenHelper(android.content.Context, java.lang.String, int, android.database.sqlite.SQLiteDatabase.OpenParams); @@ -16033,6 +16038,7 @@ package android.hardware.camera2 { method public java.util.List<android.hardware.camera2.CaptureRequest.Key<?>> getAvailablePhysicalCameraRequestKeys(); method public java.util.List<android.hardware.camera2.CaptureRequest.Key<?>> getAvailableSessionKeys(); method public java.util.List<android.hardware.camera2.CameraCharacteristics.Key<?>> getKeys(); + method public java.util.List<android.hardware.camera2.CameraCharacteristics.Key<?>> getKeysNeedingPermission(); method public java.util.Set<java.lang.String> getPhysicalCameraIds(); field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES; field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AE_AVAILABLE_ANTIBANDING_MODES; @@ -39565,6 +39571,7 @@ package android.service.notification { method public final deprecated void cancelNotification(java.lang.String, java.lang.String, int); method public final void cancelNotification(java.lang.String); method public final void cancelNotifications(java.lang.String[]); + method public final void clearRequestedListenerHints(); method public android.service.notification.StatusBarNotification[] getActiveNotifications(); method public android.service.notification.StatusBarNotification[] getActiveNotifications(java.lang.String[]); method public final int getCurrentInterruptionFilter(); @@ -39680,6 +39687,61 @@ package android.service.notification { field public static final android.os.Parcelable.Creator<android.service.notification.StatusBarNotification> CREATOR; } + public final class ZenPolicy implements android.os.Parcelable { + method public int describeContents(); + method public int getPriorityCallSenders(); + method public int getPriorityCategoryAlarms(); + method public int getPriorityCategoryCalls(); + method public int getPriorityCategoryEvents(); + method public int getPriorityCategoryMedia(); + method public int getPriorityCategoryMessages(); + method public int getPriorityCategoryReminders(); + method public int getPriorityCategoryRepeatCallers(); + method public int getPriorityCategorySystem(); + method public int getPriorityMessageSenders(); + method public int getVisualEffectAmbient(); + method public int getVisualEffectBadge(); + method public int getVisualEffectFullScreenIntent(); + method public int getVisualEffectLights(); + method public int getVisualEffectNotificationList(); + method public int getVisualEffectPeek(); + method public int getVisualEffectStatusBar(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.service.notification.ZenPolicy> CREATOR; + field public static final int PEOPLE_TYPE_ANYONE = 1; // 0x1 + field public static final int PEOPLE_TYPE_CONTACTS = 2; // 0x2 + field public static final int PEOPLE_TYPE_NONE = 4; // 0x4 + field public static final int PEOPLE_TYPE_STARRED = 3; // 0x3 + field public static final int PEOPLE_TYPE_UNSET = 0; // 0x0 + field public static final int STATE_ALLOW = 1; // 0x1 + field public static final int STATE_DISALLOW = 2; // 0x2 + field public static final int STATE_UNSET = 0; // 0x0 + } + + public static class ZenPolicy.Builder { + ctor public ZenPolicy.Builder(); + method public android.service.notification.ZenPolicy.Builder allowAlarms(boolean); + method public android.service.notification.ZenPolicy.Builder allowAllSounds(); + method public android.service.notification.ZenPolicy.Builder allowCalls(int); + method public android.service.notification.ZenPolicy.Builder allowEvents(boolean); + method public android.service.notification.ZenPolicy.Builder allowMedia(boolean); + method public android.service.notification.ZenPolicy.Builder allowMessages(int); + method public android.service.notification.ZenPolicy.Builder allowReminders(boolean); + method public android.service.notification.ZenPolicy.Builder allowRepeatCallers(boolean); + method public android.service.notification.ZenPolicy.Builder allowSystem(boolean); + method public android.service.notification.ZenPolicy build(); + method public android.service.notification.ZenPolicy.Builder disallowAllSounds(); + method public android.service.notification.ZenPolicy.Builder hideAllVisualEffects(); + method public android.service.notification.ZenPolicy.Builder showAllVisualEffects(); + method public android.service.notification.ZenPolicy.Builder showBadges(boolean); + method public android.service.notification.ZenPolicy.Builder showFullScreenIntent(boolean); + method public android.service.notification.ZenPolicy.Builder showInAmbientDisplay(boolean); + method public android.service.notification.ZenPolicy.Builder showInNotificationList(boolean); + method public android.service.notification.ZenPolicy.Builder showLights(boolean); + method public android.service.notification.ZenPolicy.Builder showPeeking(boolean); + method public android.service.notification.ZenPolicy.Builder showStatusBarIcons(boolean); + } + } package android.service.quicksettings { diff --git a/api/system-current.txt b/api/system-current.txt index e2c59cb10dd4..d29b2167092f 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -355,6 +355,11 @@ package android.app { field public static final android.os.Parcelable.Creator<android.app.AppOpsManager.PackageOps> CREATOR; } + public final class AutomaticZenRule implements android.os.Parcelable { + ctor public AutomaticZenRule(java.lang.String, android.content.ComponentName, android.net.Uri, int, boolean, long); + ctor public AutomaticZenRule(java.lang.String, android.content.ComponentName, android.net.Uri, int, boolean, long, android.service.notification.ZenPolicy); + } + public class BroadcastOptions { method public static android.app.BroadcastOptions makeBasic(); method public void setDontSendToRestrictedApps(boolean); @@ -3807,6 +3812,14 @@ package android.os { field public static final java.lang.String ACTION_UPDATE_SMS_SHORT_CODES = "android.intent.action.UPDATE_SMS_SHORT_CODES"; } + public class Environment { + method public static java.io.File getOdmDirectory(); + method public static java.io.File getOemDirectory(); + method public static java.io.File getProductDirectory(); + method public static java.io.File getProductServicesDirectory(); + method public static java.io.File getVendorDirectory(); + } + public class HidlSupport { method public static boolean deepEquals(java.lang.Object, java.lang.Object); method public static int deepHashCode(java.lang.Object); @@ -4107,6 +4120,9 @@ package android.os { method public boolean isSystem(); method public static int myUserId(); method public static android.os.UserHandle of(int); + field public static final android.os.UserHandle ALL; + field public static final android.os.UserHandle CURRENT; + field public static final android.os.UserHandle SYSTEM; field public static final int USER_NULL = -10000; // 0xffffd8f0 } diff --git a/api/test-current.txt b/api/test-current.txt index 28e97347db48..e22f516d9d78 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -259,6 +259,7 @@ package android.content { } public abstract class Context { + method public android.content.Context createPackageContextAsUser(java.lang.String, int, android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException; method public abstract java.lang.String getOpPackageName(); method public android.os.UserHandle getUser(); method public int getUserId(); @@ -765,6 +766,8 @@ package android.os { method public static int getAppId(int); method public int getIdentifier(); method public static boolean isApp(int); + field public static final android.os.UserHandle ALL; + field public static final android.os.UserHandle CURRENT; field public static final android.os.UserHandle SYSTEM; } diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h index 302b4ef7ae34..86d956ff75d8 100644 --- a/cmds/incidentd/src/Section.h +++ b/cmds/incidentd/src/Section.h @@ -171,7 +171,7 @@ private: */ class TombstoneSection : public WorkerThreadSection { public: - TombstoneSection(int id, const char* type, int64_t timeoutMs = 30000 /* 30 seconds */); + TombstoneSection(int id, const char* type, int64_t timeoutMs = 120000 /* 2 minutes */); virtual ~TombstoneSection(); virtual status_t BlockingCall(int pipeWriteFd) const; diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 2c07431ee38a..3bd1bb08fd0c 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -132,6 +132,7 @@ message Atom { KeyValuePairsAtom key_value_pairs_atom = 83; VibratorStateChanged vibrator_state_changed = 84; DeferredJobStatsReported deferred_job_stats_reported = 85; + ThermalThrottlingStateChanged thermal_throttling = 86; } // Pulled events will start at field 10000. @@ -229,6 +230,26 @@ message KeyValuePairsAtom { */ /** + * Logs when the Thermal service HAL notifies the throttling start/stop events. + * + * Logged from: + * frameworks/base/services/core/java/com/android/server/stats/StatsCompanionService.java + */ +message ThermalThrottlingStateChanged { + optional android.os.TemperatureTypeEnum sensor_type = 1; + + enum State { + UNKNOWN = 0; + START = 1; + STOP = 2; + } + + optional State state = 2; + + optional float temperature = 3; +} + +/** * Logs when the screen state changes. * * Logged from: diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index dbc13dc36ea7..f6ba0b6017be 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -168,7 +168,7 @@ const std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_STATE)}}, // temperature - {android::util::TEMPERATURE, {{}, {}, 1, new ResourceThermalManagerPuller()}}, + {android::util::TEMPERATURE, {{}, {}, 1 * NS_PER_SEC, new ResourceThermalManagerPuller()}}, // binder_calls {android::util::BINDER_CALLS, {{4, 5, 6, 8, 12}, diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt index a4f13a0c5788..5d37c5fdbaff 100644 --- a/config/hiddenapi-light-greylist.txt +++ b/config/hiddenapi-light-greylist.txt @@ -728,8 +728,6 @@ Landroid/os/Environment;->buildExternalStorageAppMediaDirs(Ljava/lang/String;)[L Landroid/os/Environment;->buildExternalStorageAppObbDirs(Ljava/lang/String;)[Ljava/io/File; Landroid/os/Environment;->getDataSystemDirectory()Ljava/io/File; Landroid/os/Environment;->getLegacyExternalStorageObbDirectory()Ljava/io/File; -Landroid/os/Environment;->getOemDirectory()Ljava/io/File; -Landroid/os/Environment;->getVendorDirectory()Ljava/io/File; Landroid/os/Environment;->initForCurrentUser()V Landroid/os/Environment;->maybeTranslateEmulatedPathToInternal(Ljava/io/File;)Ljava/io/File; Landroid/os/Environment;->sCurrentUser:Landroid/os/Environment$UserEnvironment; @@ -1001,8 +999,6 @@ Landroid/os/UserHandle;->AID_APP_START:I Landroid/os/UserHandle;->AID_CACHE_GID_START:I Landroid/os/UserHandle;->AID_ROOT:I Landroid/os/UserHandle;->AID_SHARED_GID_START:I -Landroid/os/UserHandle;->ALL:Landroid/os/UserHandle; -Landroid/os/UserHandle;->CURRENT:Landroid/os/UserHandle; Landroid/os/UserHandle;->CURRENT_OR_SELF:Landroid/os/UserHandle; Landroid/os/UserHandle;->ERR_GID:I Landroid/os/UserHandle;->formatUid(Ljava/io/PrintWriter;I)V diff --git a/config/preloaded-classes b/config/preloaded-classes index 63c583f9264c..50e97c53fe83 100644 --- a/config/preloaded-classes +++ b/config/preloaded-classes @@ -1436,12 +1436,6 @@ android.hardware.usb.UsbDevice$1 android.hardware.usb.UsbDeviceConnection android.hardware.usb.UsbManager android.hardware.usb.UsbRequest -android.hidl.base.V1_0.DebugInfo -android.hidl.base.V1_0.IBase -android.hidl.manager.V1_0.IServiceManager -android.hidl.manager.V1_0.IServiceManager$Proxy -android.hidl.manager.V1_0.IServiceNotification -android.hidl.manager.V1_0.IServiceNotification$Stub android.icu.impl.BMPSet android.icu.impl.CacheBase android.icu.impl.CacheValue diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 446e6cc0769c..9a061b0eb79c 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -73,7 +73,7 @@ import java.util.List; * follows the established service life cycle. Starting an accessibility service is triggered * exclusively by the user explicitly turning the service on in device settings. After the system * binds to a service, it calls {@link AccessibilityService#onServiceConnected()}. This method can - * be overriden by clients that want to perform post binding setup. + * be overridden by clients that want to perform post binding setup. * </p> * <p> * An accessibility service stops either when the user turns it off in device settings or when diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 7b8664658422..17de9976c89b 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -6385,11 +6385,13 @@ public class Activity extends ContextThemeWrapper * closed for you after you return. * @param args additional arguments to the dump request. */ - public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { + public void dump(@NonNull String prefix, @Nullable FileDescriptor fd, + @NonNull PrintWriter writer, @Nullable String[] args) { dumpInner(prefix, fd, writer, args); } - void dumpInner(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { + void dumpInner(@NonNull String prefix, @Nullable FileDescriptor fd, + @NonNull PrintWriter writer, @Nullable String[] args) { if (args != null && args.length > 0 && args[0].equals("--autofill")) { dumpAutofillManager(prefix, writer); return; diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 912070144986..0e884d67d011 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -2131,7 +2131,7 @@ public final class ActivityThread extends ClientTransactionHandler { ai = getPackageManager().getApplicationInfo(packageName, PackageManager.GET_SHARED_LIBRARY_FILES | PackageManager.MATCH_DEBUG_TRIAGED_MISSING, - userId); + (userId < 0) ? UserHandle.myUserId() : userId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/AlertDialog.java b/core/java/android/app/AlertDialog.java index dbc8c5d1727b..cf40e067e5b1 100644 --- a/core/java/android/app/AlertDialog.java +++ b/core/java/android/app/AlertDialog.java @@ -397,7 +397,7 @@ public class AlertDialog extends Dialog implements DialogInterface { * @param listener The {@link DialogInterface.OnClickListener} to use. * @deprecated Use * {@link #setButton(int, CharSequence, android.content.DialogInterface.OnClickListener)} - * with {@link DialogInterface#BUTTON_POSITIVE} + * with {@link DialogInterface#BUTTON_NEUTRAL} */ @Deprecated public void setButton3(CharSequence text, final OnClickListener listener) { diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java index cd4ace669b6c..62f6bac2a24f 100644 --- a/core/java/android/app/AutomaticZenRule.java +++ b/core/java/android/app/AutomaticZenRule.java @@ -16,11 +16,14 @@ package android.app; +import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY; + import android.app.NotificationManager.InterruptionFilter; import android.content.ComponentName; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; +import android.service.notification.ZenPolicy; import java.util.Objects; @@ -35,6 +38,7 @@ public final class AutomaticZenRule implements Parcelable { private Uri conditionId; private ComponentName owner; private long creationTime; + private ZenPolicy mZenPolicy; /** * Creates an automatic zen rule. @@ -58,7 +62,27 @@ public final class AutomaticZenRule implements Parcelable { } /** - * @SystemApi + * Creates an automatic zen rule. + * + * @param name The name of the rule. + * @param owner The Condition Provider service that owns this rule. + * @param conditionId A representation of the state that should cause the Condition Provider + * service to apply the given interruption filter. + * @param policy The policy defines which notifications are allowed to interrupt the user + * while this rule is active + * @param enabled Whether the rule is enabled. + */ + public AutomaticZenRule(String name, ComponentName owner, Uri conditionId, ZenPolicy policy, + boolean enabled) { + this.name = name; + this.owner = owner; + this.conditionId = conditionId; + this.interruptionFilter = INTERRUPTION_FILTER_PRIORITY; + this.enabled = enabled; + this.mZenPolicy = policy; + } + + /** * @hide */ public AutomaticZenRule(String name, ComponentName owner, Uri conditionId, @@ -67,6 +91,15 @@ public final class AutomaticZenRule implements Parcelable { this.creationTime = creationTime; } + /** + * @hide + */ + public AutomaticZenRule(String name, ComponentName owner, Uri conditionId, ZenPolicy policy, + boolean enabled, long creationTime) { + this(name, owner, conditionId, policy, enabled); + this.creationTime = creationTime; + } + public AutomaticZenRule(Parcel source) { enabled = source.readInt() == 1; if (source.readInt() == 1) { @@ -76,6 +109,7 @@ public final class AutomaticZenRule implements Parcelable { conditionId = source.readParcelable(null); owner = source.readParcelable(null); creationTime = source.readLong(); + mZenPolicy = source.readParcelable(null); } /** @@ -114,6 +148,13 @@ public final class AutomaticZenRule implements Parcelable { } /** + * Gets the zen policy. + */ + public ZenPolicy getZenPolicy() { + return this.mZenPolicy.copy(); + } + + /** * Returns the time this rule was created, represented as milliseconds since the epoch. */ public long getCreationTime() { @@ -149,6 +190,13 @@ public final class AutomaticZenRule implements Parcelable { this.enabled = enabled; } + /** + * Sets the zen policy. + */ + public void setZenPolicy(ZenPolicy zenPolicy) { + this.mZenPolicy = zenPolicy; + } + @Override public int describeContents() { return 0; @@ -167,6 +215,7 @@ public final class AutomaticZenRule implements Parcelable { dest.writeParcelable(conditionId, 0); dest.writeParcelable(owner, 0); dest.writeLong(creationTime); + dest.writeParcelable(mZenPolicy, 0); } @Override @@ -178,6 +227,7 @@ public final class AutomaticZenRule implements Parcelable { .append(",conditionId=").append(conditionId) .append(",owner=").append(owner) .append(",creationTime=").append(creationTime) + .append(",mZenPolicy=").append(mZenPolicy) .append(']').toString(); } @@ -191,12 +241,14 @@ public final class AutomaticZenRule implements Parcelable { && other.interruptionFilter == interruptionFilter && Objects.equals(other.conditionId, conditionId) && Objects.equals(other.owner, owner) - && other.creationTime == creationTime; + && other.creationTime == creationTime + && Objects.equals(other.mZenPolicy, mZenPolicy); } @Override public int hashCode() { - return Objects.hash(enabled, name, interruptionFilter, conditionId, owner, creationTime); + return Objects.hash(enabled, name, interruptionFilter, conditionId, owner, creationTime, + mZenPolicy); } public static final Parcelable.Creator<AutomaticZenRule> CREATOR diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 4f004d93e3bf..357420bd0217 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -112,6 +112,7 @@ interface INotificationManager ParceledListSlice getActiveNotificationsFromListener(in INotificationListener token, in String[] keys, int trim); ParceledListSlice getSnoozedNotificationsFromListener(in INotificationListener token, int trim); + void clearRequestedListenerHints(in INotificationListener token); void requestHintsFromListener(in INotificationListener token, int hints); int getHintsFromListener(in INotificationListener token); void requestInterruptionFilterFromListener(in INotificationListener token, int interruptionFilter); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 9dca0614dc5f..491af6094d96 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -3499,7 +3499,7 @@ public class Notification implements Parcelable /** * Set the small icon, which will be used to represent the notification in the - * status bar and content view (unless overriden there by a + * status bar and content view (unless overridden there by a * {@link #setLargeIcon(Bitmap) large icon}). * * @param icon An Icon object to use. diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index b96b39df9aa0..9f819b9739b8 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -841,9 +841,14 @@ public class NotificationManager { List<ZenModeConfig.ZenRule> rules = service.getZenRules(); Map<String, AutomaticZenRule> ruleMap = new HashMap<>(); for (ZenModeConfig.ZenRule rule : rules) { - ruleMap.put(rule.id, new AutomaticZenRule(rule.name, rule.component, - rule.conditionId, zenModeToInterruptionFilter(rule.zenMode), rule.enabled, - rule.creationTime)); + if (rule.zenPolicy == null) { + ruleMap.put(rule.id, new AutomaticZenRule(rule.name, rule.component, + rule.conditionId, zenModeToInterruptionFilter(rule.zenMode), + rule.enabled, rule.creationTime)); + } else { + ruleMap.put(rule.id, new AutomaticZenRule(rule.name, rule.component, + rule.conditionId, rule.zenPolicy, rule.enabled, rule.creationTime)); + } } return ruleMap; } catch (RemoteException e) { diff --git a/core/java/android/content/ContentUris.java b/core/java/android/content/ContentUris.java index dbe8a7c322ae..fd7b37210822 100644 --- a/core/java/android/content/ContentUris.java +++ b/core/java/android/content/ContentUris.java @@ -18,6 +18,8 @@ package android.content; import android.net.Uri; +import java.util.List; + /** * Utility methods useful for working with {@link android.net.Uri} objects * that use the "content" (content://) scheme. @@ -109,4 +111,30 @@ public class ContentUris { public static Uri withAppendedId(Uri contentUri, long id) { return appendId(contentUri.buildUpon(), id).build(); } + + /** + * Removes any ID from the end of the path. + * + * @param contentUri that ends with an ID + * @return a new URI with the ID removed from the end of the path + * @throws IllegalArgumentException when the given URI has no ID to remove + * from the end of the path + */ + public static Uri removeId(Uri contentUri) { + // Verify that we have a valid ID to actually remove + final String last = contentUri.getLastPathSegment(); + if (last == null) { + throw new IllegalArgumentException("No path segments to remove"); + } else { + Long.parseLong(last); + } + + final List<String> segments = contentUri.getPathSegments(); + final Uri.Builder builder = contentUri.buildUpon(); + builder.path(null); + for (int i = 0; i < segments.size() - 1; i++) { + builder.appendPath(segments.get(i)); + } + return builder.build(); + } } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index ddd12a546650..3c1690a68689 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3407,7 +3407,7 @@ public abstract class Context { /** * Use with {@link #getSystemService(String)} to retrieve a - * {@link android.app.NotificationManager} for controlling keyguard. + * {@link android.app.KeyguardManager} for controlling keyguard. * * @see #getSystemService(String) * @see android.app.KeyguardManager @@ -4792,6 +4792,7 @@ public abstract class Context { * @hide */ @SystemApi + @TestApi public Context createPackageContextAsUser( String packageName, @CreatePackageOptions int flags, UserHandle user) throws PackageManager.NameNotFoundException { diff --git a/core/java/android/content/pm/AndroidHidlUpdater.java b/core/java/android/content/pm/AndroidHidlUpdater.java new file mode 100644 index 000000000000..69cc94ffef55 --- /dev/null +++ b/core/java/android/content/pm/AndroidHidlUpdater.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.content.pm; + +import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_BASE; +import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_MANAGER; + +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.hidl.base-V1.0-java + * and android.hidl.manager-V1.0-java libraries are included by default. + * + * @hide + */ +@VisibleForTesting +public class AndroidHidlUpdater extends PackageSharedLibraryUpdater { + + @Override + public void updatePackage(Package pkg) { + // This was the default <= P and is maintained for backwards compatibility. + if (pkg.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.P) { + prefixRequiredLibrary(pkg, ANDROID_HIDL_BASE); + prefixRequiredLibrary(pkg, ANDROID_HIDL_MANAGER); + } + } +} diff --git a/core/java/android/content/pm/PackageBackwardCompatibility.java b/core/java/android/content/pm/PackageBackwardCompatibility.java index a16f81b11ae6..03eefedd2b30 100644 --- a/core/java/android/content/pm/PackageBackwardCompatibility.java +++ b/core/java/android/content/pm/PackageBackwardCompatibility.java @@ -53,6 +53,8 @@ public class PackageBackwardCompatibility extends PackageSharedLibraryUpdater { "android.content.pm.OrgApacheHttpLegacyUpdater", RemoveUnnecessaryOrgApacheHttpLegacyLibrary::new); + packageUpdaters.add(new AndroidHidlUpdater()); + // Add this before adding AndroidTestBaseUpdater so that android.test.base comes before // android.test.mock. packageUpdaters.add(new AndroidTestRunnerSplitUpdater()); diff --git a/core/java/android/content/pm/SharedLibraryNames.java b/core/java/android/content/pm/SharedLibraryNames.java index 83e86636424a..387d29e81dfc 100644 --- a/core/java/android/content/pm/SharedLibraryNames.java +++ b/core/java/android/content/pm/SharedLibraryNames.java @@ -22,6 +22,10 @@ package android.content.pm; */ public class SharedLibraryNames { + static final String ANDROID_HIDL_BASE = "android.hidl.base-V1.0-java"; + + static final String ANDROID_HIDL_MANAGER = "android.hidl.manager-V1.0-java"; + static final String ANDROID_TEST_BASE = "android.test.base"; static final String ANDROID_TEST_MOCK = "android.test.mock"; diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 43e1612298fd..88b1c8803713 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -1129,10 +1129,8 @@ public class Resources { * * @throws NotFoundException Throws NotFoundException if the given ID does * not exist or is not a floating-point value. - * @hide Pending API council approval. */ - @UnsupportedAppUsage - public float getFloat(int id) { + public float getFloat(@DimenRes int id) { final TypedValue value = obtainTempTypedValue(); try { mResourcesImpl.getValue(id, value, true); diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java index 1377806cb213..19c6745ef8d4 100644 --- a/core/java/android/database/sqlite/SQLiteOpenHelper.java +++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java @@ -49,7 +49,7 @@ import java.io.File; * <p class="note"><strong>Note:</strong> this class assumes * monotonically increasing version numbers for upgrades.</p> */ -public abstract class SQLiteOpenHelper { +public abstract class SQLiteOpenHelper implements AutoCloseable { private static final String TAG = SQLiteOpenHelper.class.getSimpleName(); private final Context mContext; diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index 35584ae21869..46e66e0dbef5 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -188,6 +188,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri @UnsupportedAppUsage private final CameraMetadataNative mProperties; private List<CameraCharacteristics.Key<?>> mKeys; + private List<CameraCharacteristics.Key<?>> mKeysNeedingPermission; private List<CaptureRequest.Key<?>> mAvailableRequestKeys; private List<CaptureRequest.Key<?>> mAvailableSessionKeys; private List<CaptureRequest.Key<?>> mAvailablePhysicalRequestKeys; @@ -268,11 +269,50 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri } mKeys = Collections.unmodifiableList( - getKeys(getClass(), getKeyClass(), this, filterTags)); + getKeys(getClass(), getKeyClass(), this, filterTags, true)); return mKeys; } /** + * <p>Returns a subset of the list returned by {@link #getKeys} with all keys that + * require camera clients to obtain the {@link android.Manifest.permission#CAMERA} permission. + * </p> + * + * <p>If an application calls {@link CameraManager#getCameraCharacteristics} without holding the + * {@link android.Manifest.permission#CAMERA} permission, + * all keys in this list will not be available, and calling {@link #get} will + * return null for those keys. If the application obtains the + * {@link android.Manifest.permission#CAMERA} permission, then the + * CameraCharacteristics from a call to a subsequent + * {@link CameraManager#getCameraCharacteristics} will have the keys available.</p> + * + * <p>The list returned is not modifiable, so any attempts to modify it will throw + * a {@code UnsupportedOperationException}.</p> + * + * <p>Each key is only listed once in the list. The order of the keys is undefined.</p> + * + * @return List of camera characteristic keys that require the + * {@link android.Manifest.permission#CAMERA} permission. The list can be null in case + * there are no currently present keys that need additional permission. + */ + public List<Key<?>> getKeysNeedingPermission() { + if (mKeysNeedingPermission == null) { + Object crKey = CameraCharacteristics.Key.class; + Class<CameraCharacteristics.Key<?>> crKeyTyped = + (Class<CameraCharacteristics.Key<?>>)crKey; + + int[] filterTags = get(REQUEST_CHARACTERISTIC_KEYS_NEEDING_PERMISSION); + if (filterTags == null) { + return null; + } + mKeysNeedingPermission = + getAvailableKeyList(CameraCharacteristics.class, crKeyTyped, filterTags, + /*includeSynthetic*/ false); + } + return mKeysNeedingPermission; + } + + /** * <p>Returns a subset of {@link #getAvailableCaptureRequestKeys} keys that the * camera device can pass as part of the capture session initialization.</p> * @@ -328,17 +368,18 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri return null; } mAvailableSessionKeys = - getAvailableKeyList(CaptureRequest.class, crKeyTyped, filterTags); + getAvailableKeyList(CaptureRequest.class, crKeyTyped, filterTags, + /*includeSynthetic*/ false); } return mAvailableSessionKeys; } /** * <p>Returns a subset of {@link #getAvailableCaptureRequestKeys} keys that can - * be overriden for physical devices backing a logical multi-camera.</p> + * be overridden for physical devices backing a logical multi-camera.</p> * * <p>This is a subset of android.request.availableRequestKeys which contains a list - * of keys that can be overriden using {@link CaptureRequest.Builder#setPhysicalCameraKey }. + * of keys that can be overridden using {@link CaptureRequest.Builder#setPhysicalCameraKey }. * The respective value of such request key can be obtained by calling * {@link CaptureRequest.Builder#getPhysicalCameraKey }. Capture requests that contain * individual physical device requests must be built via @@ -353,7 +394,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * * <p>Each key is only listed once in the list. The order of the keys is undefined.</p> * - * @return List of keys that can be overriden in individual physical device requests. + * @return List of keys that can be overridden in individual physical device requests. * In case the camera device doesn't support such keys the list can be null. */ @SuppressWarnings({"unchecked"}) @@ -367,7 +408,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri return null; } mAvailablePhysicalRequestKeys = - getAvailableKeyList(CaptureRequest.class, crKeyTyped, filterTags); + getAvailableKeyList(CaptureRequest.class, crKeyTyped, filterTags, + /*includeSynthetic*/ false); } return mAvailablePhysicalRequestKeys; } @@ -399,7 +441,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri + "in the characteristics"); } mAvailableRequestKeys = - getAvailableKeyList(CaptureRequest.class, crKeyTyped, filterTags); + getAvailableKeyList(CaptureRequest.class, crKeyTyped, filterTags, + /*includeSynthetic*/ true); } return mAvailableRequestKeys; } @@ -430,7 +473,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri throw new AssertionError("android.request.availableResultKeys must be non-null " + "in the characteristics"); } - mAvailableResultKeys = getAvailableKeyList(CaptureResult.class, crKeyTyped, filterTags); + mAvailableResultKeys = getAvailableKeyList(CaptureResult.class, crKeyTyped, filterTags, + /*includeSynthetic*/ true); } return mAvailableResultKeys; } @@ -445,13 +489,16 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * * @param metadataClass The subclass of CameraMetadata that you want to get the keys for. * @param keyClass The class of the metadata key, e.g. CaptureRequest.Key.class + * @param filterTags An array of tags to be used for filtering + * @param includeSynthetic Include public syntethic tag by default. * * @return List of keys supported by this CameraDevice for metadataClass. * * @throws IllegalArgumentException if metadataClass is not a subclass of CameraMetadata */ private <TKey> List<TKey> - getAvailableKeyList(Class<?> metadataClass, Class<TKey> keyClass, int[] filterTags) { + getAvailableKeyList(Class<?> metadataClass, Class<TKey> keyClass, int[] filterTags, + boolean includeSynthetic) { if (metadataClass.equals(CameraMetadata.class)) { throw new AssertionError( @@ -462,7 +509,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri } List<TKey> staticKeyList = getKeys( - metadataClass, keyClass, /*instance*/null, filterTags); + metadataClass, keyClass, /*instance*/null, filterTags, includeSynthetic); return Collections.unmodifiableList(staticKeyList); } @@ -474,6 +521,13 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA capability. If the camera device * doesn't have the capability, the return value will be an empty set. </p> * + * <p>Prior to API level 29, all returned IDs are guaranteed to be returned by {@link + * CameraManager#getCameraIdList}, and can be opened directly by + * {@link CameraManager#openCamera}. Starting from API level 29, for each of the returned ID, + * if it's also returned by {@link CameraManager#getCameraIdList}, it can be used as a + * standalone camera by {@link CameraManager#openCamera}. Otherwise, the camera ID can only be + * used as part of the current logical camera.</p> + * * <p>The set returned is not modifiable, so any attempts to modify it will throw * a {@code UnsupportedOperationException}.</p> * @@ -1103,6 +1157,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <p><b>Limited capability</b> - * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p> + * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p> * * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL * @see CameraCharacteristics#LENS_INFO_FOCUS_DISTANCE_CALIBRATION @@ -1124,6 +1179,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <p><b>Limited capability</b> - * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p> + * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p> * * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL * @see CameraCharacteristics#LENS_INFO_FOCUS_DISTANCE_CALIBRATION @@ -1238,6 +1294,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <p><b>Units</b>: * Quaternion coefficients</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p> */ @PublicKey public static final Key<float[]> LENS_POSE_ROTATION = @@ -1273,6 +1330,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * with PRIMARY_CAMERA.</p> * <p><b>Units</b>: Meters</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p> * * @see CameraCharacteristics#LENS_DISTORTION * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION @@ -1344,6 +1402,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} * coordinate system.</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p> * * @see CameraCharacteristics#LENS_DISTORTION * @see CameraCharacteristics#LENS_POSE_ROTATION @@ -1387,6 +1446,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <p><b>Units</b>: * Unitless coefficients.</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p> * * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION * @deprecated @@ -1411,6 +1471,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <li>{@link #LENS_POSE_REFERENCE_GYROSCOPE GYROSCOPE}</li> * </ul></p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p> * * @see CameraCharacteristics#LENS_POSE_TRANSLATION * @see #LENS_POSE_REFERENCE_PRIMARY_CAMERA @@ -1452,6 +1513,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <p><b>Units</b>: * Unitless coefficients.</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p> * * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION * @see CameraCharacteristics#LENS_RADIAL_DISTORTION @@ -1860,10 +1922,10 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri new Key<int[]>("android.request.availableSessionKeys", int[].class); /** - * <p>A subset of the available request keys that can be overriden for + * <p>A subset of the available request keys that can be overridden for * physical devices backing a logical multi-camera.</p> * <p>This is a subset of android.request.availableRequestKeys which contains a list - * of keys that can be overriden using {@link CaptureRequest.Builder#setPhysicalCameraKey }. + * of keys that can be overridden using {@link CaptureRequest.Builder#setPhysicalCameraKey }. * The respective value of such request key can be obtained by calling * {@link CaptureRequest.Builder#getPhysicalCameraKey }. Capture requests that contain * individual physical device requests must be built via @@ -1880,6 +1942,21 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri new Key<int[]>("android.request.availablePhysicalCameraRequestKeys", int[].class); /** + * <p>A list of camera characteristics keys that are only available + * in case the camera client has camera permission.</p> + * <p>The entry contains a subset of + * {@link android.hardware.camera2.CameraCharacteristics#getKeys } that require camera clients + * to acquire the {@link android.Manifest.permission#CAMERA } permission before calling + * {@link android.hardware.camera2.CameraManager#getCameraCharacteristics }. If the + * permission is not held by the camera client, then the values of the repsective properties + * will not be present in {@link android.hardware.camera2.CameraCharacteristics }.</p> + * <p>This key is available on all devices.</p> + * @hide + */ + public static final Key<int[]> REQUEST_CHARACTERISTIC_KEYS_NEEDING_PERMISSION = + new Key<int[]>("android.request.characteristicKeysNeedingPermission", int[].class); + + /** * <p>The list of image formats that are supported by this * camera device for output streams.</p> * <p>All camera devices will support JPEG and YUV_420_888 formats.</p> @@ -2703,6 +2780,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <li>{@link #SENSOR_REFERENCE_ILLUMINANT1_ISO_STUDIO_TUNGSTEN ISO_STUDIO_TUNGSTEN}</li> * </ul></p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p> * * @see CameraCharacteristics#SENSOR_CALIBRATION_TRANSFORM1 * @see CameraCharacteristics#SENSOR_COLOR_TRANSFORM1 @@ -2744,6 +2822,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <p><b>Range of valid values:</b><br> * Any value listed in {@link CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT1 android.sensor.referenceIlluminant1}</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p> * * @see CameraCharacteristics#SENSOR_CALIBRATION_TRANSFORM2 * @see CameraCharacteristics#SENSOR_COLOR_TRANSFORM2 @@ -2766,6 +2845,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * space under the first reference illuminant * ({@link CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT1 android.sensor.referenceIlluminant1}).</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p> * * @see CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT1 */ @@ -2788,6 +2868,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <p>This matrix will only be present if the second reference * illuminant is present.</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p> * * @see CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT2 */ @@ -2811,6 +2892,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * match the standard white point for the first reference illuminant * (i.e. no chromatic adaptation will be applied by this transform).</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p> * * @see CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT1 */ @@ -2836,6 +2918,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <p>This matrix will only be present if the second reference * illuminant is present.</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p> * * @see CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT2 */ @@ -2857,6 +2940,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * illuminant in the reference sensor colorspace is mapped to D50 in the * CIE XYZ colorspace.</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p> * * @see CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT1 */ @@ -2880,6 +2964,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * <p>This matrix will only be present if the second reference * illuminant is present.</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p> * * @see CameraCharacteristics#SENSOR_REFERENCE_ILLUMINANT2 */ diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index 7ebe0f9a8d8d..44d7364ce43c 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -34,7 +34,6 @@ import android.os.Binder; import android.os.DeadObjectException; import android.os.Handler; import android.os.IBinder; -import android.os.Looper; import android.os.RemoteException; import android.os.ServiceManager; import android.os.ServiceSpecificException; @@ -45,7 +44,6 @@ import android.util.Log; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; - import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.RejectedExecutionException; @@ -97,6 +95,9 @@ public final class CameraManager { * identifiers, while removable cameras have a unique identifier for each * individual device, even if they are the same model.</p> * + * <p>This list doesn't contain physical cameras that can only used as part of a logical + * multi-camera device.</p> + * * @return The list of currently connected camera devices. */ @NonNull @@ -234,7 +235,13 @@ public final class CameraManager { * <p>Query the capabilities of a camera device. These capabilities are * immutable for a given camera.</p> * - * @param cameraId The id of the camera device to query + * <p>From API level 29, this function can also be used to query the capabilities of physical + * cameras that can only be used as part of logical multi-camera. These cameras cannot not be + * opened directly via {@link #openCamera}</p> + * + * @param cameraId The id of the camera device to query. This could be either a standalone + * camera ID which can be directly opened by {@link #openCamera}, or a physical camera ID that + * can only used as part of a logical multi-camera. * @return The properties of the given camera * * @throws IllegalArgumentException if the cameraId does not match any @@ -262,7 +269,9 @@ public final class CameraManager { "Camera service is currently unavailable"); } try { - if (!supportsCamera2ApiLocked(cameraId)) { + // First check isHiddenPhysicalCamera to avoid supportsCamera2ApiLocked throwing + // exception in case cameraId is a hidden physical camera. + if (!isHiddenPhysicalCamera(cameraId) && !supportsCamera2ApiLocked(cameraId)) { // Legacy backwards compatibility path; build static info from the camera // parameters int id = Integer.parseInt(cameraId); @@ -454,7 +463,7 @@ public final class CameraManager { * * @throws IllegalArgumentException if cameraId or the callback was null, * or the cameraId does not match any currently or previously available - * camera device. + * camera device returned by {@link #getCameraIdList}. * * @throws SecurityException if the application does not have permission to * access the camera @@ -778,6 +787,29 @@ public final class CameraManager { } /** + * Queries the camera service if a cameraId is a hidden physical camera that belongs to a + * logical camera device. + * + * A hidden physical camera is a camera that cannot be opened by the application. But it + * can be used as part of a logical camera. + * + * @param cameraId a non-{@code null} camera identifier + * @return {@code true} if cameraId is a hidden physical camera device + */ + private boolean isHiddenPhysicalCamera(String cameraId) { + try { + ICameraService cameraService = CameraManagerGlobal.get().getCameraService(); + // If no camera service, no support + if (cameraService == null) return false; + + return cameraService.isHiddenPhysicalCamera(cameraId); + } catch (RemoteException e) { + // Camera service is now down, no support for any API level + } + return false; + } + + /** * A per-process global camera manager instance, to retain a connection to the camera service, * and to distribute camera availability notices to API-registered callbacks */ diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index caa99d5cb2a8..486b054e1c00 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -116,7 +116,8 @@ public abstract class CameraMetadata<TKey> { public List<TKey> getKeys() { Class<CameraMetadata<TKey>> thisClass = (Class<CameraMetadata<TKey>>) getClass(); return Collections.unmodifiableList( - getKeys(thisClass, getKeyClass(), this, /*filterTags*/null)); + getKeys(thisClass, getKeyClass(), this, /*filterTags*/null, + /*includeSynthetic*/ true)); } /** @@ -131,13 +132,14 @@ public abstract class CameraMetadata<TKey> { * Optionally, if {@code filterTags} is not {@code null}, then filter out any keys * whose native {@code tag} is not in {@code filterTags}. The {@code filterTags} array will be * sorted as a side effect. + * {@code includeSynthetic} Includes public syntenthic fields by default. * </p> */ /*package*/ @SuppressWarnings("unchecked") <TKey> ArrayList<TKey> getKeys( Class<?> type, Class<TKey> keyClass, CameraMetadata<TKey> instance, - int[] filterTags) { + int[] filterTags, boolean includeSynthetic) { if (DEBUG) Log.v(TAG, "getKeysStatic for " + type); @@ -168,7 +170,7 @@ public abstract class CameraMetadata<TKey> { } if (instance == null || instance.getProtected(key) != null) { - if (shouldKeyBeAdded(key, field, filterTags)) { + if (shouldKeyBeAdded(key, field, filterTags, includeSynthetic)) { keyList.add(key); if (DEBUG) { @@ -215,7 +217,8 @@ public abstract class CameraMetadata<TKey> { } @SuppressWarnings("rawtypes") - private static <TKey> boolean shouldKeyBeAdded(TKey key, Field field, int[] filterTags) { + private static <TKey> boolean shouldKeyBeAdded(TKey key, Field field, int[] filterTags, + boolean includeSynthetic) { if (key == null) { throw new NullPointerException("key must not be null"); } @@ -249,8 +252,7 @@ public abstract class CameraMetadata<TKey> { if (field.getAnnotation(SyntheticKey.class) != null) { // This key is synthetic, so calling #getTag will throw IAE - // TODO: don't just assume all public+synthetic keys are always available - return true; + return includeSynthetic; } /* @@ -811,8 +813,14 @@ public abstract class CameraMetadata<TKey> { public static final int REQUEST_AVAILABLE_CAPABILITIES_MOTION_TRACKING = 10; /** - * <p>The camera device is a logical camera backed by two or more physical cameras that are - * also exposed to the application.</p> + * <p>The camera device is a logical camera backed by two or more physical cameras. In + * API level 28, the physical cameras must also be exposed to the application via + * {@link android.hardware.camera2.CameraManager#getCameraIdList }. Starting from API + * level 29, some or all physical cameras may not be independently exposed to the + * application, in which case the physical camera IDs will not be available in + * {@link android.hardware.camera2.CameraManager#getCameraIdList }. But the application + * can still query the physical cameras' characteristics by calling + * {@link android.hardware.camera2.CameraManager#getCameraCharacteristics }.</p> * <p>Camera application shouldn't assume that there are at most 1 rear camera and 1 front * camera in the system. For an application that switches between front and back cameras, * the recommendation is to switch between the first rear camera and the first front diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index 3c6c11d24c7c..a7e185c9853b 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -2841,6 +2841,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * <p><b>Units</b>: * Quaternion coefficients</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p> */ @PublicKey public static final Key<float[]> LENS_POSE_ROTATION = @@ -2876,6 +2877,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * with PRIMARY_CAMERA.</p> * <p><b>Units</b>: Meters</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p> * * @see CameraCharacteristics#LENS_DISTORTION * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION @@ -2947,6 +2949,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * {@link CameraCharacteristics#SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE android.sensor.info.preCorrectionActiveArraySize} * coordinate system.</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p> * * @see CameraCharacteristics#LENS_DISTORTION * @see CameraCharacteristics#LENS_POSE_ROTATION @@ -2990,6 +2993,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * <p><b>Units</b>: * Unitless coefficients.</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p> * * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION * @deprecated @@ -3036,6 +3040,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * <p><b>Units</b>: * Unitless coefficients.</p> * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p> * * @see CameraCharacteristics#LENS_INTRINSIC_CALIBRATION * @see CameraCharacteristics#LENS_RADIAL_DISTORTION diff --git a/core/java/android/net/NetworkFactory.java b/core/java/android/net/NetworkFactory.java index 010d72f3b17a..9c836c3382db 100644 --- a/core/java/android/net/NetworkFactory.java +++ b/core/java/android/net/NetworkFactory.java @@ -211,7 +211,7 @@ public class NetworkFactory extends Handler { * Called for every request every time a new NetworkRequest is seen * and whenever the filterScore or filterNetworkCapabilities change. * - * acceptRequest can be overriden to provide complex filter behavior + * acceptRequest can be overridden to provide complex filter behavior * for the incoming requests * * For output, this class will call {@link #needNetworkFor} and diff --git a/core/java/android/net/NetworkSpecifier.java b/core/java/android/net/NetworkSpecifier.java index 9ce2a5bd1b54..be2f9551daff 100644 --- a/core/java/android/net/NetworkSpecifier.java +++ b/core/java/android/net/NetworkSpecifier.java @@ -35,7 +35,7 @@ public abstract class NetworkSpecifier { public abstract boolean satisfiedBy(NetworkSpecifier other); /** - * Optional method which can be overriden by concrete implementations of NetworkSpecifier to + * Optional method which can be overridden by concrete implementations of NetworkSpecifier to * check a self-reported UID. A concrete implementation may contain a UID which would be self- * reported by the caller (since NetworkSpecifier implementations should be non-mutable). This * function is called by ConnectivityService and is passed the actual UID of the caller - diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index 347f60f6b9be..3c43fd189337 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -16,6 +16,7 @@ package android.os; +import android.annotation.SystemApi; import android.annotation.TestApi; import android.app.admin.DevicePolicyManager; import android.content.Context; @@ -165,6 +166,7 @@ public class Environment { * * @hide */ + @SystemApi public static File getOemDirectory() { return DIR_OEM_ROOT; } @@ -175,6 +177,7 @@ public class Environment { * * @hide */ + @SystemApi public static File getOdmDirectory() { return DIR_ODM_ROOT; } @@ -184,6 +187,7 @@ public class Environment { * software that should persist across simple reflashing of the "system" partition. * @hide */ + @SystemApi public static File getVendorDirectory() { return DIR_VENDOR_ROOT; } @@ -194,6 +198,7 @@ public class Environment { * * @hide */ + @SystemApi public static File getProductDirectory() { return DIR_PRODUCT_ROOT; } @@ -204,6 +209,7 @@ public class Environment { * * @hide */ + @SystemApi public static File getProductServicesDirectory() { return DIR_PRODUCT_SERVICES_ROOT; } @@ -1063,7 +1069,6 @@ public class Environment { return cur; } - /** * If the given path exists on emulated external storage, return the * translated backing path hosted on internal storage. This bypasses any @@ -1074,8 +1079,10 @@ public class Environment { * must hold {@link android.Manifest.permission#WRITE_MEDIA_STORAGE} * permission. * + * @deprecated disabled now that FUSE has been replaced by sdcardfs * @hide */ + @Deprecated public static File maybeTranslateEmulatedPathToInternal(File path) { return StorageManager.maybeTranslateEmulatedPathToInternal(path); } diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index 1c8029caab70..a9cb0d9579c9 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -16,6 +16,18 @@ package android.os; +import static android.os.ParcelFileDescriptor.MODE_APPEND; +import static android.os.ParcelFileDescriptor.MODE_CREATE; +import static android.os.ParcelFileDescriptor.MODE_READ_ONLY; +import static android.os.ParcelFileDescriptor.MODE_READ_WRITE; +import static android.os.ParcelFileDescriptor.MODE_TRUNCATE; +import static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY; +import static android.system.OsConstants.O_APPEND; +import static android.system.OsConstants.O_CREAT; +import static android.system.OsConstants.O_RDONLY; +import static android.system.OsConstants.O_RDWR; +import static android.system.OsConstants.O_TRUNC; +import static android.system.OsConstants.O_WRONLY; import static android.system.OsConstants.SPLICE_F_MORE; import static android.system.OsConstants.SPLICE_F_MOVE; import static android.system.OsConstants.S_ISFIFO; @@ -1061,8 +1073,13 @@ public class FileUtils { mimeTypeFromExt = ContentResolver.MIME_TYPE_DEFAULT; } - final String extFromMimeType = MimeTypeMap.getSingleton().getExtensionFromMimeType( - mimeType); + final String extFromMimeType; + if (ContentResolver.MIME_TYPE_DEFAULT.equals(mimeType)) { + extFromMimeType = null; + } else { + extFromMimeType = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType); + } + if (Objects.equals(mimeType, mimeTypeFromExt) || Objects.equals(ext, extFromMimeType)) { // Extension maps back to requested MIME type; allow it } else { @@ -1180,6 +1197,96 @@ public class FileUtils { } /** {@hide} */ + public static int translateModeStringToPosix(String mode) { + int res = 0; + if (mode.startsWith("rw")) { + res |= O_RDWR | O_CREAT; + } else if (mode.startsWith("w")) { + res |= O_WRONLY | O_CREAT; + } else if (mode.startsWith("r")) { + res |= O_RDONLY; + } else { + throw new IllegalArgumentException("Bad mode: " + mode); + } + if (mode.indexOf('t') != -1) { + res |= O_TRUNC; + } + if (mode.indexOf('a') != -1) { + res |= O_APPEND; + } + return res; + } + + /** {@hide} */ + public static String translateModePosixToString(int mode) { + String res = ""; + if ((mode & O_RDWR) == O_RDWR) { + res += "rw"; + } else if ((mode & O_WRONLY) == O_WRONLY) { + res += "w"; + } else if ((mode & O_RDONLY) == O_RDONLY) { + res += "r"; + } else { + throw new IllegalArgumentException("Bad mode: " + mode); + } + if ((mode & O_TRUNC) == O_TRUNC) { + res += "t"; + } + if ((mode & O_APPEND) == O_APPEND) { + res += "a"; + } + return res; + } + + /** {@hide} */ + public static int translateModePosixToPfd(int mode) { + int res = 0; + if ((mode & O_RDWR) == O_RDWR) { + res |= MODE_READ_WRITE; + } else if ((mode & O_WRONLY) == O_WRONLY) { + res |= MODE_WRITE_ONLY; + } else if ((mode & O_RDONLY) == O_RDONLY) { + res |= MODE_READ_ONLY; + } else { + throw new IllegalArgumentException("Bad mode: " + mode); + } + if ((mode & O_CREAT) == O_CREAT) { + res |= MODE_CREATE; + } + if ((mode & O_TRUNC) == O_TRUNC) { + res |= MODE_TRUNCATE; + } + if ((mode & O_APPEND) == O_APPEND) { + res |= MODE_APPEND; + } + return res; + } + + /** {@hide} */ + public static int translateModePfdToPosix(int mode) { + int res = 0; + if ((mode & MODE_READ_WRITE) == MODE_READ_WRITE) { + res |= O_RDWR; + } else if ((mode & MODE_WRITE_ONLY) == MODE_WRITE_ONLY) { + res |= O_WRONLY; + } else if ((mode & MODE_READ_ONLY) == MODE_READ_ONLY) { + res |= O_RDONLY; + } else { + throw new IllegalArgumentException("Bad mode: " + mode); + } + if ((mode & MODE_CREATE) == MODE_CREATE) { + res |= O_CREAT; + } + if ((mode & MODE_TRUNCATE) == MODE_TRUNCATE) { + res |= O_TRUNC; + } + if ((mode & MODE_APPEND) == MODE_APPEND) { + res |= O_APPEND; + } + return res; + } + + /** {@hide} */ @VisibleForTesting public static class MemoryPipe extends Thread implements AutoCloseable { private final FileDescriptor[] pipe; diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java index c9edc531d134..a54c589ab26f 100644 --- a/core/java/android/os/ParcelFileDescriptor.java +++ b/core/java/android/os/ParcelFileDescriptor.java @@ -559,28 +559,7 @@ public class ParcelFileDescriptor implements Parcelable, Closeable { * @throws IllegalArgumentException if the given string does not match a known file mode. */ public static int parseMode(String mode) { - final int modeBits; - if ("r".equals(mode)) { - modeBits = ParcelFileDescriptor.MODE_READ_ONLY; - } else if ("w".equals(mode) || "wt".equals(mode)) { - modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY - | ParcelFileDescriptor.MODE_CREATE - | ParcelFileDescriptor.MODE_TRUNCATE; - } else if ("wa".equals(mode)) { - modeBits = ParcelFileDescriptor.MODE_WRITE_ONLY - | ParcelFileDescriptor.MODE_CREATE - | ParcelFileDescriptor.MODE_APPEND; - } else if ("rw".equals(mode)) { - modeBits = ParcelFileDescriptor.MODE_READ_WRITE - | ParcelFileDescriptor.MODE_CREATE; - } else if ("rwt".equals(mode)) { - modeBits = ParcelFileDescriptor.MODE_READ_WRITE - | ParcelFileDescriptor.MODE_CREATE - | ParcelFileDescriptor.MODE_TRUNCATE; - } else { - throw new IllegalArgumentException("Bad mode '" + mode + "'"); - } - return modeBits; + return FileUtils.translateModePosixToPfd(FileUtils.translateModeStringToPosix(mode)); } /** diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java index 995156a5a6c5..4fe2d58ff75f 100644 --- a/core/java/android/os/UserHandle.java +++ b/core/java/android/os/UserHandle.java @@ -38,12 +38,16 @@ public final class UserHandle implements Parcelable { public static final @UserIdInt int USER_ALL = -1; /** @hide A user handle to indicate all users on the device */ + @SystemApi + @TestApi public static final UserHandle ALL = new UserHandle(USER_ALL); /** @hide A user id to indicate the currently active user */ public static final @UserIdInt int USER_CURRENT = -2; /** @hide A user handle to indicate the current user of the device */ + @SystemApi + @TestApi public static final UserHandle CURRENT = new UserHandle(USER_CURRENT); /** @hide A user id to indicate that we would like to send to the current @@ -83,6 +87,7 @@ public final class UserHandle implements Parcelable { public static final int USER_SERIAL_SYSTEM = 0; /** @hide A user handle to indicate the "system" user of the device */ + @SystemApi @TestApi public static final UserHandle SYSTEM = new UserHandle(USER_SYSTEM); diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 8d5017b4e176..50ca4abd6713 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -1521,7 +1521,11 @@ public class StorageManager { return SystemProperties.getBoolean(PROP_HAS_ADOPTABLE, false); } - /** {@hide} */ + /** + * @deprecated disabled now that FUSE has been replaced by sdcardfs + * @hide + */ + @Deprecated public static File maybeTranslateEmulatedPathToInternal(File path) { // Disabled now that FUSE has been replaced by sdcardfs return path; diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java index b12d9cf37485..ee64ca2b8673 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -1555,7 +1555,7 @@ public final class DocumentsContract { * * <p>Note, that due to internal limitations, if there is already a web link * intent created for the specified document but with different options, - * then it may be overriden. + * then it may be overridden. * * <p>Providers are required to show confirmation UI for all new permissions granted * for the linked document. diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java index 81b1921dd809..0e782d752815 100644 --- a/core/java/android/provider/DocumentsProvider.java +++ b/core/java/android/provider/DocumentsProvider.java @@ -771,7 +771,7 @@ public abstract class DocumentsProvider extends ContentProvider { } /** - * Implementation is provided by the parent class. Cannot be overriden. + * Implementation is provided by the parent class. Cannot be overridden. * * @see #queryRoots(String[]) * @see #queryRecentDocuments(String, String[]) @@ -828,7 +828,7 @@ public abstract class DocumentsProvider extends ContentProvider { } /** - * Implementation is provided by the parent class. Cannot be overriden. + * Implementation is provided by the parent class. Cannot be overridden. * * @see #getDocumentType(String) */ @@ -903,7 +903,7 @@ public abstract class DocumentsProvider extends ContentProvider { /** * Implementation is provided by the parent class. Throws by default, and - * cannot be overriden. + * cannot be overridden. * * @see #createDocument(String, String, String) */ @@ -914,7 +914,7 @@ public abstract class DocumentsProvider extends ContentProvider { /** * Implementation is provided by the parent class. Throws by default, and - * cannot be overriden. + * cannot be overridden. * * @see #deleteDocument(String) */ @@ -925,7 +925,7 @@ public abstract class DocumentsProvider extends ContentProvider { /** * Implementation is provided by the parent class. Throws by default, and - * cannot be overriden. + * cannot be overridden. */ @Override public final int update( @@ -1165,7 +1165,7 @@ public abstract class DocumentsProvider extends ContentProvider { } /** - * Implementation is provided by the parent class. Cannot be overriden. + * Implementation is provided by the parent class. Cannot be overridden. * * @see #openDocument(String, String, CancellationSignal) */ @@ -1176,7 +1176,7 @@ public abstract class DocumentsProvider extends ContentProvider { } /** - * Implementation is provided by the parent class. Cannot be overriden. + * Implementation is provided by the parent class. Cannot be overridden. * * @see #openDocument(String, String, CancellationSignal) */ @@ -1188,7 +1188,7 @@ public abstract class DocumentsProvider extends ContentProvider { } /** - * Implementation is provided by the parent class. Cannot be overriden. + * Implementation is provided by the parent class. Cannot be overridden. * * @see #openDocument(String, String, CancellationSignal) */ @@ -1202,7 +1202,7 @@ public abstract class DocumentsProvider extends ContentProvider { } /** - * Implementation is provided by the parent class. Cannot be overriden. + * Implementation is provided by the parent class. Cannot be overridden. * * @see #openDocument(String, String, CancellationSignal) */ @@ -1216,7 +1216,7 @@ public abstract class DocumentsProvider extends ContentProvider { } /** - * Implementation is provided by the parent class. Cannot be overriden. + * Implementation is provided by the parent class. Cannot be overridden. * * @see #openDocumentThumbnail(String, Point, CancellationSignal) * @see #openTypedDocument(String, String, Bundle, CancellationSignal) @@ -1229,7 +1229,7 @@ public abstract class DocumentsProvider extends ContentProvider { } /** - * Implementation is provided by the parent class. Cannot be overriden. + * Implementation is provided by the parent class. Cannot be overridden. * * @see #openDocumentThumbnail(String, Point, CancellationSignal) * @see #openTypedDocument(String, String, Bundle, CancellationSignal) diff --git a/core/java/android/provider/SearchIndexablesProvider.java b/core/java/android/provider/SearchIndexablesProvider.java index 138e77b69627..02a5e6fd157f 100644 --- a/core/java/android/provider/SearchIndexablesProvider.java +++ b/core/java/android/provider/SearchIndexablesProvider.java @@ -181,7 +181,7 @@ public abstract class SearchIndexablesProvider extends ContentProvider { } /** - * Implementation is provided by the parent class. Throws by default, and cannot be overriden. + * Implementation is provided by the parent class. Throws by default, and cannot be overridden. */ @Override public final Uri insert(Uri uri, ContentValues values) { @@ -189,7 +189,7 @@ public abstract class SearchIndexablesProvider extends ContentProvider { } /** - * Implementation is provided by the parent class. Throws by default, and cannot be overriden. + * Implementation is provided by the parent class. Throws by default, and cannot be overridden. */ @Override public final int delete(Uri uri, String selection, String[] selectionArgs) { @@ -197,7 +197,7 @@ public abstract class SearchIndexablesProvider extends ContentProvider { } /** - * Implementation is provided by the parent class. Throws by default, and cannot be overriden. + * Implementation is provided by the parent class. Throws by default, and cannot be overridden. */ @Override public final int update( diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index d9ed2aafc9cd..3319c9ecb605 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -989,6 +989,21 @@ public abstract class NotificationListenerService extends Service { } /** + * Clears listener hints set via {@link #getCurrentListenerHints()}. + * + * <p>The service should wait for the {@link #onListenerConnected()} event + * before performing this operation. + */ + public final void clearRequestedListenerHints() { + if (!isBound()) return; + try { + getNotificationInterface().clearRequestedListenerHints(mWrapper); + } catch (android.os.RemoteException ex) { + Log.v(TAG, "Unable to contact notification manager", ex); + } + } + + /** * Sets the desired {@link #getCurrentListenerHints() listener hints}. * * <p> diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index f90eb14c796b..f2fa45d59c1e 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -16,6 +16,7 @@ package android.service.notification; +import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_REMINDERS; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; @@ -763,6 +764,145 @@ public class ZenModeConfig implements Parcelable { } }; + /** + * @return notification policy based on manual and automatic rules + */ + public Policy getConsolidatedNotificationPolicy() { + ZenPolicy policy = new ZenPolicy(); + + // assumption: manual rule always uses the default policy + for (ZenRule rule : automaticRules.values()) { + if (rule.isAutomaticActive()) { + if (rule.zenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) { + policy.apply(rule.zenPolicy); + } + } + } + + return toNotificationPolicy(policy); + } + + /** + * Converts a zenPolicy to a notificationPolicy using this ZenModeConfig's values as its + * defaults for all unset values in zenPolicy + */ + public Policy toNotificationPolicy(ZenPolicy zenPolicy) { + NotificationManager.Policy defaultPolicy = toNotificationPolicy(); + int priorityCategories = 0; + int suppressedVisualEffects = 0; + int callSenders = defaultPolicy.priorityCallSenders; + int messageSenders = defaultPolicy.priorityMessageSenders; + + if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_REMINDERS, + isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_REMINDERS, defaultPolicy))) { + priorityCategories |= PRIORITY_CATEGORY_REMINDERS; + } + + if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_EVENTS, + isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_EVENTS, defaultPolicy))) { + priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS; + } + + if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_MESSAGES, + isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_MESSAGES, defaultPolicy))) { + priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES; + messageSenders = getNotificationPolicySenders(zenPolicy.getPriorityMessageSenders()); + } + + if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_CALLS, + isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_CALLS, defaultPolicy))) { + priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS; + messageSenders = getNotificationPolicySenders(zenPolicy.getPriorityCallSenders()); + } + + if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS, + isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_REPEAT_CALLERS, + defaultPolicy))) { + priorityCategories |= Policy.PRIORITY_CATEGORY_REPEAT_CALLERS; + } + + if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_ALARMS, + isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_ALARMS, defaultPolicy))) { + priorityCategories |= Policy.PRIORITY_CATEGORY_ALARMS; + } + + if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_MEDIA, + isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_MEDIA, defaultPolicy))) { + priorityCategories |= Policy.PRIORITY_CATEGORY_MEDIA; + } + + if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_SYSTEM, + isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_SYSTEM, defaultPolicy))) { + priorityCategories |= Policy.PRIORITY_CATEGORY_SYSTEM; + } + + if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT, + isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT, + defaultPolicy))) { + suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT; + } + + if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_LIGHTS, + isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_LIGHTS, + defaultPolicy))) { + suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_LIGHTS; + } + + if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_PEEK, + isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_PEEK, + defaultPolicy))) { + suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_PEEK; + } + + if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_STATUS_BAR, + isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_STATUS_BAR, + defaultPolicy))) { + suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_STATUS_BAR; + } + + if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_BADGE, + isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_BADGE, + defaultPolicy))) { + suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE; + } + + if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_AMBIENT, + isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_AMBIENT, + defaultPolicy))) { + suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_AMBIENT; + } + + if (!zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST, + isVisualEffectAllowed(Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST, + defaultPolicy))) { + suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST; + } + + return new NotificationManager.Policy(priorityCategories, callSenders, + messageSenders, suppressedVisualEffects); + } + + private boolean isPriorityCategoryEnabled(int categoryType, Policy policy) { + return (policy.priorityCategories & categoryType) != 0; + } + + private boolean isVisualEffectAllowed(int visualEffect, Policy policy) { + return (policy.suppressedVisualEffects & visualEffect) == 0; + } + + private int getNotificationPolicySenders(@ZenPolicy.PeopleType int senders) { + switch (senders) { + case ZenPolicy.PEOPLE_TYPE_ANYONE: + return Policy.PRIORITY_SENDERS_ANY; + case ZenPolicy.PEOPLE_TYPE_CONTACTS: + return Policy.PRIORITY_SENDERS_CONTACTS; + case ZenPolicy.PEOPLE_TYPE_STARRED: + default: + return Policy.PRIORITY_SENDERS_STARRED; + } + + } + public Policy toNotificationPolicy() { int priorityCategories = 0; int priorityCallSenders = Policy.PRIORITY_SENDERS_CONTACTS; @@ -1315,6 +1455,7 @@ public class ZenModeConfig implements Parcelable { @UnsupportedAppUsage public long creationTime; // required for automatic public String enabler; // package name, only used for manual rules. + public ZenPolicy zenPolicy; public ZenRule() { } @@ -1335,6 +1476,7 @@ public class ZenModeConfig implements Parcelable { if (source.readInt() == 1) { enabler = source.readString(); } + zenPolicy = source.readParcelable(null); } @Override @@ -1369,6 +1511,7 @@ public class ZenModeConfig implements Parcelable { } else { dest.writeInt(0); } + dest.writeParcelable(zenPolicy, 0); } @Override @@ -1384,6 +1527,7 @@ public class ZenModeConfig implements Parcelable { .append(",component=").append(component) .append(",creationTime=").append(creationTime) .append(",enabler=").append(enabler) + .append(",zenPolicy=").append(zenPolicy) .append(']').toString(); } @@ -1407,7 +1551,7 @@ public class ZenModeConfig implements Parcelable { if (component != null) { component.writeToProto(proto, ZenRuleProto.COMPONENT); } - + // TODO: write zenPolicy to proto (b/115370281) proto.end(token); } @@ -1454,9 +1598,12 @@ public class ZenModeConfig implements Parcelable { if (creationTime != to.creationTime) { d.addLine(item, "creationTime", creationTime, to.creationTime); } - if (enabler != to.enabler) { + if (!Objects.equals(enabler, to.enabler)) { d.addLine(item, "enabler", enabler, to.enabler); } + if (!Objects.equals(zenPolicy, to.zenPolicy)) { + d.addLine(item, "zenPolicy", zenPolicy, to.zenPolicy); + } } @Override @@ -1472,13 +1619,14 @@ public class ZenModeConfig implements Parcelable { && Objects.equals(other.condition, condition) && Objects.equals(other.component, component) && Objects.equals(other.id, id) - && Objects.equals(other.enabler, enabler); + && Objects.equals(other.enabler, enabler) + && Objects.equals(other.zenPolicy, zenPolicy); } @Override public int hashCode() { return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition, - component, id, enabler); + component, id, enabler, zenPolicy); } public boolean isAutomaticActive() { diff --git a/core/java/android/service/notification/ZenPolicy.java b/core/java/android/service/notification/ZenPolicy.java new file mode 100644 index 000000000000..88825f08967e --- /dev/null +++ b/core/java/android/service/notification/ZenPolicy.java @@ -0,0 +1,868 @@ +/* + * 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.notification; + +import android.annotation.IntDef; +import android.app.Notification; +import android.app.NotificationChannel; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Objects; + +/** + * ZenPolicy determines whether to allow certain notifications and their corresponding sounds to + * play when a device is in Do Not Disturb mode. + * ZenPolicy also dictates the visual effects of notifications that are intercepted when + * a device is in Do Not Disturb mode. + */ +public final class ZenPolicy implements Parcelable { + private ArrayList<Integer> mPriorityCategories; + private ArrayList<Integer> mVisualEffects; + private @PeopleType int mPriorityMessages = PEOPLE_TYPE_UNSET; + private @PeopleType int mPriorityCalls = PEOPLE_TYPE_UNSET; + + /** @hide */ + @IntDef(prefix = { "PRIORITY_CATEGORY_" }, value = { + PRIORITY_CATEGORY_REMINDERS, + PRIORITY_CATEGORY_EVENTS, + PRIORITY_CATEGORY_MESSAGES, + PRIORITY_CATEGORY_CALLS, + PRIORITY_CATEGORY_REPEAT_CALLERS, + PRIORITY_CATEGORY_ALARMS, + PRIORITY_CATEGORY_MEDIA, + PRIORITY_CATEGORY_SYSTEM, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface PriorityCategory {} + + /** @hide */ + public static final int PRIORITY_CATEGORY_REMINDERS = 0; + /** @hide */ + public static final int PRIORITY_CATEGORY_EVENTS = 1; + /** @hide */ + public static final int PRIORITY_CATEGORY_MESSAGES = 2; + /** @hide */ + public static final int PRIORITY_CATEGORY_CALLS = 3; + /** @hide */ + public static final int PRIORITY_CATEGORY_REPEAT_CALLERS = 4; + /** @hide */ + public static final int PRIORITY_CATEGORY_ALARMS = 5; + /** @hide */ + public static final int PRIORITY_CATEGORY_MEDIA = 6; + /** @hide */ + public static final int PRIORITY_CATEGORY_SYSTEM = 7; + + /** @hide */ + @IntDef(prefix = { "VISUAL_EFFECT_" }, value = { + VISUAL_EFFECT_FULL_SCREEN_INTENT, + VISUAL_EFFECT_LIGHTS, + VISUAL_EFFECT_PEEK, + VISUAL_EFFECT_STATUS_BAR, + VISUAL_EFFECT_BADGE, + VISUAL_EFFECT_AMBIENT, + VISUAL_EFFECT_NOTIFICATION_LIST, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface VisualEffect {} + + /** @hide */ + public static final int VISUAL_EFFECT_FULL_SCREEN_INTENT = 0; + /** @hide */ + public static final int VISUAL_EFFECT_LIGHTS = 1; + /** @hide */ + public static final int VISUAL_EFFECT_PEEK = 2; + /** @hide */ + public static final int VISUAL_EFFECT_STATUS_BAR = 3; + /** @hide */ + public static final int VISUAL_EFFECT_BADGE = 4; + /** @hide */ + public static final int VISUAL_EFFECT_AMBIENT = 5; + /** @hide */ + public static final int VISUAL_EFFECT_NOTIFICATION_LIST = 6; + + /** @hide */ + @IntDef(prefix = { "PEOPLE_TYPE_" }, value = { + PEOPLE_TYPE_UNSET, + PEOPLE_TYPE_ANYONE, + PEOPLE_TYPE_CONTACTS, + PEOPLE_TYPE_STARRED, + PEOPLE_TYPE_NONE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface PeopleType {} + + /** + * Used to indicate no preference for the type of people that can bypass dnd for either + * calls or messages. + */ + public static final int PEOPLE_TYPE_UNSET = 0; + + /** + * Used to indicate all calls or messages can bypass dnd. + */ + public static final int PEOPLE_TYPE_ANYONE = 1; + + /** + * Used to indicate calls or messages from contacts can bypass dnd. + */ + public static final int PEOPLE_TYPE_CONTACTS = 2; + + /** + * Used to indicate calls or messages from starred contacts can bypass dnd. + */ + public static final int PEOPLE_TYPE_STARRED = 3; + + /** + * Used to indicate no calls or messages can bypass dnd. + */ + public static final int PEOPLE_TYPE_NONE = 4; + + /** @hide */ + @IntDef(prefix = { "STATE_" }, value = { + STATE_UNSET, + STATE_ALLOW, + STATE_DISALLOW, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface State {} + + /** + * Indicates no preference for whether a type of sound or visual effect is or isn't allowed + * to play/show when DND is active. Will default to the current set policy. + */ + public static final int STATE_UNSET = 0; + + /** + * Indicates a type of sound or visual effect is allowed to play/show when DND is active. + */ + public static final int STATE_ALLOW = 1; + + /** + * Indicates a type of sound or visual effect is not allowed to play/show when DND is active. + */ + public static final int STATE_DISALLOW = 2; + + /** @hide */ + public ZenPolicy() { + mPriorityCategories = new ArrayList<>(Collections.nCopies(8, 0)); + mVisualEffects = new ArrayList<>(Collections.nCopies(7, 0)); + } + + /** + * Message senders that can bypass DND. + * @return {@link #PEOPLE_TYPE_UNSET}, {@link #PEOPLE_TYPE_ANYONE}, + * {@link #PEOPLE_TYPE_CONTACTS}, {@link #PEOPLE_TYPE_STARRED} or {@link #PEOPLE_TYPE_NONE} + */ + public @PeopleType int getPriorityMessageSenders() { + return mPriorityMessages; + } + + /** + * Callers that can bypass DND. + * @return {@link #PEOPLE_TYPE_UNSET}, {@link #PEOPLE_TYPE_ANYONE}, + * {@link #PEOPLE_TYPE_CONTACTS}, {@link #PEOPLE_TYPE_STARRED} or {@link #PEOPLE_TYPE_NONE} + */ + public @PeopleType int getPriorityCallSenders() { + return mPriorityCalls; + } + + /** + * Whether this policy wants to allow notifications with category + * {@link Notification#CATEGORY_REMINDER} to play sounds and visually appear + * or to intercept them when DND is active. + * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW} + */ + public @State int getPriorityCategoryReminders() { + return mPriorityCategories.get(PRIORITY_CATEGORY_REMINDERS); + } + + /** + * Whether this policy wants to allow notifications with category + * {@link Notification#CATEGORY_EVENT} to play sounds and visually appear + * or to intercept them when DND is active. + * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW} + */ + public @State int getPriorityCategoryEvents() { + return mPriorityCategories.get(PRIORITY_CATEGORY_EVENTS); + } + + /** + * Whether this policy wants to allow notifications with category + * {@link Notification#CATEGORY_MESSAGE} to play sounds and visually appear + * or to intercept them when DND is active. Types of message senders that are allowed + * are specified by {@link #getPriorityMessageSenders}. + * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW} + */ + public @State int getPriorityCategoryMessages() { + return mPriorityCategories.get(PRIORITY_CATEGORY_MESSAGES); + } + + /** + * Whether this policy wants to allow notifications with category + * {@link Notification#CATEGORY_CALL} to play sounds and visually appear + * or to intercept them when DND is active. Types of callers that are allowed + * are specified by {@link #getPriorityCallSenders()}. + * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW} + */ + public @State int getPriorityCategoryCalls() { + return mPriorityCategories.get(PRIORITY_CATEGORY_CALLS); + } + + /** + * Whether this policy wants to allow repeat callers (notifications with category + * {@link Notification#CATEGORY_CALL} that have recently called) to play sounds and + * visually appear or to intercept them when DND is active. + * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW} + */ + public @State int getPriorityCategoryRepeatCallers() { + return mPriorityCategories.get(PRIORITY_CATEGORY_REPEAT_CALLERS); + } + + /** + * Whether this policy wants to allow notifications with category + * {@link Notification#CATEGORY_ALARM} to play sounds and visually appear + * or to intercept them when DND is active. + * When alarms are {@link #STATE_DISALLOW disallowed}, the alarm stream will be muted when DND + * is active. + * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW} + */ + public @State int getPriorityCategoryAlarms() { + return mPriorityCategories.get(PRIORITY_CATEGORY_ALARMS); + } + + /** + * Whether this policy wants to allow media notifications to play sounds and visually appear + * or to intercept them when DND is active. + * When media is {@link #STATE_DISALLOW disallowed}, the media stream will be muted when DND is + * active. + * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW} + */ + public @State int getPriorityCategoryMedia() { + return mPriorityCategories.get(PRIORITY_CATEGORY_MEDIA); + } + + /** + * Whether this policy wants to allow system sounds when DND is active. + * When system is {@link #STATE_DISALLOW}, the system stream will be muted when DND is active. + * @return {@link #STATE_UNSET}, {@link #STATE_ALLOW} or {@link #STATE_DISALLOW} + */ + public @State int getPriorityCategorySystem() { + return mPriorityCategories.get(PRIORITY_CATEGORY_SYSTEM); + } + + /** + * Whether this policy allows {@link Notification#fullScreenIntent full screen intents} from + * notifications intercepted by DND. + */ + public @State int getVisualEffectFullScreenIntent() { + return mVisualEffects.get(VISUAL_EFFECT_FULL_SCREEN_INTENT); + } + + /** + * Whether this policy allows {@link NotificationChannel#shouldShowLights() notification + * lights} from notifications intercepted by DND. + */ + public @State int getVisualEffectLights() { + return mVisualEffects.get(VISUAL_EFFECT_LIGHTS); + } + + /** + * Whether this policy allows peeking from notifications intercepted by DND. + */ + public @State int getVisualEffectPeek() { + return mVisualEffects.get(VISUAL_EFFECT_PEEK); + } + + /** + * Whether this policy allows notifications intercepted by DND from appearing in the status bar + * on devices that support status bars. + */ + public @State int getVisualEffectStatusBar() { + return mVisualEffects.get(VISUAL_EFFECT_STATUS_BAR); + } + + /** + * Whether this policy allows {@link NotificationChannel#canShowBadge() badges} from + * notifications intercepted by DND on devices that support badging. + */ + public @State int getVisualEffectBadge() { + return mVisualEffects.get(VISUAL_EFFECT_BADGE); + } + + /** + * Whether this policy allows notifications intercepted by DND from appearing on ambient + * displays on devices that support ambient display. + */ + public @State int getVisualEffectAmbient() { + return mVisualEffects.get(VISUAL_EFFECT_AMBIENT); + } + + /** + * Whether this policy allows notifications intercepted by DND from appearing in notification + * list views like the notification shade or lockscreen on devices that support those + * views. + */ + public @State int getVisualEffectNotificationList() { + return mVisualEffects.get(VISUAL_EFFECT_NOTIFICATION_LIST); + } + + /** + * Builder class for {@link ZenPolicy} objects. + * Provides a convenient way to set the various fields of a {@link ZenPolicy}. If a field + * is not set, it is (@link STATE_UNSET} and will not change the current set policy. + */ + public static class Builder { + private ZenPolicy mZenPolicy; + + public Builder() { + mZenPolicy = new ZenPolicy(); + } + + /** + * Builds the current ZenPolicy. + */ + public ZenPolicy build() { + return mZenPolicy.copy(); + } + + /** + * Allows all notifications to bypass DND and unmutes all streams. + */ + public Builder allowAllSounds() { + for (int i = 0; i < mZenPolicy.mPriorityCategories.size(); i++) { + mZenPolicy.mPriorityCategories.set(i, STATE_ALLOW); + } + mZenPolicy.mPriorityMessages = PEOPLE_TYPE_ANYONE; + mZenPolicy.mPriorityCalls = PEOPLE_TYPE_ANYONE; + return this; + } + + /** + * Intercepts all notifications and prevents them from playing sounds + * when DND is active. Also mutes alarm, system and media streams. + * Notification channels can still play sounds only if they + * {@link NotificationChannel#canBypassDnd can bypass DND}. If no channels can bypass DND, + * the ringer stream is also muted. + */ + public Builder disallowAllSounds() { + for (int i = 0; i < mZenPolicy.mPriorityCategories.size(); i++) { + mZenPolicy.mPriorityCategories.set(i, STATE_DISALLOW); + } + mZenPolicy.mPriorityMessages = PEOPLE_TYPE_NONE; + mZenPolicy.mPriorityCalls = PEOPLE_TYPE_NONE; + return this; + } + + /** + * Allows notifications intercepted by DND to show on all surfaces when DND is active. + */ + public Builder showAllVisualEffects() { + for (int i = 0; i < mZenPolicy.mVisualEffects.size(); i++) { + mZenPolicy.mVisualEffects.set(i, STATE_ALLOW); + } + return this; + } + + /** + * Disallows notifications intercepted by DND from showing when DND is active. + */ + public Builder hideAllVisualEffects() { + for (int i = 0; i < mZenPolicy.mVisualEffects.size(); i++) { + mZenPolicy.mVisualEffects.set(i, STATE_DISALLOW); + } + return this; + } + + /** + * Unsets a priority category, neither allowing or disallowing. When applying this policy, + * unset categories will default to the current applied policy. + * @hide + */ + public Builder unsetPriorityCategory(@PriorityCategory int category) { + mZenPolicy.mPriorityCategories.set(category, STATE_UNSET); + + if (category == PRIORITY_CATEGORY_MESSAGES) { + mZenPolicy.mPriorityMessages = STATE_UNSET; + } else if (category == PRIORITY_CATEGORY_CALLS) { + mZenPolicy.mPriorityCalls = STATE_UNSET; + } + + return this; + } + + /** + * Unsets a visual effect, neither allowing or disallowing. When applying this policy, + * unset effects will default to the current applied policy. + * @hide + */ + public Builder unsetVisualEffect(@VisualEffect int effect) { + mZenPolicy.mVisualEffects.set(effect, STATE_UNSET); + return this; + } + + /** + * Whether to allow notifications with category {@link Notification#CATEGORY_REMINDER} + * to play sounds and visually appear or to intercept them when DND is active. + */ + public Builder allowReminders(boolean allow) { + mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_REMINDERS, + allow ? STATE_ALLOW : STATE_DISALLOW); + return this; + } + + /** + * Whether to allow notifications with category {@link Notification#CATEGORY_EVENT} + * to play sounds and visually appear or to intercept them when DND is active. + */ + public Builder allowEvents(boolean allow) { + mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_EVENTS, + allow ? STATE_ALLOW : STATE_DISALLOW); + return this; + } + + /** + * Whether to allow notifications with category {@link Notification#CATEGORY_MESSAGE} + * that match audienceType to play sounds and visually appear or to intercept + * them when DND is active. + * @param audienceType message senders that are allowed to bypass DND + */ + public Builder allowMessages(@PeopleType int audienceType) { + if (audienceType == STATE_UNSET) { + return unsetPriorityCategory(PRIORITY_CATEGORY_MESSAGES); + } + + if (audienceType == PEOPLE_TYPE_NONE) { + mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_MESSAGES, STATE_DISALLOW); + } else if (audienceType == PEOPLE_TYPE_ANYONE || audienceType == PEOPLE_TYPE_CONTACTS + || audienceType == PEOPLE_TYPE_STARRED) { + mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_MESSAGES, STATE_ALLOW); + } else { + return this; + } + + mZenPolicy.mPriorityMessages = audienceType; + return this; + } + + /** + * Whether to allow notifications with category {@link Notification#CATEGORY_CALL} + * that match audienceType to play sounds and visually appear or to intercept + * them when DND is active. + * @param audienceType callers that are allowed to bypass DND + */ + public Builder allowCalls(@PeopleType int audienceType) { + if (audienceType == STATE_UNSET) { + return unsetPriorityCategory(PRIORITY_CATEGORY_CALLS); + } + + if (audienceType == PEOPLE_TYPE_NONE) { + mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_CALLS, STATE_DISALLOW); + } else if (audienceType == PEOPLE_TYPE_ANYONE || audienceType == PEOPLE_TYPE_CONTACTS + || audienceType == PEOPLE_TYPE_STARRED) { + mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_CALLS, STATE_ALLOW); + } else { + return this; + } + + mZenPolicy.mPriorityCalls = audienceType; + return this; + } + + /** + * Whether to allow repeat callers (notifications with category + * {@link Notification#CATEGORY_CALL} that have recently called + * to play sounds and visually appear. + */ + public Builder allowRepeatCallers(boolean allow) { + mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_REPEAT_CALLERS, + allow ? STATE_ALLOW : STATE_DISALLOW); + return this; + } + + + /** + * Whether to allow notifications with category {@link Notification#CATEGORY_ALARM} + * to play sounds and visually appear or to intercept them when DND is active. + * Disallowing alarms will mute the alarm stream when DND is active. + */ + public Builder allowAlarms(boolean allow) { + mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_ALARMS, + allow ? STATE_ALLOW : STATE_DISALLOW); + return this; + } + + /** + * Whether to allow media notifications to play sounds and visually + * appear or to intercept them when DND is active. + * Disallowing media will mute the media stream when DND is active. + */ + public Builder allowMedia(boolean allow) { + mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_MEDIA, + allow ? STATE_ALLOW : STATE_DISALLOW); + return this; + } + + /** + * Whether to allow system sounds to play when DND is active. + * Disallowing system sounds will mute the system stream when DND is active. + */ + public Builder allowSystem(boolean allow) { + mZenPolicy.mPriorityCategories.set(PRIORITY_CATEGORY_SYSTEM, + allow ? STATE_ALLOW : STATE_DISALLOW); + return this; + } + + /** + * Whether {@link Notification#fullScreenIntent full screen intents} that are intercepted + * by DND are shown. + */ + public Builder showFullScreenIntent(boolean show) { + mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_FULL_SCREEN_INTENT, + show ? STATE_ALLOW : STATE_DISALLOW); + return this; + } + + /** + * Whether {@link NotificationChannel#shouldShowLights() notification lights} from + * notifications intercepted by DND are blocked. + */ + public Builder showLights(boolean show) { + mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_LIGHTS, + show ? STATE_ALLOW : STATE_DISALLOW); + return this; + } + + /** + * Whether notifications intercepted by DND are prevented from peeking. + */ + public Builder showPeeking(boolean show) { + mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_PEEK, + show ? STATE_ALLOW : STATE_DISALLOW); + return this; + } + + /** + * Whether notifications intercepted by DND are prevented from appearing in the status bar + * on devices that support status bars. + */ + public Builder showStatusBarIcons(boolean show) { + mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_STATUS_BAR, + show ? STATE_ALLOW : STATE_DISALLOW); + return this; + } + + /** + * Whether {@link NotificationChannel#canShowBadge() badges} from + * notifications intercepted by DND are allowed on devices that support badging. + */ + public Builder showBadges(boolean show) { + mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_BADGE, + show ? STATE_ALLOW : STATE_DISALLOW); + return this; + } + + /** + * Whether notification intercepted by DND are prevented from appearing on ambient displays + * on devices that support ambient display. + */ + public Builder showInAmbientDisplay(boolean show) { + mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_AMBIENT, + show ? STATE_ALLOW : STATE_DISALLOW); + return this; + } + + /** + * Whether notification intercepted by DND are prevented from appearing in notification + * list views like the notification shade or lockscreen on devices that support those + * views. + */ + public Builder showInNotificationList(boolean show) { + mZenPolicy.mVisualEffects.set(VISUAL_EFFECT_NOTIFICATION_LIST, + show ? STATE_ALLOW : STATE_DISALLOW); + return this; + } + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeList(mPriorityCategories); + dest.writeList(mVisualEffects); + dest.writeInt(mPriorityCalls); + dest.writeInt(mPriorityMessages); + } + + public static final Parcelable.Creator<ZenPolicy> CREATOR = + new Parcelable.Creator<ZenPolicy>() { + @Override + public ZenPolicy createFromParcel(Parcel source) { + ZenPolicy policy = new ZenPolicy(); + policy.mPriorityCategories = source.readArrayList(Integer.class.getClassLoader()); + policy.mVisualEffects = source.readArrayList(Integer.class.getClassLoader()); + policy.mPriorityCalls = source.readInt(); + policy.mPriorityMessages = source.readInt(); + return policy; + } + + @Override + public ZenPolicy[] newArray(int size) { + return new ZenPolicy[size]; + } + }; + + @Override + public String toString() { + return new StringBuilder(ZenPolicy.class.getSimpleName()) + .append('{') + .append("priorityCategories=[").append(priorityCategoriesToString()) + .append("], visualEffects=[").append(visualEffectsToString()) + .append(", priorityCalls=").append(stateToString(mPriorityCalls)) + .append("], priorityMessages=").append(stateToString(mPriorityMessages)) + .append('}') + .toString(); + } + + + private String priorityCategoriesToString() { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < mPriorityCategories.size(); i++) { + if (mPriorityCategories.get(i) != STATE_UNSET) { + builder.append(indexToCategory(i)) + .append("=") + .append(stateToString(mPriorityCategories.get(i))) + .append(" "); + } + + } + return builder.toString(); + } + + private String visualEffectsToString() { + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < mVisualEffects.size(); i++) { + if (mVisualEffects.get(i) != STATE_UNSET) { + builder.append(indexToVisualEffect(i)) + .append("=") + .append(stateToString(mVisualEffects.get(i))) + .append(" "); + } + + } + return builder.toString(); + } + + private String indexToVisualEffect(@VisualEffect int visualEffectIndex) { + switch (visualEffectIndex) { + case VISUAL_EFFECT_FULL_SCREEN_INTENT: + return "fullScreenIntent"; + case VISUAL_EFFECT_LIGHTS: + return "lights"; + case VISUAL_EFFECT_PEEK: + return "peek"; + case VISUAL_EFFECT_STATUS_BAR: + return "statusBar"; + case VISUAL_EFFECT_BADGE: + return "badge"; + case VISUAL_EFFECT_AMBIENT: + return "ambient"; + case VISUAL_EFFECT_NOTIFICATION_LIST: + return "notificationList"; + } + return null; + } + + private String indexToCategory(@PriorityCategory int categoryIndex) { + switch (categoryIndex) { + case PRIORITY_CATEGORY_REMINDERS: + return "reminders"; + case PRIORITY_CATEGORY_EVENTS: + return "events"; + case PRIORITY_CATEGORY_MESSAGES: + return "messages"; + case PRIORITY_CATEGORY_CALLS: + return "calls"; + case PRIORITY_CATEGORY_REPEAT_CALLERS: + return "repeatCallers"; + case PRIORITY_CATEGORY_ALARMS: + return "alarms"; + case PRIORITY_CATEGORY_MEDIA: + return "media"; + case PRIORITY_CATEGORY_SYSTEM: + return "system"; + } + return null; + } + + private String stateToString(@State int state) { + switch (state) { + case STATE_UNSET: + return "unset"; + case STATE_DISALLOW: + return "disallow"; + case STATE_ALLOW: + return "allow"; + } + return null; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof ZenPolicy)) return false; + if (o == this) return true; + final ZenPolicy other = (ZenPolicy) o; + + return Objects.equals(other.mPriorityCategories, mPriorityCategories) + && Objects.equals(other.mVisualEffects, mVisualEffects) + && other.mPriorityCalls == mPriorityCalls + && other.mPriorityMessages == mPriorityMessages; + } + + @Override + public int hashCode() { + return Objects.hash(mPriorityCategories, mVisualEffects, mPriorityCalls, mPriorityMessages); + } + + private @ZenPolicy.State int getZenPolicyPriorityCategoryState(@PriorityCategory int + category) { + switch (category) { + case PRIORITY_CATEGORY_REMINDERS: + return getPriorityCategoryReminders(); + case PRIORITY_CATEGORY_EVENTS: + return getPriorityCategoryEvents(); + case PRIORITY_CATEGORY_MESSAGES: + return getPriorityCategoryMessages(); + case PRIORITY_CATEGORY_CALLS: + return getPriorityCategoryCalls(); + case PRIORITY_CATEGORY_REPEAT_CALLERS: + return getPriorityCategoryRepeatCallers(); + case PRIORITY_CATEGORY_ALARMS: + return getPriorityCategoryAlarms(); + case PRIORITY_CATEGORY_MEDIA: + return getPriorityCategoryMedia(); + case PRIORITY_CATEGORY_SYSTEM: + return getPriorityCategorySystem(); + } + return -1; + } + + private @ZenPolicy.State int getZenPolicyVisualEffectState(@VisualEffect int effect) { + switch (effect) { + case VISUAL_EFFECT_FULL_SCREEN_INTENT: + return getVisualEffectFullScreenIntent(); + case VISUAL_EFFECT_LIGHTS: + return getVisualEffectLights(); + case VISUAL_EFFECT_PEEK: + return getVisualEffectPeek(); + case VISUAL_EFFECT_STATUS_BAR: + return getVisualEffectStatusBar(); + case VISUAL_EFFECT_BADGE: + return getVisualEffectBadge(); + case VISUAL_EFFECT_AMBIENT: + return getVisualEffectAmbient(); + case VISUAL_EFFECT_NOTIFICATION_LIST: + return getVisualEffectNotificationList(); + } + return -1; + } + + /** @hide */ + public boolean isCategoryAllowed(@PriorityCategory int category, boolean defaultVal) { + switch (getZenPolicyPriorityCategoryState(category)) { + case ZenPolicy.STATE_ALLOW: + return true; + case ZenPolicy.STATE_DISALLOW: + return false; + default: + return defaultVal; + } + } + + /** @hide */ + public boolean isVisualEffectAllowed(@VisualEffect int effect, boolean defaultVal) { + switch (getZenPolicyVisualEffectState(effect)) { + case ZenPolicy.STATE_ALLOW: + return true; + case ZenPolicy.STATE_DISALLOW: + return false; + default: + return defaultVal; + } + } + + /** + * Applies another policy on top of this policy + * @hide + */ + public void apply(ZenPolicy policyToApply) { + // apply priority categories + for (int category = 0; category < mPriorityCategories.size(); category++) { + if (mPriorityCategories.get(category) == STATE_DISALLOW) { + // if a priority category is already disallowed by the policy, cannot allow + continue; + } + + @State int newState = policyToApply.mPriorityCategories.get(category); + if (newState != STATE_UNSET) { + mPriorityCategories.set(category, newState); + + if (category == PRIORITY_CATEGORY_MESSAGES + && mPriorityMessages < policyToApply.mPriorityMessages) { + mPriorityMessages = policyToApply.mPriorityMessages; + } else if (category == PRIORITY_CATEGORY_CALLS + && mPriorityCalls < policyToApply.mPriorityCalls) { + mPriorityCalls = policyToApply.mPriorityCalls; + } + } + } + + // apply visual effects + for (int visualEffect = 0; visualEffect < mVisualEffects.size(); visualEffect++) { + if (mVisualEffects.get(visualEffect) == STATE_DISALLOW) { + // if a visual effect is already disallowed by the policy, cannot allow + continue; + } + + if (policyToApply.mVisualEffects.get(visualEffect) != STATE_UNSET) { + mVisualEffects.set(visualEffect, policyToApply.mVisualEffects.get(visualEffect)); + } + } + } + + /** + * Makes deep copy of this ZenPolicy. + * @hide + */ + public ZenPolicy copy() { + final Parcel parcel = Parcel.obtain(); + try { + writeToParcel(parcel, 0); + parcel.setDataPosition(0); + return CREATOR.createFromParcel(parcel); + } finally { + parcel.recycle(); + } + } +} diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index 0476cf8961a5..ff6e86ebff61 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -45,6 +45,7 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put("settings_dynamic_homepage", "false"); DEFAULT_FLAGS.put("settings_mobile_network_v2", "false"); DEFAULT_FLAGS.put("settings_data_usage_v2", "false"); + DEFAULT_FLAGS.put("settings_seamless_transfer", "false"); DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "true"); } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 0119d2e6b8b0..1b3e62d64ef3 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -8116,7 +8116,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager /** * Sets the margins, in pixels. A call to {@link android.view.View#requestLayout()} needs * to be done so that the new margins are taken into account. Left and right margins may be - * overriden by {@link android.view.View#requestLayout()} depending on layout direction. + * overridden by {@link android.view.View#requestLayout()} depending on layout direction. * Margin values should be positive. * * @param left the left margin size @@ -8146,8 +8146,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager /** * Sets the relative margins, in pixels. A call to {@link android.view.View#requestLayout()} * needs to be done so that the new relative margins are taken into account. Left and right - * margins may be overriden by {@link android.view.View#requestLayout()} depending on layout - * direction. Margin values should be positive. + * margins may be overridden by {@link android.view.View#requestLayout()} depending on + * layout direction. Margin values should be positive. * * @param start the start margin size * @param top the top margin size diff --git a/core/java/android/webkit/SafeBrowsingResponse.java b/core/java/android/webkit/SafeBrowsingResponse.java index 1d3a617a6bec..7839a00eff69 100644 --- a/core/java/android/webkit/SafeBrowsingResponse.java +++ b/core/java/android/webkit/SafeBrowsingResponse.java @@ -36,14 +36,14 @@ public abstract class SafeBrowsingResponse { public abstract void showInterstitial(boolean allowReporting); /** - * Act as if the user clicked "visit this unsafe site." + * Act as if the user clicked the "visit this unsafe site" button. * * @param report {@code true} to enable Safe Browsing reporting. */ public abstract void proceed(boolean report); /** - * Act as if the user clicked "back to safety." + * Act as if the user clicked the "back to safety" button. * * @param report {@code true} to enable Safe Browsing reporting. */ diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java index dd6c923e7f47..f3fe16e8a675 100644 --- a/core/java/android/widget/AdapterView.java +++ b/core/java/android/widget/AdapterView.java @@ -682,7 +682,7 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { /** * Sets the currently selected item. To support accessibility subclasses that - * override this method must invoke the overriden super method first. + * override this method must invoke the overridden super method first. * * @param position Index (starting at 0) of the data item to be selected. */ diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java index 452e9036cc54..f2e478d0e072 100644 --- a/core/java/android/widget/LinearLayout.java +++ b/core/java/android/widget/LinearLayout.java @@ -653,7 +653,7 @@ public class LinearLayout extends ViewGroup { } /** - * <p>Returns the view at the specified index. This method can be overriden + * <p>Returns the view at the specified index. This method can be overridden * to take into account virtual children. Refer to * {@link android.widget.TableLayout} and {@link android.widget.TableRow} * for an example.</p> @@ -1527,7 +1527,7 @@ public class LinearLayout extends ViewGroup { /** * <p>Measure the child according to the parent's measure specs. This - * method should be overriden by subclasses to force the sizing of + * method should be overridden by subclasses to force the sizing of * children. This method is called by {@link #measureVertical(int, int)} and * {@link #measureHorizontal(int, int)}.</p> * diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index d9132736232a..a86e6f81f006 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -3609,7 +3609,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** * Read the Text Appearance attributes from a given TypedArray and set its values to the given * set. If the TypedArray contains a value that was already set in the given attributes, that - * will be overriden. + * will be overridden. * * @param context The Context to be used * @param appearance The TypedArray to read properties from diff --git a/core/proto/android/service/notification.proto b/core/proto/android/service/notification.proto index 25059bea8bfd..ccbd02764d7e 100644 --- a/core/proto/android/service/notification.proto +++ b/core/proto/android/service/notification.proto @@ -72,7 +72,8 @@ message ListenersDisablingEffectsProto { option (android.msg_privacy).dest = DEST_AUTOMATIC; optional int32 hint = 1; - repeated ManagedServiceInfoProto listeners = 2; + reserved 2; // ManagedServiceInfoProto listeners + repeated android.content.ComponentNameProto listener_components = 3; } message ManagedServiceInfoProto { diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 9ab55d62839b..2bf16d3f99c9 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3756,6 +3756,10 @@ <permission android:name="android.permission.USE_BIOMETRIC_INTERNAL" android:protectionLevel="signature" /> + <!-- Allows the system to control the BiometricDialog (SystemUI). Reserved for the system. @hide --> + <permission android:name="android.permission.MANAGE_BIOMETRIC_DIALOG" + android:protectionLevel="signature" /> + <!-- Allows an app to reset face authentication attempt counter. Reserved for the system. @hide --> <permission android:name="android.permission.RESET_FACE_LOCKOUT" android:protectionLevel="signature" /> diff --git a/core/tests/coretests/src/android/content/pm/AndroidHidlUpdaterTest.java b/core/tests/coretests/src/android/content/pm/AndroidHidlUpdaterTest.java new file mode 100644 index 000000000000..7218b3a286a2 --- /dev/null +++ b/core/tests/coretests/src/android/content/pm/AndroidHidlUpdaterTest.java @@ -0,0 +1,98 @@ +/* + * 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.pm; + +import static android.content.pm.PackageBuilder.builder; +import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_BASE; +import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_MANAGER; + +import android.os.Build; +import android.support.test.filters.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Test for {@link AndroidHidlUpdater} + */ +@SmallTest +@RunWith(JUnit4.class) +public class AndroidHidlUpdaterTest extends PackageSharedLibraryUpdaterTest { + + private static final String OTHER_LIBRARY = "other.library"; + + @Test + public void targeted_at_O() { + PackageBuilder before = builder() + .targetSdkVersion(Build.VERSION_CODES.O); + + // Should add both HIDL libraries + PackageBuilder after = builder() + .targetSdkVersion(Build.VERSION_CODES.O) + .requiredLibraries(ANDROID_HIDL_MANAGER, ANDROID_HIDL_BASE); + + checkBackwardsCompatibility(before, after); + } + + @Test + public void targeted_at_O_not_empty_usesLibraries() { + PackageBuilder before = builder() + .targetSdkVersion(Build.VERSION_CODES.O) + .requiredLibraries(OTHER_LIBRARY); + + // The hidl jars 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) + .requiredLibraries(ANDROID_HIDL_MANAGER, ANDROID_HIDL_BASE, OTHER_LIBRARY); + + checkBackwardsCompatibility(before, after); + } + + @Test + public void targeted_at_O_in_usesLibraries() { + PackageBuilder before = builder() + .targetSdkVersion(Build.VERSION_CODES.O) + .requiredLibraries(ANDROID_HIDL_MANAGER, ANDROID_HIDL_BASE); + + // No change is required because although the HIDL libraries has been removed from + // the bootclasspath the package explicitly requests it. + checkBackwardsCompatibility(before, before); + } + + @Test + public void in_usesLibraries() { + PackageBuilder before = builder().requiredLibraries(ANDROID_HIDL_BASE); + + // No change is required because the package explicitly requests the HIDL libraries + // and is targeted at the current version so does not need backwards compatibility. + checkBackwardsCompatibility(before, before); + } + + @Test + public void in_usesOptionalLibraries() { + PackageBuilder before = builder().optionalLibraries(ANDROID_HIDL_BASE); + + // No change is required because the package explicitly requests the HIDL libraries + // and is targeted at the current version so does not need backwards compatibility. + checkBackwardsCompatibility(before, before); + } + + private void checkBackwardsCompatibility(PackageBuilder before, PackageBuilder after) { + checkBackwardsCompatibility(before, after, AndroidHidlUpdater::new); + } +} diff --git a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java index 1c4039b85441..3ce258969822 100644 --- a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java +++ b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java @@ -44,7 +44,7 @@ import java.io.InputStream; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.StandardCopyOption; -import java.util.HashSet; +import java.util.ArrayList; import java.util.Locale; @SmallTest @@ -112,7 +112,7 @@ public class TypefaceSystemFallbackTest { private static void buildSystemFallback(String xml, ArrayMap<String, Typeface> fontMap, ArrayMap<String, FontFamily[]> fallbackMap) { - final HashSet<Font> availableFonts = new HashSet<>(); + final ArrayList<Font> availableFonts = new ArrayList<>(); try (FileOutputStream fos = new FileOutputStream(TEST_FONTS_XML)) { fos.write(xml.getBytes(Charset.forName("UTF-8"))); } catch (IOException e) { @@ -127,7 +127,7 @@ public class TypefaceSystemFallbackTest { public void testBuildSystemFallback() { final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final HashSet<Font> availableFonts = new HashSet<>(); + final ArrayList<Font> availableFonts = new ArrayList<>(); final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(SYSTEM_FONTS_XML, SYSTEM_FONT_DIR, fallbackMap, availableFonts); diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java index 9c9f11b76ab4..20fe16251854 100644 --- a/core/tests/coretests/src/android/os/FileUtilsTest.java +++ b/core/tests/coretests/src/android/os/FileUtilsTest.java @@ -17,6 +17,22 @@ package android.os; import static android.os.FileUtils.roundStorageSize; +import static android.os.FileUtils.translateModePfdToPosix; +import static android.os.FileUtils.translateModePosixToPfd; +import static android.os.FileUtils.translateModePosixToString; +import static android.os.FileUtils.translateModeStringToPosix; +import static android.os.ParcelFileDescriptor.MODE_APPEND; +import static android.os.ParcelFileDescriptor.MODE_CREATE; +import static android.os.ParcelFileDescriptor.MODE_READ_ONLY; +import static android.os.ParcelFileDescriptor.MODE_READ_WRITE; +import static android.os.ParcelFileDescriptor.MODE_TRUNCATE; +import static android.os.ParcelFileDescriptor.MODE_WRITE_ONLY; +import static android.system.OsConstants.O_APPEND; +import static android.system.OsConstants.O_CREAT; +import static android.system.OsConstants.O_RDONLY; +import static android.system.OsConstants.O_RDWR; +import static android.system.OsConstants.O_TRUNC; +import static android.system.OsConstants.O_WRONLY; import static android.text.format.DateUtils.DAY_IN_MILLIS; import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static android.text.format.DateUtils.WEEK_IN_MILLIS; @@ -476,6 +492,32 @@ public class FileUtilsTest { assertEquals(G64, roundStorageSize(G32 + 1)); } + @Test + public void testTranslateMode() throws Exception { + assertTranslate("r", O_RDONLY, MODE_READ_ONLY); + + assertTranslate("rw", O_RDWR | O_CREAT, + MODE_READ_WRITE | MODE_CREATE); + assertTranslate("rwt", O_RDWR | O_CREAT | O_TRUNC, + MODE_READ_WRITE | MODE_CREATE | MODE_TRUNCATE); + assertTranslate("rwa", O_RDWR | O_CREAT | O_APPEND, + MODE_READ_WRITE | MODE_CREATE | MODE_APPEND); + + assertTranslate("w", O_WRONLY | O_CREAT, + MODE_WRITE_ONLY | MODE_CREATE | MODE_CREATE); + assertTranslate("wt", O_WRONLY | O_CREAT | O_TRUNC, + MODE_WRITE_ONLY | MODE_CREATE | MODE_TRUNCATE); + assertTranslate("wa", O_WRONLY | O_CREAT | O_APPEND, + MODE_WRITE_ONLY | MODE_CREATE | MODE_APPEND); + } + + private static void assertTranslate(String string, int posix, int pfd) { + assertEquals(posix, translateModeStringToPosix(string)); + assertEquals(string, translateModePosixToString(posix)); + assertEquals(pfd, translateModePosixToPfd(posix)); + assertEquals(posix, translateModePfdToPosix(pfd)); + } + private static void assertNameEquals(String expected, File actual) { assertEquals(expected, actual.getName()); } diff --git a/core/tests/coretests/src/android/text/FontFallbackSetup.java b/core/tests/coretests/src/android/text/FontFallbackSetup.java index 355be619ea91..898e78c651e6 100644 --- a/core/tests/coretests/src/android/text/FontFallbackSetup.java +++ b/core/tests/coretests/src/android/text/FontFallbackSetup.java @@ -33,7 +33,7 @@ import java.io.InputStream; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.StandardCopyOption; -import java.util.HashSet; +import java.util.ArrayList; public class FontFallbackSetup implements AutoCloseable { private final String[] mTestFontFiles; @@ -76,7 +76,7 @@ public class FontFallbackSetup implements AutoCloseable { } final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final HashSet<Font> availableFonts = new HashSet<>(); + final ArrayList<Font> availableFonts = new ArrayList<>(); final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(testFontsXml, mTestFontsDir, fallbackMap, availableFonts); Typeface.initSystemDefaultTypefaces(mFontMap, fallbackMap, aliases); diff --git a/data/etc/platform.xml b/data/etc/platform.xml index 6f52fbd1b4f5..c4017d15268f 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -188,6 +188,12 @@ <library name="android.test.runner" file="/system/framework/android.test.runner.impl.jar" /> + <!-- In BOOT_JARS historically, and now added to legacy applications. --> + <library name="android.hidl.base-V1.0-java" + file="/system/framework/android.hidl.base-V1.0-java.jar" /> + <library name="android.hidl.manager-V1.0-java" + file="/system/framework/android.hidl.manager-V1.0-java.jar" /> + <!-- These are the standard packages that are white-listed to always have internet access while in power save mode, even if they aren't in the foreground. --> <allow-in-power-save package="com.android.providers.downloads" /> diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java index 8aa48456ebff..1458c66a2c54 100644 --- a/graphics/java/android/graphics/fonts/Font.java +++ b/graphics/java/android/graphics/fonts/Font.java @@ -119,7 +119,7 @@ public final class Font { private @Nullable ByteBuffer mBuffer; private @Nullable File mFile; - private @NonNull LocaleList mLocaleList = LocaleList.getEmptyLocaleList(); + private @NonNull String mLocaleList = ""; private @IntRange(from = -1, to = 1000) int mWeight = NOT_SPECIFIED; private @IntRange(from = -1, to = 1) int mItalic = NOT_SPECIFIED; private @IntRange(from = 0) int mTtcIndex = 0; @@ -150,7 +150,7 @@ public final class Font { * @hide */ public Builder(@NonNull ByteBuffer buffer, @NonNull File path, - @NonNull LocaleList localeList) { + @NonNull String localeList) { this(buffer); mFile = path; mLocaleList = localeList; @@ -457,7 +457,7 @@ public final class Font { private final boolean mItalic; private final @IntRange(from = 0) int mTtcIndex; private final @Nullable FontVariationAxis[] mAxes; - private final @NonNull LocaleList mLocaleList; + private final @NonNull String mLocaleList; /** * Use Builder instead @@ -465,7 +465,7 @@ public final class Font { private Font(long nativePtr, @NonNull ByteBuffer buffer, @Nullable File file, @IntRange(from = 0, to = 1000) int weight, boolean italic, @IntRange(from = 0) int ttcIndex, @Nullable FontVariationAxis[] axes, - @NonNull LocaleList localeList) { + @NonNull String localeList) { mBuffer = buffer; mFile = file; mWeight = weight; @@ -546,7 +546,7 @@ public final class Font { * @return a locale list */ public @NonNull LocaleList getLocaleList() { - return mLocaleList; + return LocaleList.forLanguageTags(mLocaleList); } /** @hide */ @@ -580,7 +580,7 @@ public final class Font { + ", italic=" + mItalic + ", ttcIndex=" + mTtcIndex + ", axes=" + FontVariationAxis.toFontVariationSettings(mAxes) - + ", localeList=" + mLocaleList.toLanguageTags() + + ", localeList=" + mLocaleList + ", buffer=" + mBuffer + "}"; } diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java index 1c957b8880b9..f4a2199a6688 100644 --- a/graphics/java/android/graphics/fonts/SystemFonts.java +++ b/graphics/java/android/graphics/fonts/SystemFonts.java @@ -19,7 +19,6 @@ package android.graphics.fonts; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.FontListParser; -import android.os.LocaleList; import android.text.FontConfig; import android.util.ArrayMap; import android.util.Log; @@ -54,7 +53,7 @@ public class SystemFonts { private static final Map<String, FontFamily[]> sSystemFallbackMap; private static final FontConfig.Alias[] sAliases; - private static final Set<Font> sAvailableFonts; + private static final List<Font> sAvailableFonts; /** * Returns all available font files in the system. @@ -63,7 +62,9 @@ public class SystemFonts { * @return an array of system fonts */ public static @NonNull Set<Font> getAvailableFonts() { - return sAvailableFonts; + HashSet<Font> set = new HashSet<>(); + set.addAll(sAvailableFonts); + return set; } /** @@ -114,7 +115,7 @@ public class SystemFonts { @NonNull ArrayMap<String, ArrayList<FontFamily>> fallbackMap, @NonNull Map<String, ByteBuffer> cache, @NonNull String fontDir, - @NonNull HashSet<Font> availableFonts) { + @NonNull ArrayList<Font> availableFonts) { final String languageTags = xmlFamily.getLanguages(); final int variant = xmlFamily.getVariant(); @@ -170,13 +171,12 @@ public class SystemFonts { @FontConfig.Family.Variant int variant, @NonNull Map<String, ByteBuffer> cache, @NonNull String fontDir, - @NonNull HashSet<Font> availableFonts) { + @NonNull ArrayList<Font> availableFonts) { if (fonts.size() == 0) { return null; } FontFamily.Builder b = null; - final LocaleList localeList = LocaleList.forLanguageTags(languageTags); for (int i = 0; i < fonts.size(); i++) { final FontConfig.Font fontConfig = fonts.get(i); final String fullPath = fontDir + fontConfig.getFontName(); @@ -194,7 +194,7 @@ public class SystemFonts { final Font font; try { - font = new Font.Builder(buffer, new File(fullPath), localeList) + font = new Font.Builder(buffer, new File(fullPath), languageTags) .setWeight(fontConfig.getWeight()) .setItalic(fontConfig.isItalic()) .setTtcIndex(fontConfig.getTtcIndex()) @@ -228,7 +228,7 @@ public class SystemFonts { public static FontConfig.Alias[] buildSystemFallback(@NonNull String xmlPath, @NonNull String fontDir, @NonNull ArrayMap<String, FontFamily[]> fallbackMap, - @NonNull HashSet<Font> availableFonts) { + @NonNull ArrayList<Font> availableFonts) { try { final FileInputStream fontsIn = new FileInputStream(xmlPath); final FontConfig fontConfig = FontListParser.parse(fontsIn); @@ -284,11 +284,11 @@ public class SystemFonts { static { final ArrayMap<String, FontFamily[]> systemFallbackMap = new ArrayMap<>(); - final HashSet<Font> availableFonts = new HashSet<>(); + final ArrayList<Font> availableFonts = new ArrayList<>(); sAliases = buildSystemFallback("/system/etc/fonts.xml", "/system/fonts/", systemFallbackMap, availableFonts); sSystemFallbackMap = Collections.unmodifiableMap(systemFallbackMap); - sAvailableFonts = Collections.unmodifiableSet(availableFonts); + sAvailableFonts = Collections.unmodifiableList(availableFonts); } } diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp index a43f58cd1e56..b5b87d516ff7 100644 --- a/libs/hwui/DeviceInfo.cpp +++ b/libs/hwui/DeviceInfo.cpp @@ -66,12 +66,16 @@ void DeviceInfo::initialize(int maxTextureSize) { sDeviceInfo = new DeviceInfo(); sDeviceInfo->mDisplayInfo = DeviceInfo::queryDisplayInfo(); sDeviceInfo->mMaxTextureSize = maxTextureSize; + sDeviceInfo->queryCompositionPreference(&sDeviceInfo->mTargetDataSpace, + &sDeviceInfo->mTargetPixelFormat); }); } void DeviceInfo::load() { mDisplayInfo = queryDisplayInfo(); glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); + sDeviceInfo->queryCompositionPreference(&sDeviceInfo->mTargetDataSpace, + &sDeviceInfo->mTargetPixelFormat); } DisplayInfo DeviceInfo::queryDisplayInfo() { @@ -86,5 +90,17 @@ DisplayInfo DeviceInfo::queryDisplayInfo() { return displayInfo; } +void DeviceInfo::queryCompositionPreference(ui::Dataspace* dataSpace, + ui::PixelFormat* pixelFormat) { + if (Properties::isolatedProcess) { + *dataSpace = ui::Dataspace::V0_SRGB; + *pixelFormat = ui::PixelFormat::RGBA_8888; + } + + status_t status = + SurfaceComposerClient::getCompositionPreference(dataSpace, pixelFormat); + LOG_ALWAYS_FATAL_IF(status, "Failed to get composition preference, error %d", status); +} + } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/DeviceInfo.h b/libs/hwui/DeviceInfo.h index 297b2664414b..416af179eb21 100644 --- a/libs/hwui/DeviceInfo.h +++ b/libs/hwui/DeviceInfo.h @@ -17,6 +17,7 @@ #define DEVICEINFO_H #include <ui/DisplayInfo.h> +#include <ui/GraphicTypes.h> #include "Extensions.h" #include "utils/Macros.h" @@ -39,6 +40,8 @@ public: static void initialize(int maxTextureSize); int maxTextureSize() const { return mMaxTextureSize; } + ui::Dataspace getTargetDataSpace() const { return mTargetDataSpace; } + ui::PixelFormat getTargetPixelFormat() const { return mTargetPixelFormat; } const DisplayInfo& displayInfo() const { return mDisplayInfo; } const Extensions& extensions() const { return mExtensions; } @@ -50,6 +53,9 @@ public: static DisplayInfo queryDisplayInfo(); private: + static void queryCompositionPreference(ui::Dataspace* dataSpace, + ui::PixelFormat* pixelFormat); + DeviceInfo() {} ~DeviceInfo() {} @@ -58,6 +64,9 @@ private: int mMaxTextureSize; DisplayInfo mDisplayInfo; Extensions mExtensions; + // TODO(lpy) Replace below with android_ prefix types. + ui::Dataspace mTargetDataSpace; + ui::PixelFormat mTargetPixelFormat; }; } /* namespace uirenderer */ diff --git a/libs/hwui/LayerUpdateQueue.h b/libs/hwui/LayerUpdateQueue.h index b14b80cc598a..6857999500f0 100644 --- a/libs/hwui/LayerUpdateQueue.h +++ b/libs/hwui/LayerUpdateQueue.h @@ -19,6 +19,7 @@ #include <utils/StrongPointer.h> #include "Rect.h" +#include "RenderNode.h" #include "utils/Macros.h" #include <unordered_map> diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index d58b59e83380..26f01cecf802 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -62,21 +62,23 @@ Frame SkiaOpenGLPipeline::getFrame() { bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty, const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds, - bool opaque, bool wideColorGamut, const LightInfo& lightInfo, + bool opaque, const LightInfo& lightInfo, const std::vector<sp<RenderNode>>& renderNodes, FrameInfoVisualizer* profiler) { mEglManager.damageFrame(frame, dirty); - SkColorType colorType; + SkColorType colorType = getSurfaceColorType(); // setup surface for fbo0 GrGLFramebufferInfo fboInfo; fboInfo.fFBOID = 0; - if (wideColorGamut) { + if (colorType == kRGBA_F16_SkColorType) { fboInfo.fFormat = GL_RGBA16F; - colorType = kRGBA_F16_SkColorType; - } else { + } else if (colorType == kN32_SkColorType) { + // Note: The default preference of pixel format is RGBA_8888, when other + // pixel format is available, we should branch out and do more check. fboInfo.fFormat = GL_RGBA8; - colorType = kN32_SkColorType; + } else { + LOG_ALWAYS_FATAL("Unsupported color type."); } GrBackendRenderTarget backendRT(frame.width(), frame.height(), 0, STENCIL_BUFFER_SIZE, fboInfo); @@ -89,8 +91,7 @@ bool SkiaOpenGLPipeline::draw(const Frame& frame, const SkRect& screenDirty, con nullptr, &props)); SkiaPipeline::updateLighting(lightGeometry, lightInfo); - renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, wideColorGamut, contentDrawBounds, - surface); + renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); layerUpdateQueue->clear(); // Draw visual debugging features @@ -147,8 +148,7 @@ bool SkiaOpenGLPipeline::setSurface(Surface* surface, SwapBehavior swapBehavior, if (surface) { mRenderThread.requireGlContext(); - const bool wideColorGamut = colorMode == ColorMode::WideColorGamut; - mEglSurface = mEglManager.createSurface(surface, wideColorGamut); + mEglSurface = mEglManager.createSurface(surface, colorMode); } if (mEglSurface != EGL_NO_SURFACE) { @@ -168,6 +168,14 @@ bool SkiaOpenGLPipeline::isContextReady() { return CC_LIKELY(mEglManager.hasEglContext()); } +SkColorType SkiaOpenGLPipeline::getSurfaceColorType() const { + return mEglManager.getSurfaceColorType(); +} + +sk_sp<SkColorSpace> SkiaOpenGLPipeline::getSurfaceColorSpace() { + return mEglManager.getSurfaceColorSpace(); +} + void SkiaOpenGLPipeline::invokeFunctor(const RenderThread& thread, Functor* functor) { DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext; if (thread.eglManager().hasEglContext()) { diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h index 808685ad4460..fbdf3139dabd 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h @@ -34,8 +34,7 @@ public: renderthread::Frame getFrame() override; bool draw(const renderthread::Frame& frame, const SkRect& screenDirty, const SkRect& dirty, const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, - const Rect& contentDrawBounds, bool opaque, bool wideColorGamut, - const LightInfo& lightInfo, + const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo, const std::vector<sp<RenderNode> >& renderNodes, FrameInfoVisualizer* profiler) override; bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty, @@ -46,6 +45,8 @@ public: void onStop() override; bool isSurfaceReady() override; bool isContextReady() override; + SkColorType getSurfaceColorType() const override; + sk_sp<SkColorSpace> getSurfaceColorSpace() override; static void invokeFunctor(const renderthread::RenderThread& thread, Functor* functor); diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 988981d65775..7f8abb8afa97 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -83,16 +83,15 @@ void SkiaPipeline::onPrepareTree() { void SkiaPipeline::renderLayers(const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, bool opaque, - bool wideColorGamut, const LightInfo& lightInfo) { + const LightInfo& lightInfo) { updateLighting(lightGeometry, lightInfo); ATRACE_NAME("draw layers"); renderVectorDrawableCache(); - renderLayersImpl(*layerUpdateQueue, opaque, wideColorGamut); + renderLayersImpl(*layerUpdateQueue, opaque); layerUpdateQueue->clear(); } -void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque, - bool wideColorGamut) { +void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque) { sk_sp<GrContext> cachedContext; // Render all layers that need to be updated, in order. @@ -161,7 +160,7 @@ void SkiaPipeline::renderLayersImpl(const LayerUpdateQueue& layers, bool opaque, } bool SkiaPipeline::createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator, - bool wideColorGamut, ErrorHandler* errorHandler) { + ErrorHandler* errorHandler) { // compute the size of the surface (i.e. texture) to be allocated for this layer const int surfaceWidth = ceilf(node->getWidth() / float(LAYER_SIZE)) * LAYER_SIZE; const int surfaceHeight = ceilf(node->getHeight() / float(LAYER_SIZE)) * LAYER_SIZE; @@ -169,12 +168,8 @@ bool SkiaPipeline::createOrUpdateLayer(RenderNode* node, const DamageAccumulator SkSurface* layer = node->getLayerSurface(); if (!layer || layer->width() != surfaceWidth || layer->height() != surfaceHeight) { SkImageInfo info; - if (wideColorGamut) { - info = SkImageInfo::Make(surfaceWidth, surfaceHeight, kRGBA_F16_SkColorType, - kPremul_SkAlphaType); - } else { - info = SkImageInfo::MakeN32Premul(surfaceWidth, surfaceHeight); - } + info = SkImageInfo::Make(surfaceWidth, surfaceHeight, getSurfaceColorType(), + kPremul_SkAlphaType); SkSurfaceProps props(0, kUnknown_SkPixelGeometry); SkASSERT(mRenderThread.getGrContext() != nullptr); node->setLayerSurface(SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), @@ -321,19 +316,18 @@ void SkiaPipeline::endCapture(SkSurface* surface) { void SkiaPipeline::renderFrame(const LayerUpdateQueue& layers, const SkRect& clip, const std::vector<sp<RenderNode>>& nodes, bool opaque, - bool wideColorGamut, const Rect& contentDrawBounds, - sk_sp<SkSurface> surface) { + const Rect& contentDrawBounds, sk_sp<SkSurface> surface) { renderVectorDrawableCache(); // draw all layers up front - renderLayersImpl(layers, opaque, wideColorGamut); + renderLayersImpl(layers, opaque); // initialize the canvas for the current frame, that might be a recording canvas if SKP // capture is enabled. std::unique_ptr<SkPictureRecorder> recorder; SkCanvas* canvas = tryCapture(surface.get()); - renderFrameImpl(layers, clip, nodes, opaque, wideColorGamut, contentDrawBounds, canvas); + renderFrameImpl(layers, clip, nodes, opaque, contentDrawBounds, canvas); endCapture(surface.get()); @@ -354,13 +348,12 @@ static Rect nodeBounds(RenderNode& node) { void SkiaPipeline::renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip, const std::vector<sp<RenderNode>>& nodes, bool opaque, - bool wideColorGamut, const Rect& contentDrawBounds, - SkCanvas* canvas) { + const Rect& contentDrawBounds, SkCanvas* canvas) { SkAutoCanvasRestore saver(canvas, true); canvas->androidFramework_setDeviceClipRestriction(clip.roundOut()); // STOPSHIP: Revert, temporary workaround to clear always F16 frame buffer for b/74976293 - if (!opaque || wideColorGamut) { + if (!opaque || getSurfaceColorType() == kRGBA_F16_SkColorType) { canvas->clear(SK_ColorTRANSPARENT); } @@ -493,7 +486,7 @@ void SkiaPipeline::renderOverdraw(const LayerUpdateQueue& layers, const SkRect& // each time a pixel would have been drawn. // Pass true for opaque so we skip the clear - the overdrawCanvas is already zero // initialized. - renderFrameImpl(layers, clip, nodes, true, false, contentDrawBounds, &overdrawCanvas); + renderFrameImpl(layers, clip, nodes, true, contentDrawBounds, &overdrawCanvas); sk_sp<SkImage> counts = offscreen->makeImageSnapshot(); // Draw overdraw colors to the canvas. The color filter will convert counts to colors. diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h index 8c9c80381b84..b78dea1a5c87 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaPipeline.h @@ -42,15 +42,14 @@ public: void unpinImages() override; void onPrepareTree() override; - void renderLayers(const LightGeometry& lightGeometry, - LayerUpdateQueue* layerUpdateQueue, bool opaque, bool wideColorGamut, - const LightInfo& lightInfo) override; + void renderLayers(const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, + bool opaque, const LightInfo& lightInfo) override; bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator, - bool wideColorGamut, ErrorHandler* errorHandler) override; + ErrorHandler* errorHandler) override; void renderFrame(const LayerUpdateQueue& layers, const SkRect& clip, - const std::vector<sp<RenderNode>>& nodes, bool opaque, bool wideColorGamut, + const std::vector<sp<RenderNode>>& nodes, bool opaque, const Rect& contentDrawBounds, sk_sp<SkSurface> surface); std::vector<VectorDrawableRoot*>* getVectorDrawables() { return &mVectorDrawables; } @@ -59,7 +58,7 @@ public: static void prepareToDraw(const renderthread::RenderThread& thread, Bitmap* bitmap); - void renderLayersImpl(const LayerUpdateQueue& layers, bool opaque, bool wideColorGamut); + void renderLayersImpl(const LayerUpdateQueue& layers, bool opaque); static float getLightRadius() { if (CC_UNLIKELY(Properties::overrideLightRadius > 0)) { @@ -112,7 +111,7 @@ protected: private: void renderFrameImpl(const LayerUpdateQueue& layers, const SkRect& clip, - const std::vector<sp<RenderNode>>& nodes, bool opaque, bool wideColorGamut, + const std::vector<sp<RenderNode>>& nodes, bool opaque, const Rect& contentDrawBounds, SkCanvas* canvas); /** diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp index 611a34c069d4..5cbe33debbce 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -63,8 +63,7 @@ Frame SkiaVulkanPipeline::getFrame() { bool SkiaVulkanPipeline::draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty, const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds, - bool opaque, bool wideColorGamut, - const LightInfo& lightInfo, + bool opaque, const LightInfo& lightInfo, const std::vector<sp<RenderNode>>& renderNodes, FrameInfoVisualizer* profiler) { sk_sp<SkSurface> backBuffer = mVkSurface->getBackBufferSurface(); @@ -72,8 +71,7 @@ bool SkiaVulkanPipeline::draw(const Frame& frame, const SkRect& screenDirty, con return false; } SkiaPipeline::updateLighting(lightGeometry, lightInfo); - renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, wideColorGamut, contentDrawBounds, - backBuffer); + renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, backBuffer); layerUpdateQueue->clear(); // Draw visual debugging features @@ -139,6 +137,14 @@ bool SkiaVulkanPipeline::isContextReady() { return CC_LIKELY(mVkManager.hasVkContext()); } +SkColorType SkiaVulkanPipeline::getSurfaceColorType() const { + return mVkManager.getSurfaceColorType(); +} + +sk_sp<SkColorSpace> SkiaVulkanPipeline::getSurfaceColorSpace() { + return mVkManager.getSurfaceColorSpace(); +} + void SkiaVulkanPipeline::invokeFunctor(const RenderThread& thread, Functor* functor) { // TODO: we currently don't support OpenGL WebView's DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext; diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h index 900b054e35bd..6e723a8373e1 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h @@ -32,8 +32,7 @@ public: renderthread::Frame getFrame() override; bool draw(const renderthread::Frame& frame, const SkRect& screenDirty, const SkRect& dirty, const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, - const Rect& contentDrawBounds, bool opaque, bool wideColorGamut, - const LightInfo& lightInfo, + const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo, const std::vector<sp<RenderNode> >& renderNodes, FrameInfoVisualizer* profiler) override; bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty, @@ -44,6 +43,8 @@ public: void onStop() override; bool isSurfaceReady() override; bool isContextReady() override; + SkColorType getSurfaceColorType() const override; + sk_sp<SkColorSpace> getSurfaceColorSpace() override; static void invokeFunctor(const renderthread::RenderThread& thread, Functor* functor); static sk_sp<Bitmap> allocateHardwareBitmap(renderthread::RenderThread& thread, diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 727cef3035f5..ea6a851419c0 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -151,7 +151,8 @@ void CanvasContext::setSurface(sp<Surface>&& surface) { mNativeSurface = std::move(surface); - ColorMode colorMode = mWideColorGamut ? ColorMode::WideColorGamut : ColorMode::Srgb; + // TODO(b/111436479) Introduce a way for app to specify DisplayColorGamut mode. + ColorMode colorMode = mWideColorGamut ? ColorMode::WideColorGamut : ColorMode::Legacy; bool hasSurface = mRenderPipeline->setSurface(mNativeSurface.get(), mSwapBehavior, colorMode); mFrameNumber = -1; @@ -416,7 +417,7 @@ void CanvasContext::draw() { SkRect windowDirty = computeDirtyRect(frame, &dirty); bool drew = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue, - mContentDrawBounds, mOpaque, mWideColorGamut, mLightInfo, + mContentDrawBounds, mOpaque, mLightInfo, mRenderNodes, &(profiler())); int64_t frameCompleteNr = mFrameCompleteCallbacks.size() ? getFrameNumber() : -1; @@ -555,8 +556,7 @@ void CanvasContext::buildLayer(RenderNode* node) { // purposes when the frame is actually drawn node->setPropertyFieldsDirty(RenderNode::GENERIC); - mRenderPipeline->renderLayers(mLightGeometry, &mLayerUpdateQueue, mOpaque, mWideColorGamut, - mLightInfo); + mRenderPipeline->renderLayers(mLightGeometry, &mLayerUpdateQueue, mOpaque, mLightInfo); node->incStrong(nullptr); mPrefetchedLayers.insert(node); diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 02ee72f05f04..8448788eb9a6 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -75,8 +75,7 @@ public: */ bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& dmgAccumulator, ErrorHandler* errorHandler) { - return mRenderPipeline->createOrUpdateLayer(node, dmgAccumulator, mWideColorGamut, - errorHandler); + return mRenderPipeline->createOrUpdateLayer(node, dmgAccumulator, errorHandler); } /** diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp index 5f8d7ad3373a..8e44d633f505 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -20,6 +20,7 @@ #include <log/log.h> #include <private/gui/SyncFeatures.h> #include <utils/Trace.h> +#include "utils/Color.h" #include "utils/StringUtils.h" #include "DeviceInfo.h" @@ -75,6 +76,7 @@ static struct { bool pixelFormatFloat = false; bool glColorSpace = false; bool scRGB = false; + bool displayP3 = false; bool contextPriority = false; bool surfacelessContext = false; } EglExtensions; @@ -125,6 +127,17 @@ void EglManager::initialize() { createPBufferSurface(); makeCurrent(mPBufferSurface, nullptr, /* force */ true); DeviceInfo::initialize(); + + mSurfaceColorGamut = DataSpaceToColorGamut( + static_cast<android_dataspace>(DeviceInfo::get()->getTargetDataSpace())); + + LOG_ALWAYS_FATAL_IF(mSurfaceColorGamut == SkColorSpace::kDCIP3_D65_Gamut && + !EglExtensions.displayP3, "EGL doesn't support Display P3."); + + mSurfaceColorType = PixelFormatToColorType( + static_cast<android_pixel_format>(DeviceInfo::get()->getTargetPixelFormat())); + mSurfaceColorSpace = DataSpaceToColorSpace( + static_cast<android_dataspace>(DeviceInfo::get()->getTargetDataSpace())); } void EglManager::initExtensions() { @@ -148,6 +161,7 @@ void EglManager::initExtensions() { #else EglExtensions.scRGB = extensions.has("EGL_EXT_gl_colorspace_scrgb"); #endif + EglExtensions.displayP3 = extensions.has("EGL_EXT_gl_colorspace_display_p3"); EglExtensions.contextPriority = extensions.has("EGL_IMG_context_priority"); EglExtensions.surfacelessContext = extensions.has("EGL_KHR_surfaceless_context"); } @@ -160,6 +174,10 @@ void EglManager::loadConfigs() { ALOGD("Swap behavior %d", static_cast<int>(mSwapBehavior)); EGLint swapBehavior = (mSwapBehavior == SwapBehavior::Preserved) ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0; + + // Note: The default pixel format is RGBA_8888, when other formats are + // available, we should check the target pixel format and configure the + // attributes list properly. EGLint attribs[] = {EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_RED_SIZE, @@ -255,11 +273,12 @@ void EglManager::createPBufferSurface() { } } -EGLSurface EglManager::createSurface(EGLNativeWindowType window, bool wideColorGamut) { +EGLSurface EglManager::createSurface(EGLNativeWindowType window, ColorMode colorMode) { LOG_ALWAYS_FATAL_IF(!hasEglContext(), "Not initialized"); - wideColorGamut = wideColorGamut && EglExtensions.glColorSpace && EglExtensions.scRGB && - EglExtensions.pixelFormatFloat && EglExtensions.noConfigContext; + bool wideColorGamut = colorMode == ColorMode::WideColorGamut && EglExtensions.glColorSpace && + EglExtensions.scRGB && EglExtensions.pixelFormatFloat && + EglExtensions.noConfigContext; // The color space we want to use depends on whether linear blending is turned // on and whether the app has requested wide color gamut rendering. When wide @@ -269,9 +288,9 @@ EGLSurface EglManager::createSurface(EGLNativeWindowType window, bool wideColorG // When wide gamut rendering is off: // - Blending is done by default in gamma space, which requires using a // linear EGL color space (the GPU uses the color values as is) - // - If linear blending is on, we must use the sRGB EGL color space (the - // GPU will perform sRGB to linear and linear to SRGB conversions before - // and after blending) + // - If linear blending is on, we must use the non-linear EGL color space + // (the GPU will perform sRGB to linear and linear to SRGB conversions + // before and after blending) // // When wide gamut rendering is on we cannot rely on the GPU performing // linear blending for us. We use two different color spaces to tag the @@ -279,7 +298,7 @@ EGLSurface EglManager::createSurface(EGLNativeWindowType window, bool wideColorG // - Gamma blending (default) requires the use of the scRGB-nl color space // - Linear blending requires the use of the scRGB color space - // Not all Android targets support the EGL_GL_COLOR_SPACE_KHR extension + // Not all Android targets support the EGL_GL_COLORSPACE_KHR extension // We insert to placeholders to set EGL_GL_COLORSPACE_KHR and its value. // According to section 3.4.1 of the EGL specification, the attributes // list is considered empty if the first entry is EGL_NONE @@ -291,13 +310,21 @@ EGLSurface EglManager::createSurface(EGLNativeWindowType window, bool wideColorG if (wideColorGamut) { attribs[1] = EGL_GL_COLORSPACE_SCRGB_LINEAR_EXT; } else { - attribs[1] = EGL_GL_COLORSPACE_SRGB_KHR; + if (mSurfaceColorGamut == SkColorSpace::kDCIP3_D65_Gamut) { + attribs[1] = EGL_GL_COLORSPACE_DISPLAY_P3_EXT; + } else { + attribs[1] = EGL_GL_COLORSPACE_SRGB_KHR; + } } #else if (wideColorGamut) { attribs[1] = EGL_GL_COLORSPACE_SCRGB_EXT; } else { - attribs[1] = EGL_GL_COLORSPACE_LINEAR_KHR; + if (mSurfaceColorGamut == SkColorSpace::kDCIP3_D65_Gamut) { + attribs[1] = EGL_GL_COLORSPACE_DISPLAY_P3_EXT; + } else { + attribs[1] = EGL_GL_COLORSPACE_LINEAR_KHR; + } } #endif } diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h index 507673adf26e..e97228cd0a39 100644 --- a/libs/hwui/renderthread/EglManager.h +++ b/libs/hwui/renderthread/EglManager.h @@ -18,11 +18,13 @@ #include <EGL/egl.h> #include <EGL/eglext.h> +#include <SkImageInfo.h> #include <SkRect.h> #include <cutils/compiler.h> #include <ui/Fence.h> #include <ui/GraphicBuffer.h> #include <utils/StrongPointer.h> +#include "IRenderPipeline.h" namespace android { namespace uirenderer { @@ -45,7 +47,7 @@ public: bool hasEglContext(); - EGLSurface createSurface(EGLNativeWindowType window, bool wideColorGamut); + EGLSurface createSurface(EGLNativeWindowType window, ColorMode colorMode); void destroySurface(EGLSurface surface); void destroy(); @@ -76,6 +78,9 @@ public: // Depending on installed extensions, the result is either Android native fence or EGL fence. status_t createReleaseFence(bool useFenceSync, EGLSyncKHR* eglFence, sp<Fence>& nativeFence); + SkColorType getSurfaceColorType() const { return mSurfaceColorType; } + sk_sp<SkColorSpace> getSurfaceColorSpace() { return mSurfaceColorSpace; } + private: void initExtensions(); @@ -89,8 +94,10 @@ private: EGLConfig mEglConfigWideGamut; EGLContext mEglContext; EGLSurface mPBufferSurface; - EGLSurface mCurrentSurface; + SkColorSpace::Gamut mSurfaceColorGamut; + SkColorType mSurfaceColorType; + sk_sp<SkColorSpace> mSurfaceColorSpace; enum class SwapBehavior { Discard, diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h index b7b7853e6ed7..0297c9c141ff 100644 --- a/libs/hwui/renderthread/IRenderPipeline.h +++ b/libs/hwui/renderthread/IRenderPipeline.h @@ -16,11 +16,12 @@ #pragma once +#include "DamageAccumulator.h" #include "FrameInfoVisualizer.h" #include "LayerUpdateQueue.h" +#include "Lighting.h" #include "SwapBehavior.h" #include "hwui/Bitmap.h" -#include "thread/TaskManager.h" #include <SkRect.h> #include <utils/RefBase.h> @@ -35,13 +36,25 @@ namespace uirenderer { class DeferredLayerUpdater; class ErrorHandler; +class TaskManager; namespace renderthread { enum class MakeCurrentResult { AlreadyCurrent, Failed, Succeeded }; enum class ColorMode { - Srgb, + // Legacy means HWUI will produce buffer with whatever platform prefers + // HWUI to produce, however, HWUI doesn't accurately convert color from + // source color space to destination color space, instead HWUI will take + // the pixel value directly and interpret it destination color space. + Legacy, + // DisplayColorGamut means HWUI will produce buffer with whatever platform + // prefers HWUI to produce and accurately convert color from source color + // space to destination color space. + DisplayColorGamut, + // WideColorGamut means HWUI would support rendering scRGB non-linear into + // a signed buffer with enough range to support the wide color gamut of the + // display. WideColorGamut, // Hdr }; @@ -55,7 +68,7 @@ public: virtual bool draw(const Frame& frame, const SkRect& screenDirty, const SkRect& dirty, const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds, - bool opaque, bool wideColorGamut, const LightInfo& lightInfo, + bool opaque, const LightInfo& lightInfo, const std::vector<sp<RenderNode>>& renderNodes, FrameInfoVisualizer* profiler) = 0; virtual bool swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty, @@ -67,15 +80,17 @@ public: virtual bool isContextReady() = 0; virtual void onDestroyHardwareResources() = 0; virtual void renderLayers(const LightGeometry& lightGeometry, - LayerUpdateQueue* layerUpdateQueue, bool opaque, bool wideColorGamut, + LayerUpdateQueue* layerUpdateQueue, bool opaque, const LightInfo& lightInfo) = 0; virtual TaskManager* getTaskManager() = 0; virtual bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& damageAccumulator, - bool wideColorGamut, ErrorHandler* errorHandler) = 0; + ErrorHandler* errorHandler) = 0; virtual bool pinImages(std::vector<SkImage*>& mutableImages) = 0; virtual bool pinImages(LsaVector<sk_sp<Bitmap>>& images) = 0; virtual void unpinImages() = 0; virtual void onPrepareTree() = 0; + virtual SkColorType getSurfaceColorType() const = 0; + virtual sk_sp<SkColorSpace> getSurfaceColorSpace() = 0; virtual ~IRenderPipeline() {} }; diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h index ebc11a50685e..7a539ae42605 100644 --- a/libs/hwui/renderthread/VulkanManager.h +++ b/libs/hwui/renderthread/VulkanManager.h @@ -118,6 +118,10 @@ public: // Creates a fence that is signaled, when all the pending Vulkan commands are flushed. status_t createReleaseFence(sp<Fence>& nativeFence); + // TODO(b/115636873): Handle composition preference. + SkColorType getSurfaceColorType() const { return SkColorType::kN32_SkColorType; } + sk_sp<SkColorSpace> getSurfaceColorSpace() { return SkColorSpace::MakeSRGB(); } + private: friend class RenderThread; diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp index 48bc8e4df155..2926ef3a2d95 100644 --- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp +++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp @@ -537,7 +537,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, projectionHwLayer) { layerUpdateQueue.enqueueLayerWithDamage(child.get(), android::uirenderer::Rect(LAYER_WIDTH, LAYER_HEIGHT)); auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); - pipeline->renderLayersImpl(layerUpdateQueue, true, false); + pipeline->renderLayersImpl(layerUpdateQueue, true); EXPECT_EQ(1, drawCounter); // assert index 0 is drawn on the layer RenderNodeDrawable drawable(parent.get(), surfaceLayer1->getCanvas(), true); diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp index a9124d90f9a5..5e5d13485942 100644 --- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp +++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp @@ -50,7 +50,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrame) { auto surface = SkSurface::MakeRasterN32Premul(1, 1); surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds, + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED); } @@ -83,7 +83,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, testOnPrepareTree) { ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); // drawFrame will crash if "SkiaPipeline::onPrepareTree" did not clean invalid VD pointer - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds, + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorRED); } @@ -105,11 +105,11 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckOpaque) { auto surface = SkSurface::MakeRasterN32Premul(2, 2); surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, false, contentDrawBounds, + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, false, false, contentDrawBounds, + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, false, contentDrawBounds, surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned int)SK_ColorTRANSPARENT); ASSERT_EQ(TestUtils::getColor(surface, 0, 1), SK_ColorGREEN); @@ -129,7 +129,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderFrameCheckDirtyRect) { auto surface = SkSurface::MakeRasterN32Premul(2, 2); surface->getCanvas()->drawColor(SK_ColorBLUE, SkBlendMode::kSrcOver); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, false, contentDrawBounds, + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, true, contentDrawBounds, surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); ASSERT_EQ(TestUtils::getColor(surface, 1, 0), SK_ColorBLUE); @@ -171,7 +171,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderLayer) { lightGeometry.center = {0.0f, 0.0f, 0.0f}; LightInfo lightInfo; auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); - pipeline->renderLayers(lightGeometry, &layerUpdateQueue, opaque, false, lightInfo); + pipeline->renderLayers(lightGeometry, &layerUpdateQueue, opaque, lightInfo); ASSERT_EQ(TestUtils::getColor(surfaceLayer1, 0, 0), SK_ColorRED); ASSERT_EQ(TestUtils::getColor(surfaceLayer2, 0, 0), SK_ColorBLUE); ASSERT_EQ(TestUtils::getColor(surfaceLayer2, 0, 1), SK_ColorWHITE); @@ -202,37 +202,37 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, renderOverdraw) { ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorBLUE); // Single draw, should be white. - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds, + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), SK_ColorWHITE); // 1 Overdraw, should be blue blended onto white. renderNodes.push_back(whiteNode); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds, + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffd0d0ff); // 2 Overdraw, should be green blended onto white renderNodes.push_back(whiteNode); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds, + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffd0ffd0); // 3 Overdraw, should be pink blended onto white. renderNodes.push_back(whiteNode); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds, + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffffc0c0); // 4 Overdraw, should be red blended onto white. renderNodes.push_back(whiteNode); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds, + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffff8080); // 5 Overdraw, should be red blended onto white. renderNodes.push_back(whiteNode); - pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, false, contentDrawBounds, + pipeline->renderFrame(layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface); ASSERT_EQ(TestUtils::getColor(surface, 0, 0), (unsigned)0xffff8080); } @@ -318,7 +318,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, deferRenderNodeScene) { SkRect dirty = SkRect::MakeWH(800, 600); auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); sk_sp<DeferLayer<DeferTestCanvas>> surface(new DeferLayer<DeferTestCanvas>()); - pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true, false, contentDrawBounds, surface); + pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true, contentDrawBounds, surface); EXPECT_EQ(4, surface->canvas()->mDrawCounter); } @@ -348,7 +348,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clipped) { SkRect dirty = SkRect::MakeLTRB(10, 20, 30, 40); auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); sk_sp<DeferLayer<ClippedTestCanvas>> surface(new DeferLayer<ClippedTestCanvas>()); - pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true, false, + pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true, SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), surface); EXPECT_EQ(1, surface->canvas()->mDrawCounter); } @@ -378,7 +378,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(SkiaPipeline, clip_replace) { SkRect dirty = SkRect::MakeLTRB(10, 10, 40, 40); auto pipeline = std::make_unique<SkiaOpenGLPipeline>(renderThread); sk_sp<DeferLayer<ClipReplaceTestCanvas>> surface(new DeferLayer<ClipReplaceTestCanvas>()); - pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true, false, + pipeline->renderFrame(layerUpdateQueue, dirty, nodes, true, SkRect::MakeWH(CANVAS_WIDTH, CANVAS_HEIGHT), surface); EXPECT_EQ(1, surface->canvas()->mDrawCounter); } diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp index a3e785957526..d35fe4f01aa0 100644 --- a/libs/hwui/utils/Color.cpp +++ b/libs/hwui/utils/Color.cpp @@ -57,6 +57,45 @@ bool transferFunctionCloseToSRGB(const SkColorSpace* colorSpace) { return false; } +SkColorType PixelFormatToColorType(android_pixel_format pixelFormat) { + switch (pixelFormat) { + case HAL_PIXEL_FORMAT_RGBA_8888: + case HAL_PIXEL_FORMAT_BGRA_8888: + return SkColorType::kN32_SkColorType; + case HAL_PIXEL_FORMAT_RGBA_FP16: + return SkColorType::kRGBA_F16_SkColorType; + case HAL_PIXEL_FORMAT_RGBA_1010102: + return SkColorType::kRGBA_1010102_SkColorType; + default: + ALOGW("Unsupported pixel format: %d, return kN32 by default", pixelFormat); + return SkColorType::kN32_SkColorType; + } +} + +SkColorSpace::Gamut DataSpaceToColorGamut(android_dataspace dataSpace) { + switch (dataSpace & HAL_DATASPACE_STANDARD_MASK) { + case HAL_DATASPACE_STANDARD_BT709: + return SkColorSpace::kSRGB_Gamut; + case HAL_DATASPACE_STANDARD_BT2020: + return SkColorSpace::kRec2020_Gamut; + case HAL_DATASPACE_STANDARD_DCI_P3: + return SkColorSpace::kDCIP3_D65_Gamut; + case HAL_DATASPACE_STANDARD_ADOBE_RGB: + return SkColorSpace::kAdobeRGB_Gamut; + case HAL_DATASPACE_STANDARD_UNSPECIFIED: + case HAL_DATASPACE_STANDARD_BT601_625: + case HAL_DATASPACE_STANDARD_BT601_625_UNADJUSTED: + case HAL_DATASPACE_STANDARD_BT601_525: + case HAL_DATASPACE_STANDARD_BT601_525_UNADJUSTED: + case HAL_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE: + case HAL_DATASPACE_STANDARD_BT470M: + case HAL_DATASPACE_STANDARD_FILM: + default: + ALOGW("Unsupported Gamut: %d, return SRGB gamut by default", dataSpace); + return SkColorSpace::kSRGB_Gamut; + } +} + sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace) { SkColorSpace::Gamut gamut; diff --git a/libs/hwui/utils/Color.h b/libs/hwui/utils/Color.h index 3c13a548fe76..ff0e755934f2 100644 --- a/libs/hwui/utils/Color.h +++ b/libs/hwui/utils/Color.h @@ -21,6 +21,7 @@ #include <SkColor.h> #include <SkColorSpace.h> +#include <SkImageInfo.h> namespace android { namespace uirenderer { @@ -113,6 +114,10 @@ static constexpr float EOCF(float srgb) { // returns true for sRGB, gamma 2.2 and Display P3 for instance bool transferFunctionCloseToSRGB(const SkColorSpace* colorSpace); +SkColorType PixelFormatToColorType(android_pixel_format pixelFormat); + +SkColorSpace::Gamut DataSpaceToColorGamut(android_dataspace dataSpace); + sk_sp<SkColorSpace> DataSpaceToColorSpace(android_dataspace dataspace); struct Lab { diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index aaf7dd793060..62e58cab0772 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -1656,7 +1656,7 @@ public class AudioTrack extends PlayerBase * @param timestamp a reference to a non-null AudioTimestamp instance allocated * and owned by caller. * @return true if a timestamp is available, or false if no timestamp is available. - * If a timestamp if available, + * If a timestamp is available, * the AudioTimestamp instance is filled in with a position in frame units, together * with the estimated time when that frame was presented or is committed to * be presented. diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java index b6f51bc4d781..95569dc6d1b7 100644 --- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java @@ -613,7 +613,7 @@ public class SettingsProviderTest extends BaseSettingsProviderTest { final String name, final String value, final int userId) throws Exception { ContentResolver contentResolver = getContext().getContentResolver(); - final Uri settingUri = getBaseUriForType(type); + final Uri settingUri = getBaseUriForType(type).buildUpon().appendPath(name).build(); final AtomicBoolean success = new AtomicBoolean(); @@ -640,20 +640,22 @@ public class SettingsProviderTest extends BaseSettingsProviderTest { final long startTimeMillis = SystemClock.uptimeMillis(); synchronized (mLock) { - if (success.get()) { - return; - } - final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; - if (elapsedTimeMillis > WAIT_FOR_SETTING_URI_CHANGE_TIMEOUT_MILLIS) { - fail("Could not change setting for " - + WAIT_FOR_SETTING_URI_CHANGE_TIMEOUT_MILLIS + " ms"); - } - final long remainingTimeMillis = WAIT_FOR_SETTING_URI_CHANGE_TIMEOUT_MILLIS - - elapsedTimeMillis; - try { - mLock.wait(remainingTimeMillis); - } catch (InterruptedException ie) { - /* ignore */ + while (true) { + if (success.get()) { + return; + } + final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; + if (elapsedTimeMillis > WAIT_FOR_SETTING_URI_CHANGE_TIMEOUT_MILLIS) { + fail("Could not change setting for " + + WAIT_FOR_SETTING_URI_CHANGE_TIMEOUT_MILLIS + " ms"); + } + final long remainingTimeMillis = WAIT_FOR_SETTING_URI_CHANGE_TIMEOUT_MILLIS + - elapsedTimeMillis; + try { + mLock.wait(remainingTimeMillis); + } catch (InterruptedException ie) { + /* ignore */ + } } } } finally { @@ -689,39 +691,50 @@ public class SettingsProviderTest extends BaseSettingsProviderTest { } @Test - public void testUpdateLocationProvidersAllowedLocked_enableProviders() throws Exception { - setSettingViaFrontEndApiAndAssertSuccessfulChange( + public void testLocationModeChanges_viaFrontEndApi() throws Exception { + setStringViaFrontEndApiSetting( SETTING_TYPE_SECURE, Settings.Secure.LOCATION_MODE, String.valueOf(Settings.Secure.LOCATION_MODE_OFF), UserHandle.USER_SYSTEM); - - // Enable one provider - updateStringViaProviderApiSetting( - SETTING_TYPE_SECURE, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "+gps"); - assertEquals( "Wrong location providers", - "gps", - queryStringViaProviderApi( - SETTING_TYPE_SECURE, Settings.Secure.LOCATION_PROVIDERS_ALLOWED)); + "", + getStringViaFrontEndApiSetting( + SETTING_TYPE_SECURE, + Settings.Secure.LOCATION_PROVIDERS_ALLOWED, + UserHandle.USER_SYSTEM)); - // Enable a list of providers, including the one that is already enabled - updateStringViaProviderApiSetting( + setStringViaFrontEndApiSetting( SETTING_TYPE_SECURE, - Settings.Secure.LOCATION_PROVIDERS_ALLOWED, - "+gps,+network,+network"); + Settings.Secure.LOCATION_MODE, + String.valueOf(Settings.Secure.LOCATION_MODE_BATTERY_SAVING), + UserHandle.USER_SYSTEM); + assertEquals( + "Wrong location providers", + "network", + getStringViaFrontEndApiSetting( + SETTING_TYPE_SECURE, + Settings.Secure.LOCATION_PROVIDERS_ALLOWED, + UserHandle.USER_SYSTEM)); + setStringViaFrontEndApiSetting( + SETTING_TYPE_SECURE, + Settings.Secure.LOCATION_MODE, + String.valueOf(Settings.Secure.LOCATION_MODE_HIGH_ACCURACY), + UserHandle.USER_SYSTEM); assertEquals( "Wrong location providers", "gps,network", - queryStringViaProviderApi( - SETTING_TYPE_SECURE, Settings.Secure.LOCATION_PROVIDERS_ALLOWED)); + getStringViaFrontEndApiSetting( + SETTING_TYPE_SECURE, + Settings.Secure.LOCATION_PROVIDERS_ALLOWED, + UserHandle.USER_SYSTEM)); } @Test - public void testUpdateLocationProvidersAllowedLocked_disableProviders() throws Exception { - setSettingViaFrontEndApiAndAssertSuccessfulChange( + public void testLocationProvidersAllowed_disableProviders() throws Exception { + setStringViaFrontEndApiSetting( SETTING_TYPE_SECURE, Settings.Secure.LOCATION_MODE, String.valueOf(Settings.Secure.LOCATION_MODE_HIGH_ACCURACY), @@ -732,7 +745,6 @@ public class SettingsProviderTest extends BaseSettingsProviderTest { SETTING_TYPE_SECURE, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "-gps,-network"); - assertEquals( "Wrong location providers", "", @@ -744,7 +756,6 @@ public class SettingsProviderTest extends BaseSettingsProviderTest { SETTING_TYPE_SECURE, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, "-test"); - assertEquals( "Wrong location providers", "", @@ -753,8 +764,8 @@ public class SettingsProviderTest extends BaseSettingsProviderTest { } @Test - public void testUpdateLocationProvidersAllowedLocked_enableAndDisable() throws Exception { - setSettingViaFrontEndApiAndAssertSuccessfulChange( + public void testLocationProvidersAllowed_enableAndDisable() throws Exception { + setStringViaFrontEndApiSetting( SETTING_TYPE_SECURE, Settings.Secure.LOCATION_MODE, String.valueOf(Settings.Secure.LOCATION_MODE_OFF), @@ -775,8 +786,8 @@ public class SettingsProviderTest extends BaseSettingsProviderTest { } @Test - public void testUpdateLocationProvidersAllowedLocked_invalidInput() throws Exception { - setSettingViaFrontEndApiAndAssertSuccessfulChange( + public void testLocationProvidersAllowedLocked_invalidInput() throws Exception { + setStringViaFrontEndApiSetting( SETTING_TYPE_SECURE, Settings.Secure.LOCATION_MODE, String.valueOf(Settings.Secure.LOCATION_MODE_OFF), diff --git a/packages/Shell/src/com/android/shell/BugreportStorageProvider.java b/packages/Shell/src/com/android/shell/BugreportStorageProvider.java index 0734e0dae790..4fbc22663675 100644 --- a/packages/Shell/src/com/android/shell/BugreportStorageProvider.java +++ b/packages/Shell/src/com/android/shell/BugreportStorageProvider.java @@ -20,6 +20,7 @@ import android.database.Cursor; import android.database.MatrixCursor; import android.database.MatrixCursor.RowBuilder; import android.net.Uri; +import android.os.Bundle; import android.os.CancellationSignal; import android.os.FileUtils; import android.os.ParcelFileDescriptor; @@ -68,6 +69,18 @@ public class BugreportStorageProvider extends FileSystemProvider { } @Override + public Cursor queryChildDocuments( + String parentDocumentId, String[] projection, String sortOrder) + throws FileNotFoundException { + final Cursor c = super.queryChildDocuments(parentDocumentId, projection, sortOrder); + final Bundle extras = new Bundle(); + extras.putCharSequence(DocumentsContract.EXTRA_INFO, + getContext().getText(R.string.bugreport_confirm)); + c.setExtras(extras); + return c; + } + + @Override public Cursor queryDocument(String documentId, String[] projection) throws FileNotFoundException { if (DOC_ID_ROOT.equals(documentId)) { diff --git a/packages/SystemUI/res/layout/operator_name.xml b/packages/SystemUI/res/layout/operator_name.xml index c4f75e927604..015e30a5d050 100644 --- a/packages/SystemUI/res/layout/operator_name.xml +++ b/packages/SystemUI/res/layout/operator_name.xml @@ -27,5 +27,6 @@ android:maxLength="20" android:gravity="center_vertical|start" android:textAppearance="?android:attr/textAppearanceSmall" - android:singleLine="true" /> + android:singleLine="true" + android:paddingEnd="5dp" /> </com.android.systemui.statusbar.AlphaOptimizedFrameLayout> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java index 5fe362fc4d10..6b6566cc1d68 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupManager.java @@ -415,10 +415,10 @@ public class NotificationGroupManager implements OnHeadsUpChangedListener { return false; } NotificationGroup group = mGroupMap.get(getGroupKey(sbn)); - if (group == null) { + if (group == null || group.summary == null) { return false; } - return !group.children.isEmpty(); + return !group.children.isEmpty() && Objects.equals(group.summary.notification, sbn); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java index 3c16329e6f19..e9efaa14ef7c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java @@ -205,6 +205,8 @@ public class HotspotControllerImpl implements HotspotController, WifiManager.Sof public void onTetheringFailed() { if (DEBUG) Log.d(TAG, "onTetheringFailed"); mWaitingForCallback = false; + // TODO(b/110697252): stopTethering must be called to reset soft ap state after failure + mConnectivityManager.stopTethering(ConnectivityManager.TETHERING_WIFI); fireHotspotChangedCallback(isHotspotEnabled()); // TODO: Show error. } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java new file mode 100644 index 000000000000..6a3c8a850831 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationGroupManagerTest.java @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.verify; + +import android.app.ActivityManager; +import android.app.Notification; +import android.os.UserHandle; +import android.service.notification.StatusBarNotification; +import android.support.test.filters.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.notification.NotificationData; +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; +import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener; +import com.android.systemui.statusbar.policy.HeadsUpManager; + +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.MockitoJUnit; +import org.mockito.junit.MockitoRule; + + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class NotificationGroupManagerTest extends SysuiTestCase { + @Rule + public MockitoRule rule = MockitoJUnit.rule(); + + private static final String TEST_CHANNEL_ID = "test_channel"; + private static final String TEST_GROUP_ID = "test_group"; + private static final String TEST_PACKAGE_NAME = "test_pkg"; + private NotificationGroupManager mGroupManager = new NotificationGroupManager(); + private int mId = 0; + + @Mock HeadsUpManager mHeadsUpManager; + + @Before + public void setup() { + mGroupManager.setHeadsUpManager(mHeadsUpManager); + mGroupManager.setOnGroupChangeListener(mock(OnGroupChangeListener.class)); + } + + @Test + public void testIsOnlyChildInGroup() { + NotificationData.Entry childEntry = createChildNotification(); + NotificationData.Entry summaryEntry = createSummaryNotification(); + + mGroupManager.onEntryAdded(summaryEntry); + mGroupManager.onEntryAdded(childEntry); + + assertTrue(mGroupManager.isOnlyChildInGroup(childEntry.notification)); + } + + @Test + public void testIsChildInGroupWithSummary() { + NotificationData.Entry childEntry = createChildNotification(); + NotificationData.Entry summaryEntry = createSummaryNotification(); + + mGroupManager.onEntryAdded(summaryEntry); + mGroupManager.onEntryAdded(childEntry); + mGroupManager.onEntryAdded(createChildNotification()); + + assertTrue(mGroupManager.isChildInGroupWithSummary(childEntry.notification)); + } + + @Test + public void testIsSummaryOfGroupWithChildren() { + NotificationData.Entry childEntry = createChildNotification(); + NotificationData.Entry summaryEntry = createSummaryNotification(); + + mGroupManager.onEntryAdded(summaryEntry); + mGroupManager.onEntryAdded(childEntry); + mGroupManager.onEntryAdded(createChildNotification()); + + assertTrue(mGroupManager.isSummaryOfGroup(summaryEntry.notification)); + assertEquals(summaryEntry.row, mGroupManager.getGroupSummary(childEntry.notification)); + } + + @Test + public void testRemoveChildFromGroupWithSummary() { + NotificationData.Entry childEntry = createChildNotification(); + NotificationData.Entry summaryEntry = createSummaryNotification(); + mGroupManager.onEntryAdded(summaryEntry); + mGroupManager.onEntryAdded(childEntry); + mGroupManager.onEntryAdded(createChildNotification()); + + mGroupManager.onEntryRemoved(childEntry); + + assertFalse(mGroupManager.isChildInGroupWithSummary(childEntry.notification)); + } + + @Test + public void testRemoveSummaryFromGroupWithSummary() { + NotificationData.Entry childEntry = createChildNotification(); + NotificationData.Entry summaryEntry = createSummaryNotification(); + mGroupManager.onEntryAdded(summaryEntry); + mGroupManager.onEntryAdded(childEntry); + mGroupManager.onEntryAdded(createChildNotification()); + + mGroupManager.onEntryRemoved(summaryEntry); + + assertNull(mGroupManager.getGroupSummary(childEntry.notification)); + assertFalse(mGroupManager.isSummaryOfGroup(summaryEntry.notification)); + } + + @Test + public void testHeadsUpEntryIsIsolated() { + NotificationData.Entry childEntry = createChildNotification(); + NotificationData.Entry summaryEntry = createSummaryNotification(); + mGroupManager.onEntryAdded(summaryEntry); + mGroupManager.onEntryAdded(childEntry); + mGroupManager.onEntryAdded(createChildNotification()); + when(childEntry.row.isHeadsUp()).thenReturn(true); + when(mHeadsUpManager.contains(childEntry.key)).thenReturn(true); + + mGroupManager.onHeadsUpStateChanged(childEntry, true); + + // Child entries that are heads upped should be considered separate groups visually even if + // they are the same group logically + assertEquals(childEntry.row, mGroupManager.getGroupSummary(childEntry.notification)); + assertEquals(summaryEntry.row, + mGroupManager.getLogicalGroupSummary(childEntry.notification)); + } + + @Test + public void testSuppressedSummaryHeadsUpTransfersToChild() { + NotificationData.Entry summaryEntry = createSummaryNotification(); + when(summaryEntry.row.isHeadsUp()).thenReturn(true); + when(mHeadsUpManager.contains(summaryEntry.key)).thenReturn(true); + NotificationData.Entry childEntry = createChildNotification(); + + // Summary will be suppressed because there is only one child + mGroupManager.onEntryAdded(summaryEntry); + mGroupManager.onEntryAdded(childEntry); + + // A suppressed summary should transfer its heads up state to the child + verify(mHeadsUpManager, never()).showNotification(summaryEntry); + verify(mHeadsUpManager).showNotification(childEntry); + } + + @Test + public void testSuppressedSummaryHeadsUpTransfersToChildButBackAgain() { + mHeadsUpManager = new HeadsUpManager(mContext) {}; + mGroupManager.setHeadsUpManager(mHeadsUpManager); + NotificationData.Entry summaryEntry = + createSummaryNotification(Notification.GROUP_ALERT_SUMMARY); + when(summaryEntry.row.isHeadsUp()).thenReturn(true); + NotificationData.Entry childEntry = + createChildNotification(Notification.GROUP_ALERT_SUMMARY); + NotificationData.Entry childEntry2 = + createChildNotification(Notification.GROUP_ALERT_SUMMARY); + // Trigger a transfer of heads up state from summary to child. + mGroupManager.onEntryAdded(summaryEntry); + mGroupManager.onEntryAdded(childEntry); + when(summaryEntry.row.isHeadsUp()).thenReturn(false); + when(childEntry.row.isHeadsUp()).thenReturn(true); + + // Add second child notification so that summary is no longer suppressed. + mGroupManager.onEntryAdded(childEntry2); + + // The heads up state should transfer back to the summary as there is now more than one + // child and the summary should no longer be suppressed. + assertTrue(mHeadsUpManager.contains(summaryEntry.key)); + assertFalse(mHeadsUpManager.contains(childEntry.key)); + } + + private NotificationData.Entry createSummaryNotification() { + return createSummaryNotification(Notification.GROUP_ALERT_ALL); + } + + private NotificationData.Entry createSummaryNotification(int groupAlertBehavior) { + return createEntry(true, groupAlertBehavior); + } + + private NotificationData.Entry createChildNotification() { + return createChildNotification(Notification.GROUP_ALERT_ALL); + } + + private NotificationData.Entry createChildNotification(int groupAlertBehavior) { + return createEntry(false, groupAlertBehavior); + } + + private NotificationData.Entry createEntry(boolean isSummary, int groupAlertBehavior) { + Notification notif = new Notification.Builder(mContext, TEST_CHANNEL_ID) + .setContentTitle("Title") + .setSmallIcon(R.drawable.ic_person) + .setGroupAlertBehavior(groupAlertBehavior) + .setGroupSummary(isSummary) + .setGroup(TEST_GROUP_ID) + .build(); + StatusBarNotification sbn = new StatusBarNotification( + TEST_PACKAGE_NAME /* pkg */, + TEST_PACKAGE_NAME, + mId++, + null /* tag */, + 0, /* uid */ + 0 /* initialPid */, + notif, + new UserHandle(ActivityManager.getCurrentUser()), + null /* overrideGroupKey */, + 0 /* postTime */); + NotificationData.Entry entry = new NotificationData.Entry(sbn); + ExpandableNotificationRow row = mock(ExpandableNotificationRow.class); + entry.row = row; + when(row.getEntry()).thenReturn(entry); + when(row.getStatusBarNotification()).thenReturn(sbn); + return entry; + } +} diff --git a/services/core/java/com/android/server/biometrics/BiometricPromptService.java b/services/core/java/com/android/server/biometrics/BiometricPromptService.java index 29eda8ba126e..d1371d1e0bf5 100644 --- a/services/core/java/com/android/server/biometrics/BiometricPromptService.java +++ b/services/core/java/com/android/server/biometrics/BiometricPromptService.java @@ -41,7 +41,6 @@ import android.os.UserHandle; import android.util.Slog; import com.android.internal.R; -import com.android.internal.os.SomeArgs; import com.android.server.SystemService; import java.util.ArrayList; @@ -133,6 +132,12 @@ public class BiometricPromptService extends SystemService { // AppOps and foreground check. checkPermission(); + if (token == null || receiver == null || opPackageName == null || bundle == null + || dialogReceiver == null) { + Slog.e(TAG, "Unable to authenticate, one or more null arguments"); + return; + } + final int callingUid = Binder.getCallingUid(); final int callingPid = Binder.getCallingPid(); final int callingUserId = UserHandle.getCallingUserId(); @@ -166,6 +171,11 @@ public class BiometricPromptService extends SystemService { throws RemoteException { checkPermission(); + if (token == null || opPackageName == null) { + Slog.e(TAG, "Unable to cancel, one or more null arguments"); + return; + } + final int callingUid = Binder.getCallingUid(); final int callingPid = Binder.getCallingPid(); final int callingUserId = UserHandle.getCallingUserId(); diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java index d16c2776dc3b..a8f7259050c1 100644 --- a/services/core/java/com/android/server/connectivity/Tethering.java +++ b/services/core/java/com/android/server/connectivity/Tethering.java @@ -68,6 +68,7 @@ import android.content.res.Resources; import android.hardware.usb.UsbManager; import android.net.INetworkPolicyManager; import android.net.INetworkStatsService; +import android.net.ip.IpServer; import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; @@ -112,10 +113,8 @@ import com.android.internal.util.Protocol; import com.android.internal.util.State; import com.android.internal.util.StateMachine; import com.android.server.LocalServices; -import com.android.server.connectivity.tethering.IControlsTethering; import com.android.server.connectivity.tethering.IPv6TetheringCoordinator; import com.android.server.connectivity.tethering.OffloadController; -import com.android.server.connectivity.tethering.TetherInterfaceStateMachine; import com.android.server.connectivity.tethering.TetheringConfiguration; import com.android.server.connectivity.tethering.TetheringDependencies; import com.android.server.connectivity.tethering.TetheringInterfaceUtils; @@ -149,7 +148,7 @@ public class Tethering extends BaseNetworkObserver { protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning"; private static final Class[] messageClasses = { - Tethering.class, TetherMasterSM.class, TetherInterfaceStateMachine.class + Tethering.class, TetherMasterSM.class, IpServer.class }; private static final SparseArray<String> sMagicDecoderRing = MessageUtils.findMessageNames(messageClasses); @@ -159,21 +158,21 @@ public class Tethering extends BaseNetworkObserver { .getSystem().getString(com.android.internal.R.string.config_wifi_tether_enable)); private static class TetherState { - public final TetherInterfaceStateMachine stateMachine; + public final IpServer ipServer; public int lastState; public int lastError; - public TetherState(TetherInterfaceStateMachine sm) { - stateMachine = sm; + public TetherState(IpServer ipServer) { + this.ipServer = ipServer; // Assume all state machines start out available and with no errors. - lastState = IControlsTethering.STATE_AVAILABLE; + lastState = IpServer.STATE_AVAILABLE; lastError = TETHER_ERROR_NO_ERROR; } public boolean isCurrentlyServing() { switch (lastState) { - case IControlsTethering.STATE_TETHERED: - case IControlsTethering.STATE_LOCAL_ONLY: + case IpServer.STATE_TETHERED: + case IpServer.STATE_LOCAL_ONLY: return true; default: return false; @@ -198,7 +197,7 @@ public class Tethering extends BaseNetworkObserver { private final UpstreamNetworkMonitor mUpstreamNetworkMonitor; // TODO: Figure out how to merge this and other downstream-tracking objects // into a single coherent structure. - private final HashSet<TetherInterfaceStateMachine> mForwardedDownstreams; + private final HashSet<IpServer> mForwardedDownstreams; private final VersionedBroadcastListener mCarrierConfigChange; private final TetheringDependencies mDeps; @@ -604,7 +603,7 @@ public class Tethering extends BaseNetworkObserver { } public int tether(String iface) { - return tether(iface, IControlsTethering.STATE_TETHERED); + return tether(iface, IpServer.STATE_TETHERED); } private int tether(String iface, int requestedState) { @@ -617,7 +616,7 @@ public class Tethering extends BaseNetworkObserver { } // Ignore the error status of the interface. If the interface is available, // the errors are referring to past tethering attempts anyway. - if (tetherState.lastState != IControlsTethering.STATE_AVAILABLE) { + if (tetherState.lastState != IpServer.STATE_AVAILABLE) { Log.e(TAG, "Tried to Tether an unavailable iface: " + iface + ", ignoring"); return TETHER_ERROR_UNAVAIL_IFACE; } @@ -626,8 +625,7 @@ public class Tethering extends BaseNetworkObserver { // return an error. // // TODO: reexamine the threading and messaging model. - tetherState.stateMachine.sendMessage( - TetherInterfaceStateMachine.CMD_TETHER_REQUESTED, requestedState); + tetherState.ipServer.sendMessage(IpServer.CMD_TETHER_REQUESTED, requestedState); return TETHER_ERROR_NO_ERROR; } } @@ -644,8 +642,7 @@ public class Tethering extends BaseNetworkObserver { Log.e(TAG, "Tried to untether an inactive iface :" + iface + ", ignoring"); return TETHER_ERROR_UNAVAIL_IFACE; } - tetherState.stateMachine.sendMessage( - TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED); + tetherState.ipServer.sendMessage(IpServer.CMD_TETHER_UNREQUESTED); return TETHER_ERROR_NO_ERROR; } } @@ -689,11 +686,11 @@ public class Tethering extends BaseNetworkObserver { String iface = mTetherStates.keyAt(i); if (tetherState.lastError != TETHER_ERROR_NO_ERROR) { erroredList.add(iface); - } else if (tetherState.lastState == IControlsTethering.STATE_AVAILABLE) { + } else if (tetherState.lastState == IpServer.STATE_AVAILABLE) { availableList.add(iface); - } else if (tetherState.lastState == IControlsTethering.STATE_LOCAL_ONLY) { + } else if (tetherState.lastState == IpServer.STATE_LOCAL_ONLY) { localOnlyList.add(iface); - } else if (tetherState.lastState == IControlsTethering.STATE_TETHERED) { + } else if (tetherState.lastState == IpServer.STATE_TETHERED) { if (cfg.isUsb(iface)) { usbTethered = true; } else if (cfg.isWifi(iface)) { @@ -882,10 +879,10 @@ public class Tethering extends BaseNetworkObserver { synchronized (Tethering.this.mPublicSync) { if (!usbConnected && mRndisEnabled) { // Turn off tethering if it was enabled and there is a disconnect. - tetherMatchingInterfaces(IControlsTethering.STATE_AVAILABLE, TETHERING_USB); + tetherMatchingInterfaces(IpServer.STATE_AVAILABLE, TETHERING_USB); } else if (usbConfigured && rndisEnabled) { // Tether if rndis is enabled and usb is configured. - tetherMatchingInterfaces(IControlsTethering.STATE_TETHERED, TETHERING_USB); + tetherMatchingInterfaces(IpServer.STATE_TETHERED, TETHERING_USB); } mRndisEnabled = usbConfigured && rndisEnabled; } @@ -959,15 +956,15 @@ public class Tethering extends BaseNetworkObserver { if (!TextUtils.isEmpty(ifname)) { final TetherState ts = mTetherStates.get(ifname); if (ts != null) { - ts.stateMachine.unwanted(); + ts.ipServer.unwanted(); return; } } for (int i = 0; i < mTetherStates.size(); i++) { - TetherInterfaceStateMachine tism = mTetherStates.valueAt(i).stateMachine; - if (tism.interfaceType() == TETHERING_WIFI) { - tism.unwanted(); + final IpServer ipServer = mTetherStates.valueAt(i).ipServer; + if (ipServer.interfaceType() == TETHERING_WIFI) { + ipServer.unwanted(); return; } } @@ -978,15 +975,15 @@ public class Tethering extends BaseNetworkObserver { } private void enableWifiIpServingLocked(String ifname, int wifiIpMode) { - // Map wifiIpMode values to IControlsTethering serving states, inferring + // Map wifiIpMode values to IpServer.Callback serving states, inferring // from mWifiTetherRequested as a final "best guess". final int ipServingMode; switch (wifiIpMode) { case IFACE_IP_MODE_TETHERED: - ipServingMode = IControlsTethering.STATE_TETHERED; + ipServingMode = IpServer.STATE_TETHERED; break; case IFACE_IP_MODE_LOCAL_ONLY: - ipServingMode = IControlsTethering.STATE_LOCAL_ONLY; + ipServingMode = IpServer.STATE_LOCAL_ONLY; break; default: mLog.e("Cannot enable IP serving in unknown WiFi mode: " + wifiIpMode); @@ -1041,12 +1038,12 @@ public class Tethering extends BaseNetworkObserver { private void changeInterfaceState(String ifname, int requestedState) { final int result; switch (requestedState) { - case IControlsTethering.STATE_UNAVAILABLE: - case IControlsTethering.STATE_AVAILABLE: + case IpServer.STATE_UNAVAILABLE: + case IpServer.STATE_AVAILABLE: result = untether(ifname); break; - case IControlsTethering.STATE_TETHERED: - case IControlsTethering.STATE_LOCAL_ONLY: + case IpServer.STATE_TETHERED: + case IpServer.STATE_LOCAL_ONLY: result = tether(ifname, requestedState); break; default: @@ -1104,7 +1101,7 @@ public class Tethering extends BaseNetworkObserver { synchronized (mPublicSync) { for (int i = 0; i < mTetherStates.size(); i++) { TetherState tetherState = mTetherStates.valueAt(i); - if (tetherState.lastState == IControlsTethering.STATE_TETHERED) { + if (tetherState.lastState == IpServer.STATE_TETHERED) { list.add(mTetherStates.keyAt(i)); } } @@ -1117,7 +1114,7 @@ public class Tethering extends BaseNetworkObserver { synchronized (mPublicSync) { for (int i = 0; i < mTetherStates.size(); i++) { TetherState tetherState = mTetherStates.valueAt(i); - if (tetherState.lastState == IControlsTethering.STATE_AVAILABLE) { + if (tetherState.lastState == IpServer.STATE_AVAILABLE) { list.add(mTetherStates.keyAt(i)); } } @@ -1177,7 +1174,7 @@ public class Tethering extends BaseNetworkObserver { synchronized (mPublicSync) { for (int i = 0; i < mTetherStates.size(); i++) { TetherState tetherState = mTetherStates.valueAt(i); - if (tetherState.lastState != IControlsTethering.STATE_TETHERED) { + if (tetherState.lastState != IpServer.STATE_TETHERED) { continue; // Skip interfaces that aren't tethered. } String iface = mTetherStates.keyAt(i); @@ -1231,7 +1228,7 @@ public class Tethering extends BaseNetworkObserver { // Because we excise interfaces immediately from mTetherStates, we must maintain mNotifyList // so that the garbage collector does not clean up the state machine before it has a chance // to tear itself down. - private final ArrayList<TetherInterfaceStateMachine> mNotifyList; + private final ArrayList<IpServer> mNotifyList; private final IPv6TetheringCoordinator mIPv6TetheringCoordinator; private final OffloadWrapper mOffload; @@ -1268,17 +1265,19 @@ public class Tethering extends BaseNetworkObserver { public boolean processMessage(Message message) { logMessage(this, message.what); switch (message.what) { - case EVENT_IFACE_SERVING_STATE_ACTIVE: - TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj; + case EVENT_IFACE_SERVING_STATE_ACTIVE: { + final IpServer who = (IpServer) message.obj; if (VDBG) Log.d(TAG, "Tether Mode requested by " + who); handleInterfaceServingStateActive(message.arg1, who); transitionTo(mTetherModeAliveState); break; - case EVENT_IFACE_SERVING_STATE_INACTIVE: - who = (TetherInterfaceStateMachine) message.obj; + } + case EVENT_IFACE_SERVING_STATE_INACTIVE: { + final IpServer who = (IpServer) message.obj; if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who); handleInterfaceServingStateInactive(who); break; + } case EVENT_IFACE_UPDATE_LINKPROPERTIES: // Silently ignore these for now. break; @@ -1410,8 +1409,8 @@ public class Tethering extends BaseNetworkObserver { protected void notifyDownstreamsOfNewUpstreamIface(InterfaceSet ifaces) { mCurrentUpstreamIfaceSet = ifaces; - for (TetherInterfaceStateMachine sm : mNotifyList) { - sm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED, ifaces); + for (IpServer ipServer : mNotifyList) { + ipServer.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED, ifaces); } } @@ -1420,13 +1419,13 @@ public class Tethering extends BaseNetworkObserver { mOffload.updateUpstreamNetworkState(ns); } - private void handleInterfaceServingStateActive(int mode, TetherInterfaceStateMachine who) { + private void handleInterfaceServingStateActive(int mode, IpServer who) { if (mNotifyList.indexOf(who) < 0) { mNotifyList.add(who); mIPv6TetheringCoordinator.addActiveDownstream(who, mode); } - if (mode == IControlsTethering.STATE_TETHERED) { + if (mode == IpServer.STATE_TETHERED) { // No need to notify OffloadController just yet as there are no // "offload-able" prefixes to pass along. This will handled // when the TISM informs Tethering of its LinkProperties. @@ -1441,10 +1440,10 @@ public class Tethering extends BaseNetworkObserver { final WifiManager mgr = getWifiManager(); final String iface = who.interfaceName(); switch (mode) { - case IControlsTethering.STATE_TETHERED: + case IpServer.STATE_TETHERED: mgr.updateInterfaceIpState(iface, IFACE_IP_MODE_TETHERED); break; - case IControlsTethering.STATE_LOCAL_ONLY: + case IpServer.STATE_LOCAL_ONLY: mgr.updateInterfaceIpState(iface, IFACE_IP_MODE_LOCAL_ONLY); break; default: @@ -1454,7 +1453,7 @@ public class Tethering extends BaseNetworkObserver { } } - private void handleInterfaceServingStateInactive(TetherInterfaceStateMachine who) { + private void handleInterfaceServingStateInactive(IpServer who) { mNotifyList.remove(who); mIPv6TetheringCoordinator.removeActiveDownstream(who); mOffload.excludeDownstreamInterface(who.interfaceName()); @@ -1563,10 +1562,10 @@ public class Tethering extends BaseNetworkObserver { boolean retValue = true; switch (message.what) { case EVENT_IFACE_SERVING_STATE_ACTIVE: { - TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj; + IpServer who = (IpServer) message.obj; if (VDBG) Log.d(TAG, "Tether Mode requested by " + who); handleInterfaceServingStateActive(message.arg1, who); - who.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED, + who.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED, mCurrentUpstreamIfaceSet); // If there has been a change and an upstream is now // desired, kick off the selection process. @@ -1577,7 +1576,7 @@ public class Tethering extends BaseNetworkObserver { break; } case EVENT_IFACE_SERVING_STATE_INACTIVE: { - TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj; + IpServer who = (IpServer) message.obj; if (VDBG) Log.d(TAG, "Tether Mode unrequested by " + who); handleInterfaceServingStateInactive(who); @@ -1591,7 +1590,7 @@ public class Tethering extends BaseNetworkObserver { if (DBG) { Log.d(TAG, "TetherModeAlive still has " + mNotifyList.size() + " live requests:"); - for (TetherInterfaceStateMachine o : mNotifyList) { + for (IpServer o : mNotifyList) { Log.d(TAG, " " + o); } } @@ -1605,7 +1604,7 @@ public class Tethering extends BaseNetworkObserver { } case EVENT_IFACE_UPDATE_LINKPROPERTIES: { final LinkProperties newLp = (LinkProperties) message.obj; - if (message.arg1 == IControlsTethering.STATE_TETHERED) { + if (message.arg1 == IpServer.STATE_TETHERED) { mOffload.updateDownstreamLinkProperties(newLp); } else { mOffload.excludeDownstreamInterface(newLp.getInterfaceName()); @@ -1650,7 +1649,7 @@ public class Tethering extends BaseNetworkObserver { boolean retValue = true; switch (message.what) { case EVENT_IFACE_SERVING_STATE_ACTIVE: - TetherInterfaceStateMachine who = (TetherInterfaceStateMachine) message.obj; + IpServer who = (IpServer) message.obj; who.sendMessage(mErrorNotification); break; case CMD_CLEAR_ERROR: @@ -1665,8 +1664,8 @@ public class Tethering extends BaseNetworkObserver { void notify(int msgType) { mErrorNotification = msgType; - for (TetherInterfaceStateMachine sm : mNotifyList) { - sm.sendMessage(msgType); + for (IpServer ipServer : mNotifyList) { + ipServer.sendMessage(msgType); } } @@ -1676,7 +1675,7 @@ public class Tethering extends BaseNetworkObserver { @Override public void enter() { Log.e(TAG, "Error in setIpForwardingEnabled"); - notify(TetherInterfaceStateMachine.CMD_IP_FORWARDING_ENABLE_ERROR); + notify(IpServer.CMD_IP_FORWARDING_ENABLE_ERROR); } } @@ -1684,7 +1683,7 @@ public class Tethering extends BaseNetworkObserver { @Override public void enter() { Log.e(TAG, "Error in setIpForwardingDisabled"); - notify(TetherInterfaceStateMachine.CMD_IP_FORWARDING_DISABLE_ERROR); + notify(IpServer.CMD_IP_FORWARDING_DISABLE_ERROR); } } @@ -1692,7 +1691,7 @@ public class Tethering extends BaseNetworkObserver { @Override public void enter() { Log.e(TAG, "Error in startTethering"); - notify(TetherInterfaceStateMachine.CMD_START_TETHERING_ERROR); + notify(IpServer.CMD_START_TETHERING_ERROR); try { mNMService.setIpForwardingEnabled(false); } catch (Exception e) {} @@ -1703,7 +1702,7 @@ public class Tethering extends BaseNetworkObserver { @Override public void enter() { Log.e(TAG, "Error in stopTethering"); - notify(TetherInterfaceStateMachine.CMD_STOP_TETHERING_ERROR); + notify(IpServer.CMD_STOP_TETHERING_ERROR); try { mNMService.setIpForwardingEnabled(false); } catch (Exception e) {} @@ -1714,7 +1713,7 @@ public class Tethering extends BaseNetworkObserver { @Override public void enter() { Log.e(TAG, "Error in setDnsForwarders"); - notify(TetherInterfaceStateMachine.CMD_SET_DNS_FORWARDERS_ERROR); + notify(IpServer.CMD_SET_DNS_FORWARDERS_ERROR); try { mNMService.stopTethering(); } catch (Exception e) {} @@ -1771,15 +1770,15 @@ public class Tethering extends BaseNetworkObserver { // Maybe add prefixes or addresses for downstreams, depending on // the IP serving mode of each. - for (TetherInterfaceStateMachine tism : mNotifyList) { - final LinkProperties lp = tism.linkProperties(); + for (IpServer ipServer : mNotifyList) { + final LinkProperties lp = ipServer.linkProperties(); - switch (tism.servingMode()) { - case IControlsTethering.STATE_UNAVAILABLE: - case IControlsTethering.STATE_AVAILABLE: + switch (ipServer.servingMode()) { + case IpServer.STATE_UNAVAILABLE: + case IpServer.STATE_AVAILABLE: // No usable LinkProperties in these states. continue; - case IControlsTethering.STATE_TETHERED: + case IpServer.STATE_TETHERED: // Only add IPv4 /32 and IPv6 /128 prefixes. The // directly-connected prefixes will be sent as // downstream "offload-able" prefixes. @@ -1789,7 +1788,7 @@ public class Tethering extends BaseNetworkObserver { localPrefixes.add(PrefixUtils.ipAddressAsPrefix(ip)); } break; - case IControlsTethering.STATE_LOCAL_ONLY: + case IpServer.STATE_LOCAL_ONLY: // Add prefixes covering all local IPs. localPrefixes.addAll(PrefixUtils.localPrefixesFrom(lp)); break; @@ -1826,16 +1825,16 @@ public class Tethering extends BaseNetworkObserver { pw.print(iface + " - "); switch (tetherState.lastState) { - case IControlsTethering.STATE_UNAVAILABLE: + case IpServer.STATE_UNAVAILABLE: pw.print("UnavailableState"); break; - case IControlsTethering.STATE_AVAILABLE: + case IpServer.STATE_AVAILABLE: pw.print("AvailableState"); break; - case IControlsTethering.STATE_TETHERED: + case IpServer.STATE_TETHERED: pw.print("TetheredState"); break; - case IControlsTethering.STATE_LOCAL_ONLY: + case IpServer.STATE_LOCAL_ONLY: pw.print("LocalHotspotState"); break; default: @@ -1873,28 +1872,26 @@ public class Tethering extends BaseNetworkObserver { return false; } - private IControlsTethering makeControlCallback(String ifname) { - return new IControlsTethering() { + private IpServer.Callback makeControlCallback() { + return new IpServer.Callback() { @Override - public void updateInterfaceState( - TetherInterfaceStateMachine who, int state, int lastError) { - notifyInterfaceStateChange(ifname, who, state, lastError); + public void updateInterfaceState(IpServer who, int state, int lastError) { + notifyInterfaceStateChange(who, state, lastError); } @Override - public void updateLinkProperties( - TetherInterfaceStateMachine who, LinkProperties newLp) { - notifyLinkPropertiesChanged(ifname, who, newLp); + public void updateLinkProperties(IpServer who, LinkProperties newLp) { + notifyLinkPropertiesChanged(who, newLp); } }; } // TODO: Move into TetherMasterSM. - private void notifyInterfaceStateChange( - String iface, TetherInterfaceStateMachine who, int state, int error) { + private void notifyInterfaceStateChange(IpServer who, int state, int error) { + final String iface = who.interfaceName(); synchronized (mPublicSync) { final TetherState tetherState = mTetherStates.get(iface); - if (tetherState != null && tetherState.stateMachine.equals(who)) { + if (tetherState != null && tetherState.ipServer.equals(who)) { tetherState.lastState = state; tetherState.lastError = error; } else { @@ -1908,7 +1905,7 @@ public class Tethering extends BaseNetworkObserver { // Notify that we're tethering (or not) this interface. // This is how data saver for instance knows if the user explicitly // turned on tethering (thus keeping us from being in data saver mode). - mPolicyManager.onTetheringChanged(iface, state == IControlsTethering.STATE_TETHERED); + mPolicyManager.onTetheringChanged(iface, state == IpServer.STATE_TETHERED); } catch (RemoteException e) { // Not really very much we can do here. } @@ -1921,12 +1918,12 @@ public class Tethering extends BaseNetworkObserver { } int which; switch (state) { - case IControlsTethering.STATE_UNAVAILABLE: - case IControlsTethering.STATE_AVAILABLE: + case IpServer.STATE_UNAVAILABLE: + case IpServer.STATE_AVAILABLE: which = TetherMasterSM.EVENT_IFACE_SERVING_STATE_INACTIVE; break; - case IControlsTethering.STATE_TETHERED: - case IControlsTethering.STATE_LOCAL_ONLY: + case IpServer.STATE_TETHERED: + case IpServer.STATE_LOCAL_ONLY: which = TetherMasterSM.EVENT_IFACE_SERVING_STATE_ACTIVE; break; default: @@ -1937,12 +1934,12 @@ public class Tethering extends BaseNetworkObserver { sendTetherStateChangedBroadcast(); } - private void notifyLinkPropertiesChanged(String iface, TetherInterfaceStateMachine who, - LinkProperties newLp) { + private void notifyLinkPropertiesChanged(IpServer who, LinkProperties newLp) { + final String iface = who.interfaceName(); final int state; synchronized (mPublicSync) { final TetherState tetherState = mTetherStates.get(iface); - if (tetherState != null && tetherState.stateMachine.equals(who)) { + if (tetherState != null && tetherState.ipServer.equals(who)) { state = tetherState.lastState; } else { mLog.log("got notification from stale iface " + iface); @@ -1952,7 +1949,7 @@ public class Tethering extends BaseNetworkObserver { mLog.log(String.format( "OBSERVED LinkProperties update iface=%s state=%s lp=%s", - iface, IControlsTethering.getStateString(state), newLp)); + iface, IpServer.getStateString(state), newLp)); final int which = TetherMasterSM.EVENT_IFACE_UPDATE_LINKPROPERTIES; mTetherMasterSM.sendMessage(which, state, 0, newLp); } @@ -1976,11 +1973,11 @@ public class Tethering extends BaseNetworkObserver { mLog.log("adding TetheringInterfaceStateMachine for: " + iface); final TetherState tetherState = new TetherState( - new TetherInterfaceStateMachine( - iface, mLooper, interfaceType, mLog, mNMService, mStatsService, - makeControlCallback(iface), mConfig.enableLegacyDhcpServer, mDeps)); + new IpServer(iface, mLooper, interfaceType, mLog, mNMService, mStatsService, + makeControlCallback(), mConfig.enableLegacyDhcpServer, + mDeps.getIpServerDependencies())); mTetherStates.put(iface, tetherState); - tetherState.stateMachine.start(); + tetherState.ipServer.start(); } private void stopTrackingInterfaceLocked(final String iface) { @@ -1989,36 +1986,11 @@ public class Tethering extends BaseNetworkObserver { mLog.log("attempting to remove unknown iface (" + iface + "), ignoring"); return; } - tetherState.stateMachine.stop(); + tetherState.ipServer.stop(); mLog.log("removing TetheringInterfaceStateMachine for: " + iface); mTetherStates.remove(iface); } - private static String getIPv4DefaultRouteInterface(NetworkState ns) { - if (ns == null) return null; - return getInterfaceForDestination(ns.linkProperties, Inet4Address.ANY); - } - - private static String getIPv6DefaultRouteInterface(NetworkState ns) { - if (ns == null) return null; - // An upstream network's IPv6 capability is currently only useful if it - // can be 64share'd downstream (RFC 7278). For now, that means mobile - // upstream networks only. - if (ns.networkCapabilities == null || - !ns.networkCapabilities.hasTransport(TRANSPORT_CELLULAR)) { - return null; - } - - return getInterfaceForDestination(ns.linkProperties, Inet6Address.ANY); - } - - private static String getInterfaceForDestination(LinkProperties lp, InetAddress dst) { - final RouteInfo ri = (lp != null) - ? RouteInfo.selectBestRoute(lp.getAllRoutes(), dst) - : null; - return (ri != null) ? ri.getInterface() : null; - } - private static String[] copy(String[] strarray) { return Arrays.copyOf(strarray, strarray.length); } diff --git a/services/core/java/com/android/server/connectivity/tethering/IControlsTethering.java b/services/core/java/com/android/server/connectivity/tethering/IControlsTethering.java deleted file mode 100644 index 2b813475222f..000000000000 --- a/services/core/java/com/android/server/connectivity/tethering/IControlsTethering.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.connectivity.tethering; - -import android.net.LinkProperties; - -/** - * @hide - * - * Interface with methods necessary to notify that a given interface is ready for tethering. - * - * Rename to something more representative, e.g. IpServingControlCallback. - * - * All methods MUST be called on the TetherMasterSM main Looper's thread. - */ -public class IControlsTethering { - public static final int STATE_UNAVAILABLE = 0; - public static final int STATE_AVAILABLE = 1; - public static final int STATE_TETHERED = 2; - public static final int STATE_LOCAL_ONLY = 3; - - public static String getStateString(int state) { - switch (state) { - case STATE_UNAVAILABLE: return "UNAVAILABLE"; - case STATE_AVAILABLE: return "AVAILABLE"; - case STATE_TETHERED: return "TETHERED"; - case STATE_LOCAL_ONLY: return "LOCAL_ONLY"; - } - return "UNKNOWN: " + state; - } - - /** - * Notify that |who| has changed its tethering state. - * - * TODO: Remove the need for the |who| argument. - * - * @param who corresponding instance of a TetherInterfaceStateMachine - * @param state one of IControlsTethering.STATE_* - * @param lastError one of ConnectivityManager.TETHER_ERROR_* - */ - public void updateInterfaceState(TetherInterfaceStateMachine who, int state, int lastError) {} - - /** - * Notify that |who| has new LinkProperties. - * - * TODO: Remove the need for the |who| argument. - * - * @param who corresponding instance of a TetherInterfaceStateMachine - * @param newLp the new LinkProperties to report - */ - public void updateLinkProperties(TetherInterfaceStateMachine who, LinkProperties newLp) {} -} diff --git a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java index ba67c94d1cd4..100014898127 100644 --- a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java +++ b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java @@ -17,6 +17,7 @@ package com.android.server.connectivity.tethering; import android.net.ConnectivityManager; +import android.net.ip.IpServer; import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; @@ -50,19 +51,19 @@ public class IPv6TetheringCoordinator { private static final boolean VDBG = false; private static class Downstream { - public final TetherInterfaceStateMachine tism; - public final int mode; // IControlsTethering.STATE_* + public final IpServer ipServer; + public final int mode; // IpServer.STATE_* // Used to append to a ULA /48, constructing a ULA /64 for local use. public final short subnetId; - Downstream(TetherInterfaceStateMachine tism, int mode, short subnetId) { - this.tism = tism; + Downstream(IpServer ipServer, int mode, short subnetId) { + this.ipServer = ipServer; this.mode = mode; this.subnetId = subnetId; } } - private final ArrayList<TetherInterfaceStateMachine> mNotifyList; + private final ArrayList<IpServer> mNotifyList; private final SharedLog mLog; // NOTE: mActiveDownstreams is a list and not a hash data structure because // we keep active downstreams in arrival order. This is done so /64s can @@ -74,8 +75,7 @@ public class IPv6TetheringCoordinator { private short mNextSubnetId; private NetworkState mUpstreamNetworkState; - public IPv6TetheringCoordinator(ArrayList<TetherInterfaceStateMachine> notifyList, - SharedLog log) { + public IPv6TetheringCoordinator(ArrayList<IpServer> notifyList, SharedLog log) { mNotifyList = notifyList; mLog = log.forSubComponent(TAG); mActiveDownstreams = new LinkedList<>(); @@ -83,7 +83,7 @@ public class IPv6TetheringCoordinator { mNextSubnetId = 0; } - public void addActiveDownstream(TetherInterfaceStateMachine downstream, int mode) { + public void addActiveDownstream(IpServer downstream, int mode) { if (findDownstream(downstream) == null) { // Adding a new downstream appends it to the list. Adding a // downstream a second time without first removing it has no effect. @@ -98,7 +98,7 @@ public class IPv6TetheringCoordinator { } } - public void removeActiveDownstream(TetherInterfaceStateMachine downstream) { + public void removeActiveDownstream(IpServer downstream) { stopIPv6TetheringOn(downstream); if (mActiveDownstreams.remove(findDownstream(downstream))) { updateIPv6TetheringInterfaces(); @@ -133,8 +133,8 @@ public class IPv6TetheringCoordinator { } private void stopIPv6TetheringOnAllInterfaces() { - for (TetherInterfaceStateMachine sm : mNotifyList) { - stopIPv6TetheringOn(sm); + for (IpServer ipServer : mNotifyList) { + stopIPv6TetheringOn(ipServer); } } @@ -156,28 +156,28 @@ public class IPv6TetheringCoordinator { } private void updateIPv6TetheringInterfaces() { - for (TetherInterfaceStateMachine sm : mNotifyList) { - final LinkProperties lp = getInterfaceIPv6LinkProperties(sm); - sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, lp); + for (IpServer ipServer : mNotifyList) { + final LinkProperties lp = getInterfaceIPv6LinkProperties(ipServer); + ipServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, lp); break; } } - private LinkProperties getInterfaceIPv6LinkProperties(TetherInterfaceStateMachine sm) { - if (sm.interfaceType() == ConnectivityManager.TETHERING_BLUETOOTH) { + private LinkProperties getInterfaceIPv6LinkProperties(IpServer ipServer) { + if (ipServer.interfaceType() == ConnectivityManager.TETHERING_BLUETOOTH) { // TODO: Figure out IPv6 support on PAN interfaces. return null; } - final Downstream ds = findDownstream(sm); + final Downstream ds = findDownstream(ipServer); if (ds == null) return null; - if (ds.mode == IControlsTethering.STATE_LOCAL_ONLY) { + if (ds.mode == IpServer.STATE_LOCAL_ONLY) { // Build a Unique Locally-assigned Prefix configuration. return getUniqueLocalConfig(mUniqueLocalPrefix, ds.subnetId); } - // This downstream is in IControlsTethering.STATE_TETHERED mode. + // This downstream is in IpServer.STATE_TETHERED mode. if (mUpstreamNetworkState == null || mUpstreamNetworkState.linkProperties == null) { return null; } @@ -188,7 +188,7 @@ public class IPv6TetheringCoordinator { // IPv6 toward the oldest (first requested) active downstream. final Downstream currentActive = mActiveDownstreams.peek(); - if (currentActive != null && currentActive.tism == sm) { + if (currentActive != null && currentActive.ipServer == ipServer) { final LinkProperties lp = getIPv6OnlyLinkProperties( mUpstreamNetworkState.linkProperties); if (lp.hasIPv6DefaultRoute() && lp.hasGlobalIPv6Address()) { @@ -199,9 +199,9 @@ public class IPv6TetheringCoordinator { return null; } - Downstream findDownstream(TetherInterfaceStateMachine tism) { + Downstream findDownstream(IpServer ipServer) { for (Downstream ds : mActiveDownstreams) { - if (ds.tism == tism) return ds; + if (ds.ipServer == ipServer) return ds; } return null; } @@ -304,7 +304,7 @@ public class IPv6TetheringCoordinator { ns.linkProperties); } - private static void stopIPv6TetheringOn(TetherInterfaceStateMachine sm) { - sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, null); + private static void stopIPv6TetheringOn(IpServer ipServer) { + ipServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null); } } diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java index caa867c616b8..8b4006916736 100644 --- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java +++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java @@ -21,6 +21,7 @@ import android.net.INetd; import android.net.NetworkRequest; import android.net.dhcp.DhcpServer; import android.net.dhcp.DhcpServingParams; +import android.net.ip.IpServer; import android.net.ip.RouterAdvertisementDaemon; import android.net.util.InterfaceParams; import android.net.util.NetdService; @@ -49,20 +50,12 @@ public class TetheringDependencies { } public IPv6TetheringCoordinator getIPv6TetheringCoordinator( - ArrayList<TetherInterfaceStateMachine> notifyList, SharedLog log) { + ArrayList<IpServer> notifyList, SharedLog log) { return new IPv6TetheringCoordinator(notifyList, log); } - public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) { - return new RouterAdvertisementDaemon(ifParams); - } - - public InterfaceParams getInterfaceParams(String ifName) { - return InterfaceParams.getByName(ifName); - } - - public INetd getNetdService() { - return NetdService.getInstance(); + public IpServer.Dependencies getIpServerDependencies() { + return new IpServer.Dependencies(); } public boolean isTetheringSupported() { @@ -72,9 +65,4 @@ public class TetheringDependencies { public NetworkRequest getDefaultNetworkRequest() { return null; } - - public DhcpServer makeDhcpServer(Looper looper, InterfaceParams iface, DhcpServingParams params, - SharedLog log) { - return new DhcpServer(looper, iface, params, log); - } } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 03b7652e32ec..6e4d9e03f28f 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -345,7 +345,7 @@ public class NotificationManagerService extends SystemService { private String mSoundNotificationKey; private String mVibrateNotificationKey; - private final SparseArray<ArraySet<ManagedServiceInfo>> mListenersDisablingEffects = + private final SparseArray<ArraySet<ComponentName>> mListenersDisablingEffects = new SparseArray<>(); private List<ComponentName> mEffectsSuppressors = new ArrayList<>(); private int mListenerHints; // right now, all hints are global @@ -1784,10 +1784,10 @@ public class NotificationManagerService extends SystemService { private ArrayList<ComponentName> getSuppressors() { ArrayList<ComponentName> names = new ArrayList<ComponentName>(); for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) { - ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i); + ArraySet<ComponentName> serviceInfoList = mListenersDisablingEffects.valueAt(i); - for (ManagedServiceInfo info : serviceInfoList) { - names.add(info.component); + for (ComponentName info : serviceInfoList) { + names.add(info); } } @@ -1803,11 +1803,10 @@ public class NotificationManagerService extends SystemService { for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) { final int hint = mListenersDisablingEffects.keyAt(i); - final ArraySet<ManagedServiceInfo> listeners = - mListenersDisablingEffects.valueAt(i); + final ArraySet<ComponentName> listeners = mListenersDisablingEffects.valueAt(i); if (hints == 0 || (hint & hints) == hint) { - removed = removed || listeners.remove(info); + removed |= listeners.remove(info.component); } } @@ -1830,18 +1829,18 @@ public class NotificationManagerService extends SystemService { private void addDisabledHint(ManagedServiceInfo info, int hint) { if (mListenersDisablingEffects.indexOfKey(hint) < 0) { - mListenersDisablingEffects.put(hint, new ArraySet<ManagedServiceInfo>()); + mListenersDisablingEffects.put(hint, new ArraySet<>()); } - ArraySet<ManagedServiceInfo> hintListeners = mListenersDisablingEffects.get(hint); - hintListeners.add(info); + ArraySet<ComponentName> hintListeners = mListenersDisablingEffects.get(hint); + hintListeners.add(info.component); } private int calculateHints() { int hints = 0; for (int i = mListenersDisablingEffects.size() - 1; i >= 0; --i) { int hint = mListenersDisablingEffects.keyAt(i); - ArraySet<ManagedServiceInfo> serviceInfoList = mListenersDisablingEffects.valueAt(i); + ArraySet<ComponentName> serviceInfoList = mListenersDisablingEffects.valueAt(i); if (!serviceInfoList.isEmpty()) { hints |= hint; @@ -2955,6 +2954,21 @@ public class NotificationManagerService extends SystemService { } @Override + public void clearRequestedListenerHints(INotificationListener token) { + final long identity = Binder.clearCallingIdentity(); + try { + synchronized (mNotificationLock) { + final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token); + removeDisabledHints(info); + updateListenerHintsLocked(); + updateEffectsSuppressorLocked(); + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override public void requestHintsFromListener(INotificationListener token, int hints) { final long identity = Binder.clearCallingIdentity(); try { @@ -3860,11 +3874,12 @@ public class NotificationManagerService extends SystemService { proto.write( ListenersDisablingEffectsProto.HINT, mListenersDisablingEffects.keyAt(i)); - final ArraySet<ManagedServiceInfo> listeners = + final ArraySet<ComponentName> listeners = mListenersDisablingEffects.valueAt(i); for (int j = 0; j < listeners.size(); j++) { - final ManagedServiceInfo listener = listeners.valueAt(i); - listener.writeToProto(proto, ListenersDisablingEffectsProto.LISTENERS, null); + final ComponentName componentName = listeners.valueAt(i); + componentName.writeToProto(proto, + ListenersDisablingEffectsProto.LISTENER_COMPONENTS); } proto.end(effectsToken); @@ -4003,15 +4018,14 @@ public class NotificationManagerService extends SystemService { if (i > 0) pw.print(';'); pw.print("hint[" + hint + "]:"); - final ArraySet<ManagedServiceInfo> listeners = - mListenersDisablingEffects.valueAt(i); + final ArraySet<ComponentName> listeners = mListenersDisablingEffects.valueAt(i); final int listenerSize = listeners.size(); for (int j = 0; j < listenerSize; j++) { if (i > 0) pw.print(','); - final ManagedServiceInfo listener = listeners.valueAt(i); + final ComponentName listener = listeners.valueAt(i); if (listener != null) { - pw.print(listener.component); + pw.print(listener); } } } diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 6ac72d3ae167..4dd2bf25ed67 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -501,9 +501,14 @@ public class ZenModeHelper { } protected AutomaticZenRule createAutomaticZenRule(ZenRule rule) { - return new AutomaticZenRule(rule.name, rule.component, rule.conditionId, - NotificationManager.zenModeToInterruptionFilter(rule.zenMode), rule.enabled, - rule.creationTime); + if (rule.zenPolicy != null) { + return new AutomaticZenRule(rule.name, rule.component, rule.conditionId, rule.zenPolicy, + rule.enabled, rule.creationTime); + } else { + return new AutomaticZenRule(rule.name, rule.component, rule.conditionId, + NotificationManager.zenModeToInterruptionFilter(rule.zenMode), rule.enabled, + rule.creationTime); + } } public void setManualZenMode(int zenMode, Uri conditionId, String caller, String reason) { @@ -667,6 +672,9 @@ public class ZenModeHelper { } } + /** + * @return user-specified default notification policy for priority only do not disturb + */ public Policy getNotificationPolicy() { return getNotificationPolicy(mConfig); } @@ -675,6 +683,9 @@ public class ZenModeHelper { return config == null ? null : config.toNotificationPolicy(); } + /** + * Sets the global notification policy used for priority only do not disturb + */ public void setNotificationPolicy(Policy policy) { if (policy == null || mConfig == null) return; synchronized (mConfig) { @@ -770,9 +781,7 @@ public class ZenModeHelper { ComponentName triggeringComponent, boolean setRingerMode) { final String val = Integer.toString(config.hashCode()); Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_CONFIG_ETAG, val); - if (!evaluateZenMode(reason, setRingerMode)) { - applyRestrictions(); // evaluateZenMode will also apply restrictions if changed - } + evaluateZenMode(reason, setRingerMode); mConditions.evaluateConfig(config, triggeringComponent, true /*processSubscriptions*/); } @@ -798,7 +807,7 @@ public class ZenModeHelper { } @VisibleForTesting - protected boolean evaluateZenMode(String reason, boolean setRingerMode) { + protected void evaluateZenMode(String reason, boolean setRingerMode) { if (DEBUG) Log.d(TAG, "evaluateZenMode"); final int zenBefore = mZenMode; final int zen = computeZenMode(); @@ -813,7 +822,6 @@ public class ZenModeHelper { if (zen != zenBefore) { mHandler.postDispatchOnZenModeChanged(); } - return true; } private void updateRingerModeAffectedStreams() { @@ -822,7 +830,9 @@ public class ZenModeHelper { } } + private int computeZenMode() { + // TODO: use mConfig.zenPolicy if (mConfig == null) return Global.ZEN_MODE_OFF; synchronized (mConfig) { if (mConfig.manualRule != null) return mConfig.manualRule.zenMode; diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index 6f9d8033cd88..9f09c817e067 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -44,6 +44,8 @@ import android.os.IBinder; import android.os.IStatsCompanionService; import android.os.IStatsManager; import android.os.IStoraged; +import android.os.IThermalEventListener; +import android.os.IThermalService; import android.os.Parcelable; import android.os.Process; import android.os.RemoteException; @@ -53,6 +55,7 @@ import android.os.StatsDimensionsValue; import android.os.StatsLogEventWrapper; import android.os.SynchronousResultReceiver; import android.os.SystemClock; +import android.os.Temperature; import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageManager; @@ -169,6 +172,8 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { private KernelUidCpuClusterTimeReader mKernelUidCpuClusterTimeReader = new KernelUidCpuClusterTimeReader(); + private static IThermalService sThermalService; + public StatsCompanionService(Context context) { super(); mContext = context; @@ -213,6 +218,24 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { long[] freqs = mKernelUidCpuFreqTimeReader.readFreqs(powerProfile); mKernelUidCpuClusterTimeReader.setThrottleInterval(0); mKernelUidCpuActiveTimeReader.setThrottleInterval(0); + + // Enable push notifications of throttling from vendor thermal + // management subsystem via thermalservice. + IBinder b = ServiceManager.getService("thermalservice"); + + if (b != null) { + sThermalService = IThermalService.Stub.asInterface(b); + try { + sThermalService.registerThermalEventListener( + new ThermalEventListener()); + Slog.i(TAG, "register thermal listener successfully"); + } catch (RemoteException e) { + // Should never happen. + Slog.e(TAG, "register thermal listener error"); + } + } else { + Slog.e(TAG, "cannot find thermalservice, no throttling push notifications"); + } } @Override @@ -1456,4 +1479,11 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { } } + // Thermal event received from vendor thermal management subsystem + private static final class ThermalEventListener extends IThermalEventListener.Stub { + @Override public void notifyThrottling(boolean isThrottling, Temperature temp) { + StatsLog.write(StatsLog.THERMAL_THROTTLING, temp.getType(), + isThrottling ? 1 : 0, temp.getValue()); + } + } } diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 14294ec54ef1..c2d8188649da 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -566,6 +566,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub { @Override public void showBiometricDialog(Bundle bundle, IBiometricPromptReceiver receiver, int type) { + enforceBiometricDialog(); if (mBar != null) { try { mBar.showBiometricDialog(bundle, receiver, type); @@ -576,6 +577,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub { @Override public void onBiometricAuthenticated() { + enforceBiometricDialog(); if (mBar != null) { try { mBar.onBiometricAuthenticated(); @@ -586,6 +588,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub { @Override public void onBiometricHelp(String message) { + enforceBiometricDialog(); if (mBar != null) { try { mBar.onBiometricHelp(message); @@ -596,6 +599,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub { @Override public void onBiometricError(String error) { + enforceBiometricDialog(); if (mBar != null) { try { mBar.onBiometricError(error); @@ -606,6 +610,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub { @Override public void hideBiometricDialog() { + enforceBiometricDialog(); if (mBar != null) { try { mBar.hideBiometricDialog(); @@ -866,6 +871,12 @@ public class StatusBarManagerService extends IStatusBarService.Stub { "StatusBarManagerService"); } + private void enforceBiometricDialog() { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.MANAGE_BIOMETRIC_DIALOG, + "StatusBarManagerService"); + } + // ================================================================================ // Callbacks from the status bar service. // ================================================================================ diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 33097986b2e1..ef3a770390ef 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -268,11 +268,11 @@ final class InputMonitor { } final InputWindowHandle dragWindowHandle = mService.mDragDropController.getInputWindowHandleLocked(); - if (dragWindowHandle != null) { - addInputWindowHandle(dragWindowHandle); - } else { + if (dragWindowHandle == null) { Slog.w(TAG_WM, "Drag is in progress but there is no " + "drag window handle."); + } else if (dragWindowHandle.displayId == mDisplayId) { + addInputWindowHandle(dragWindowHandle); } } @@ -283,11 +283,11 @@ final class InputMonitor { } final InputWindowHandle dragWindowHandle = mService.mTaskPositioningController.getDragWindowHandleLocked(); - if (dragWindowHandle != null) { - addInputWindowHandle(dragWindowHandle); - } else { + if (dragWindowHandle == null) { Slog.e(TAG_WM, "Repositioning is in progress but there is no drag window handle."); + } else if (dragWindowHandle.displayId == mDisplayId) { + addInputWindowHandle(dragWindowHandle); } } diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java index 8effc6bbdd59..d2696c0479af 100644 --- a/services/core/java/com/android/server/wm/TaskPositioner.java +++ b/services/core/java/com/android/server/wm/TaskPositioner.java @@ -19,17 +19,17 @@ package com.android.server.wm; import static android.app.ActivityTaskManager.RESIZE_MODE_USER; import static android.app.ActivityTaskManager.RESIZE_MODE_USER_FORCED; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; + +import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerService.dipToPixel; -import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM; import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP; import static com.android.server.wm.WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP; import android.annotation.IntDef; -import android.app.IActivityManager; import android.app.IActivityTaskManager; import android.graphics.Point; import android.graphics.Rect; @@ -51,7 +51,6 @@ import android.view.WindowManager; import com.android.internal.annotations.VisibleForTesting; import com.android.server.input.InputApplicationHandle; import com.android.server.input.InputWindowHandle; -import com.android.server.wm.WindowManagerService.H; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -209,7 +208,6 @@ class TaskPositioner { // Post back to WM to handle clean-ups. We still need the input // event handler for the last finishInputEvent()! mService.mTaskPositioningController.finishTaskPositioning(); - mTask.getDisplayContent().getInputMonitor().updateInputWindowsLw(true /*force*/); } handled = true; } catch (Exception e) { @@ -237,7 +235,7 @@ class TaskPositioner { } /** - * @param display The Display that the window being dragged is on. + * @param displayContent The Display that the window being dragged is on. */ void register(DisplayContent displayContent) { final Display display = displayContent.getDisplay(); @@ -303,6 +301,9 @@ class TaskPositioner { } mDisplayContent.pauseRotationLocked(); + // Notify InputMonitor to take mDragWindowHandle. + mDisplayContent.getInputMonitor().updateInputWindowsLw(true /*force*/); + mSideMargin = dipToPixel(SIDE_MARGIN_DIP, mDisplayMetrics); mMinVisibleWidth = dipToPixel(MINIMUM_VISIBLE_WIDTH_IN_DP, mDisplayMetrics); mMinVisibleHeight = dipToPixel(MINIMUM_VISIBLE_HEIGHT_IN_DP, mDisplayMetrics); @@ -334,6 +335,9 @@ class TaskPositioner { mDragApplicationHandle = null; mDragEnded = true; + // Notify InputMonitor to remove mDragWindowHandle. + mDisplayContent.getInputMonitor().updateInputWindowsLw(true /*force*/); + // Resume rotations after a drag. if (DEBUG_ORIENTATION) { Slog.d(TAG, "Resuming rotation after re-position"); diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java index 9cdc6b75c3b6..33416f66f85b 100644 --- a/services/core/java/com/android/server/wm/TaskPositioningController.java +++ b/services/core/java/com/android/server/wm/TaskPositioningController.java @@ -20,14 +20,13 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITION import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.annotation.Nullable; -import android.app.IActivityManager; import android.app.IActivityTaskManager; -import android.os.RemoteException; import android.os.Handler; import android.os.Looper; +import android.os.RemoteException; import android.util.Slog; -import android.view.Display; import android.view.IWindow; + import com.android.internal.annotations.GuardedBy; import com.android.server.input.InputManagerService; import com.android.server.input.InputWindowHandle; @@ -124,10 +123,8 @@ class TaskPositioningController { return false; } - Display display = displayContent.getDisplay(); mTaskPositioner = TaskPositioner.create(mService); mTaskPositioner.register(displayContent); - displayContent.getInputMonitor().updateInputWindowsLw(true /*force*/); // We need to grab the touch focus so that the touch events during the // resizing/scrolling are not sent to the app. 'win' is the main window diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index c80b9d84ce61..bb08345b2fe1 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -785,6 +785,8 @@ public final class SystemServer { boolean disableSystemTextClassifier = SystemProperties.getBoolean( "config.disable_systemtextclassifier", false); + boolean disableNetworkTime = SystemProperties.getBoolean("config.disable_networktime", + false); boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice", false); boolean disableSlices = SystemProperties.getBoolean("config.disable_slices", false); @@ -1459,7 +1461,7 @@ public final class SystemServer { traceEnd(); } - if (!isWatch) { + if (!isWatch && !disableNetworkTime) { traceBeginAndSlog("StartNetworkTimeUpdateService"); try { if (useNewTimeServices) { diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/net/java/android/net/ip/IpServer.java index 5accb452d10a..33010a19d810 100644 --- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java +++ b/services/net/java/android/net/ip/IpServer.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package android.net.ip; import static android.net.NetworkUtils.numericToInetAddress; import static android.net.util.NetworkConstants.asByte; @@ -31,11 +31,10 @@ import android.net.LinkProperties; import android.net.RouteInfo; import android.net.dhcp.DhcpServer; import android.net.dhcp.DhcpServingParams; -import android.net.ip.InterfaceController; -import android.net.ip.RouterAdvertisementDaemon; import android.net.ip.RouterAdvertisementDaemon.RaParams; import android.net.util.InterfaceParams; import android.net.util.InterfaceSet; +import android.net.util.NetdService; import android.net.util.SharedLog; import android.os.INetworkManagementService; import android.os.Looper; @@ -67,7 +66,22 @@ import java.util.Set; * * @hide */ -public class TetherInterfaceStateMachine extends StateMachine { +public class IpServer extends StateMachine { + public static final int STATE_UNAVAILABLE = 0; + public static final int STATE_AVAILABLE = 1; + public static final int STATE_TETHERED = 2; + public static final int STATE_LOCAL_ONLY = 3; + + public static String getStateString(int state) { + switch (state) { + case STATE_UNAVAILABLE: return "UNAVAILABLE"; + case STATE_AVAILABLE: return "AVAILABLE"; + case STATE_TETHERED: return "TETHERED"; + case STATE_LOCAL_ONLY: return "LOCAL_ONLY"; + } + return "UNKNOWN: " + state; + } + private static final IpPrefix LINK_LOCAL_PREFIX = new IpPrefix("fe80::/64"); private static final byte DOUG_ADAMS = (byte) 42; @@ -83,15 +97,53 @@ public class TetherInterfaceStateMachine extends StateMachine { // TODO: have this configurable private static final int DHCP_LEASE_TIME_SECS = 3600; - private final static String TAG = "TetherInterfaceSM"; + private final static String TAG = "IpServer"; private final static boolean DBG = false; private final static boolean VDBG = false; private static final Class[] messageClasses = { - TetherInterfaceStateMachine.class + IpServer.class }; private static final SparseArray<String> sMagicDecoderRing = MessageUtils.findMessageNames(messageClasses); + public static class Callback { + /** + * Notify that |who| has changed its tethering state. + * + * @param who the calling instance of IpServer + * @param state one of STATE_* + * @param lastError one of ConnectivityManager.TETHER_ERROR_* + */ + public void updateInterfaceState(IpServer who, int state, int lastError) {} + + /** + * Notify that |who| has new LinkProperties. + * + * @param who the calling instance of IpServer + * @param newLp the new LinkProperties to report + */ + public void updateLinkProperties(IpServer who, LinkProperties newLp) {} + } + + public static class Dependencies { + public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) { + return new RouterAdvertisementDaemon(ifParams); + } + + public InterfaceParams getInterfaceParams(String ifName) { + return InterfaceParams.getByName(ifName); + } + + public INetd getNetdService() { + return NetdService.getInstance(); + } + + public DhcpServer makeDhcpServer(Looper looper, InterfaceParams iface, + DhcpServingParams params, SharedLog log) { + return new DhcpServer(looper, iface, params, log); + } + } + private static final int BASE_IFACE = Protocol.BASE_TETHERING + 100; // request from the user that it wants to tether public static final int CMD_TETHER_REQUESTED = BASE_IFACE + 2; @@ -123,7 +175,7 @@ public class TetherInterfaceStateMachine extends StateMachine { private final INetworkManagementService mNMService; private final INetd mNetd; private final INetworkStatsService mStatsService; - private final IControlsTethering mTetherController; + private final Callback mCallback; private final InterfaceController mInterfaceCtrl; private final String mIfaceName; @@ -131,7 +183,7 @@ public class TetherInterfaceStateMachine extends StateMachine { private final LinkProperties mLinkProperties; private final boolean mUsingLegacyDhcp; - private final TetheringDependencies mDeps; + private final Dependencies mDeps; private int mLastError; private int mServingMode; @@ -148,17 +200,16 @@ public class TetherInterfaceStateMachine extends StateMachine { private DhcpServer mDhcpServer; private RaParams mLastRaParams; - public TetherInterfaceStateMachine( + public IpServer( String ifaceName, Looper looper, int interfaceType, SharedLog log, INetworkManagementService nMService, INetworkStatsService statsService, - IControlsTethering tetherController, boolean usingLegacyDhcp, - TetheringDependencies deps) { + Callback callback, boolean usingLegacyDhcp, Dependencies deps) { super(ifaceName, looper); mLog = log.forSubComponent(ifaceName); mNMService = nMService; mNetd = deps.getNetdService(); mStatsService = statsService; - mTetherController = tetherController; + mCallback = callback; mInterfaceCtrl = new InterfaceController(ifaceName, nMService, mNetd, mLog); mIfaceName = ifaceName; mInterfaceType = interfaceType; @@ -167,7 +218,7 @@ public class TetherInterfaceStateMachine extends StateMachine { mDeps = deps; resetLinkProperties(); mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR; - mServingMode = IControlsTethering.STATE_AVAILABLE; + mServingMode = STATE_AVAILABLE; mInitialState = new InitialState(); mLocalHotspotState = new LocalHotspotState(); @@ -521,14 +572,12 @@ public class TetherInterfaceStateMachine extends StateMachine { private void sendInterfaceState(int newInterfaceState) { mServingMode = newInterfaceState; - mTetherController.updateInterfaceState( - TetherInterfaceStateMachine.this, newInterfaceState, mLastError); + mCallback.updateInterfaceState(this, newInterfaceState, mLastError); sendLinkProperties(); } private void sendLinkProperties() { - mTetherController.updateLinkProperties( - TetherInterfaceStateMachine.this, new LinkProperties(mLinkProperties)); + mCallback.updateLinkProperties(this, new LinkProperties(mLinkProperties)); } private void resetLinkProperties() { @@ -539,7 +588,7 @@ public class TetherInterfaceStateMachine extends StateMachine { class InitialState extends State { @Override public void enter() { - sendInterfaceState(IControlsTethering.STATE_AVAILABLE); + sendInterfaceState(STATE_AVAILABLE); } @Override @@ -549,10 +598,10 @@ public class TetherInterfaceStateMachine extends StateMachine { case CMD_TETHER_REQUESTED: mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR; switch (message.arg1) { - case IControlsTethering.STATE_LOCAL_ONLY: + case STATE_LOCAL_ONLY: transitionTo(mLocalHotspotState); break; - case IControlsTethering.STATE_TETHERED: + case STATE_TETHERED: transitionTo(mTetheredState); break; default: @@ -649,7 +698,7 @@ public class TetherInterfaceStateMachine extends StateMachine { // problematic because transitioning during a multi-state jump yields // a Log.wtf(). Ultimately, there should be only one ServingState, // and forwarding and NAT rules should be handled by a coordinating - // functional element outside of TetherInterfaceStateMachine. + // functional element outside of IpServer. class LocalHotspotState extends BaseServingState { @Override public void enter() { @@ -659,7 +708,7 @@ public class TetherInterfaceStateMachine extends StateMachine { } if (DBG) Log.d(TAG, "Local hotspot " + mIfaceName); - sendInterfaceState(IControlsTethering.STATE_LOCAL_ONLY); + sendInterfaceState(STATE_LOCAL_ONLY); } @Override @@ -685,7 +734,7 @@ public class TetherInterfaceStateMachine extends StateMachine { // problematic because transitioning during a multi-state jump yields // a Log.wtf(). Ultimately, there should be only one ServingState, // and forwarding and NAT rules should be handled by a coordinating - // functional element outside of TetherInterfaceStateMachine. + // functional element outside of IpServer. class TetheredState extends BaseServingState { @Override public void enter() { @@ -695,7 +744,7 @@ public class TetherInterfaceStateMachine extends StateMachine { } if (DBG) Log.d(TAG, "Tethered " + mIfaceName); - sendInterfaceState(IControlsTethering.STATE_TETHERED); + sendInterfaceState(STATE_TETHERED); } @Override @@ -817,7 +866,7 @@ public class TetherInterfaceStateMachine extends StateMachine { @Override public void enter() { mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR; - sendInterfaceState(IControlsTethering.STATE_UNAVAILABLE); + sendInterfaceState(STATE_UNAVAILABLE); } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java new file mode 100644 index 000000000000..100f9c610efc --- /dev/null +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java @@ -0,0 +1,151 @@ +/* + * 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 distriZenbuted 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.notification; + +import static junit.framework.Assert.assertEquals; + +import android.app.NotificationManager.Policy; +import android.service.notification.ZenModeConfig; +import android.service.notification.ZenPolicy; +import android.support.test.runner.AndroidJUnit4; +import android.test.suitebuilder.annotation.SmallTest; + +import com.android.server.UiServiceTestCase; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class ZenModeConfigTest extends UiServiceTestCase { + + @Test + public void testPriorityOnlyMutingAllNotifications() { + ZenModeConfig config = getMutedNotificationsConfig(); + assertEquals(true, ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(config)); + + config.allowReminders = true; + assertEquals(false, ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(config)); + config.allowReminders = false; + + config.areChannelsBypassingDnd = true; + assertEquals(false, ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(config)); + config.areChannelsBypassingDnd = false; + + assertEquals(true, ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(config)); + } + + @Test + public void testZenPolicyNothingSetToNotificationPolicy() { + ZenModeConfig config = getMutedAllConfig(); + ZenPolicy zenPolicy = new ZenPolicy.Builder().build(); + assertEquals(config.toNotificationPolicy(), config.toNotificationPolicy(zenPolicy)); + } + + @Test + public void testZenPolicyToNotificationPolicy() { + ZenModeConfig config = getMutedAllConfig(); + config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE; + + ZenPolicy zenPolicy = new ZenPolicy.Builder() + .allowAlarms(true) + .allowReminders(true) + .allowEvents(true) + .showLights(false) + .showInAmbientDisplay(false) + .build(); + + Policy originalPolicy = config.toNotificationPolicy(); + int priorityCategories = originalPolicy.priorityCategories; + int priorityCallSenders = originalPolicy.priorityCallSenders; + int priorityMessageSenders = originalPolicy.priorityMessageSenders; + int suppressedVisualEffects = originalPolicy.suppressedVisualEffects; + priorityCategories |= Policy.PRIORITY_CATEGORY_ALARMS; + priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS; + priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS; + suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_LIGHTS; + suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_AMBIENT; + + Policy expectedPolicy = new Policy(priorityCategories, priorityCallSenders, + priorityMessageSenders, suppressedVisualEffects); + + assertEquals(expectedPolicy, config.toNotificationPolicy(zenPolicy)); + } + + @Test + public void testPriorityOnlyMutingAll() { + ZenModeConfig config = getMutedAllConfig(); + assertEquals(true, ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(config)); + assertEquals(true, ZenModeConfig.areAllZenBehaviorSoundsMuted(config)); + + config.allowReminders = true; + assertEquals(false, ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(config)); + assertEquals(false, ZenModeConfig.areAllZenBehaviorSoundsMuted(config)); + config.allowReminders = false; + + config.areChannelsBypassingDnd = true; + assertEquals(false, ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(config)); + assertEquals(false, ZenModeConfig.areAllZenBehaviorSoundsMuted(config)); + config.areChannelsBypassingDnd = false; + + config.allowAlarms = true; + assertEquals(true, ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(config)); + assertEquals(false, ZenModeConfig.areAllZenBehaviorSoundsMuted(config)); + config.allowAlarms = false; + + assertEquals(true, ZenModeConfig.areAllPriorityOnlyNotificationZenSoundsMuted(config)); + assertEquals(true, ZenModeConfig.areAllZenBehaviorSoundsMuted(config)); + } + + private ZenModeConfig getMutedNotificationsConfig() { + ZenModeConfig config = new ZenModeConfig(); + // Allow alarms, media, and system + config.allowAlarms = true; + config.allowMedia = true; + config.allowSystem = true; + + // All notification sounds are not allowed + config.allowCalls = false; + config.allowRepeatCallers = false; + config.allowMessages = false; + config.allowReminders = false; + config.allowEvents = false; + config.areChannelsBypassingDnd = false; + + config.suppressedVisualEffects = 0; + + return config; + } + + private ZenModeConfig getMutedAllConfig() { + ZenModeConfig config = new ZenModeConfig(); + // No sounds allowed + config.allowAlarms = false; + config.allowMedia = false; + config.allowSystem = false; + config.allowCalls = false; + config.allowRepeatCallers = false; + config.allowMessages = false; + config.allowReminders = false; + config.allowEvents = false; + config.areChannelsBypassingDnd = false; + + config.suppressedVisualEffects = 0; + return config; + } +} diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenPolicyTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenPolicyTest.java new file mode 100644 index 000000000000..7c6b1c15177e --- /dev/null +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenPolicyTest.java @@ -0,0 +1,456 @@ +/* + * 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 distriZenbuted 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.notification; + +import static junit.framework.Assert.assertEquals; + +import android.service.notification.ZenPolicy; +import android.support.test.runner.AndroidJUnit4; +import android.test.suitebuilder.annotation.SmallTest; + +import com.android.server.UiServiceTestCase; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class ZenPolicyTest extends UiServiceTestCase { + + @Test + public void testZenPolicyApplyAllowedToDisallowed() { + ZenPolicy.Builder builder = new ZenPolicy.Builder(); + + // reminders are disallowed + builder.allowReminders(false); + ZenPolicy remindersDisallowed = builder.build(); + assertEquals(ZenPolicy.STATE_DISALLOW, + remindersDisallowed.getPriorityCategoryReminders()); + + // reminders are allowed + builder.allowReminders(true); + ZenPolicy remindersAllowed = builder.build(); + assertEquals(ZenPolicy.STATE_ALLOW, + remindersAllowed.getPriorityCategoryReminders()); + assertEquals(ZenPolicy.STATE_DISALLOW, + remindersDisallowed.getPriorityCategoryReminders()); + + // we apply reminders allowed to reminders disallowed + // -> reminders should remain disallowed + remindersDisallowed.apply(remindersAllowed); + assertEquals(ZenPolicy.STATE_DISALLOW, + remindersDisallowed.getPriorityCategoryReminders()); + } + + @Test + public void testZenPolicyApplyAllowedToUnset() { + ZenPolicy.Builder builder = new ZenPolicy.Builder(); + + // reminders are unset + ZenPolicy remindersUnset = builder.build(); + + // reminders are allowed + builder.allowReminders(true); + ZenPolicy remindersAllowed = builder.build(); + + // we apply reminders allowed to reminders unset + // -> reminders should be allowed + remindersUnset.apply(remindersAllowed); + assertEquals(ZenPolicy.STATE_ALLOW, remindersUnset.getPriorityCategoryReminders()); + } + + @Test + public void testZenPolicyApplyDisallowedToUnset() { + ZenPolicy.Builder builder = new ZenPolicy.Builder(); + + // reminders are unset + ZenPolicy remindersUnset = builder.build(); + + // reminders are disallowed + builder.allowReminders(false); + ZenPolicy remindersDisallowed = builder.build(); + + // we apply reminders disallowed to reminders unset + // -> reminders should remain disallowed + remindersUnset.apply(remindersDisallowed); + assertEquals(ZenPolicy.STATE_DISALLOW, + remindersUnset.getPriorityCategoryReminders()); + } + + @Test + public void testZenPolicyApplyDisallowedToAllowed() { + ZenPolicy.Builder builder = new ZenPolicy.Builder(); + + // reminders are allowed + builder.allowReminders(true); + ZenPolicy remindersAllowed = builder.build(); + + // reminders are disallowed + builder.allowReminders(false); + ZenPolicy remindersDisallowed = builder.build(); + + // we apply reminders allowed to reminders disallowed + // -> reminders should change to disallowed + remindersAllowed.apply(remindersDisallowed); + assertEquals(ZenPolicy.STATE_DISALLOW, remindersAllowed.getPriorityCategoryReminders()); + } + + @Test + public void testZenPolicyApplyUnsetToAllowed() { + ZenPolicy.Builder builder = new ZenPolicy.Builder(); + + // reminders are allowed + builder.allowReminders(true); + ZenPolicy remindersAllowed = builder.build(); + + // reminders are unset + ZenPolicy.Builder builder2 = new ZenPolicy.Builder(); + ZenPolicy remindersUnset = builder2.build(); + + // we apply reminders allowed to reminders unset + // -> reminders should remain allowed + remindersAllowed.apply(remindersUnset); + assertEquals(ZenPolicy.STATE_ALLOW, remindersAllowed.getPriorityCategoryReminders()); + } + + @Test + public void testZenPolicyApplyMoreSevereCallSenders() { + ZenPolicy.Builder builder = new ZenPolicy.Builder(); + + // calls from contacts allowed + builder.allowCalls(ZenPolicy.PEOPLE_TYPE_CONTACTS); + ZenPolicy contactsAllowed = builder.build(); + + // calls from starred contacts allowed + builder.allowCalls(ZenPolicy.PEOPLE_TYPE_STARRED); + ZenPolicy starredAllowed = builder.build(); + + // we apply starredAllowed to contactsAllowed -> starred contacts allowed (more restrictive) + contactsAllowed.apply(starredAllowed); + assertEquals(ZenPolicy.STATE_ALLOW, contactsAllowed.getPriorityCategoryCalls()); + assertEquals(ZenPolicy.PEOPLE_TYPE_STARRED, contactsAllowed.getPriorityCallSenders()); + } + + @Test + public void testZenPolicyApplyLessSevereCallSenders() { + ZenPolicy.Builder builder = new ZenPolicy.Builder(); + + // calls from contacts allowed + builder.allowCalls(ZenPolicy.PEOPLE_TYPE_CONTACTS); + ZenPolicy contactsAllowed = builder.build(); + + // calls from anyone allowed + builder.allowCalls(ZenPolicy.PEOPLE_TYPE_ANYONE); + ZenPolicy anyoneAllowed = builder.build(); + + // we apply anyoneAllowed to contactsAllowed -> contactsAllowed (more restrictive) + contactsAllowed.apply(anyoneAllowed); + assertEquals(ZenPolicy.STATE_ALLOW, contactsAllowed.getPriorityCategoryCalls()); + assertEquals(ZenPolicy.PEOPLE_TYPE_CONTACTS, contactsAllowed.getPriorityCallSenders()); + } + + @Test + public void testZenPolicyApplyMoreSevereMessageSenders() { + ZenPolicy.Builder builder = new ZenPolicy.Builder(); + + // messsages from contacts allowed + builder.allowMessages(ZenPolicy.PEOPLE_TYPE_CONTACTS); + ZenPolicy contactsAllowed = builder.build(); + + // messsages from no one allowed + builder.allowMessages(ZenPolicy.PEOPLE_TYPE_NONE); + ZenPolicy noneAllowed = builder.build(); + + // noneAllowed to contactsAllowed -> no messages allowed (more restrictive) + contactsAllowed.apply(noneAllowed); + assertEquals(ZenPolicy.STATE_DISALLOW, contactsAllowed.getPriorityCategoryMessages()); + assertEquals(ZenPolicy.PEOPLE_TYPE_NONE, contactsAllowed.getPriorityMessageSenders()); + } + + @Test + public void testZenPolicyMessagesInvalid() { + ZenPolicy.Builder builder = new ZenPolicy.Builder(); + + builder.allowMessages(20); // invalid #, won't change policy + ZenPolicy policy = builder.build(); + assertEquals(ZenPolicy.STATE_UNSET, policy.getPriorityCategoryMessages()); + assertEquals(ZenPolicy.PEOPLE_TYPE_UNSET, policy.getPriorityMessageSenders()); + } + + @Test + public void testZenPolicyCallsInvalid() { + ZenPolicy.Builder builder = new ZenPolicy.Builder(); + + builder.allowCalls(ZenPolicy.PEOPLE_TYPE_ANYONE); + builder.allowCalls(20); // invalid #, won't change policy + ZenPolicy policy = builder.build(); + assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryCalls()); + assertEquals(ZenPolicy.PEOPLE_TYPE_ANYONE, policy.getPriorityCallSenders()); + } + + @Test + public void testEmptyZenPolicy() { + ZenPolicy.Builder builder = new ZenPolicy.Builder(); + + ZenPolicy policy = builder.build(); + assertAllPriorityCategoriesUnsetExcept(policy, -1); + assertAllVisualEffectsUnsetExcept(policy, -1); + } + + @Test + public void testAllowReminders() { + ZenPolicy.Builder builder = new ZenPolicy.Builder(); + + builder.allowReminders(true); + ZenPolicy policy = builder.build(); + assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_REMINDERS); + assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryReminders()); + assertAllVisualEffectsUnsetExcept(policy, -1); + + builder.allowReminders(false); + policy = builder.build(); + assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_REMINDERS); + assertEquals(ZenPolicy.STATE_DISALLOW, policy.getPriorityCategoryReminders()); + assertAllVisualEffectsUnsetExcept(policy, -1); + } + + @Test + public void testAllowEvents() { + ZenPolicy.Builder builder = new ZenPolicy.Builder(); + + builder.allowEvents(true); + ZenPolicy policy = builder.build(); + assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_EVENTS); + assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryEvents()); + assertAllVisualEffectsUnsetExcept(policy, -1); + + builder.allowEvents(false); + policy = builder.build(); + assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_EVENTS); + assertEquals(ZenPolicy.STATE_DISALLOW, policy.getPriorityCategoryEvents()); + assertAllVisualEffectsUnsetExcept(policy, -1); + } + + @Test + public void testAllowMessages() { + ZenPolicy.Builder builder = new ZenPolicy.Builder(); + + builder.allowMessages(ZenPolicy.PEOPLE_TYPE_ANYONE); + ZenPolicy policy = builder.build(); + assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_MESSAGES); + assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryMessages()); + assertEquals(ZenPolicy.PEOPLE_TYPE_ANYONE, policy.getPriorityMessageSenders()); + assertAllVisualEffectsUnsetExcept(policy, -1); + + builder.allowMessages(ZenPolicy.PEOPLE_TYPE_CONTACTS); + policy = builder.build(); + assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_MESSAGES); + assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryMessages()); + assertEquals(ZenPolicy.PEOPLE_TYPE_CONTACTS, policy.getPriorityMessageSenders()); + assertAllVisualEffectsUnsetExcept(policy, -1); + + builder.allowMessages(ZenPolicy.PEOPLE_TYPE_STARRED); + policy = builder.build(); + assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_MESSAGES); + assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryMessages()); + assertEquals(ZenPolicy.PEOPLE_TYPE_STARRED, policy.getPriorityMessageSenders()); + assertAllVisualEffectsUnsetExcept(policy, -1); + + builder.allowMessages(ZenPolicy.PEOPLE_TYPE_NONE); + policy = builder.build(); + assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_MESSAGES); + assertEquals(ZenPolicy.STATE_DISALLOW, policy.getPriorityCategoryMessages()); + assertEquals(ZenPolicy.PEOPLE_TYPE_NONE, policy.getPriorityMessageSenders()); + assertAllVisualEffectsUnsetExcept(policy, -1); + + builder.allowMessages(ZenPolicy.PEOPLE_TYPE_UNSET); + policy = builder.build(); + assertAllPriorityCategoriesUnsetExcept(policy, -1); + assertAllVisualEffectsUnsetExcept(policy, -1); + } + + @Test + public void testAllowCalls() { + ZenPolicy.Builder builder = new ZenPolicy.Builder(); + + builder.allowCalls(ZenPolicy.PEOPLE_TYPE_ANYONE); + ZenPolicy policy = builder.build(); + assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_CALLS); + assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryCalls()); + assertEquals(ZenPolicy.PEOPLE_TYPE_ANYONE, policy.getPriorityCallSenders()); + assertAllVisualEffectsUnsetExcept(policy, -1); + + builder.allowCalls(ZenPolicy.PEOPLE_TYPE_CONTACTS); + policy = builder.build(); + assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_CALLS); + assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryCalls()); + assertEquals(ZenPolicy.PEOPLE_TYPE_CONTACTS, policy.getPriorityCallSenders()); + assertAllVisualEffectsUnsetExcept(policy, -1); + + builder.allowCalls(ZenPolicy.PEOPLE_TYPE_STARRED); + policy = builder.build(); + assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_CALLS); + assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryCalls()); + assertEquals(ZenPolicy.PEOPLE_TYPE_STARRED, policy.getPriorityCallSenders()); + assertAllVisualEffectsUnsetExcept(policy, -1); + + builder.allowCalls(ZenPolicy.PEOPLE_TYPE_NONE); + policy = builder.build(); + assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_CALLS); + assertEquals(ZenPolicy.STATE_DISALLOW, policy.getPriorityCategoryCalls()); + assertEquals(ZenPolicy.PEOPLE_TYPE_NONE, policy.getPriorityCallSenders()); + assertAllVisualEffectsUnsetExcept(policy, -1); + + builder.allowCalls(ZenPolicy.PEOPLE_TYPE_UNSET); + policy = builder.build(); + assertAllPriorityCategoriesUnsetExcept(policy, -1); + assertAllVisualEffectsUnsetExcept(policy, -1); + } + + @Test + public void testAllowRepeatCallers() { + ZenPolicy.Builder builder = new ZenPolicy.Builder(); + + builder.allowRepeatCallers(true); + ZenPolicy policy = builder.build(); + assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS); + assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryRepeatCallers()); + assertAllVisualEffectsUnsetExcept(policy, -1); + + builder.allowRepeatCallers(false); + policy = builder.build(); + assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS); + assertEquals(ZenPolicy.STATE_DISALLOW, policy.getPriorityCategoryRepeatCallers()); + assertAllVisualEffectsUnsetExcept(policy, -1); + } + + @Test + public void testAllowAlarms() { + ZenPolicy.Builder builder = new ZenPolicy.Builder(); + + builder.allowAlarms(true); + ZenPolicy policy = builder.build(); + assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_ALARMS); + assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryAlarms()); + assertAllVisualEffectsUnsetExcept(policy, -1); + + builder.allowAlarms(false); + policy = builder.build(); + assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_ALARMS); + assertEquals(ZenPolicy.STATE_DISALLOW, policy.getPriorityCategoryAlarms()); + assertAllVisualEffectsUnsetExcept(policy, -1); + } + + @Test + public void testAllowMedia() { + ZenPolicy.Builder builder = new ZenPolicy.Builder(); + + builder.allowMedia(true); + ZenPolicy policy = builder.build(); + assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_MEDIA); + assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategoryMedia()); + assertAllVisualEffectsUnsetExcept(policy, -1); + + builder.allowMedia(false); + policy = builder.build(); + assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_MEDIA); + assertEquals(ZenPolicy.STATE_DISALLOW, policy.getPriorityCategoryMedia()); + assertAllVisualEffectsUnsetExcept(policy, -1); + } + + @Test + public void testAllowSystem() { + ZenPolicy.Builder builder = new ZenPolicy.Builder(); + + builder.allowSystem(true); + ZenPolicy policy = builder.build(); + assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_SYSTEM); + assertEquals(ZenPolicy.STATE_ALLOW, policy.getPriorityCategorySystem()); + assertAllVisualEffectsUnsetExcept(policy, -1); + + builder.allowSystem(false); + policy = builder.build(); + assertAllPriorityCategoriesUnsetExcept(policy, ZenPolicy.PRIORITY_CATEGORY_SYSTEM); + assertEquals(ZenPolicy.STATE_DISALLOW, policy.getPriorityCategorySystem()); + assertAllVisualEffectsUnsetExcept(policy, -1); + } + + private void assertAllPriorityCategoriesUnsetExcept(ZenPolicy policy, int except) { + if (except != ZenPolicy.PRIORITY_CATEGORY_REMINDERS) { + assertEquals(ZenPolicy.STATE_UNSET, policy.getPriorityCategoryReminders()); + } + + if (except != ZenPolicy.PRIORITY_CATEGORY_EVENTS) { + assertEquals(ZenPolicy.STATE_UNSET, policy.getPriorityCategoryEvents()); + } + + if (except != ZenPolicy.PRIORITY_CATEGORY_MESSAGES) { + assertEquals(ZenPolicy.STATE_UNSET, policy.getPriorityCategoryMessages()); + } + + if (except != ZenPolicy.PRIORITY_CATEGORY_CALLS) { + assertEquals(ZenPolicy.STATE_UNSET, policy.getPriorityCategoryCalls()); + } + + if (except != ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS) { + assertEquals(ZenPolicy.STATE_UNSET, policy.getPriorityCategoryRepeatCallers()); + } + + if (except != ZenPolicy.PRIORITY_CATEGORY_ALARMS) { + assertEquals(ZenPolicy.STATE_UNSET, policy.getPriorityCategoryAlarms()); + } + + if (except != ZenPolicy.PRIORITY_CATEGORY_MEDIA) { + assertEquals(ZenPolicy.STATE_UNSET, policy.getPriorityCategoryMedia()); + } + + if (except != ZenPolicy.PRIORITY_CATEGORY_SYSTEM) { + assertEquals(ZenPolicy.STATE_UNSET, policy.getPriorityCategorySystem()); + } + } + + private void assertAllVisualEffectsUnsetExcept(ZenPolicy policy, int except) { + if (except != ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT) { + assertEquals(ZenPolicy.STATE_UNSET, policy.getVisualEffectFullScreenIntent()); + } + + if (except != ZenPolicy.VISUAL_EFFECT_LIGHTS) { + assertEquals(ZenPolicy.STATE_UNSET, policy.getVisualEffectLights()); + } + + if (except != ZenPolicy.VISUAL_EFFECT_PEEK) { + assertEquals(ZenPolicy.STATE_UNSET, policy.getVisualEffectPeek()); + } + + if (except != ZenPolicy.VISUAL_EFFECT_STATUS_BAR) { + assertEquals(ZenPolicy.STATE_UNSET, policy.getVisualEffectStatusBar()); + } + + if (except != ZenPolicy.VISUAL_EFFECT_BADGE) { + assertEquals(ZenPolicy.STATE_UNSET, policy.getVisualEffectBadge()); + } + + if (except != ZenPolicy.VISUAL_EFFECT_AMBIENT) { + assertEquals(ZenPolicy.STATE_UNSET, policy.getVisualEffectAmbient()); + } + + if (except != ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST) { + assertEquals(ZenPolicy.STATE_UNSET, policy.getVisualEffectNotificationList()); + } + } +} diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java index 589bcdc14871..cf47ad3520ec 100644 --- a/services/usb/java/com/android/server/usb/UsbHostManager.java +++ b/services/usb/java/com/android/server/usb/UsbHostManager.java @@ -40,6 +40,7 @@ import com.android.internal.util.dump.DualDumpOutputStream; import com.android.server.usb.descriptors.UsbDescriptor; import com.android.server.usb.descriptors.UsbDescriptorParser; import com.android.server.usb.descriptors.UsbDeviceDescriptor; +import com.android.server.usb.descriptors.UsbInterfaceDescriptor; import com.android.server.usb.descriptors.report.TextReportCanvas; import com.android.server.usb.descriptors.tree.UsbDescriptorsTree; @@ -352,8 +353,6 @@ public class UsbHostManager { } return false; } - UsbDescriptorParser parser = new UsbDescriptorParser(deviceAddress, descriptors); - logUsbDevice(parser); if (isBlackListed(deviceClass, deviceSubclass)) { if (DEBUG) { @@ -362,6 +361,15 @@ public class UsbHostManager { return false; } + UsbDescriptorParser parser = new UsbDescriptorParser(deviceAddress, descriptors); + if (deviceClass == UsbConstants.USB_CLASS_PER_INTERFACE + && !checkUsbInterfacesBlackListed(parser)) { + return false; + } + + // Potentially can block as it may read data from the USB device. + logUsbDevice(parser); + synchronized (mLock) { if (mDevices.get(deviceAddress) != null) { Slog.w(TAG, "device already on mDevices list: " + deviceAddress); @@ -513,6 +521,29 @@ public class UsbHostManager { } } + private boolean checkUsbInterfacesBlackListed(UsbDescriptorParser parser) { + // Device class needs to be obtained through the device interface. Ignore device only + // if ALL interfaces are black-listed. + boolean shouldIgnoreDevice = false; + for (UsbDescriptor descriptor: parser.getDescriptors()) { + if (!(descriptor instanceof UsbInterfaceDescriptor)) { + continue; + } + UsbInterfaceDescriptor iface = (UsbInterfaceDescriptor) descriptor; + shouldIgnoreDevice = isBlackListed(iface.getUsbClass(), iface.getUsbSubclass()); + if (!shouldIgnoreDevice) { + break; + } + } + if (shouldIgnoreDevice) { + if (DEBUG) { + Slog.d(TAG, "usb interface class is black listed"); + } + return false; + } + return true; + } + private native void monitorUsbHostBus(); private native ParcelFileDescriptor nativeOpenDevice(String deviceAddress); } diff --git a/startop/scripts/app_startup/force_compiler_filter b/startop/scripts/app_startup/force_compiler_filter new file mode 100755 index 000000000000..78e915bb4d53 --- /dev/null +++ b/startop/scripts/app_startup/force_compiler_filter @@ -0,0 +1,173 @@ +#!/bin/bash +# +# 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. + +# +# Forces an application APK to be compiled (by ART's dex2oat) +# with a specific compiler filter. +# +# Example usage: +# $> ./force_compiler_filter -p com.google.android.apps.maps -c speed-profile +# +# (The application may be started/stopped as a side effect) +# + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +source "$DIR/lib/common" + +usage() { + cat <<EOF +Usage: $(basename $0) [OPTION]... + + Required: + -p, --package package of the app to recompile + -c, --compiler-filter override the compiler filter if set (default none) + valid options are listed by: adb shell cmd package, under compile -m + + Optional: + -a, --activity activity of the app to recompile + -h, --help usage information (this) + -v, --verbose enable extra verbose printing + -w, --wait_time how long to wait for app startup (default 10) in seconds +EOF +} + +wait_time="10" # seconds + +parse_arguments() { + while [[ $# -gt 0 ]]; do + case "$1" in + -a|--activity) + activity="$2" + shift + ;; + -h|--help) + usage + exit 0 + ;; + -p|--package) + package="$2" + shift + ;; + -w|--wait_time) + wait_time="$2" + shift + ;; + -c|--compiler-filter) + compiler_filter="$2" + shift + ;; + -v|--verbose) + verbose="y" + ;; + esac + shift + done + + if [[ -z "$compiler_filter" ]]; then + echo "Missing required --compiler-filter" >&2 + echo "" + usage + exit 1 + fi + if [[ -z "$package" ]]; then + echo "Missing required --package" >&2 + echo "" + usage + exit 1 + fi + + if [[ "$activity" == "" ]]; then + activity="$(get_activity_name "$package")" + if [[ "$activity" == "" ]]; then + echo "Activity name could not be found, invalid package name?" 1>&2 + exit 1 + else + verbose_print "Activity name inferred: " "$activity" + fi + fi +} + +get_activity_name() { + local package="$1" + local action_key="android.intent.action.MAIN:" + + local activity_line="$(adb shell cmd package query-activities --brief -a android.intent.action.MAIN -c android.intent.category.LAUNCHER | grep "$package")" + verbose_print $activity_line + IFS="/" read -a array <<< "$activity_line" + local activity_name="${array[1]}" + echo "$activity_name" + #adb shell am start "$package/$activity_name" +} + +remote_pidof() { + local procname="$1" + adb shell ps | grep "$procname" | awk '{print $2;}' +} + +remote_pkill() { + local procname="$1" + shift + + local the_pids=$(remote_pidof "$procname") + local pid + + for pid in $the_pids; do + verbose_print adb shell kill "$@" "$pid" + adb shell kill "$@" "$pid" + done +} + +force_package_compilation() { + local arg_compiler_filter="$1" + local arg_package="$2" + + if [[ $arg_compiler_filter == speed-profile ]]; then + # Force the running app to dump its profiles to disk. + remote_pkill "$arg_package" -SIGUSR1 + sleep 1 # give some time for above to complete. + fi + + adb shell cmd package compile -m "$arg_compiler_filter" -f "$arg_package" +} + +main() { + parse_arguments "$@" + + if [[ $compiler_filter == speed-profile ]]; then + # screen needs to be unlocked in order to run an app + "$DIR"/unlock_screen + + am_output="$(adb shell am start -S -W "$package"/"$activity")" + if [[ $? -ne 0 ]]; then + echo "am start failed" >&2 + exit 1 + fi + + verbose_print "$am_output" + # give some time for app startup to complete. + # this is supposed to be an upper bound for measuring startup time. + sleep "$wait_time" + fi + + force_package_compilation "$compiler_filter" "$package" + + # kill the application to ensure next time it's started, + # it picks up the correct compilation filter. + adb shell am force-stop "$package" + remote_pkill "$package" +} + +main "$@" diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsCbConstants.java b/telephony/java/com/android/internal/telephony/gsm/SmsCbConstants.java index 0fabc2ff6b86..541ca8d1e5c0 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SmsCbConstants.java +++ b/telephony/java/com/android/internal/telephony/gsm/SmsCbConstants.java @@ -118,71 +118,103 @@ public class SmsCbConstants { public static final int MESSAGE_ID_CMAS_ALERT_OPERATOR_DEFINED_USE = 0x111E; // 4382 - /** CMAS Message Identifier for Presidential Level alerts for additional languages - * for additional languages. */ + /** + * CMAS Message Identifier for Presidential Level alerts for additional languages. + */ public static final int MESSAGE_ID_CMAS_ALERT_PRESIDENTIAL_LEVEL_LANGUAGE = 0x111F; // 4383 - /** CMAS Message Identifier for Extreme alerts, Urgency=Immediate, Certainty=Observed - * for additional languages. */ + /** + * CMAS Message Identifier for Extreme alerts, Urgency=Immediate, Certainty=Observed + * for additional languages. + */ public static final int MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED_LANGUAGE = 0x1120; // 4384 - /** CMAS Message Identifier for Extreme alerts, Urgency=Immediate, Certainty=Likely - * for additional languages. */ + /** + * CMAS Message Identifier for Extreme alerts, Urgency=Immediate, Certainty=Likely + * for additional languages. + */ public static final int MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY_LANGUAGE = 0x1121; // 4385 - /** CMAS Message Identifier for Extreme alerts, Urgency=Expected, Certainty=Observed - * for additional languages. */ + /** + * CMAS Message Identifier for Extreme alerts, Urgency=Expected, Certainty=Observed + * for additional languages. + */ public static final int MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED_LANGUAGE = 0x1122; // 4386 - /** CMAS Message Identifier for Extreme alerts, Urgency=Expected, Certainty=Likely - * for additional languages. */ + /** + * CMAS Message Identifier for Extreme alerts, Urgency=Expected, Certainty=Likely + * for additional languages. + */ public static final int MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY_LANGUAGE = 0x1123; // 4387 - /** CMAS Message Identifier for Severe alerts, Urgency=Immediate, Certainty=Observed - * for additional languages. */ + /** + * CMAS Message Identifier for Severe alerts, Urgency=Immediate, Certainty=Observed + * for additional languages. + */ public static final int MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED_LANGUAGE = 0x1124; // 4388 - /** CMAS Message Identifier for Severe alerts, Urgency=Immediate, Certainty=Likely - * for additional languages.*/ + /** + * CMAS Message Identifier for Severe alerts, Urgency=Immediate, Certainty=Likely + * for additional languages. + */ public static final int MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY_LANGUAGE = 0x1125; // 4389 - /** CMAS Message Identifier for Severe alerts, Urgency=Expected, Certainty=Observed - * for additional languages. */ + /** + * CMAS Message Identifier for Severe alerts, Urgency=Expected, Certainty=Observed + * for additional languages. + */ public static final int MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED_LANGUAGE = 0x1126; // 4390 - /** CMAS Message Identifier for Severe alerts, Urgency=Expected, Certainty=Likely - * for additional languages.*/ + /** + * CMAS Message Identifier for Severe alerts, Urgency=Expected, Certainty=Likely + * for additional languages. + */ public static final int MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY_LANGUAGE = 0x1127; // 4391 - /** CMAS Message Identifier for Child Abduction Emergency (Amber Alert) - * for additional languages. */ + /** + * CMAS Message Identifier for Child Abduction Emergency (Amber Alert) + * for additional languages. + */ public static final int MESSAGE_ID_CMAS_ALERT_CHILD_ABDUCTION_EMERGENCY_LANGUAGE = 0x1128; // 4392 - /** CMAS Message Identifier for the Required Monthly Test - * for additional languages. */ + /** CMAS Message Identifier for the Required Monthly Test for additional languages. */ public static final int MESSAGE_ID_CMAS_ALERT_REQUIRED_MONTHLY_TEST_LANGUAGE = 0x1129; // 4393 - /** CMAS Message Identifier for CMAS Exercise - * for additional languages. */ + /** CMAS Message Identifier for CMAS Exercise for additional languages. */ public static final int MESSAGE_ID_CMAS_ALERT_EXERCISE_LANGUAGE = 0x112A; // 4394 - /** CMAS Message Identifier for operator defined use - * for additional languages. */ + /** CMAS Message Identifier for operator defined use for additional languages. */ public static final int MESSAGE_ID_CMAS_ALERT_OPERATOR_DEFINED_USE_LANGUAGE = 0x112B; // 4395 + /** CMAS Message Identifier for CMAS Public Safety Alerts. */ + public static final int MESSAGE_ID_CMAS_ALERT_PUBLIC_SAFETY + = 0x112C; // 4396 + + /** CMAS Message Identifier for CMAS Public Safety Alerts for additional languages. */ + public static final int MESSAGE_ID_CMAS_ALERT_PUBLIC_SAFETY_LANGUAGE + = 0x112D; // 4397 + + /** CMAS Message Identifier for CMAS State/Local Test. */ + public static final int MESSAGE_ID_CMAS_ALERT_STATE_LOCAL_TEST + = 0x112E; // 4398 + + /** CMAS Message Identifier for CMAS State/Local Test for additional languages. */ + public static final int MESSAGE_ID_CMAS_ALERT_STATE_LOCAL_TEST_LANGUAGE + = 0x112F; // 4399 + /** End of CMAS Message Identifier range (including future extensions). */ public static final int MESSAGE_ID_CMAS_LAST_IDENTIFIER = 0x112F; // 4399 diff --git a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java b/tests/net/java/android/net/ip/IpServerTest.java index 593465380263..cff0b5469d47 100644 --- a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java +++ b/tests/net/java/android/net/ip/IpServerTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.connectivity.tethering; +package android.net.ip; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -37,9 +37,9 @@ import static android.net.ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR; import static android.net.ConnectivityManager.TETHERING_BLUETOOTH; import static android.net.ConnectivityManager.TETHERING_USB; import static android.net.ConnectivityManager.TETHERING_WIFI; -import static com.android.server.connectivity.tethering.IControlsTethering.STATE_AVAILABLE; -import static com.android.server.connectivity.tethering.IControlsTethering.STATE_TETHERED; -import static com.android.server.connectivity.tethering.IControlsTethering.STATE_UNAVAILABLE; +import static android.net.ip.IpServer.STATE_AVAILABLE; +import static android.net.ip.IpServer.STATE_TETHERED; +import static android.net.ip.IpServer.STATE_UNAVAILABLE; import android.net.INetworkStatsService; import android.net.InterfaceConfiguration; @@ -50,7 +50,6 @@ import android.net.MacAddress; import android.net.RouteInfo; import android.net.dhcp.DhcpServer; import android.net.dhcp.DhcpServingParams; -import android.net.ip.RouterAdvertisementDaemon; import android.net.util.InterfaceParams; import android.net.util.InterfaceSet; import android.net.util.SharedLog; @@ -74,7 +73,7 @@ import org.mockito.MockitoAnnotations; @RunWith(AndroidJUnit4.class) @SmallTest -public class TetherInterfaceStateMachineTest { +public class IpServerTest { private static final String IFACE_NAME = "testnet1"; private static final String UPSTREAM_IFACE = "upstream0"; private static final String UPSTREAM_IFACE2 = "upstream1"; @@ -85,39 +84,38 @@ public class TetherInterfaceStateMachineTest { @Mock private INetworkManagementService mNMService; @Mock private INetworkStatsService mStatsService; - @Mock private IControlsTethering mTetherHelper; + @Mock private IpServer.Callback mCallback; @Mock private InterfaceConfiguration mInterfaceConfiguration; @Mock private SharedLog mSharedLog; @Mock private DhcpServer mDhcpServer; @Mock private RouterAdvertisementDaemon mRaDaemon; - @Mock private TetheringDependencies mTetheringDependencies; + @Mock private IpServer.Dependencies mDependencies; @Captor private ArgumentCaptor<DhcpServingParams> mDhcpParamsCaptor; private final TestLooper mLooper = new TestLooper(); private final ArgumentCaptor<LinkProperties> mLinkPropertiesCaptor = ArgumentCaptor.forClass(LinkProperties.class); - private TetherInterfaceStateMachine mTestedSm; + private IpServer mIpServer; private void initStateMachine(int interfaceType) throws Exception { initStateMachine(interfaceType, false /* usingLegacyDhcp */); } private void initStateMachine(int interfaceType, boolean usingLegacyDhcp) throws Exception { - mTestedSm = new TetherInterfaceStateMachine( + mIpServer = new IpServer( IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, - mNMService, mStatsService, mTetherHelper, usingLegacyDhcp, - mTetheringDependencies); - mTestedSm.start(); + mNMService, mStatsService, mCallback, usingLegacyDhcp, mDependencies); + mIpServer.start(); // Starting the state machine always puts us in a consistent state and notifies // the rest of the world that we've changed from an unknown to available state. mLooper.dispatchAll(); - reset(mNMService, mStatsService, mTetherHelper); + reset(mNMService, mStatsService, mCallback); when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration); - when(mTetheringDependencies.makeDhcpServer( + when(mDependencies.makeDhcpServer( any(), any(), mDhcpParamsCaptor.capture(), any())).thenReturn(mDhcpServer); - when(mTetheringDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon); - when(mTetheringDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS); + when(mDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon); + when(mDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS); when(mRaDaemon.start()).thenReturn(true); } @@ -130,11 +128,11 @@ public class TetherInterfaceStateMachineTest { private void initTetheredStateMachine(int interfaceType, String upstreamIface, boolean usingLegacyDhcp) throws Exception { initStateMachine(interfaceType, usingLegacyDhcp); - dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED, STATE_TETHERED); + dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED); if (upstreamIface != null) { dispatchTetherConnectionChanged(upstreamIface); } - reset(mNMService, mStatsService, mTetherHelper); + reset(mNMService, mStatsService, mCallback); when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration); } @@ -145,34 +143,34 @@ public class TetherInterfaceStateMachineTest { @Test public void startsOutAvailable() { - mTestedSm = new TetherInterfaceStateMachine(IFACE_NAME, mLooper.getLooper(), - TETHERING_BLUETOOTH, mSharedLog, mNMService, mStatsService, mTetherHelper, - false /* usingLegacyDhcp */, mTetheringDependencies); - mTestedSm.start(); + mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), + TETHERING_BLUETOOTH, mSharedLog, mNMService, mStatsService, mCallback, + false /* usingLegacyDhcp */, mDependencies); + mIpServer.start(); mLooper.dispatchAll(); - verify(mTetherHelper).updateInterfaceState( - mTestedSm, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR); - verify(mTetherHelper).updateLinkProperties(eq(mTestedSm), any(LinkProperties.class)); - verifyNoMoreInteractions(mTetherHelper, mNMService, mStatsService); + verify(mCallback).updateInterfaceState( + mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR); + verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class)); + verifyNoMoreInteractions(mCallback, mNMService, mStatsService); } @Test public void shouldDoNothingUntilRequested() throws Exception { initStateMachine(TETHERING_BLUETOOTH); final int [] NOOP_COMMANDS = { - TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED, - TetherInterfaceStateMachine.CMD_IP_FORWARDING_ENABLE_ERROR, - TetherInterfaceStateMachine.CMD_IP_FORWARDING_DISABLE_ERROR, - TetherInterfaceStateMachine.CMD_START_TETHERING_ERROR, - TetherInterfaceStateMachine.CMD_STOP_TETHERING_ERROR, - TetherInterfaceStateMachine.CMD_SET_DNS_FORWARDERS_ERROR, - TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED + IpServer.CMD_TETHER_UNREQUESTED, + IpServer.CMD_IP_FORWARDING_ENABLE_ERROR, + IpServer.CMD_IP_FORWARDING_DISABLE_ERROR, + IpServer.CMD_START_TETHERING_ERROR, + IpServer.CMD_STOP_TETHERING_ERROR, + IpServer.CMD_SET_DNS_FORWARDERS_ERROR, + IpServer.CMD_TETHER_CONNECTION_CHANGED }; for (int command : NOOP_COMMANDS) { // None of these commands should trigger us to request action from // the rest of the system. dispatchCommand(command); - verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper); + verifyNoMoreInteractions(mNMService, mStatsService, mCallback); } } @@ -180,57 +178,57 @@ public class TetherInterfaceStateMachineTest { public void handlesImmediateInterfaceDown() throws Exception { initStateMachine(TETHERING_BLUETOOTH); - dispatchCommand(TetherInterfaceStateMachine.CMD_INTERFACE_DOWN); - verify(mTetherHelper).updateInterfaceState( - mTestedSm, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR); - verify(mTetherHelper).updateLinkProperties(eq(mTestedSm), any(LinkProperties.class)); - verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper); + dispatchCommand(IpServer.CMD_INTERFACE_DOWN); + verify(mCallback).updateInterfaceState( + mIpServer, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR); + verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class)); + verifyNoMoreInteractions(mNMService, mStatsService, mCallback); } @Test public void canBeTethered() throws Exception { initStateMachine(TETHERING_BLUETOOTH); - dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED, STATE_TETHERED); - InOrder inOrder = inOrder(mTetherHelper, mNMService); + dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED); + InOrder inOrder = inOrder(mCallback, mNMService); inOrder.verify(mNMService).tetherInterface(IFACE_NAME); - inOrder.verify(mTetherHelper).updateInterfaceState( - mTestedSm, STATE_TETHERED, TETHER_ERROR_NO_ERROR); - inOrder.verify(mTetherHelper).updateLinkProperties( - eq(mTestedSm), any(LinkProperties.class)); - verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper); + inOrder.verify(mCallback).updateInterfaceState( + mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR); + inOrder.verify(mCallback).updateLinkProperties( + eq(mIpServer), any(LinkProperties.class)); + verifyNoMoreInteractions(mNMService, mStatsService, mCallback); } @Test public void canUnrequestTethering() throws Exception { initTetheredStateMachine(TETHERING_BLUETOOTH, null); - dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED); - InOrder inOrder = inOrder(mNMService, mStatsService, mTetherHelper); + dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED); + InOrder inOrder = inOrder(mNMService, mStatsService, mCallback); inOrder.verify(mNMService).untetherInterface(IFACE_NAME); inOrder.verify(mNMService).setInterfaceConfig(eq(IFACE_NAME), any()); - inOrder.verify(mTetherHelper).updateInterfaceState( - mTestedSm, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR); - inOrder.verify(mTetherHelper).updateLinkProperties( - eq(mTestedSm), any(LinkProperties.class)); - verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper); + inOrder.verify(mCallback).updateInterfaceState( + mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR); + inOrder.verify(mCallback).updateLinkProperties( + eq(mIpServer), any(LinkProperties.class)); + verifyNoMoreInteractions(mNMService, mStatsService, mCallback); } @Test public void canBeTetheredAsUsb() throws Exception { initStateMachine(TETHERING_USB); - dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED, STATE_TETHERED); - InOrder inOrder = inOrder(mTetherHelper, mNMService); + dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED); + InOrder inOrder = inOrder(mCallback, mNMService); inOrder.verify(mNMService).getInterfaceConfig(IFACE_NAME); inOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration); inOrder.verify(mNMService).tetherInterface(IFACE_NAME); - inOrder.verify(mTetherHelper).updateInterfaceState( - mTestedSm, STATE_TETHERED, TETHER_ERROR_NO_ERROR); - inOrder.verify(mTetherHelper).updateLinkProperties( - eq(mTestedSm), mLinkPropertiesCaptor.capture()); + inOrder.verify(mCallback).updateInterfaceState( + mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR); + inOrder.verify(mCallback).updateLinkProperties( + eq(mIpServer), mLinkPropertiesCaptor.capture()); assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue()); - verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper); + verifyNoMoreInteractions(mNMService, mStatsService, mCallback); } @Test @@ -243,7 +241,7 @@ public class TetherInterfaceStateMachineTest { InOrder inOrder = inOrder(mNMService); inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE); inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE); - verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper); + verifyNoMoreInteractions(mNMService, mStatsService, mCallback); } @Test @@ -257,7 +255,7 @@ public class TetherInterfaceStateMachineTest { inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE); inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2); inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2); - verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper); + verifyNoMoreInteractions(mNMService, mStatsService, mCallback); } @Test @@ -300,18 +298,18 @@ public class TetherInterfaceStateMachineTest { public void canUnrequestTetheringWithUpstream() throws Exception { initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE); - dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED); - InOrder inOrder = inOrder(mNMService, mStatsService, mTetherHelper); + dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED); + InOrder inOrder = inOrder(mNMService, mStatsService, mCallback); inOrder.verify(mStatsService).forceUpdate(); inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE); inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE); inOrder.verify(mNMService).untetherInterface(IFACE_NAME); inOrder.verify(mNMService).setInterfaceConfig(eq(IFACE_NAME), any()); - inOrder.verify(mTetherHelper).updateInterfaceState( - mTestedSm, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR); - inOrder.verify(mTetherHelper).updateLinkProperties( - eq(mTestedSm), any(LinkProperties.class)); - verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper); + inOrder.verify(mCallback).updateInterfaceState( + mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR); + inOrder.verify(mCallback).updateLinkProperties( + eq(mIpServer), any(LinkProperties.class)); + verifyNoMoreInteractions(mNMService, mStatsService, mCallback); } @Test @@ -322,15 +320,15 @@ public class TetherInterfaceStateMachineTest { if (shouldThrow) { doThrow(RemoteException.class).when(mNMService).untetherInterface(IFACE_NAME); } - dispatchCommand(TetherInterfaceStateMachine.CMD_INTERFACE_DOWN); - InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mTetherHelper); + dispatchCommand(IpServer.CMD_INTERFACE_DOWN); + InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mCallback); usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown(); usbTeardownOrder.verify(mNMService).setInterfaceConfig( IFACE_NAME, mInterfaceConfiguration); - usbTeardownOrder.verify(mTetherHelper).updateInterfaceState( - mTestedSm, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR); - usbTeardownOrder.verify(mTetherHelper).updateLinkProperties( - eq(mTestedSm), mLinkPropertiesCaptor.capture()); + usbTeardownOrder.verify(mCallback).updateInterfaceState( + mIpServer, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR); + usbTeardownOrder.verify(mCallback).updateLinkProperties( + eq(mIpServer), mLinkPropertiesCaptor.capture()); assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue()); } } @@ -340,15 +338,15 @@ public class TetherInterfaceStateMachineTest { initStateMachine(TETHERING_USB); doThrow(RemoteException.class).when(mNMService).tetherInterface(IFACE_NAME); - dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED, STATE_TETHERED); - InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mTetherHelper); + dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED); + InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mCallback); usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown(); usbTeardownOrder.verify(mNMService).setInterfaceConfig( IFACE_NAME, mInterfaceConfiguration); - usbTeardownOrder.verify(mTetherHelper).updateInterfaceState( - mTestedSm, STATE_AVAILABLE, TETHER_ERROR_TETHER_IFACE_ERROR); - usbTeardownOrder.verify(mTetherHelper).updateLinkProperties( - eq(mTestedSm), mLinkPropertiesCaptor.capture()); + usbTeardownOrder.verify(mCallback).updateInterfaceState( + mIpServer, STATE_AVAILABLE, TETHER_ERROR_TETHER_IFACE_ERROR); + usbTeardownOrder.verify(mCallback).updateLinkProperties( + eq(mIpServer), mLinkPropertiesCaptor.capture()); assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue()); } @@ -358,13 +356,13 @@ public class TetherInterfaceStateMachineTest { doThrow(RemoteException.class).when(mNMService).enableNat(anyString(), anyString()); dispatchTetherConnectionChanged(UPSTREAM_IFACE); - InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mTetherHelper); + InOrder usbTeardownOrder = inOrder(mNMService, mInterfaceConfiguration, mCallback); usbTeardownOrder.verify(mInterfaceConfiguration).setInterfaceDown(); usbTeardownOrder.verify(mNMService).setInterfaceConfig(IFACE_NAME, mInterfaceConfiguration); - usbTeardownOrder.verify(mTetherHelper).updateInterfaceState( - mTestedSm, STATE_AVAILABLE, TETHER_ERROR_ENABLE_NAT_ERROR); - usbTeardownOrder.verify(mTetherHelper).updateLinkProperties( - eq(mTestedSm), mLinkPropertiesCaptor.capture()); + usbTeardownOrder.verify(mCallback).updateInterfaceState( + mIpServer, STATE_AVAILABLE, TETHER_ERROR_ENABLE_NAT_ERROR); + usbTeardownOrder.verify(mCallback).updateLinkProperties( + eq(mIpServer), mLinkPropertiesCaptor.capture()); assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue()); } @@ -372,11 +370,11 @@ public class TetherInterfaceStateMachineTest { public void ignoresDuplicateUpstreamNotifications() throws Exception { initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE); - verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper); + verifyNoMoreInteractions(mNMService, mStatsService, mCallback); for (int i = 0; i < 5; i++) { dispatchTetherConnectionChanged(UPSTREAM_IFACE); - verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper); + verifyNoMoreInteractions(mNMService, mStatsService, mCallback); } } @@ -401,11 +399,11 @@ public class TetherInterfaceStateMachineTest { initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, true /* usingLegacyDhcp */); dispatchTetherConnectionChanged(UPSTREAM_IFACE); - verify(mTetheringDependencies, never()).makeDhcpServer(any(), any(), any(), any()); + verify(mDependencies, never()).makeDhcpServer(any(), any(), any(), any()); } private void assertDhcpStarted(IpPrefix expectedPrefix) { - verify(mTetheringDependencies, times(1)).makeDhcpServer( + verify(mDependencies, times(1)).makeDhcpServer( eq(mLooper.getLooper()), eq(TEST_IFACE_PARAMS), any(), eq(mSharedLog)); verify(mDhcpServer, times(1)).start(); final DhcpServingParams params = mDhcpParamsCaptor.getValue(); @@ -422,21 +420,21 @@ public class TetherInterfaceStateMachineTest { /** * Send a command to the state machine under test, and run the event loop to idle. * - * @param command One of the TetherInterfaceStateMachine.CMD_* constants. + * @param command One of the IpServer.CMD_* constants. * @param arg1 An additional argument to pass. */ private void dispatchCommand(int command, int arg1) { - mTestedSm.sendMessage(command, arg1); + mIpServer.sendMessage(command, arg1); mLooper.dispatchAll(); } /** * Send a command to the state machine under test, and run the event loop to idle. * - * @param command One of the TetherInterfaceStateMachine.CMD_* constants. + * @param command One of the IpServer.CMD_* constants. */ private void dispatchCommand(int command) { - mTestedSm.sendMessage(command); + mIpServer.sendMessage(command); mLooper.dispatchAll(); } @@ -447,7 +445,7 @@ public class TetherInterfaceStateMachineTest { * @param upstreamIface String name of upstream interface (or null) */ private void dispatchTetherConnectionChanged(String upstreamIface) { - mTestedSm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED, + mIpServer.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED, new InterfaceSet(upstreamIface)); mLooper.dispatchAll(); } diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java index 0d3b8e4a0452..40d5544dccd8 100644 --- a/tests/net/java/com/android/server/connectivity/TetheringTest.java +++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java @@ -75,6 +75,7 @@ import android.net.NetworkRequest; import android.net.NetworkState; import android.net.NetworkUtils; import android.net.RouteInfo; +import android.net.ip.IpServer; import android.net.ip.RouterAdvertisementDaemon; import android.net.util.InterfaceParams; import android.net.util.NetworkConstants; @@ -99,10 +100,8 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.StateMachine; import com.android.internal.util.test.BroadcastInterceptingContext; import com.android.internal.util.test.FakeSettingsProvider; -import com.android.server.connectivity.tethering.IControlsTethering; import com.android.server.connectivity.tethering.IPv6TetheringCoordinator; import com.android.server.connectivity.tethering.OffloadHardwareInterface; -import com.android.server.connectivity.tethering.TetherInterfaceStateMachine; import com.android.server.connectivity.tethering.TetheringDependencies; import com.android.server.connectivity.tethering.UpstreamNetworkMonitor; @@ -190,7 +189,7 @@ public class TetheringTest { public class MockTetheringDependencies extends TetheringDependencies { StateMachine upstreamNetworkMonitorMasterSM; - ArrayList<TetherInterfaceStateMachine> ipv6CoordinatorNotifyList; + ArrayList<IpServer> ipv6CoordinatorNotifyList; int isTetheringSupportedCalls; public void reset() { @@ -213,29 +212,35 @@ public class TetheringTest { @Override public IPv6TetheringCoordinator getIPv6TetheringCoordinator( - ArrayList<TetherInterfaceStateMachine> notifyList, SharedLog log) { + ArrayList<IpServer> notifyList, SharedLog log) { ipv6CoordinatorNotifyList = notifyList; return mIPv6TetheringCoordinator; } @Override - public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) { - return mRouterAdvertisementDaemon; - } - - @Override - public INetd getNetdService() { - return mNetd; - } - - @Override - public InterfaceParams getInterfaceParams(String ifName) { - final String[] ifaces = new String[] { TEST_USB_IFNAME, TEST_WLAN_IFNAME, - TEST_MOBILE_IFNAME }; - final int index = ArrayUtils.indexOf(ifaces, ifName); - assertTrue("Non-mocked interface: " + ifName, index >= 0); - return new InterfaceParams(ifName, index + IFINDEX_OFFSET, - MacAddress.ALL_ZEROS_ADDRESS); + public IpServer.Dependencies getIpServerDependencies() { + return new IpServer.Dependencies() { + @Override + public RouterAdvertisementDaemon getRouterAdvertisementDaemon( + InterfaceParams ifParams) { + return mRouterAdvertisementDaemon; + } + + @Override + public InterfaceParams getInterfaceParams(String ifName) { + final String[] ifaces = new String[] { + TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_MOBILE_IFNAME }; + final int index = ArrayUtils.indexOf(ifaces, ifName); + assertTrue("Non-mocked interface: " + ifName, index >= 0); + return new InterfaceParams(ifName, index + IFINDEX_OFFSET, + MacAddress.ALL_ZEROS_ADDRESS); + } + + @Override + public INetd getNetdService() { + return mNetd; + } + }; } @Override @@ -458,9 +463,9 @@ public class TetheringTest { sendWifiApStateChanged(WIFI_AP_STATE_ENABLED); mLooper.dispatchAll(); - // If, and only if, Tethering received an interface status changed - // then it creates a TetherInterfaceStateMachine and sends out a - // broadcast indicating that the interface is "available". + // If, and only if, Tethering received an interface status changed then + // it creates a IpServer and sends out a broadcast indicating that the + // interface is "available". if (emulateInterfaceStatusChanged) { assertEquals(1, mTetheringDependencies.isTetheringSupportedCalls); verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER); @@ -557,18 +562,18 @@ public class TetheringTest { } /** - * Send CMD_IPV6_TETHER_UPDATE to TISMs as would be done by IPv6TetheringCoordinator. + * Send CMD_IPV6_TETHER_UPDATE to IpServers as would be done by IPv6TetheringCoordinator. */ private void sendIPv6TetherUpdates(NetworkState upstreamState) { // IPv6TetheringCoordinator must have been notified of downstream verify(mIPv6TetheringCoordinator, times(1)).addActiveDownstream( argThat(sm -> sm.linkProperties().getInterfaceName().equals(TEST_USB_IFNAME)), - eq(IControlsTethering.STATE_TETHERED)); + eq(IpServer.STATE_TETHERED)); - for (TetherInterfaceStateMachine tism : + for (IpServer ipSrv : mTetheringDependencies.ipv6CoordinatorNotifyList) { NetworkState ipv6OnlyState = buildMobileUpstreamState(false, true, false); - tism.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, + ipSrv.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, upstreamState.linkProperties.isIPv6Provisioned() ? ipv6OnlyState.linkProperties : null); @@ -812,7 +817,7 @@ public class TetheringTest { // We verify get/set called thrice here: once for setup and twice during // teardown because all events happen over the course of the single - // dispatchAll() above. Note that once the TISM IPv4 address config + // dispatchAll() above. Note that once the IpServer IPv4 address config // code is refactored the two calls during shutdown will revert to one. verify(mNMService, times(2)).getInterfaceConfig(TEST_WLAN_IFNAME); verify(mNMService, times(3)) diff --git a/tools/aapt2/Diagnostics.h b/tools/aapt2/Diagnostics.h index 50e8b33ab9b1..30deb5555b21 100644 --- a/tools/aapt2/Diagnostics.h +++ b/tools/aapt2/Diagnostics.h @@ -133,11 +133,19 @@ class SourcePathDiagnostics : public IDiagnostics { void Log(Level level, DiagMessageActual& actual_msg) override { actual_msg.source.path = source_.path; diag_->Log(level, actual_msg); + if (level == Level::Error) { + error = true; + } + } + + bool HadError() { + return error; } private: Source source_; IDiagnostics* diag_; + bool error = false; DISALLOW_COPY_AND_ASSIGN(SourcePathDiagnostics); }; diff --git a/tools/aapt2/compile/XmlIdCollector.cpp b/tools/aapt2/compile/XmlIdCollector.cpp index d61a15af0d85..2199d003bccb 100644 --- a/tools/aapt2/compile/XmlIdCollector.cpp +++ b/tools/aapt2/compile/XmlIdCollector.cpp @@ -21,6 +21,7 @@ #include "ResourceUtils.h" #include "ResourceValues.h" +#include "text/Unicode.h" #include "xml/XmlDom.h" namespace aapt { @@ -35,8 +36,9 @@ struct IdCollector : public xml::Visitor { public: using xml::Visitor::Visit; - explicit IdCollector(std::vector<SourcedResourceName>* out_symbols) - : out_symbols_(out_symbols) {} + explicit IdCollector(std::vector<SourcedResourceName>* out_symbols, + SourcePathDiagnostics* source_diag) : out_symbols_(out_symbols), + source_diag_(source_diag) {} void Visit(xml::Element* element) override { for (xml::Attribute& attr : element->attributes) { @@ -44,12 +46,16 @@ struct IdCollector : public xml::Visitor { bool create = false; if (ResourceUtils::ParseReference(attr.value, &name, &create, nullptr)) { if (create && name.type == ResourceType::kId) { - auto iter = std::lower_bound(out_symbols_->begin(), - out_symbols_->end(), name, cmp_name); - if (iter == out_symbols_->end() || iter->name != name) { - out_symbols_->insert(iter, - SourcedResourceName{name.ToResourceName(), - element->line_number}); + if (!text::IsValidResourceEntryName(name.entry)) { + source_diag_->Error(DiagMessage(element->line_number) + << "id '" << name << "' has an invalid entry name"); + } else { + auto iter = std::lower_bound(out_symbols_->begin(), + out_symbols_->end(), name, cmp_name); + if (iter == out_symbols_->end() || iter->name != name) { + out_symbols_->insert(iter, SourcedResourceName{name.ToResourceName(), + element->line_number}); + } } } } @@ -60,15 +66,17 @@ struct IdCollector : public xml::Visitor { private: std::vector<SourcedResourceName>* out_symbols_; + SourcePathDiagnostics* source_diag_; }; } // namespace bool XmlIdCollector::Consume(IAaptContext* context, xml::XmlResource* xmlRes) { xmlRes->file.exported_symbols.clear(); - IdCollector collector(&xmlRes->file.exported_symbols); + SourcePathDiagnostics source_diag(xmlRes->file.source, context->GetDiagnostics()); + IdCollector collector(&xmlRes->file.exported_symbols, &source_diag); xmlRes->root->Accept(&collector); - return true; + return !source_diag.HadError(); } } // namespace aapt diff --git a/tools/aapt2/compile/XmlIdCollector_test.cpp b/tools/aapt2/compile/XmlIdCollector_test.cpp index 98da56d03ae3..d49af3bf903c 100644 --- a/tools/aapt2/compile/XmlIdCollector_test.cpp +++ b/tools/aapt2/compile/XmlIdCollector_test.cpp @@ -64,4 +64,14 @@ TEST(XmlIdCollectorTest, DontCollectNonIds) { EXPECT_TRUE(doc->file.exported_symbols.empty()); } +TEST(XmlIdCollectorTest, ErrorOnInvalidIds) { + std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build(); + + std::unique_ptr<xml::XmlResource> doc = + test::BuildXmlDom("<View foo=\"@+id/foo$bar\"/>"); + + XmlIdCollector collector; + ASSERT_FALSE(collector.Consume(context.get(), doc.get())); +} + } // namespace aapt diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp index c5c78d9d3827..fa6538d7b4e7 100644 --- a/tools/aapt2/link/ManifestFixer.cpp +++ b/tools/aapt2/link/ManifestFixer.cpp @@ -252,6 +252,7 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, xml::XmlNodeAction component_action; component_action.Action(RequiredNameIsJavaClassName); component_action["intent-filter"] = intent_filter_action; + component_action["preferred"] = intent_filter_action; component_action["meta-data"] = meta_data_action; // Manifest actions. @@ -414,6 +415,8 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, application_action["provider"]["grant-uri-permission"]; application_action["provider"]["path-permission"]; + manifest_action["package"] = manifest_action; + return true; } diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp index 7cd023bca369..73105e16559b 100644 --- a/tools/aapt2/util/Files.cpp +++ b/tools/aapt2/util/Files.cpp @@ -102,12 +102,25 @@ FileType GetFileType(const std::string& path) { #endif bool mkdirs(const std::string& path) { - constexpr const mode_t mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP; + #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; + } + #else // Start after the first character so that we don't consume the root '/'. // This is safe to do with unicode because '/' will never match with a continuation character. size_t current_pos = 1u; + #endif + constexpr const mode_t mode = S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP; while ((current_pos = path.find(sDirSep, current_pos)) != std::string::npos) { std::string parent_path = path.substr(0, current_pos); + if (parent_path.empty()) { + continue; + } + int result = ::android::base::utf8::mkdir(parent_path.c_str(), mode); if (result < 0 && errno != EEXIST) { return false; |