diff options
168 files changed, 3903 insertions, 2377 deletions
diff --git a/Android.bp b/Android.bp index 6b2fb0c6b1ae..3b6eaa7a3afe 100644 --- a/Android.bp +++ b/Android.bp @@ -194,13 +194,14 @@ filegroup { ":framework-core-sources", ":framework-drm-sources", ":framework-graphics-sources", + ":framework-jobscheduler-sources", // jobscheduler is not a module for R ":framework-keystore-sources", ":framework-location-sources", ":framework-lowpan-sources", - ":framework-media-sources", ":framework-mca-effect-sources", ":framework-mca-filterfw-sources", ":framework-mca-filterpacks-sources", + ":framework-media-sources", ":framework-mime-sources", ":framework-mms-sources", ":framework-opengl-sources", @@ -410,7 +411,6 @@ java_library { installable: true, static_libs: [ "framework-minus-apex", - "jobscheduler-framework", ], required: [ "framework-platform-compat-config", @@ -965,7 +965,6 @@ stubs_defaults { ":updatable-media-srcs", "test-mock/src/**/*.java", "test-runner/src/**/*.java", - ":jobscheduler-framework-source", ], libs: framework_docs_only_libs, local_sourcepaths: frameworks_base_subdirs, @@ -1028,7 +1027,6 @@ stubs_defaults { ":core-current-stubs-source", ":core_public_api_files", ":updatable-media-srcs", - ":jobscheduler-framework-source", ], libs: ["framework-internal-utils"], local_sourcepaths: frameworks_base_subdirs, diff --git a/apex/jobscheduler/framework/Android.bp b/apex/jobscheduler/framework/Android.bp index 621ff9a92bc5..3902aa212e32 100644 --- a/apex/jobscheduler/framework/Android.bp +++ b/apex/jobscheduler/framework/Android.bp @@ -1,5 +1,5 @@ filegroup { - name: "jobscheduler-framework-source", + name: "framework-jobscheduler-sources", srcs: [ "java/**/*.java", "java/android/app/job/IJobCallback.aidl", @@ -12,13 +12,12 @@ filegroup { java_library { name: "jobscheduler-framework", - installable: true, + installable: false, + compile_dex: true, sdk_version: "core_platform", - srcs: [ - ":jobscheduler-framework-source", + ":framework-jobscheduler-sources", ], - aidl: { export_include_dirs: [ "java", diff --git a/apex/permission/Android.bp b/apex/permission/Android.bp new file mode 100644 index 000000000000..027481401557 --- /dev/null +++ b/apex/permission/Android.bp @@ -0,0 +1,33 @@ +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +apex { + name: "com.android.permission", + + manifest: "apex_manifest.json", + + key: "com.android.permission.key", + certificate: ":com.android.permission.certificate", +} + +apex_key { + name: "com.android.permission.key", + public_key: "com.android.permission.avbpubkey", + private_key: "com.android.permission.pem", +} + +android_app_certificate { + name: "com.android.permission.certificate", + certificate: "com.android.permission", +} diff --git a/apex/permission/OWNERS b/apex/permission/OWNERS new file mode 100644 index 000000000000..957e10a582a0 --- /dev/null +++ b/apex/permission/OWNERS @@ -0,0 +1,6 @@ +svetoslavganov@google.com +moltmann@google.com +eugenesusla@google.com +zhanghai@google.com +evanseverson@google.com +ntmyren@google.com diff --git a/apex/permission/apex_manifest.json b/apex/permission/apex_manifest.json new file mode 100644 index 000000000000..2a8c4f737dff --- /dev/null +++ b/apex/permission/apex_manifest.json @@ -0,0 +1,4 @@ +{ + "name": "com.android.permission", + "version": 1 +} diff --git a/apex/permission/com.android.permission.avbpubkey b/apex/permission/com.android.permission.avbpubkey Binary files differnew file mode 100644 index 000000000000..9eaf85259637 --- /dev/null +++ b/apex/permission/com.android.permission.avbpubkey diff --git a/apex/permission/com.android.permission.pem b/apex/permission/com.android.permission.pem new file mode 100644 index 000000000000..3d584be5440d --- /dev/null +++ b/apex/permission/com.android.permission.pem @@ -0,0 +1,51 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIJKgIBAAKCAgEA6snt4eqoz85xiL9Sf6w1S1b9FgSHK05zYTh2JYPvQKQ3yeZp +E6avJ6FN6XcbmkDzSd658BvUGDBSPhOlzuUO4BsoKBuLMxP6TxIQXFKidzDqY0vQ +4qkS++bdIhUjwBP3OSZ3Czu0BiihK8GC75Abr//EyCyObGIGGfHEGANiOgrpP4X5 ++OmLzQLCjk4iE1kg+U6cRSRI/XLaoWC0TvIIuzxznrQ6r5GmzgTOwyBWyIB+bj73 +bmsweHTU+w9Y7kGOx4hO3XCLIhoBWEw0EbuW9nZmQ4sZls5Jo/CbyJlCclF11yVo +SCf2LG/T+9pah5NOmDQ1kPbU+0iKZIV4YFHGTIhyGDE/aPOuUT05ziCGDifgHr0u +SG1x/RLqsVh/POvNxnvP9cQFMQ08BvbEJaTTgB785iwKsvdqCfmng/SAyxSetmzP +StXVB3fh1OoZ8vunRbQYxnmUxycVqaA96zmBx2wLvbvzKo7pZFDE6nbhnT5+MRAM +z/VIK89W26uB4gj8sBFslqZjT0jPqsAZuvDm7swOtMwIcEolyGJuFLqlhN7UwMz2 +9y8+IpYixR+HvD1TZI9NtmuCmv3kPrWgoMZg6yvaBayTIr8RdYzi6FO/C1lLiraz +48dH3sXWRa8cgw6VcSUwYrEBIc3sotdsupO1iOjcFybIwaee0YTZJfjvbqkCAwEA +AQKCAgEArRnfdpaJi1xLPGTCMDsIt9kUku0XswgN7PmxsYsKFAB+2S40/jYAIRm9 +1YjpItsMA8RgFfSOdJ77o6TctCMQyo17F8bm4+uwuic5RLfv7Cx2QmsdQF8jDfFx +y7UGPJD7znjbf76uxXOjEB2FqZX3s9TAgkzHXIUQtoQW7RVhkCWHPjxKxgd5+NY2 +FrDoUpd9xhD9CcTsw1+wbRZdGW88nL6/B50dP2AFORM2VYo8MWr6y9FEn3YLsGOC +uu7fxBk1aUrHyl81VRkTMMROB1zkuiUk1FtzrEm+5U15rXXBFYOVe9+qeLhtuOlh +wueDoz0pzvF/JLe24uTik6YL0Ae6SD0pFXQ2KDrdH3cUHLok3r76/yGzaDNTFjS2 +2WbQ8dEJV8veNHk8gjGpFTJIsBUlcZpmUCDHlfvVMb3+2ahQ+28piQUt5t3zqJdZ +NDqsOHzY6BRPc+Wm85Xii/lWiQceZSee/b1Enu+nchsyXhSenBfC6bIGZReyMI0K +KKKuVhyR6OSOiR5ZdZ/NyXGqsWy05fn/h0X9hnpETsNaNYNKWvpHLfKll+STJpf7 +AZquJPIclQyiq5NONx6kfPztoCLkKV/zOgIj3Sx5oSZq+5gpO91nXWVwkTbqK1d1 +004q2Mah6UQyAk1XGQc2pHx7ouVcWawjU30vZ4C015Hv2lm/gVkCggEBAPltATYS +OqOSL1YAtIHPiHxMjNAgUdglq8JiJFXVfkocGU9eNub3Ed3sSWu6GB9Myu/sSKje +bJ5DZqxJnvB2Fqmu9I9OunLGFSD0aXs4prwsQ1Rm5FcbImtrxcciASdkoo8Pj0z4 +vk2r2NZD3VtER5Uh+YjSDkxcS9gBStXUpCL6gj69UpOxMmWqZVjyHatVB4lEvYJl +N82uT7N7QVNL1DzcZ9z4C4r7ks1Pm7ka12s5m/oaAlAMdVeofiPJe1xA9zRToSr4 +tIbMkOeXFLVRLuji/7XsOgal5Rl59p+OwLshX5cswPVOMrH6zt+hbsJ5q8M5dqnX +VAOBK7KNQ/EKZwcCggEBAPD6KVvyCim46n5EbcEqCkO7gevwZkw/9vLwmM5YsxTh +z9FQkPO0iB7mwbX8w04I91Pre4NdfcgMG0pP1b13Sb4KHBchqW1a+TCs3kSGC6gn +1SxmXHnA9jRxAkrWlGkoAQEz+aP61cXiiy2tXpQwJ8xQCKprfoqWZwhkCtEVU6CE +S7v9cscOHIqgNxx4WoceMmq4EoihHAZzHxTcNVbByckMjb2XQJ0iNw3lDP4ddvc+ +a4HzHfHkhzeQ5ZNc8SvWU8z80aSCOKRsSD3aUTZzxhZ4O2tZSW7v7p+FpvVee7bC +g8YCfszTdpVUMlLRLjScimAcovcFLSvtyupinxWg4M8CggEAN9YGEmOsSte7zwXj +YrfhtumwEBtcFwX/2Ej+F1Tuq4p0xAa0RaoDjumJWhtTsRYQy/raHSuFpzwxbNoi +QXQ+CIhI6RfXtz/OlQ0B2/rHoJJMFEXgUfuaDfAXW0eqeHYXyezSyIlamKqipPyW +Pgsf9yue39keKEv1EorfhNTQVaA8rezV4oglXwrxGyNALw2e3UTNI7ai8mFWKDis +XAg6n9E7UwUYGGnO6DUtCBgRJ0jDOQ6/e8n+LrxiWIKPIgzNCiK6jpMUXqTGv4Fb +umdNGAdQ9RnHt5tFmRlrczaSwJFtA7uaCpAR2zPpQbiywchZAiAIB2dTwGEXNiZX +kksg2wKCAQEA6pNad3qhkgPDoK6T+Jkn7M82paoaqtcJWWwEE7oceZNnbWZz9Agl +CY+vuawXonrv5+0vCq2Tp4zBdBFLC2h3jFrjBVFrUFxifpOIukOSTVqZFON/2bWQ +9XOcu6UuSz7522Xw+UNPnZXtzcUacD6AP08ZYGvLfrTyDyTzspyED5k48ALEHCkM +d5WGkFxII4etpF0TDZVnZo/iDbhe49k4yFFEGO6Ho26PESOLBkNAb2V/2bwDxlij +l9+g21Z6HiZA5SamHPH2mXgeyrcen1cL2QupK9J6vVcqfnboE6qp2zp2c+Yx8MlY +gfy4EA44YFaSDQVTTgrn8f9Eq+zc130H2QKCAQEAqOKgv68nIPdDSngNyCVyWego +boFiDaEJoBBg8FrBjTJ6wFLrNAnXmbvfTtgNmNAzF1cUPJZlIIsHgGrMCfpehbXq +WQQIw+E+yFbTGLxseGRfsLrV0CsgnAoOVeod+yIHmqc3livaUbrWhL1V2f6Ue+sE +7YLp/iP43NaMfA4kYk2ep7+ZJoEVkCjHJJaHWgAG3RynPJHkTJlSgu7wLYvGc9uE +ZsEFUM46lX02t7rrtMfasVGrUy1c2xOxFb4v1vG6iEZ7+YWeq5o3AkxUwEGn+mG4 +/3p+k4AaTXJDXgyZ0Sv6CkGuPHenAYG4cswcUUEf/G4Ag77x6LBNMgycJBxUJA== +-----END RSA PRIVATE KEY----- diff --git a/apex/permission/com.android.permission.pk8 b/apex/permission/com.android.permission.pk8 Binary files differnew file mode 100644 index 000000000000..d51673dbc2fc --- /dev/null +++ b/apex/permission/com.android.permission.pk8 diff --git a/apex/permission/com.android.permission.x509.pem b/apex/permission/com.android.permission.x509.pem new file mode 100644 index 000000000000..4b146c9edd4f --- /dev/null +++ b/apex/permission/com.android.permission.x509.pem @@ -0,0 +1,35 @@ +-----BEGIN CERTIFICATE----- +MIIGKzCCBBOgAwIBAgIUezo3fQeVZsmLpm/dkpGWJ/G/MN8wDQYJKoZIhvcNAQEL +BQAwgaMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH +DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy +b2lkMR8wHQYDVQQDDBZjb20uYW5kcm9pZC5wZXJtaXNzaW9uMSIwIAYJKoZIhvcN +AQkBFhNhbmRyb2lkQGFuZHJvaWQuY29tMCAXDTE5MTAwOTIxMzExOVoYDzQ3NTcw +OTA0MjEzMTE5WjCBozELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx +FjAUBgNVBAcMDU1vdW50YWluIFZpZXcxEDAOBgNVBAoMB0FuZHJvaWQxEDAOBgNV +BAsMB0FuZHJvaWQxHzAdBgNVBAMMFmNvbS5hbmRyb2lkLnBlcm1pc3Npb24xIjAg +BgkqhkiG9w0BCQEWE2FuZHJvaWRAYW5kcm9pZC5jb20wggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQCxefguRJ7E6tBCTEOeU2HJEGs6AQQapLz9hMed0aaJ +Qr7aTQiYJEk+sG4+jPYbjpxa8JDDzJHp+4g7DjfSb+dvT9n84A8lWaI/yRXTZTQN +Hu5m/bgHhi0LbySpiaFyodXBKUAnOhZyGPtYjtBFywFylueub8ryc1Z6UxxU7udH +1mkIr7sE48Qkq5SyjFROE96iFmYA+vS/JXOfS0NBHiMB4GBxx4V7kXpvrTI7hhZG +HiyhKvNh7wyHIhO9nDEw1rwtAH6CsL3YkQEVBeAU98m+0Au+qStLYkKHh2l8zT4W +7sVK1VSqfB+VqOUmeIGdzlBfqMsoXD+FJz6KnIdUHIwjFDjL7Xr+hd+7xve+Q3S+ +U3Blk/U6atY8PM09wNfilG+SvwcKk5IgriDcu3rWKgIFxbUUaxLrDW7pLlu6wt/d +GGtKK+Bc0jF+9Z901Tl33i5xhc5yOktT0btkKs7lSeE6VzP/Nk5g0SuzixmuRoh9 +f5Ge41N2ZCEHNXx3wZeVZwHIIPfYrL7Yql1Xoxbfs4ETFk6ChzVQcvjfDQQuK58J +uNc+TOCoI/qflXwGCwpuHl0ier8V5Z4tpMUl5rWyVR/QGRtLPvs2lLuxczDw1OXq +wEVtCMn9aNnd4y7R9PZ52hi53HAvDjpWefrLYi+Q04J6iGFQ1qAFBClK9DquBvmR +swIDAQABo1MwUTAdBgNVHQ4EFgQULpfus5s5SrqLkoUKyPXA0D1iHPMwHwYDVR0j +BBgwFoAULpfus5s5SrqLkoUKyPXA0D1iHPMwDwYDVR0TAQH/BAUwAwEB/zANBgkq +hkiG9w0BAQsFAAOCAgEAjxQG5EFv8V/9yV2glI53VOmlWMjfEgvUjd39s/XLyPlr +OzPOKSB0NFo8To3l4l+MsManxPK8y0OyfEVKbWVz9onv0ovo5MVokBmV/2G0jmsV +B4e9yjOq+DmqIvY/Qh63Ywb97sTgcFI8620MhQDbh2IpEGv4ZNV0H6rgXmgdSCBw +1EjBoYfFpN5aMgZjeyzZcq+d1IapdWqdhuEJQkMvoYS4WIumNIJlEXPQRoq/F5Ih +nszdbKI/jVyiGFa2oeZ3rja1Y6GCRU8TYEoKx1pjS8uQDOEDTwsG/QnUe9peEj0V +SsCkIidJWTomAmq9Tub9vpBe1zuTpuRAwxwR0qwgSxozV1Mvow1dJ19oFtHX0yD6 +ZjCpRn5PW9kMvSWSlrcrFs1NJf0j1Cvf7bHpkEDqLqpMnnh9jaFQq3nzDY+MWcIR +jDcgQpI+AiE2/qtauZnFEVhbce49nCnk9+5bpTTIZJdzqeaExe5KXHwEtZLaEDh4 +atLY9LuEvPsjmDIMOR6hycD9FvwGXhJOQBjESIWFwigtSb1Yud9n6201jw3MLJ4k ++WhkbmZgWy+xc+Mdm5H3XyB1lvHaHGkxu+QB9KyQuVQKwbUVcbwZIfTFPN6Zr/dS +ZXJqAbBhG/dBgF0LazuLaPVpibi+a3Y+tb9b8eXGkz4F97PWZIEDkELQ+9KOvhc= +-----END CERTIFICATE----- diff --git a/api/current.txt b/api/current.txt index 71dc58b70021..54d33a2c59b6 100644 --- a/api/current.txt +++ b/api/current.txt @@ -6851,6 +6851,7 @@ package android.app.admin { method public boolean setKeyPairCertificate(@Nullable android.content.ComponentName, @NonNull String, @NonNull java.util.List<java.security.cert.Certificate>, boolean); method public boolean setKeyguardDisabled(@NonNull android.content.ComponentName, boolean); method public void setKeyguardDisabledFeatures(@NonNull android.content.ComponentName, int); + method public void setLocationEnabled(@NonNull android.content.ComponentName, boolean); method public void setLockTaskFeatures(@NonNull android.content.ComponentName, int); method public void setLockTaskPackages(@NonNull android.content.ComponentName, @NonNull String[]) throws java.lang.SecurityException; method public void setLogoutEnabled(@NonNull android.content.ComponentName, boolean); @@ -29975,10 +29976,16 @@ package android.net.wifi { method public String getSSID(); method public android.net.wifi.SupplicantState getSupplicantState(); method @IntRange(from=0xffffffff) public int getTxLinkSpeedMbps(); + method public int getWifiTechnology(); method public void writeToParcel(android.os.Parcel, int); field public static final String FREQUENCY_UNITS = "MHz"; field public static final String LINK_SPEED_UNITS = "Mbps"; field public static final int LINK_SPEED_UNKNOWN = -1; // 0xffffffff + field public static final int WIFI_TECHNOLOGY_11AC = 5; // 0x5 + field public static final int WIFI_TECHNOLOGY_11AX = 6; // 0x6 + field public static final int WIFI_TECHNOLOGY_11N = 4; // 0x4 + field public static final int WIFI_TECHNOLOGY_LEGACY = 1; // 0x1 + field public static final int WIFI_TECHNOLOGY_UNKNOWN = 0; // 0x0 } public class WifiManager { @@ -41368,11 +41375,16 @@ package android.service.autofill { field public static final int FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE = 1; // 0x1 field public static final int NEGATIVE_BUTTON_STYLE_CANCEL = 0; // 0x0 field public static final int NEGATIVE_BUTTON_STYLE_REJECT = 1; // 0x1 + field public static final int POSITIVE_BUTTON_STYLE_CONTINUE = 1; // 0x1 + field public static final int POSITIVE_BUTTON_STYLE_SAVE = 0; // 0x0 field public static final int SAVE_DATA_TYPE_ADDRESS = 2; // 0x2 field public static final int SAVE_DATA_TYPE_CREDIT_CARD = 4; // 0x4 + field public static final int SAVE_DATA_TYPE_DEBIT_CARD = 32; // 0x20 field public static final int SAVE_DATA_TYPE_EMAIL_ADDRESS = 16; // 0x10 field public static final int SAVE_DATA_TYPE_GENERIC = 0; // 0x0 + field public static final int SAVE_DATA_TYPE_GENERIC_CARD = 128; // 0x80 field public static final int SAVE_DATA_TYPE_PASSWORD = 1; // 0x1 + field public static final int SAVE_DATA_TYPE_PAYMENT_CARD = 64; // 0x40 field public static final int SAVE_DATA_TYPE_USERNAME = 8; // 0x8 } @@ -41386,6 +41398,7 @@ package android.service.autofill { method @NonNull public android.service.autofill.SaveInfo.Builder setFlags(int); method @NonNull public android.service.autofill.SaveInfo.Builder setNegativeAction(int, @Nullable android.content.IntentSender); method @NonNull public android.service.autofill.SaveInfo.Builder setOptionalIds(@NonNull android.view.autofill.AutofillId[]); + method @NonNull public android.service.autofill.SaveInfo.Builder setPositiveAction(int); method @NonNull public android.service.autofill.SaveInfo.Builder setTriggerId(@NonNull android.view.autofill.AutofillId); method @NonNull public android.service.autofill.SaveInfo.Builder setValidator(@NonNull android.service.autofill.Validator); } diff --git a/api/system-current.txt b/api/system-current.txt index ad9a04d16a7e..10bcffc200ad 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -8294,6 +8294,9 @@ package android.telephony { public class TelephonyManager { method @Deprecated @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void call(String, String); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void carrierActionReportDefaultNetworkStatus(int, boolean); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void carrierActionResetAll(int); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void carrierActionSetRadioEnabled(int, boolean); method public int checkCarrierPrivilegesForPackage(String); method public int checkCarrierPrivilegesForPackageAnyPhone(String); method public void dial(String); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 375a06efba74..ad671dfcf80a 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -8351,6 +8351,24 @@ public class DevicePolicyManager { } /** + * Called by device owners to set the user's master location setting. + * + * @param admin Which {@link DeviceAdminReceiver} this request is associated with + * @param locationEnabled whether location should be enabled or disabled + * @throws SecurityException if {@code admin} is not a device owner. + */ + public void setLocationEnabled(@NonNull ComponentName admin, boolean locationEnabled) { + throwIfParentInstance("setLocationEnabled"); + if (mService != null) { + try { + mService.setLocationEnabled(admin, locationEnabled); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + /** * Called by profile or device owners to update {@link android.provider.Settings.Secure} * settings. Validation that the value of the setting is in the correct form for the setting * type should be performed by the caller. @@ -8379,6 +8397,11 @@ public class DevicePolicyManager { * all users. * </strong> * + * <strong>Note: Starting from Android R, apps should no longer call this method with the + * setting {@link android.provider.Settings.Secure#LOCATION_MODE}, which is deprecated. Instead, + * device owners should call {@link #setLocationEnabled(ComponentName, boolean)}. + * </strong> + * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param setting The name of the setting to update. * @param value The value to update the setting to. diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 7d2c54ea1436..6b505223163c 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -258,6 +258,8 @@ interface IDevicePolicyManager { void setSystemSetting(in ComponentName who, in String setting, in String value); void setSecureSetting(in ComponentName who, in String setting, in String value); + void setLocationEnabled(in ComponentName who, boolean locationEnabled); + boolean setTime(in ComponentName who, long millis); boolean setTimeZone(in ComponentName who, String timeZone); diff --git a/core/java/android/app/admin/PasswordMetrics.java b/core/java/android/app/admin/PasswordMetrics.java index d9bfde5af61a..0ecfca74b26e 100644 --- a/core/java/android/app/admin/PasswordMetrics.java +++ b/core/java/android/app/admin/PasswordMetrics.java @@ -73,6 +73,11 @@ public final class PasswordMetrics implements Parcelable { // consider it a complex PIN/password. public static final int MAX_ALLOWED_SEQUENCE = 3; + // One of CREDENTIAL_TYPE_NONE, CREDENTIAL_TYPE_PATTERN or CREDENTIAL_TYPE_PASSWORD. + // Note that this class still uses CREDENTIAL_TYPE_PASSWORD to represent both numeric PIN + // and alphabetic password. This is OK as long as this definition is only used internally, + // and the value never gets mixed up with credential types from other parts of the framework. + // TODO: fix this (ideally after we move logic to PasswordPolicy) public @CredentialType int credType; // Fields below only make sense when credType is PASSWORD. public int length = 0; @@ -161,24 +166,26 @@ public final class PasswordMetrics implements Parcelable { public static final @NonNull Parcelable.Creator<PasswordMetrics> CREATOR = new Parcelable.Creator<PasswordMetrics>() { - public PasswordMetrics createFromParcel(Parcel in) { - int credType = in.readInt(); - int length = in.readInt(); - int letters = in.readInt(); - int upperCase = in.readInt(); - int lowerCase = in.readInt(); - int numeric = in.readInt(); - int symbols = in.readInt(); - int nonLetter = in.readInt(); - int nonNumeric = in.readInt(); - int seqLength = in.readInt(); - return new PasswordMetrics(credType, length, letters, upperCase, lowerCase, numeric, - symbols, nonLetter, nonNumeric, seqLength); - } - - public PasswordMetrics[] newArray(int size) { - return new PasswordMetrics[size]; - } + @Override + public PasswordMetrics createFromParcel(Parcel in) { + int credType = in.readInt(); + int length = in.readInt(); + int letters = in.readInt(); + int upperCase = in.readInt(); + int lowerCase = in.readInt(); + int numeric = in.readInt(); + int symbols = in.readInt(); + int nonLetter = in.readInt(); + int nonNumeric = in.readInt(); + int seqLength = in.readInt(); + return new PasswordMetrics(credType, length, letters, upperCase, lowerCase, + numeric, symbols, nonLetter, nonNumeric, seqLength); + } + + @Override + public PasswordMetrics[] newArray(int size) { + return new PasswordMetrics[size]; + } }; /** @@ -189,7 +196,7 @@ public final class PasswordMetrics implements Parcelable { * {@link com.android.internal.widget.LockPatternUtils#CREDENTIAL_TYPE_PASSWORD}. */ public static PasswordMetrics computeForCredential(LockscreenCredential credential) { - if (credential.isPassword()) { + if (credential.isPassword() || credential.isPin()) { return PasswordMetrics.computeForPassword(credential.getCredential()); } else if (credential.isPattern()) { return new PasswordMetrics(CREDENTIAL_TYPE_PATTERN); diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 83391f368ac1..43842c5c3403 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -94,6 +94,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Collections; /** * InputMethodService provides a standard implementation of an InputMethod, @@ -434,6 +435,7 @@ public class InputMethodService extends AbstractInputMethodService { final int[] mTmpLocation = new int[2]; final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer = info -> { + onComputeInsets(mTmpInsets); if (isExtractViewShown()) { // In true fullscreen mode, we just say the window isn't covering // any content so we don't impact whatever is behind. @@ -442,12 +444,15 @@ public class InputMethodService extends AbstractInputMethodService { info.touchableRegion.setEmpty(); info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME); } else { - onComputeInsets(mTmpInsets); info.contentInsets.top = mTmpInsets.contentTopInsets; info.visibleInsets.top = mTmpInsets.visibleTopInsets; info.touchableRegion.set(mTmpInsets.touchableRegion); info.setTouchableInsets(mTmpInsets.touchableInsets); } + + if (mInputFrame != null) { + setImeExclusionRect(mTmpInsets.visibleTopInsets); + } }; final View.OnClickListener mActionClickListener = v -> { @@ -672,6 +677,14 @@ public class InputMethodService extends AbstractInputMethodService { mPrivOps.setImeWindowStatus(visibilityFlags, backDisposition); } + /** Set region of the keyboard to be avoided from back gesture */ + private void setImeExclusionRect(int visibleTopInsets) { + View inputFrameRootView = mInputFrame.getRootView(); + Rect r = new Rect(0, visibleTopInsets, inputFrameRootView.getWidth(), + inputFrameRootView.getHeight()); + inputFrameRootView.setSystemGestureExclusionRects(Collections.singletonList(r)); + } + /** * Concrete implementation of * {@link AbstractInputMethodService.AbstractInputMethodSessionImpl} that provides diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index bdc78342d42a..cff99f34126a 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -8594,13 +8594,19 @@ public final class Settings { public static final String POWER_SOUNDS_ENABLED = "power_sounds_enabled"; /** - * URI for the "wireless charging started" and "wired charging started" sound. + * URI for the "wireless charging started" sound. * @hide */ - public static final String CHARGING_STARTED_SOUND = + public static final String WIRELESS_CHARGING_STARTED_SOUND = "wireless_charging_started_sound"; /** + * URI for "wired charging started" sound. + * @hide + */ + public static final String CHARGING_STARTED_SOUND = "charging_started_sound"; + + /** * Whether to play a sound for charging events. * @deprecated Use {@link android.provider.Settings.Secure#CHARGING_SOUNDS_ENABLED} instead * @hide diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java index 94b9d050a44d..48ba4295f3c8 100644 --- a/core/java/android/service/autofill/SaveInfo.java +++ b/core/java/android/service/autofill/SaveInfo.java @@ -181,6 +181,23 @@ public final class SaveInfo implements Parcelable { public static final int SAVE_DATA_TYPE_EMAIL_ADDRESS = 0x10; /** + * Type used when the {@link FillResponse} represents a debit card. + */ + public static final int SAVE_DATA_TYPE_DEBIT_CARD = 0x20; + + /** + * Type used when the {@link FillResponse} represents a payment card except for credit and + * debit cards. + */ + public static final int SAVE_DATA_TYPE_PAYMENT_CARD = 0x40; + + /** + * Type used when the {@link FillResponse} represents a card that does not a specified card or + * cannot identify what the card is for. + */ + public static final int SAVE_DATA_TYPE_GENERIC_CARD = 0x80; + + /** * Style for the negative button of the save UI to cancel the * save operation. In this case, the user tapping the negative * button signals that they would prefer to not save the filled @@ -207,6 +224,30 @@ public final class SaveInfo implements Parcelable { @Retention(RetentionPolicy.SOURCE) @interface NegativeButtonStyle{} + /** + * Style for the positive button of save UI to request the save operation. + * In this case, the user tapping the positive button signals that they + * agrees to save the filled content. + */ + public static final int POSITIVE_BUTTON_STYLE_SAVE = 0; + + /** + * Style for the positive button of save UI to have next action before the save operation. + * This could be useful if the filled content contains sensitive personally identifiable + * information and then requires user confirmation or verification. In this case, the user + * tapping the positive button signals that they would complete the next required action + * to save the filled content. + */ + public static final int POSITIVE_BUTTON_STYLE_CONTINUE = 1; + + /** @hide */ + @IntDef(prefix = { "POSITIVE_BUTTON_STYLE_" }, value = { + POSITIVE_BUTTON_STYLE_SAVE, + POSITIVE_BUTTON_STYLE_CONTINUE + }) + @Retention(RetentionPolicy.SOURCE) + @interface PositiveButtonStyle{} + /** @hide */ @IntDef(flag = true, prefix = { "SAVE_DATA_TYPE_" }, value = { SAVE_DATA_TYPE_GENERIC, @@ -214,7 +255,10 @@ public final class SaveInfo implements Parcelable { SAVE_DATA_TYPE_ADDRESS, SAVE_DATA_TYPE_CREDIT_CARD, SAVE_DATA_TYPE_USERNAME, - SAVE_DATA_TYPE_EMAIL_ADDRESS + SAVE_DATA_TYPE_EMAIL_ADDRESS, + SAVE_DATA_TYPE_DEBIT_CARD, + SAVE_DATA_TYPE_PAYMENT_CARD, + SAVE_DATA_TYPE_GENERIC_CARD }) @Retention(RetentionPolicy.SOURCE) @interface SaveDataType{} @@ -266,6 +310,7 @@ public final class SaveInfo implements Parcelable { private final @SaveDataType int mType; private final @NegativeButtonStyle int mNegativeButtonStyle; + private final @PositiveButtonStyle int mPositiveButtonStyle; private final IntentSender mNegativeActionListener; private final AutofillId[] mRequiredIds; private final AutofillId[] mOptionalIds; @@ -281,6 +326,7 @@ public final class SaveInfo implements Parcelable { mType = builder.mType; mNegativeButtonStyle = builder.mNegativeButtonStyle; mNegativeActionListener = builder.mNegativeActionListener; + mPositiveButtonStyle = builder.mPositiveButtonStyle; mRequiredIds = builder.mRequiredIds; mOptionalIds = builder.mOptionalIds; mDescription = builder.mDescription; @@ -313,6 +359,11 @@ public final class SaveInfo implements Parcelable { } /** @hide */ + public @PositiveButtonStyle int getPositiveActionStyle() { + return mPositiveButtonStyle; + } + + /** @hide */ public @Nullable AutofillId[] getRequiredIds() { return mRequiredIds; } @@ -374,6 +425,7 @@ public final class SaveInfo implements Parcelable { private final @SaveDataType int mType; private @NegativeButtonStyle int mNegativeButtonStyle = NEGATIVE_BUTTON_STYLE_CANCEL; + private @PositiveButtonStyle int mPositiveButtonStyle = POSITIVE_BUTTON_STYLE_SAVE; private IntentSender mNegativeActionListener; private final AutofillId[] mRequiredIds; private AutofillId[] mOptionalIds; @@ -394,8 +446,9 @@ public final class SaveInfo implements Parcelable { * can be any combination of {@link SaveInfo#SAVE_DATA_TYPE_GENERIC}, * {@link SaveInfo#SAVE_DATA_TYPE_PASSWORD}, * {@link SaveInfo#SAVE_DATA_TYPE_ADDRESS}, {@link SaveInfo#SAVE_DATA_TYPE_CREDIT_CARD}, - * {@link SaveInfo#SAVE_DATA_TYPE_USERNAME}, or - * {@link SaveInfo#SAVE_DATA_TYPE_EMAIL_ADDRESS}. + * {@link SaveInfo#SAVE_DATA_TYPE_DEBIT_CARD}, {@link SaveInfo#SAVE_DATA_TYPE_PAYMENT_CARD}, + * {@link SaveInfo#SAVE_DATA_TYPE_GENERIC_CARD}, {@link SaveInfo#SAVE_DATA_TYPE_USERNAME}, + * or {@link SaveInfo#SAVE_DATA_TYPE_EMAIL_ADDRESS}. * @param requiredIds ids of all required views that will trigger a save request. * * <p>See {@link SaveInfo} for more info. @@ -418,8 +471,9 @@ public final class SaveInfo implements Parcelable { * can be any combination of {@link SaveInfo#SAVE_DATA_TYPE_GENERIC}, * {@link SaveInfo#SAVE_DATA_TYPE_PASSWORD}, * {@link SaveInfo#SAVE_DATA_TYPE_ADDRESS}, {@link SaveInfo#SAVE_DATA_TYPE_CREDIT_CARD}, - * {@link SaveInfo#SAVE_DATA_TYPE_USERNAME}, or - * {@link SaveInfo#SAVE_DATA_TYPE_EMAIL_ADDRESS}. + * {@link SaveInfo#SAVE_DATA_TYPE_DEBIT_CARD}, {@link SaveInfo#SAVE_DATA_TYPE_PAYMENT_CARD}, + * {@link SaveInfo#SAVE_DATA_TYPE_GENERIC_CARD}, {@link SaveInfo#SAVE_DATA_TYPE_USERNAME}, + * or {@link SaveInfo#SAVE_DATA_TYPE_EMAIL_ADDRESS}. * * <p>See {@link SaveInfo} for more info. */ @@ -523,6 +577,8 @@ public final class SaveInfo implements Parcelable { public @NonNull Builder setNegativeAction(@NegativeButtonStyle int style, @Nullable IntentSender listener) { throwIfDestroyed(); + Preconditions.checkArgumentInRange(style, NEGATIVE_BUTTON_STYLE_CANCEL, + NEGATIVE_BUTTON_STYLE_REJECT, "style"); if (style != NEGATIVE_BUTTON_STYLE_CANCEL && style != NEGATIVE_BUTTON_STYLE_REJECT) { throw new IllegalArgumentException("Invalid style: " + style); @@ -533,6 +589,33 @@ public final class SaveInfo implements Parcelable { } /** + * Sets the style for the positive save action. + * + * <p>This allows an autofill service to customize the style of the + * positive action in the save UI. Note that selecting the positive + * action regardless of its style would dismiss the save UI and calling + * into the {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback) save request}. + * The service should take the next action if selecting style + * {@link #POSITIVE_BUTTON_STYLE_CONTINUE}. The default style is + * {@link #POSITIVE_BUTTON_STYLE_SAVE} + * + * @param style The action style. + * @return This builder. + * + * @see #POSITIVE_BUTTON_STYLE_SAVE + * @see #POSITIVE_BUTTON_STYLE_CONTINUE + * + * @throws IllegalArgumentException If the style is invalid + */ + public @NonNull Builder setPositiveAction(@PositiveButtonStyle int style) { + throwIfDestroyed(); + Preconditions.checkArgumentInRange(style, POSITIVE_BUTTON_STYLE_SAVE, + POSITIVE_BUTTON_STYLE_CONTINUE, "style"); + mPositiveButtonStyle = style; + return this; + } + + /** * Sets an object used to validate the user input - if the input is not valid, the * autofill save UI is not shown. * @@ -717,8 +800,10 @@ public final class SaveInfo implements Parcelable { final StringBuilder builder = new StringBuilder("SaveInfo: [type=") .append(DebugUtils.flagsToString(SaveInfo.class, "SAVE_DATA_TYPE_", mType)) .append(", requiredIds=").append(Arrays.toString(mRequiredIds)) - .append(", style=").append(DebugUtils.flagsToString(SaveInfo.class, - "NEGATIVE_BUTTON_STYLE_", mNegativeButtonStyle)); + .append(", negative style=").append(DebugUtils.flagsToString(SaveInfo.class, + "NEGATIVE_BUTTON_STYLE_", mNegativeButtonStyle)) + .append(", positive style=").append(DebugUtils.flagsToString(SaveInfo.class, + "POSITIVE_BUTTON_STYLE_", mPositiveButtonStyle)); if (mOptionalIds != null) { builder.append(", optionalIds=").append(Arrays.toString(mOptionalIds)); } @@ -763,6 +848,7 @@ public final class SaveInfo implements Parcelable { parcel.writeParcelableArray(mOptionalIds, flags); parcel.writeInt(mNegativeButtonStyle); parcel.writeParcelable(mNegativeActionListener, flags); + parcel.writeInt(mPositiveButtonStyle); parcel.writeCharSequence(mDescription); parcel.writeParcelable(mCustomDescription, flags); parcel.writeParcelable(mValidator, flags); @@ -794,6 +880,7 @@ public final class SaveInfo implements Parcelable { } builder.setNegativeAction(parcel.readInt(), parcel.readParcelable(null)); + builder.setPositiveAction(parcel.readInt()); builder.setDescription(parcel.readCharSequence()); final CustomDescription customDescripton = parcel.readParcelable(null); if (customDescripton != null) { diff --git a/core/java/android/service/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java index 8a9f68942e9d..12c25806d666 100644 --- a/core/java/android/service/euicc/EuiccService.java +++ b/core/java/android/service/euicc/EuiccService.java @@ -516,7 +516,7 @@ public abstract class EuiccService extends Service { * @see android.telephony.euicc.EuiccManager#eraseSubscriptions * * @deprecated From R, callers should specify a flag for specific set of subscriptions to erase - * and use @link{onEraseSubscriptionsWithOptions} instead + * and use {@link #onEraseSubscriptionsWithOptions(int, int)} instead */ @Deprecated public abstract int onEraseSubscriptions(int slotId); diff --git a/core/java/com/android/internal/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java index b9a92b58768c..28a8a8631372 100644 --- a/core/java/com/android/internal/app/ResolverListController.java +++ b/core/java/com/android/internal/app/ResolverListController.java @@ -274,6 +274,7 @@ public class ResolverListController { // Fall into normal sort when number of ranked elements // needed is not smaller than size of input list. sort(inputList); + return; } try { long beforeRank = System.currentTimeMillis(); diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl index 4099cfa51b33..8391ad2f12c2 100644 --- a/core/java/com/android/internal/compat/IPlatformCompat.aidl +++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl @@ -49,9 +49,10 @@ interface IPlatformCompat * you do not need to call this API directly. The change will be reported for you. * * @param changeId The ID of the compatibility change taking effect. + * @param userId The ID of the user that the operation is done for. * @param packageName The package name of the app in question. */ - void reportChangeByPackageName(long changeId, in String packageName); + void reportChangeByPackageName(long changeId, in String packageName, int userId); /** * Reports that a compatibility change is affecting an app process now. @@ -86,7 +87,7 @@ interface IPlatformCompat * be called when implementing functionality on behalf of the affected app. * * <p>Same as {@link #isChangeEnabled(long, ApplicationInfo)}, except it receives a package name - * instead of an {@link ApplicationInfo} + * and userId instead of an {@link ApplicationInfo} * object, and finds an app info object based on the package name. Returns {@code true} if * there is no installed package by that name. * @@ -100,9 +101,10 @@ interface IPlatformCompat * * @param changeId The ID of the compatibility change in question. * @param packageName The package name of the app in question. + * @param userId The ID of the user that the operation is done for. * @return {@code true} if the change is enabled for the current app. */ - boolean isChangeEnabledByPackageName(long changeId, in String packageName); + boolean isChangeEnabledByPackageName(long changeId, in String packageName, int userId); /** * Query if a given compatibility change is enabled for an app process. This method should diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl index b626fc6e0608..897b982406dc 100644 --- a/core/java/com/android/internal/widget/ILockSettings.aidl +++ b/core/java/com/android/internal/widget/ILockSettings.aidl @@ -24,6 +24,7 @@ import android.security.keystore.recovery.KeyChainSnapshot; import android.security.keystore.recovery.KeyChainProtectionParams; import android.security.keystore.recovery.RecoveryCertPath; import com.android.internal.widget.ICheckCredentialProgressCallback; +import com.android.internal.widget.LockscreenCredential; import com.android.internal.widget.VerifyCredentialResponse; import java.util.Map; @@ -42,19 +43,16 @@ interface ILockSettings { long getLong(in String key, in long defaultValue, in int userId); @UnsupportedAppUsage String getString(in String key, in String defaultValue, in int userId); - boolean setLockCredential(in byte[] credential, int type, in byte[] savedCredential, int requestedQuality, int userId, boolean allowUntrustedChange); + boolean setLockCredential(in LockscreenCredential credential, in LockscreenCredential savedCredential, int userId, boolean allowUntrustedChange); void resetKeyStore(int userId); - VerifyCredentialResponse checkCredential(in byte[] credential, int type, int userId, + VerifyCredentialResponse checkCredential(in LockscreenCredential credential, int userId, in ICheckCredentialProgressCallback progressCallback); - VerifyCredentialResponse verifyCredential(in byte[] credential, int type, long challenge, int userId); - VerifyCredentialResponse verifyTiedProfileChallenge(in byte[] credential, int type, long challenge, int userId); + VerifyCredentialResponse verifyCredential(in LockscreenCredential credential, long challenge, int userId); + VerifyCredentialResponse verifyTiedProfileChallenge(in LockscreenCredential credential, long challenge, int userId); boolean checkVoldPassword(int userId); - @UnsupportedAppUsage - boolean havePattern(int userId); - @UnsupportedAppUsage - boolean havePassword(int userId); - byte[] getHashFactor(in byte[] currentCredential, int userId); - void setSeparateProfileChallengeEnabled(int userId, boolean enabled, in byte[] managedUserPassword); + int getCredentialType(int userId); + byte[] getHashFactor(in LockscreenCredential currentCredential, int userId); + void setSeparateProfileChallengeEnabled(int userId, boolean enabled, in LockscreenCredential managedUserPassword); boolean getSeparateProfileChallengeEnabled(int userId); void registerStrongAuthTracker(in IStrongAuthTracker tracker); void unregisterStrongAuthTracker(in IStrongAuthTracker tracker); diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 7a42985c7402..b534213ec859 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -17,9 +17,6 @@ package com.android.internal.widget; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC; -import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC; -import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX; -import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_MANAGED; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; @@ -59,10 +56,10 @@ import android.util.SparseLongArray; import com.android.internal.annotations.VisibleForTesting; import com.android.server.LocalServices; -import libcore.util.HexEncoding; - import com.google.android.collect.Lists; +import libcore.util.HexEncoding; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.security.MessageDigest; @@ -117,13 +114,19 @@ public class LockPatternUtils { // NOTE: When modifying this, make sure credential sufficiency validation logic is intact. public static final int CREDENTIAL_TYPE_NONE = -1; public static final int CREDENTIAL_TYPE_PATTERN = 1; - public static final int CREDENTIAL_TYPE_PASSWORD = 2; + // This is the legacy value persisted on disk. Never return it to clients, but internally + // we still need it to handle upgrade cases. + public static final int CREDENTIAL_TYPE_PASSWORD_OR_PIN = 2; + public static final int CREDENTIAL_TYPE_PIN = 3; + public static final int CREDENTIAL_TYPE_PASSWORD = 4; @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = {"CREDENTIAL_TYPE_"}, value = { CREDENTIAL_TYPE_NONE, CREDENTIAL_TYPE_PATTERN, - CREDENTIAL_TYPE_PASSWORD, // Either pin or password. + CREDENTIAL_TYPE_PASSWORD, + CREDENTIAL_TYPE_PIN, + // CREDENTIAL_TYPE_PASSWORD_OR_PIN is missing on purpose. }) public @interface CredentialType {} @@ -169,6 +172,7 @@ public class LockPatternUtils { public static final String SYNTHETIC_PASSWORD_HANDLE_KEY = "sp-handle"; public static final String SYNTHETIC_PASSWORD_ENABLED_KEY = "enable-sp"; + public static final int SYNTHETIC_PASSWORD_ENABLED_BY_DEFAULT = 1; private static final String HISTORY_DELIMITER = ","; @UnsupportedAppUsage @@ -372,8 +376,8 @@ public class LockPatternUtils { * * @param credential The credential to check. * @param challenge The challenge to verify against the credential - * @return the attestation that the challenge was verified, or null * @param userId The user whose credential is being verified + * @return the attestation that the challenge was verified, or null * @throws RequestThrottledException if credential verification is being throttled due to * to many incorrect attempts. * @throws IllegalStateException if called on the main thread. @@ -383,7 +387,7 @@ public class LockPatternUtils { throwIfCalledOnMainThread(); try { VerifyCredentialResponse response = getLockSettings().verifyCredential( - credential.getCredential(), credential.getType(), challenge, userId); + credential, challenge, userId); if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) { return response.getPayload(); } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) { @@ -392,6 +396,7 @@ public class LockPatternUtils { return null; } } catch (RemoteException re) { + Log.e(TAG, "failed to verify credential", re); return null; } } @@ -413,8 +418,7 @@ public class LockPatternUtils { throwIfCalledOnMainThread(); try { VerifyCredentialResponse response = getLockSettings().checkCredential( - credential.getCredential(), credential.getType(), - userId, wrapCallback(progressCallback)); + credential, userId, wrapCallback(progressCallback)); if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) { return true; @@ -424,6 +428,7 @@ public class LockPatternUtils { return false; } } catch (RemoteException re) { + Log.e(TAG, "failed to check credential", re); return false; } } @@ -447,8 +452,7 @@ public class LockPatternUtils { throwIfCalledOnMainThread(); try { VerifyCredentialResponse response = - getLockSettings().verifyTiedProfileChallenge( - credential.getCredential(), credential.getType(), challenge, userId); + getLockSettings().verifyTiedProfileChallenge(credential, challenge, userId); if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) { return response.getPayload(); @@ -458,6 +462,7 @@ public class LockPatternUtils { return null; } } catch (RemoteException re) { + Log.e(TAG, "failed to verify tied profile credential", re); return null; } } @@ -471,6 +476,7 @@ public class LockPatternUtils { try { return getLockSettings().checkVoldPassword(userId); } catch (RemoteException re) { + Log.e(TAG, "failed to check vold password", re); return false; } } @@ -482,7 +488,7 @@ public class LockPatternUtils { public byte[] getPasswordHistoryHashFactor(@NonNull LockscreenCredential currentPassword, int userId) { try { - return getLockSettings().getHashFactor(currentPassword.getCredential(), userId); + return getLockSettings().getHashFactor(currentPassword, userId); } catch (RemoteException e) { Log.e(TAG, "failed to get hash factor", e); return null; @@ -524,30 +530,6 @@ public class LockPatternUtils { } /** - * Check to see if the user has stored a lock pattern. - * @return Whether a saved pattern exists. - */ - private boolean savedPatternExists(int userId) { - try { - return getLockSettings().havePattern(userId); - } catch (RemoteException re) { - return false; - } - } - - /** - * Check to see if the user has stored a lock pattern. - * @return Whether a saved pattern exists. - */ - private boolean savedPasswordExists(int userId) { - try { - return getLockSettings().havePassword(userId); - } catch (RemoteException re) { - return false; - } - } - - /** * Return true if the user has ever chosen a pattern. This is true even if the pattern is * currently cleared. * @@ -568,22 +550,11 @@ public class LockPatternUtils { /** * Used by device policy manager to validate the current password * information it has. + * @Deprecated use {@link #getKeyguardStoredPasswordQuality} */ @UnsupportedAppUsage public int getActivePasswordQuality(int userId) { - int quality = getKeyguardStoredPasswordQuality(userId); - - if (isLockPasswordEnabled(quality, userId)) { - // Quality is a password and a password exists. Return the quality. - return quality; - } - - if (isLockPatternEnabled(quality, userId)) { - // Quality is a pattern and a pattern exists. Return the quality. - return quality; - } - - return PASSWORD_QUALITY_UNSPECIFIED; + return getKeyguardStoredPasswordQuality(userId); } /** @@ -641,6 +612,22 @@ public class LockPatternUtils { return quality == PASSWORD_QUALITY_NUMERIC || quality == PASSWORD_QUALITY_NUMERIC_COMPLEX; } + /** Returns the canonical password quality corresponding to the given credential type. */ + public static int credentialTypeToPasswordQuality(int credentialType) { + switch (credentialType) { + case CREDENTIAL_TYPE_NONE: + return PASSWORD_QUALITY_UNSPECIFIED; + case CREDENTIAL_TYPE_PATTERN: + return PASSWORD_QUALITY_SOMETHING; + case CREDENTIAL_TYPE_PIN: + return PASSWORD_QUALITY_NUMERIC; + case CREDENTIAL_TYPE_PASSWORD: + return PASSWORD_QUALITY_ALPHABETIC; + default: + throw new IllegalStateException("Unknown type: " + credentialType); + } + } + /** * Save a new lockscreen credential. * @@ -684,19 +671,12 @@ public class LockPatternUtils { } newCredential.checkLength(); - final int currentQuality = getKeyguardStoredPasswordQuality(userHandle); - setKeyguardStoredPasswordQuality(newCredential.getQuality(), userHandle); - try { if (!getLockSettings().setLockCredential( - newCredential.getCredential(), newCredential.getType(), - savedCredential.getCredential(), - newCredential.getQuality(), userHandle, allowUntrustedChange)) { - setKeyguardStoredPasswordQuality(currentQuality, userHandle); + newCredential, savedCredential, userHandle, allowUntrustedChange)) { return false; } - } catch (RemoteException | RuntimeException e) { - setKeyguardStoredPasswordQuality(currentQuality, userHandle); + } catch (RemoteException e) { throw new RuntimeException("Unable to save lock password", e); } @@ -904,14 +884,12 @@ public class LockPatternUtils { * @see DevicePolicyManager#getPasswordQuality(android.content.ComponentName) * * @return stored password quality + * @deprecated use {@link #getCredentialTypeForUser(int)} instead */ @UnsupportedAppUsage + @Deprecated public int getKeyguardStoredPasswordQuality(int userHandle) { - return (int) getLong(PASSWORD_TYPE_KEY, PASSWORD_QUALITY_UNSPECIFIED, userHandle); - } - - private void setKeyguardStoredPasswordQuality(int quality, int userHandle) { - setLong(PASSWORD_TYPE_KEY, quality, userHandle); + return credentialTypeToPasswordQuality(getCredentialTypeForUser(userHandle)); } /** @@ -920,17 +898,17 @@ public class LockPatternUtils { * * @param userHandle Managed profile user id * @param enabled True if separate challenge is enabled - * @param managedUserPassword Managed profile previous password. Null when {@code enabled} is + * @param profilePassword Managed profile previous password. Null when {@code enabled} is * true */ public void setSeparateProfileChallengeEnabled(int userHandle, boolean enabled, - LockscreenCredential managedUserPassword) { + LockscreenCredential profilePassword) { if (!isManagedProfile(userHandle)) { return; } try { getLockSettings().setSeparateProfileChallengeEnabled(userHandle, enabled, - managedUserPassword.getCredential()); + profilePassword); reportEnabledTrustAgentsChanged(userHandle); } catch (RemoteException e) { Log.e(TAG, "Couldn't update work profile challenge enabled"); @@ -1098,28 +1076,33 @@ public class LockPatternUtils { } /** + * Returns the credential type of the user, can be one of {@link #CREDENTIAL_TYPE_NONE}, + * {@link #CREDENTIAL_TYPE_PATTERN}, {@link #CREDENTIAL_TYPE_PIN} and + * {@link #CREDENTIAL_TYPE_PASSWORD} + */ + public @CredentialType int getCredentialTypeForUser(int userHandle) { + try { + return getLockSettings().getCredentialType(userHandle); + } catch (RemoteException re) { + Log.e(TAG, "failed to get credential type", re); + return CREDENTIAL_TYPE_NONE; + } + } + + /** * @param userId the user for which to report the value * @return Whether the lock screen is secured. */ @UnsupportedAppUsage public boolean isSecure(int userId) { - int mode = getKeyguardStoredPasswordQuality(userId); - return isLockPatternEnabled(mode, userId) || isLockPasswordEnabled(mode, userId); + int type = getCredentialTypeForUser(userId); + return type != CREDENTIAL_TYPE_NONE; } @UnsupportedAppUsage public boolean isLockPasswordEnabled(int userId) { - return isLockPasswordEnabled(getKeyguardStoredPasswordQuality(userId), userId); - } - - private boolean isLockPasswordEnabled(int mode, int userId) { - final boolean passwordEnabled = mode == PASSWORD_QUALITY_ALPHABETIC - || mode == PASSWORD_QUALITY_NUMERIC - || mode == PASSWORD_QUALITY_NUMERIC_COMPLEX - || mode == PASSWORD_QUALITY_ALPHANUMERIC - || mode == PASSWORD_QUALITY_COMPLEX - || mode == PASSWORD_QUALITY_MANAGED; - return passwordEnabled && savedPasswordExists(userId); + int type = getCredentialTypeForUser(userId); + return type == CREDENTIAL_TYPE_PASSWORD || type == CREDENTIAL_TYPE_PIN; } /** @@ -1127,7 +1110,8 @@ public class LockPatternUtils { */ @UnsupportedAppUsage public boolean isLockPatternEnabled(int userId) { - return isLockPatternEnabled(getKeyguardStoredPasswordQuality(userId), userId); + int type = getCredentialTypeForUser(userId); + return type == CREDENTIAL_TYPE_PATTERN; } @Deprecated @@ -1143,10 +1127,6 @@ public class LockPatternUtils { setBoolean(Settings.Secure.LOCK_PATTERN_ENABLED, true, userId); } - private boolean isLockPatternEnabled(int mode, int userId) { - return mode == PASSWORD_QUALITY_SOMETHING && savedPatternExists(userId); - } - /** * @return Whether the visible pattern is enabled. */ @@ -1543,8 +1523,8 @@ public class LockPatternUtils { * @param userHandle The user who's lock credential to be changed * @return {@code true} if the operation is successful. */ - public boolean setLockCredentialWithToken(LockscreenCredential credential, long tokenHandle, - byte[] token, int userHandle) { + public boolean setLockCredentialWithToken(@NonNull LockscreenCredential credential, + long tokenHandle, byte[] token, int userHandle) { if (!hasSecureLockScreen()) { throw new UnsupportedOperationException( "This operation requires the lock screen feature."); @@ -1552,19 +1532,8 @@ public class LockPatternUtils { credential.checkLength(); LockSettingsInternal localService = getLockSettingsInternal(); - final int currentQuality = getKeyguardStoredPasswordQuality(userHandle); - setKeyguardStoredPasswordQuality(credential.getQuality(), userHandle); - - try { - if (!localService.setLockCredentialWithToken(credential.getCredential(), - credential.getType(), - tokenHandle, token, credential.getQuality(), userHandle)) { - setKeyguardStoredPasswordQuality(currentQuality, userHandle); - return false; - } - } catch (RuntimeException e) { - setKeyguardStoredPasswordQuality(currentQuality, userHandle); - throw new RuntimeException("Unable to save lock credential", e); + if (!localService.setLockCredentialWithToken(credential, tokenHandle, token, userHandle)) { + return false; } onPostPasswordChanged(credential, userHandle); @@ -1765,7 +1734,8 @@ public class LockPatternUtils { } public boolean isSyntheticPasswordEnabled() { - return getLong(SYNTHETIC_PASSWORD_ENABLED_KEY, 0, UserHandle.USER_SYSTEM) != 0; + return getLong(SYNTHETIC_PASSWORD_ENABLED_KEY, SYNTHETIC_PASSWORD_ENABLED_BY_DEFAULT, + UserHandle.USER_SYSTEM) != 0; } /** diff --git a/core/java/com/android/internal/widget/LockSettingsInternal.java b/core/java/com/android/internal/widget/LockSettingsInternal.java index aab2f4b288b2..dd05576338ef 100644 --- a/core/java/com/android/internal/widget/LockSettingsInternal.java +++ b/core/java/com/android/internal/widget/LockSettingsInternal.java @@ -59,8 +59,8 @@ public abstract class LockSettingsInternal { * * @return true if password is set. */ - public abstract boolean setLockCredentialWithToken(byte[] credential, int type, - long tokenHandle, byte[] token, int requestedQuality, int userId); + public abstract boolean setLockCredentialWithToken(LockscreenCredential credential, + long tokenHandle, byte[] token, int userId); public abstract boolean unlockUserWithToken(long tokenHandle, byte[] token, int userId); diff --git a/core/java/com/android/internal/widget/LockscreenCredential.aidl b/core/java/com/android/internal/widget/LockscreenCredential.aidl new file mode 100644 index 000000000000..22501ff5a562 --- /dev/null +++ b/core/java/com/android/internal/widget/LockscreenCredential.aidl @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.widget; + +/** + * A class representing a lockscreen credential. + */ +parcelable LockscreenCredential; + diff --git a/core/java/com/android/internal/widget/LockscreenCredential.java b/core/java/com/android/internal/widget/LockscreenCredential.java index 19e6d97eaa06..f456349a8937 100644 --- a/core/java/com/android/internal/widget/LockscreenCredential.java +++ b/core/java/com/android/internal/widget/LockscreenCredential.java @@ -16,14 +16,11 @@ package com.android.internal.widget; -import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC; -import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; -import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; -import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; - import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD; +import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD_OR_PIN; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN; +import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN; import android.annotation.NonNull; import android.annotation.Nullable; @@ -61,9 +58,6 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { // Stores raw credential bytes, or null if credential has been zeroized. An empty password // is represented as a byte array of length 0. private byte[] mCredential; - // Store the quality of the password, this is used to distinguish between pin - // (PASSWORD_QUALITY_NUMERIC) and password (PASSWORD_QUALITY_ALPHABETIC). - private final int mQuality; /** * Private constructor, use static builder methods instead. @@ -72,15 +66,18 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { * LockscreenCredential will only store the reference internally without copying. This is to * minimize the number of extra copies introduced. */ - private LockscreenCredential(int type, int quality, byte[] credential) { + private LockscreenCredential(int type, byte[] credential) { Preconditions.checkNotNull(credential); if (type == CREDENTIAL_TYPE_NONE) { Preconditions.checkArgument(credential.length == 0); } else { + // Do not allow constructing a CREDENTIAL_TYPE_PASSWORD_OR_PIN object. + Preconditions.checkArgument(type == CREDENTIAL_TYPE_PIN + || type == CREDENTIAL_TYPE_PASSWORD + || type == CREDENTIAL_TYPE_PATTERN); Preconditions.checkArgument(credential.length > 0); } mType = type; - mQuality = quality; mCredential = credential; } @@ -88,8 +85,7 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { * Creates a LockscreenCredential object representing empty password. */ public static LockscreenCredential createNone() { - return new LockscreenCredential(CREDENTIAL_TYPE_NONE, PASSWORD_QUALITY_UNSPECIFIED, - new byte[0]); + return new LockscreenCredential(CREDENTIAL_TYPE_NONE, new byte[0]); } /** @@ -97,7 +93,6 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { */ public static LockscreenCredential createPattern(@NonNull List<LockPatternView.Cell> pattern) { return new LockscreenCredential(CREDENTIAL_TYPE_PATTERN, - PASSWORD_QUALITY_SOMETHING, LockPatternUtils.patternToByteArray(pattern)); } @@ -106,16 +101,25 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { */ public static LockscreenCredential createPassword(@NonNull CharSequence password) { return new LockscreenCredential(CREDENTIAL_TYPE_PASSWORD, - PASSWORD_QUALITY_ALPHABETIC, charSequenceToByteArray(password)); } /** + * Creates a LockscreenCredential object representing a managed password for profile with + * unified challenge. This credentiall will have type {@code CREDENTIAL_TYPE_PASSWORD} for now. + * TODO: consider add a new credential type for this. This can then supersede the + * isLockTiedToParent argument in various places in LSS. + */ + public static LockscreenCredential createManagedPassword(@NonNull byte[] password) { + return new LockscreenCredential(CREDENTIAL_TYPE_PASSWORD, + Arrays.copyOf(password, password.length)); + } + + /** * Creates a LockscreenCredential object representing the given numeric PIN. */ public static LockscreenCredential createPin(@NonNull CharSequence pin) { - return new LockscreenCredential(CREDENTIAL_TYPE_PASSWORD, - PASSWORD_QUALITY_NUMERIC, + return new LockscreenCredential(CREDENTIAL_TYPE_PIN, charSequenceToByteArray(pin)); } @@ -143,27 +147,13 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { } } - /** - * Create a LockscreenCredential object based on raw credential and type - * TODO: Remove once LSS.setUserPasswordMetrics accepts a LockscreenCredential - */ - public static LockscreenCredential createRaw(int type, byte[] credential) { - if (type == CREDENTIAL_TYPE_NONE) { - return createNone(); - } else { - return new LockscreenCredential(type, PASSWORD_QUALITY_UNSPECIFIED, credential); - } - } - private void ensureNotZeroized() { Preconditions.checkState(mCredential != null, "Credential is already zeroized"); } /** * Returns the type of this credential. Can be one of {@link #CREDENTIAL_TYPE_NONE}, - * {@link #CREDENTIAL_TYPE_PATTERN} or {@link #CREDENTIAL_TYPE_PASSWORD}. - * - * TODO: Remove once credential type is internal. Callers should use {@link #isNone}, - * {@link #isPattern} and {@link #isPassword} instead. + * {@link #CREDENTIAL_TYPE_PATTERN}, {@link #CREDENTIAL_TYPE_PIN} or + * {@link #CREDENTIAL_TYPE_PASSWORD}. */ public int getType() { ensureNotZeroized(); @@ -171,14 +161,6 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { } /** - * Returns the quality type of the credential - */ - public int getQuality() { - ensureNotZeroized(); - return mQuality; - } - - /** * Returns the credential bytes. This is a direct reference of the internal field so * callers should not modify it. * @@ -200,9 +182,11 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { if (isPattern()) { return StorageManager.CRYPT_TYPE_PATTERN; } + if (isPin()) { + return StorageManager.CRYPT_TYPE_PIN; + } if (isPassword()) { - return mQuality == PASSWORD_QUALITY_NUMERIC - ? StorageManager.CRYPT_TYPE_PIN : StorageManager.CRYPT_TYPE_PASSWORD; + return StorageManager.CRYPT_TYPE_PASSWORD; } throw new IllegalStateException("Unhandled credential type"); } @@ -219,7 +203,13 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { return mType == CREDENTIAL_TYPE_PATTERN; } - /** Returns whether this is a password credential */ + /** Returns whether this is a numeric pin credential */ + public boolean isPin() { + ensureNotZeroized(); + return mType == CREDENTIAL_TYPE_PIN; + } + + /** Returns whether this is an alphabetic password credential */ public boolean isPassword() { ensureNotZeroized(); return mType == CREDENTIAL_TYPE_PASSWORD; @@ -233,7 +223,7 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { /** Create a copy of the credential */ public LockscreenCredential duplicate() { - return new LockscreenCredential(mType, mQuality, + return new LockscreenCredential(mType, mCredential != null ? Arrays.copyOf(mCredential, mCredential.length) : null); } @@ -263,7 +253,7 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { } return; } - if (isPassword()) { + if (isPassword() || isPin()) { if (size() < LockPatternUtils.MIN_LOCK_PASSWORD_SIZE) { throw new IllegalArgumentException("password must not be null and at least " + "of length " + LockPatternUtils.MIN_LOCK_PASSWORD_SIZE); @@ -272,10 +262,22 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { } } + /** + * Check if this credential's type matches one that's retrieved from disk. The nuance here is + * that the framework used to not distinguish between PIN and password, so this method will + * allow a PIN/Password LockscreenCredential to match against the legacy + * {@link #CREDENTIAL_TYPE_PASSWORD_OR_PIN} stored on disk. + */ + public boolean checkAgainstStoredType(int storedCredentialType) { + if (storedCredentialType == CREDENTIAL_TYPE_PASSWORD_OR_PIN) { + return getType() == CREDENTIAL_TYPE_PASSWORD || getType() == CREDENTIAL_TYPE_PIN; + } + return getType() == storedCredentialType; + } + @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mType); - dest.writeInt(mQuality); dest.writeByteArray(mCredential); } @@ -284,8 +286,7 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { @Override public LockscreenCredential createFromParcel(Parcel source) { - return new LockscreenCredential(source.readInt(), source.readInt(), - source.createByteArray()); + return new LockscreenCredential(source.readInt(), source.createByteArray()); } @Override @@ -307,7 +308,7 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { @Override public int hashCode() { // Effective Java — Item 9 - return ((17 + mType) * 31 + mQuality) * 31 + mCredential.hashCode(); + return (17 + mType) * 31 + mCredential.hashCode(); } @Override @@ -315,8 +316,7 @@ public class LockscreenCredential implements Parcelable, AutoCloseable { if (o == this) return true; if (!(o instanceof LockscreenCredential)) return false; final LockscreenCredential other = (LockscreenCredential) o; - return mType == other.mType && mQuality == other.mQuality - && Arrays.equals(mCredential, other.mCredential); + return mType == other.mType && Arrays.equals(mCredential, other.mCredential); } /** diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java index ea0389f49a45..b7523963ec19 100644 --- a/core/java/com/android/server/SystemConfig.java +++ b/core/java/com/android/server/SystemConfig.java @@ -26,6 +26,7 @@ import android.os.Build; import android.os.Environment; import android.os.Process; import android.os.SystemProperties; +import android.os.Trace; import android.os.storage.StorageManager; import android.permission.PermissionManager.SplitPermissionInfo; import android.text.TextUtils; @@ -33,6 +34,7 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; import android.util.SparseArray; +import android.util.TimingsTraceLog; import android.util.Xml; import com.android.internal.annotations.VisibleForTesting; @@ -411,7 +413,13 @@ public class SystemConfig { } SystemConfig() { - readAllPermissions(); + TimingsTraceLog log = new TimingsTraceLog(TAG, Trace.TRACE_TAG_SYSTEM_SERVER); + log.traceBegin("readAllPermissions"); + try { + readAllPermissions(); + } finally { + log.traceEnd(); + } } private void readAllPermissions() { diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto index a568c13d7dde..f7d4b3f47edf 100644 --- a/core/proto/android/providers/settings/global.proto +++ b/core/proto/android/providers/settings/global.proto @@ -853,6 +853,7 @@ message GlobalSettingsProto { optional SettingProto low_battery_sounds_enabled = 12 [ (android.privacy).dest = DEST_AUTOMATIC ]; optional SettingProto trusted = 13; optional SettingProto unlock = 14; + optional SettingProto wireless_charging_started = 15; } optional Sounds sounds = 110; diff --git a/core/proto/android/server/notificationhistory.proto b/core/proto/android/server/notificationhistory.proto new file mode 100644 index 000000000000..148bd7e4b663 --- /dev/null +++ b/core/proto/android/server/notificationhistory.proto @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; +package com.android.server.notification; + +import "frameworks/base/core/proto/android/server/enums.proto"; + +option java_multiple_files = true; + +// On disk data store for historical notifications +message NotificationHistoryProto { + message StringPool { + optional int32 size = 1; + repeated string strings = 2; + } + + message Notification { + // The package that posted the notification + optional string package = 1; + // package_index contains the index + 1 of the package name in the string pool + optional int32 package_index = 2; + + // The name of the NotificationChannel this notification was posted to + optional string channel_name = 3; + // channel_name_index contains the index + 1 of the channel name in the string pool + optional int32 channel_name_index = 4; + + // The id of the NotificationChannel this notification was posted to + optional string channel_id = 5; + // channel_id_index contains the index + 1 of the channel id in the string pool + optional int32 channel_id_index = 6; + + // The uid of the package that posted the notification + optional int32 uid = 7; + // The user id of the package that posted the notification + optional int32 user_id = 8; + // The time at which the notification was posted + optional int64 posted_time_ms = 9; + // The title of the notification + optional string title = 10; + // The text of the notification + optional string text = 11; + // The small icon of the notification + optional Icon icon = 12; + + // Matches the constants of android.graphics.drawable.Icon + enum ImageTypeEnum { + TYPE_UNKNOWN = 0; + TYPE_BITMAP = 1; + TYPE_RESOURCE = 2; + TYPE_DATA = 3; + TYPE_URI = 4; + TYPE_ADAPTIVE_BITMAP = 5; + } + + message Icon { + optional ImageTypeEnum image_type = 1; + optional string image_bitmap_filename = 2; + optional int32 image_resource_id = 3; + optional bytes image_data = 4; + optional string image_uri = 5; + } + } + + // The time the last entry was written + optional int64 end_time_ms = 1; + // Pool of strings to save space + optional StringPool stringpool = 2; + // Versioning fields + optional int32 major_version = 3; + optional int32 minor_version = 4; + + // List of historical notifications + repeated Notification notification = 5; +} diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 75cdb42ddccc..eaf93eb9e216 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -4357,4 +4357,8 @@ create additional screen real estate outside beyond the keyboard. Note that the user needs to have a confirmed way to dismiss the keyboard when desired. --> <bool name="config_automotiveHideNavBarForKeyboard">false</bool> + + <!-- Whether or not to show the built-in charging animation when the device begins charging + wirelessly. --> + <bool name="config_showBuiltinWirelessChargingAnim">true</bool> </resources> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index a01bbe38f296..9791241a52af 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -41,6 +41,8 @@ <dimen name="quick_qs_offset_height">48dp</dimen> <!-- Total height of QQS (quick_qs_offset_height + 128) --> <dimen name="quick_qs_total_height">176dp</dimen> + <!-- Total height of QQS with two rows to fit media player (quick_qs_offset_height + 176) --> + <dimen name="quick_qs_total_height_with_media">224dp</dimen> <!-- Height of the bottom navigation / system bar. --> <dimen name="navigation_bar_height">48dp</dimen> <!-- Height of the bottom navigation bar in portrait; often the same as @dimen/navigation_bar_height --> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index bf5f706b1234..d88178d481e9 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -5205,6 +5205,8 @@ <string name="autofill_save_no">No thanks</string> <!-- Label for the autofill update button [CHAR LIMIT=NONE] --> <string name="autofill_update_yes">Update</string> + <!-- Label for the autofill continue button [CHAR LIMIT=NONE] --> + <string name="autofill_continue_yes">Continue</string> <!-- Label for the type of data being saved for autofill when it represent user credentials with a password [CHAR LIMIT=NONE] --> <string name="autofill_save_type_password">password</string> @@ -5212,6 +5214,12 @@ <string name="autofill_save_type_address">address</string> <!-- Label for the type of data being saved for autofill when it represents a credit card [CHAR LIMIT=NONE] --> <string name="autofill_save_type_credit_card">credit card</string> + <!-- Label for the type of data being saved for autofill when it represents a debit card [CHAR LIMIT=NONE] --> + <string name="autofill_save_type_debit_card">debit card</string> + <!-- Label for the type of data being saved for autofill when it represents a payment card [CHAR LIMIT=NONE] --> + <string name="autofill_save_type_payment_card">payment card</string> + <!-- Label for the type of data being saved for autofill when it represents a card that does not a specified type or cannot identify what the type is for [CHAR LIMIT=NONE] --> + <string name="autofill_save_type_generic_card">card</string> <!-- Label for the type of data being saved for autofill when it represents an username [CHAR LIMIT=NONE] --> <string name="autofill_save_type_username">username</string> <!-- Label for the type of data being saved for autofill when it represents an email address [CHAR LIMIT=NONE] --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 6bb471978645..e2f57fda5bc5 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1774,6 +1774,7 @@ <java-symbol type="dimen" name="display_cutout_touchable_region_size" /> <java-symbol type="dimen" name="quick_qs_offset_height" /> <java-symbol type="dimen" name="quick_qs_total_height" /> + <java-symbol type="dimen" name="quick_qs_total_height_with_media" /> <java-symbol type="drawable" name="ic_jog_dial_sound_off" /> <java-symbol type="drawable" name="ic_jog_dial_sound_on" /> <java-symbol type="drawable" name="ic_jog_dial_unlock" /> @@ -3355,6 +3356,7 @@ <java-symbol type="string" name="autofill_update_title_with_2types" /> <java-symbol type="string" name="autofill_update_title_with_3types" /> <java-symbol type="string" name="autofill_update_yes" /> + <java-symbol type="string" name="autofill_continue_yes" /> <java-symbol type="string" name="autofill_save_accessibility_title " /> <java-symbol type="string" name="autofill_save_title" /> <java-symbol type="string" name="autofill_save_title_with_type" /> @@ -3365,6 +3367,9 @@ <java-symbol type="string" name="autofill_save_type_password" /> <java-symbol type="string" name="autofill_save_type_address" /> <java-symbol type="string" name="autofill_save_type_credit_card" /> + <java-symbol type="string" name="autofill_save_type_debit_card" /> + <java-symbol type="string" name="autofill_save_type_payment_card" /> + <java-symbol type="string" name="autofill_save_type_generic_card" /> <java-symbol type="string" name="autofill_save_type_username" /> <java-symbol type="string" name="autofill_save_type_email_address" /> <java-symbol type="drawable" name="autofill_dataset_picker_background" /> @@ -3866,4 +3871,6 @@ <java-symbol type="bool" name="config_automotiveHideNavBarForKeyboard" /> + <java-symbol type="bool" name="config_showBuiltinWirelessChargingAnim" /> + </resources> diff --git a/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java b/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java index 5eec91c8840b..05bab1c185de 100644 --- a/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java +++ b/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java @@ -31,10 +31,23 @@ public class LockscreenCredentialTest extends AndroidTestCase { assertEquals(0, empty.size()); assertNotNull(empty.getCredential()); + assertFalse(empty.isPin()); assertFalse(empty.isPassword()); assertFalse(empty.isPattern()); } + public void testPinCredential() { + LockscreenCredential pin = LockscreenCredential.createPin("3456"); + + assertTrue(pin.isPin()); + assertEquals(4, pin.size()); + assertTrue(Arrays.equals("3456".getBytes(), pin.getCredential())); + + assertFalse(pin.isNone()); + assertFalse(pin.isPassword()); + assertFalse(pin.isPattern()); + } + public void testPasswordCredential() { LockscreenCredential password = LockscreenCredential.createPassword("password"); @@ -43,6 +56,7 @@ public class LockscreenCredentialTest extends AndroidTestCase { assertTrue(Arrays.equals("password".getBytes(), password.getCredential())); assertFalse(password.isNone()); + assertFalse(password.isPin()); assertFalse(password.isPattern()); } @@ -60,6 +74,7 @@ public class LockscreenCredentialTest extends AndroidTestCase { assertTrue(Arrays.equals("12369".getBytes(), pattern.getCredential())); assertFalse(pattern.isNone()); + assertFalse(pattern.isPin()); assertFalse(pattern.isPassword()); } @@ -72,6 +87,15 @@ public class LockscreenCredentialTest extends AndroidTestCase { LockscreenCredential.createPasswordOrNone("abcd")); } + public void testPinOrNoneCredential() { + assertEquals(LockscreenCredential.createNone(), + LockscreenCredential.createPinOrNone(null)); + assertEquals(LockscreenCredential.createNone(), + LockscreenCredential.createPinOrNone("")); + assertEquals(LockscreenCredential.createPin("1357"), + LockscreenCredential.createPinOrNone("1357")); + } + public void testSanitize() { LockscreenCredential password = LockscreenCredential.createPassword("password"); password.zeroize(); @@ -80,12 +104,15 @@ public class LockscreenCredentialTest extends AndroidTestCase { password.isNone(); fail("Sanitized credential still accessible"); } catch (IllegalStateException expected) { } - try { password.isPattern(); fail("Sanitized credential still accessible"); } catch (IllegalStateException expected) { } try { + password.isPin(); + fail("Sanitized credential still accessible"); + } catch (IllegalStateException expected) { } + try { password.isPassword(); fail("Sanitized credential still accessible"); } catch (IllegalStateException expected) { } diff --git a/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java index 9913531cdf13..50e8474e8d52 100644 --- a/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java +++ b/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java @@ -62,7 +62,9 @@ public class LockPatternUtilsTest { Settings.Global.putInt(cr, Settings.Global.DEVICE_DEMO_MODE, deviceDemoMode); final ILockSettings ils = Mockito.mock(ILockSettings.class); - when(ils.havePassword(DEMO_USER_ID)).thenReturn(isSecure); + when(ils.getCredentialType(DEMO_USER_ID)).thenReturn( + isSecure ? LockPatternUtils.CREDENTIAL_TYPE_PASSWORD + : LockPatternUtils.CREDENTIAL_TYPE_NONE); when(ils.getLong("lockscreen.password_type", PASSWORD_QUALITY_UNSPECIFIED, DEMO_USER_ID)) .thenReturn((long) PASSWORD_QUALITY_MANAGED); // TODO(b/63758238): stop spying the class under test diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 8a76d6b3fc7a..6e3e43af8c6f 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -242,7 +242,8 @@ private: nsecs_t queueDuration; }; - RingBuffer<SwapHistory, 3> mSwapHistory; + // Need at least 4 because we do quad buffer. Add a 5th for good measure. + RingBuffer<SwapHistory, 5> mSwapHistory; int64_t mFrameNumber = -1; // last vsync for a dropped frame due to stuffed queue diff --git a/native/android/aidl/com/android/internal/compat/IPlatformCompatNative.aidl b/native/android/aidl/com/android/internal/compat/IPlatformCompatNative.aidl index c022388e0aa8..347e4e8ebe4b 100644 --- a/native/android/aidl/com/android/internal/compat/IPlatformCompatNative.aidl +++ b/native/android/aidl/com/android/internal/compat/IPlatformCompatNative.aidl @@ -33,9 +33,10 @@ interface IPlatformCompatNative * you do not need to call this API directly. The change will be reported for you. * * @param changeId The ID of the compatibility change taking effect. + * @param userId The ID of the user that the operation is done for. * @param packageName The package name of the app in question. */ - void reportChangeByPackageName(long changeId, @utf8InCpp String packageName); + void reportChangeByPackageName(long changeId, @utf8InCpp String packageName, int userId); /** * Reports that a compatibility change is affecting an app process now. @@ -64,9 +65,10 @@ interface IPlatformCompatNative * * @param changeId The ID of the compatibility change in question. * @param packageName The package name of the app in question. + * @param userId The ID of the user that the operation is done for. * @return {@code true} if the change is enabled for the current app. */ - boolean isChangeEnabledByPackageName(long changeId, @utf8InCpp String packageName); + boolean isChangeEnabledByPackageName(long changeId, @utf8InCpp String packageName, int userId); /** * Query if a given compatibility change is enabled for an app process. This method should diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp index 53c01226ca1a..b34b31ac8439 100644 --- a/native/android/surface_control.cpp +++ b/native/android/surface_control.cpp @@ -294,7 +294,7 @@ void ASurfaceTransaction_setOnComplete(ASurfaceTransaction* aSurfaceTransaction, auto& aSurfaceControlStats = aSurfaceTransactionStats.aSurfaceControlStats; - for (const auto& [surfaceControl, acquireTime, previousReleaseFence] : surfaceControlStats) { + for (const auto& [surfaceControl, acquireTime, previousReleaseFence, transformHint] : surfaceControlStats) { ASurfaceControl* aSurfaceControl = reinterpret_cast<ASurfaceControl*>(surfaceControl.get()); aSurfaceControlStats[aSurfaceControl].acquireTime = acquireTime; aSurfaceControlStats[aSurfaceControl].previousReleaseFence = previousReleaseFence; diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java index 011d5ea9d86b..be4b8897d00b 100644 --- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java +++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIFactory.java @@ -20,6 +20,7 @@ import android.content.Context; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.ViewMediatorCallback; +import com.android.systemui.dagger.SystemUIRootComponent; import com.android.systemui.navigationbar.car.CarFacetButtonController; import com.android.systemui.statusbar.car.CarStatusBarKeyguardViewManager; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java index b1067f8c3df5..0f44e0815a86 100644 --- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java +++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java @@ -23,6 +23,7 @@ import android.content.Context; import com.android.systemui.car.CarNotificationEntryManager; import com.android.systemui.car.CarNotificationInterruptionStateProvider; +import com.android.systemui.dagger.SystemUIRootComponent; import com.android.systemui.dock.DockManager; import com.android.systemui.dock.DockManagerImpl; import com.android.systemui.power.EnhancedEstimates; diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java index 325c98890b11..c2847c88785b 100644 --- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java +++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java @@ -16,6 +16,12 @@ package com.android.systemui; +import com.android.systemui.dagger.DependencyBinder; +import com.android.systemui.dagger.DependencyProvider; +import com.android.systemui.dagger.SystemServicesModule; +import com.android.systemui.dagger.SystemUIModule; +import com.android.systemui.dagger.SystemUIRootComponent; + import javax.inject.Singleton; import dagger.Component; @@ -26,6 +32,7 @@ import dagger.Component; DependencyProvider.class, DependencyBinder.class, SystemUIFactory.ContextHolder.class, + SystemServicesModule.class, SystemUIModule.class, CarSystemUIModule.class, CarSystemUIBinder.class diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java index 5bb35309edd2..6fba1d516c73 100644 --- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/CarNavigationBar.java @@ -16,8 +16,6 @@ package com.android.systemui.navigationbar.car; -import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; - import android.content.Context; import android.graphics.PixelFormat; import android.inputmethodservice.InputMethodService; @@ -35,6 +33,7 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.RegisterStatusBarResult; import com.android.systemui.R; import com.android.systemui.SystemUI; +import com.android.systemui.dagger.qualifiers.MainHandler; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NavigationBarController; @@ -47,7 +46,6 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import javax.inject.Inject; -import javax.inject.Named; import dagger.Lazy; @@ -96,7 +94,7 @@ public class CarNavigationBar extends SystemUI implements CommandQueue.Callbacks WindowManager windowManager, DeviceProvisionedController deviceProvisionedController, Lazy<FacetButtonTaskStackListener> facetButtonTaskStackListener, - @Named(MAIN_HANDLER_NAME) Handler mainHandler, + @MainHandler Handler mainHandler, Lazy<KeyguardStateController> keyguardStateController, Lazy<CarFacetButtonController> facetButtonController, Lazy<NavigationBarController> navigationBarController, diff --git a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/NavigationBarViewFactory.java b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/NavigationBarViewFactory.java index 128721eede2b..519b33a2f53e 100644 --- a/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/NavigationBarViewFactory.java +++ b/packages/CarSystemUI/src/com/android/systemui/navigationbar/car/NavigationBarViewFactory.java @@ -108,7 +108,7 @@ public class NavigationBarViewFactory { private ViewGroup getWindowCached(Type type) { if (mCachedContainerMap.containsKey(type)) { - return mCachedViewMap.get(type); + return mCachedContainerMap.get(type); } ViewGroup window = (ViewGroup) View.inflate(mContext, diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index 3424eeaeb854..cd81a5cb5c4f 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -161,9 +161,18 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt private Drawable mNotificationPanelBackground; private ViewGroup mTopNavigationBarContainer; + private ViewGroup mNavigationBarWindow; + private ViewGroup mLeftNavigationBarWindow; + private ViewGroup mRightNavigationBarWindow; private CarNavigationBarView mTopNavigationBarView; + private CarNavigationBarView mNavigationBarView; + private CarNavigationBarView mLeftNavigationBarView; + private CarNavigationBarView mRightNavigationBarView; private final Object mQueueLock = new Object(); + private boolean mShowLeft; + private boolean mShowRight; + private boolean mShowBottom; private final NavigationBarViewFactory mNavigationBarViewFactory; private CarFacetButtonController mCarFacetButtonController; private DeviceProvisionedController mDeviceProvisionedController; @@ -429,6 +438,16 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt mHvacController.removeAllComponents(); mCarFacetButtonController.removeAll(); + if (mNavigationBarWindow != null) { + mNavigationBarView = null; + } + if (mLeftNavigationBarWindow != null) { + mLeftNavigationBarView = null; + } + if (mRightNavigationBarWindow != null) { + mRightNavigationBarView = null; + } + buildNavBarContent(); // CarFacetButtonController was reset therefore we need to re-add the status bar elements // to the controller. @@ -451,28 +470,28 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt * the full screen user selector is shown. */ void setNavBarVisibility(@View.Visibility int visibility) { - if (mNavigationBarViewFactory.getBottomWindow() != null) { - mNavigationBarViewFactory.getBottomWindow().setVisibility(visibility); + if (mNavigationBarWindow != null) { + mNavigationBarWindow.setVisibility(visibility); } - if (mNavigationBarViewFactory.getLeftWindow() != null) { - mNavigationBarViewFactory.getLeftWindow().setVisibility(visibility); + if (mLeftNavigationBarWindow != null) { + mLeftNavigationBarWindow.setVisibility(visibility); } - if (mNavigationBarViewFactory.getRightWindow() != null) { - mNavigationBarViewFactory.getRightWindow().setVisibility(visibility); + if (mRightNavigationBarWindow != null) { + mRightNavigationBarWindow.setVisibility(visibility); } } @Override public boolean hideKeyguard() { boolean result = super.hideKeyguard(); - if (mNavigationBarViewFactory.getBottomBar(mDeviceIsSetUpForUser) != null) { - mNavigationBarViewFactory.getBottomBar(mDeviceIsSetUpForUser).hideKeyguardButtons(); + if (mNavigationBarView != null) { + mNavigationBarView.hideKeyguardButtons(); } - if (mNavigationBarViewFactory.getLeftBar(mDeviceIsSetUpForUser) != null) { - mNavigationBarViewFactory.getLeftBar(mDeviceIsSetUpForUser).hideKeyguardButtons(); + if (mLeftNavigationBarView != null) { + mLeftNavigationBarView.hideKeyguardButtons(); } - if (mNavigationBarViewFactory.getRightBar(mDeviceIsSetUpForUser) != null) { - mNavigationBarViewFactory.getRightBar(mDeviceIsSetUpForUser).hideKeyguardButtons(); + if (mRightNavigationBarView != null) { + mRightNavigationBarView.hideKeyguardButtons(); } return result; } @@ -487,14 +506,14 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt * Switch to the keyguard applicable content contained in the nav bars */ private void updateNavBarForKeyguardContent() { - if (mNavigationBarViewFactory.getBottomBar(mDeviceIsSetUpForUser) != null) { - mNavigationBarViewFactory.getBottomBar(mDeviceIsSetUpForUser).showKeyguardButtons(); + if (mNavigationBarView != null) { + mNavigationBarView.showKeyguardButtons(); } - if (mNavigationBarViewFactory.getLeftBar(mDeviceIsSetUpForUser) != null) { - mNavigationBarViewFactory.getLeftBar(mDeviceIsSetUpForUser).showKeyguardButtons(); + if (mLeftNavigationBarView != null) { + mLeftNavigationBarView.showKeyguardButtons(); } - if (mNavigationBarViewFactory.getRightBar(mDeviceIsSetUpForUser) != null) { - mNavigationBarViewFactory.getRightBar(mDeviceIsSetUpForUser).showKeyguardButtons(); + if (mRightNavigationBarView != null) { + mRightNavigationBarView.showKeyguardButtons(); } } @@ -599,26 +618,19 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt mNotificationDataManager = new NotificationDataManager(); mNotificationDataManager.setOnUnseenCountUpdateListener( () -> { - if (mNavigationBarViewFactory.getBottomBar(mDeviceIsSetUpForUser) != null - && mNotificationDataManager != null) { + if (mNavigationBarView != null && mNotificationDataManager != null) { Boolean hasUnseen = mNotificationDataManager.getUnseenNotificationCount() > 0; - if (mNavigationBarViewFactory.getBottomBar(mDeviceIsSetUpForUser) != null) { - mNavigationBarViewFactory.getBottomBar( - mDeviceIsSetUpForUser).toggleNotificationUnseenIndicator( - hasUnseen); + if (mNavigationBarView != null) { + mNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen); } - if (mNavigationBarViewFactory.getLeftBar(mDeviceIsSetUpForUser) != null) { - mNavigationBarViewFactory.getLeftBar( - mDeviceIsSetUpForUser).toggleNotificationUnseenIndicator( - hasUnseen); + if (mLeftNavigationBarView != null) { + mLeftNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen); } - if (mNavigationBarViewFactory.getRightBar(mDeviceIsSetUpForUser) != null) { - mNavigationBarViewFactory.getRightBar( - mDeviceIsSetUpForUser).toggleNotificationUnseenIndicator( - hasUnseen); + if (mRightNavigationBarView != null) { + mRightNavigationBarView.toggleNotificationUnseenIndicator(hasUnseen); } } }); @@ -873,6 +885,10 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt @Override protected void createNavigationBar(@Nullable RegisterStatusBarResult result) { + mShowBottom = mContext.getResources().getBoolean(R.bool.config_enableBottomNavigationBar); + mShowLeft = mContext.getResources().getBoolean(R.bool.config_enableLeftNavigationBar); + mShowRight = mContext.getResources().getBoolean(R.bool.config_enableRightNavigationBar); + buildNavBarWindows(); buildNavBarContent(); } @@ -880,22 +896,40 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt private void buildNavBarContent() { buildTopBar(); - CarNavigationBarView bottom = mNavigationBarViewFactory.getBottomBar(mDeviceIsSetUpForUser); - bottom.setStatusBar(this); - bottom.setStatusBarWindowTouchListener(mNavBarNotificationTouchListener); + if (mShowBottom) { + mNavigationBarView = mNavigationBarViewFactory.getBottomBar(mDeviceIsSetUpForUser); + mNavigationBarView.setStatusBar(this); + mNavigationBarView.setStatusBarWindowTouchListener(mNavBarNotificationTouchListener); + } - CarNavigationBarView left = mNavigationBarViewFactory.getLeftBar(mDeviceIsSetUpForUser); - left.setStatusBar(this); - left.setStatusBarWindowTouchListener(mNavBarNotificationTouchListener); + if (mShowLeft) { + mLeftNavigationBarView = mNavigationBarViewFactory.getLeftBar(mDeviceIsSetUpForUser); + mLeftNavigationBarView.setStatusBar(this); + mLeftNavigationBarView.setStatusBarWindowTouchListener( + mNavBarNotificationTouchListener); + } - CarNavigationBarView right = mNavigationBarViewFactory.getLeftBar(mDeviceIsSetUpForUser); - right.setStatusBar(this); - right.setStatusBarWindowTouchListener(mNavBarNotificationTouchListener); + if (mShowRight) { + mRightNavigationBarView = mNavigationBarViewFactory.getLeftBar(mDeviceIsSetUpForUser); + mRightNavigationBarView.setStatusBar(this); + mRightNavigationBarView.setStatusBarWindowTouchListener( + mNavBarNotificationTouchListener); + } } private void buildNavBarWindows() { mTopNavigationBarContainer = mStatusBarWindow .findViewById(R.id.car_top_navigation_bar_container); + + if (mShowBottom) { + mNavigationBarWindow = mNavigationBarViewFactory.getBottomWindow(); + } + if (mShowLeft) { + mLeftNavigationBarWindow = mNavigationBarViewFactory.getLeftWindow(); + } + if (mShowRight) { + mRightNavigationBarWindow = mNavigationBarViewFactory.getRightWindow(); + } } private void buildTopBar() { diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java index 4e6c005457c0..658a0b59622c 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiUtils.java @@ -93,6 +93,7 @@ public class WifiUtils { if (bssid != null) { visibility.append(" ").append(bssid); } + visibility.append(" technology = ").append(info.getWifiTechnology()); visibility.append(" rssi=").append(info.getRssi()); visibility.append(" "); visibility.append(" score=").append(info.score); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index 17c621e6fbef..44de09b8d21d 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -1497,21 +1497,7 @@ class DatabaseHelper extends SQLiteOpenHelper { } if (upgradeVersion == 94) { - // Add wireless charging started sound setting - if (mUserHandle == UserHandle.USER_SYSTEM) { - db.beginTransaction(); - SQLiteStatement stmt = null; - try { - stmt = db.compileStatement("INSERT OR REPLACE INTO global(name,value)" - + " VALUES(?,?);"); - loadStringSetting(stmt, Settings.Global.CHARGING_STARTED_SOUND, - R.string.def_wireless_charging_started_sound); - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - if (stmt != null) stmt.close(); - } - } + // charging sound moved to SettingsProvider version 184 upgradeVersion = 95; } @@ -2562,8 +2548,6 @@ class DatabaseHelper extends SQLiteOpenHelper { R.string.def_car_dock_sound); loadStringSetting(stmt, Settings.Global.CAR_UNDOCK_SOUND, R.string.def_car_undock_sound); - loadStringSetting(stmt, Settings.Global.CHARGING_STARTED_SOUND, - R.string.def_wireless_charging_started_sound); loadIntegerSetting(stmt, Settings.Global.DOCK_AUDIO_MEDIA_ENABLED, R.integer.def_dock_audio_media_enabled); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 6bd26bf03a55..e24d387c5649 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -1347,6 +1347,9 @@ class SettingsProtoDumpUtil { dumpSetting(s, p, Settings.Global.CHARGING_STARTED_SOUND, GlobalSettingsProto.Sounds.CHARGING_STARTED); + dumpSetting(s, p, + Settings.Global.WIRELESS_CHARGING_STARTED_SOUND, + GlobalSettingsProto.Sounds.WIRELESS_CHARGING_STARTED); p.end(soundsToken); final long soundTriggerToken = p.start(GlobalSettingsProto.SOUND_TRIGGER); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 06398640acb4..80faf4766e36 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -3206,7 +3206,7 @@ public class SettingsProvider extends ContentProvider { } private final class UpgradeController { - private static final int SETTINGS_VERSION = 183; + private static final int SETTINGS_VERSION = 184; private final int mUserId; @@ -3845,23 +3845,7 @@ public class SettingsProvider extends ContentProvider { } if (currentVersion == 155) { - // Version 156: Set the default value for CHARGING_STARTED_SOUND. - final SettingsState globalSettings = getGlobalSettingsLocked(); - final String oldValue = globalSettings.getSettingLocked( - Global.CHARGING_STARTED_SOUND).getValue(); - final String oldDefault = getContext().getResources().getString( - R.string.def_wireless_charging_started_sound); - if (TextUtils.equals(null, oldValue) - || TextUtils.equals(oldValue, oldDefault)) { - final String defaultValue = getContext().getResources().getString( - R.string.def_charging_started_sound); - if (!TextUtils.isEmpty(defaultValue)) { - globalSettings.insertSettingLocked( - Settings.Global.CHARGING_STARTED_SOUND, defaultValue, - null, true, SettingsState.SYSTEM_PACKAGE_NAME); - } - - } + // Version 156: migrated to version 184 currentVersion = 156; } @@ -4416,6 +4400,48 @@ public class SettingsProvider extends ContentProvider { currentVersion = 183; } + if (currentVersion == 183) { + // Version 184: Set default values for WIRELESS_CHARGING_STARTED_SOUND + // and CHARGING_STARTED_SOUND + final SettingsState globalSettings = getGlobalSettingsLocked(); + + final String oldValueWireless = globalSettings.getSettingLocked( + Global.WIRELESS_CHARGING_STARTED_SOUND).getValue(); + final String oldValueWired = globalSettings.getSettingLocked( + Global.CHARGING_STARTED_SOUND).getValue(); + + final String defaultValueWireless = getContext().getResources().getString( + R.string.def_wireless_charging_started_sound); + final String defaultValueWired = getContext().getResources().getString( + R.string.def_charging_started_sound); + + // wireless charging sound + if (oldValueWireless == null + || TextUtils.equals(oldValueWireless, defaultValueWired)) { + if (!TextUtils.isEmpty(defaultValueWireless)) { + globalSettings.insertSettingLocked( + Global.WIRELESS_CHARGING_STARTED_SOUND, defaultValueWireless, + null /* tag */, true /* makeDefault */, + SettingsState.SYSTEM_PACKAGE_NAME); + } else if (!TextUtils.isEmpty(defaultValueWired)) { + // if the wireless sound is empty, use the wired charging sound + globalSettings.insertSettingLocked( + Global.WIRELESS_CHARGING_STARTED_SOUND, defaultValueWired, + null /* tag */, true /* makeDefault */, + SettingsState.SYSTEM_PACKAGE_NAME); + } + } + + // wired charging sound + if (oldValueWired == null && !TextUtils.isEmpty(defaultValueWired)) { + globalSettings.insertSettingLocked( + Global.CHARGING_STARTED_SOUND, defaultValueWired, + null /* tag */, true /* makeDefault */, + SettingsState.SYSTEM_PACKAGE_NAME); + } + currentVersion = 184; + } + // vXXX: Add new settings above this point. if (currentVersion != newVersion) { diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 62827bc3a98e..179ba8ad086a 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -559,6 +559,7 @@ public class SettingsBackupTest { Settings.Global.WIFI_WATCHDOG_ON, Settings.Global.WIMAX_NETWORKS_AVAILABLE_NOTIFICATION_ON, Settings.Global.CHARGING_STARTED_SOUND, + Settings.Global.WIRELESS_CHARGING_STARTED_SOUND, Settings.Global.WINDOW_ANIMATION_SCALE, Settings.Global.WTF_IS_FATAL, Settings.Global.ZEN_MODE, diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index e11c063f57fb..54e291f904eb 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -247,23 +247,6 @@ android:exported="false" /> <receiver - android:name=".BugreportReceiver" - android:permission="android.permission.DUMP"> - <intent-filter> - <action android:name="com.android.internal.intent.action.BUGREPORT_STARTED" /> - <action android:name="com.android.internal.intent.action.BUGREPORT_FINISHED" /> - </intent-filter> - </receiver> - - <receiver - android:name=".RemoteBugreportReceiver" - android:permission="android.permission.DUMP"> - <intent-filter> - <action android:name="com.android.internal.intent.action.REMOTE_BUGREPORT_FINISHED" /> - </intent-filter> - </receiver> - - <receiver android:name=".BugreportRequestedReceiver" android:permission="android.permission.TRIGGER_SHELL_BUGREPORT"> <intent-filter> diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java index 665bde341515..1b35770ccbd7 100644 --- a/packages/Shell/src/com/android/shell/BugreportProgressService.java +++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java @@ -56,16 +56,11 @@ import android.os.FileUtils; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; -import android.os.IBinder.DeathRecipient; -import android.os.IDumpstate; -import android.os.IDumpstateListener; -import android.os.IDumpstateToken; import android.os.Looper; import android.os.Message; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Parcelable; -import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; import android.os.UserHandle; @@ -80,7 +75,6 @@ import android.util.SparseArray; import android.view.ContextThemeWrapper; import android.view.IWindowManager; import android.view.View; -import android.view.View.OnFocusChangeListener; import android.view.WindowManager; import android.widget.Button; import android.widget.EditText; @@ -122,31 +116,9 @@ import java.util.zip.ZipFile; import java.util.zip.ZipOutputStream; /** - * Service used to keep progress of bugreport processes ({@code dumpstate} and - * {@code BugreportManager}). + * Service used to trigger system bugreports. * <p> - * There can be 2 workflows. One workflow via ({@code dumpstate}) is: - * <ol> - * <li>When {@code dumpstate} starts, it sends a {@code BUGREPORT_STARTED} with a sequential id, - * its pid, and the estimated total effort. - * <li>{@link BugreportReceiver} receives the intent and delegates it to this service. - * <li>Upon start, this service: - * <ol> - * <li>Issues a system notification so user can watch the progress (which is 0% initially). - * <li>Polls the {@link SystemProperties} for updates on the {@code dumpstate} progress. - * <li>If the progress changed, it updates the system notification. - * </ol> - * <li>As {@code dumpstate} progresses, it updates the system property. - * <li>When {@code dumpstate} finishes, it sends a {@code BUGREPORT_FINISHED} intent. - * <li>{@link BugreportReceiver} receives the intent and delegates it to this service, which in - * turn: - * <ol> - * <li>Updates the system notification so user can share the bugreport. - * <li>Stops monitoring that {@code dumpstate} process. - * <li>Stops itself if it doesn't have any process left to monitor. - * </ol> - * </ol> - * The second workflow using Bugreport API({@code BugreportManager}) is: + * The workflow uses Bugreport API({@code BugreportManager}) and is as follows: * <ol> * <li>System apps like Settings or SysUI broadcasts {@code BUGREPORT_REQUESTED}. * <li>{@link BugreportRequestedReceiver} receives the intent and delegates it to this service. @@ -164,18 +136,14 @@ public class BugreportProgressService extends Service { private static final String AUTHORITY = "com.android.shell"; - // External intents sent by dumpstate. - static final String INTENT_BUGREPORT_STARTED = - "com.android.internal.intent.action.BUGREPORT_STARTED"; - static final String INTENT_BUGREPORT_FINISHED = - "com.android.internal.intent.action.BUGREPORT_FINISHED"; - static final String INTENT_REMOTE_BUGREPORT_FINISHED = - "com.android.internal.intent.action.REMOTE_BUGREPORT_FINISHED"; - // External intent used to trigger bugreport API. static final String INTENT_BUGREPORT_REQUESTED = "com.android.internal.intent.action.BUGREPORT_REQUESTED"; + // Intent sent to notify external apps that bugreport finished + static final String INTENT_BUGREPORT_FINISHED = + "com.android.internal.intent.action.BUGREPORT_FINISHED"; + // Internal intents used on notification actions. static final String INTENT_BUGREPORT_CANCEL = "android.intent.action.BUGREPORT_CANCEL"; static final String INTENT_BUGREPORT_SHARE = "android.intent.action.BUGREPORT_SHARE"; @@ -188,7 +156,6 @@ public class BugreportProgressService extends Service { static final String EXTRA_BUGREPORT_TYPE = "android.intent.extra.BUGREPORT_TYPE"; static final String EXTRA_SCREENSHOT = "android.intent.extra.SCREENSHOT"; static final String EXTRA_ID = "android.intent.extra.ID"; - static final String EXTRA_PID = "android.intent.extra.PID"; static final String EXTRA_MAX = "android.intent.extra.MAX"; static final String EXTRA_NAME = "android.intent.extra.NAME"; static final String EXTRA_TITLE = "android.intent.extra.TITLE"; @@ -218,15 +185,9 @@ public class BugreportProgressService extends Service { */ static final int SCREENSHOT_DELAY_SECONDS = 3; - // TODO: will be gone once fully migrated to Binder - /** System properties used to communicate with dumpstate progress. */ - private static final String DUMPSTATE_PREFIX = "dumpstate."; - private static final String NAME_SUFFIX = ".name"; + /** System property where dumpstate stores last triggered bugreport id */ private static final String PROPERTY_LAST_ID = "dumpstate.last_id"; - /** System property (and value) used to stop dumpstate. */ - // TODO: should call ActiveManager API instead - private static final String CTL_STOP = "ctl.stop"; private static final String BUGREPORT_SERVICE = "bugreport"; /** @@ -273,8 +234,6 @@ public class BugreportProgressService extends Service { private File mScreenshotsDir; - private boolean mUsingBugreportApi; - private BugreportManager mBugreportManager; /** @@ -452,9 +411,9 @@ public class BugreportProgressService extends Service { cleanupOldFiles(MIN_KEEP_COUNT, MIN_KEEP_AGE); final Intent intent = new Intent(INTENT_BUGREPORT_FINISHED); intent.putExtra(EXTRA_BUGREPORT, bugreportFilePath); - addScreenshotToIntent(intent, mInfo); + intent.putExtra(EXTRA_SCREENSHOT, getScreenshotForIntent(mInfo)); mContext.sendBroadcast(intent, android.Manifest.permission.DUMP); - onBugreportFinished(mInfo.id); + onBugreportFinished(mInfo); } } } @@ -475,14 +434,17 @@ public class BugreportProgressService extends Service { android.Manifest.permission.DUMP); } - private static void addScreenshotToIntent(Intent intent, BugreportInfo info) { - final File screenshotFile = info.screenshotFiles.isEmpty() - ? null : info.screenshotFiles.get(0); - if (screenshotFile != null && screenshotFile.length() > 0) { + /** + * Checks if screenshot array is non-empty and returns the first screenshot's path. The first + * screenshot is the default screenshot for the bugreport types that take it. + */ + private static String getScreenshotForIntent(BugreportInfo info) { + if (!info.screenshotFiles.isEmpty()) { + final File screenshotFile = info.screenshotFiles.get(0); final String screenshotFilePath = screenshotFile.getAbsolutePath(); - intent.putExtra(EXTRA_SCREENSHOT, screenshotFilePath); + return screenshotFilePath; } - return; + return null; } private static String generateFileHash(String fileName) { @@ -558,40 +520,24 @@ public class BugreportProgressService extends Service { Log.v(TAG, "handleMessage(): " + dumpIntent((Intent) parcel)); final Intent intent; if (parcel instanceof Intent) { - // The real intent was passed to BugreportReceiver, which delegated to the service. + // The real intent was passed to BugreportRequestedReceiver, + // which delegated to the service. intent = (Intent) parcel; } else { intent = (Intent) msg.obj; } final String action = intent.getAction(); - final int pid = intent.getIntExtra(EXTRA_PID, 0); final int id = intent.getIntExtra(EXTRA_ID, 0); final int max = intent.getIntExtra(EXTRA_MAX, -1); final String name = intent.getStringExtra(EXTRA_NAME); if (DEBUG) - Log.v(TAG, "action: " + action + ", name: " + name + ", id: " + id + ", pid: " - + pid + ", max: " + max); + Log.v(TAG, "action: " + action + ", name: " + name + ", id: " + id + + ", max: " + max); switch (action) { case INTENT_BUGREPORT_REQUESTED: startBugreportAPI(intent); break; - case INTENT_BUGREPORT_STARTED: - if (!mUsingBugreportApi && !startProgress(name, id, pid, max)) { - stopSelfWhenDone(); - return; - } - break; - case INTENT_BUGREPORT_FINISHED: - if (!mUsingBugreportApi) { - if (id == 0) { - // Shouldn't happen, unless BUGREPORT_FINISHED is received - // from a legacy, out-of-sync dumpstate process. - Log.w(TAG, "Missing " + EXTRA_ID + " on intent " + intent); - } - onBugreportFinished(id, intent); - } - break; case INTENT_BUGREPORT_INFO_LAUNCH: launchBugreportInfoDialog(id); break; @@ -633,52 +579,12 @@ public class BugreportProgressService extends Service { private BugreportInfo getInfo(int id) { final BugreportInfo bugreportInfo = mBugreportInfos.get(id); if (bugreportInfo == null) { - Log.w(TAG, "Not monitoring process with ID " + id); + Log.w(TAG, "Not monitoring bugreports with ID " + id); return null; } return bugreportInfo; } - /** - * Creates the {@link BugreportInfo} for a process and issue a system notification to - * indicate its progress. - * - * @return whether it succeeded or not. - */ - private boolean startProgress(String name, int id, int pid, int max) { - if (name == null) { - Log.w(TAG, "Missing " + EXTRA_NAME + " on start intent"); - } - if (id == -1) { - Log.e(TAG, "Missing " + EXTRA_ID + " on start intent"); - return false; - } - if (pid == -1) { - Log.e(TAG, "Missing " + EXTRA_PID + " on start intent"); - return false; - } - if (max <= 0) { - Log.e(TAG, "Invalid value for extra " + EXTRA_MAX + ": " + max); - return false; - } - - final BugreportInfo info = new BugreportInfo(mContext, id, pid, name, max); - if (mBugreportInfos.indexOfKey(id) >= 0) { - // BUGREPORT_STARTED intent was already received; ignore it. - Log.w(TAG, "ID " + id + " already watched"); - return true; - } - final DumpstateListener listener = new DumpstateListener(info); - mBugreportInfos.put(info.id, info); - if (listener.connect()) { - updateProgress(info); - return true; - } else { - Log.w(TAG, "not updating progress because it could not connect to dumpstate"); - return false; - } - } - private String getBugreportBaseName(@BugreportParams.BugreportMode int type) { String buildId = SystemProperties.get("ro.build.id", "UNKNOWN_BUILD"); String deviceName = SystemProperties.get("ro.product.name", "UNKNOWN_DEVICE"); @@ -694,7 +600,6 @@ public class BugreportProgressService extends Service { } private void startBugreportAPI(Intent intent) { - mUsingBugreportApi = true; String shareTitle = intent.getStringExtra(EXTRA_TITLE); String shareDescription = intent.getStringExtra(EXTRA_DESCRIPTION); int bugreportType = intent.getIntExtra(EXTRA_BUGREPORT_TYPE, @@ -702,10 +607,8 @@ public class BugreportProgressService extends Service { String baseName = getBugreportBaseName(bugreportType); String name = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date()); - // pid not used in this workflow, so setting default = 0 - BugreportInfo info = new BugreportInfo(mContext, 0 /* pid */, baseName, name, - 100 /* max progress*/, shareTitle, shareDescription, bugreportType, - mUsingBugreportApi); + BugreportInfo info = new BugreportInfo(mContext, baseName, name, + 100 /* max progress*/, shareTitle, shareDescription, bugreportType); ParcelFileDescriptor bugreportFd = info.createBugreportFd(); if (bugreportFd == null) { @@ -885,11 +788,7 @@ public class BugreportProgressService extends Service { final BugreportInfo info = getInfo(id); if (info != null && !info.finished) { Log.i(TAG, "Cancelling bugreport service (ID=" + id + ") on user's request"); - if (mUsingBugreportApi) { - mBugreportManager.cancelBugreport(); - } else { - setSystemProperty(CTL_STOP, BUGREPORT_SERVICE); - } + mBugreportManager.cancelBugreport(); deleteScreenshots(info); } synchronized (mLock) { @@ -1087,94 +986,15 @@ public class BugreportProgressService extends Service { } /** - * Handles the BUGREPORT_FINISHED intent sent by {@code dumpstate}. - */ - private void onBugreportFinished(int id, Intent intent) { - final File bugreportFile = getFileExtra(intent, EXTRA_BUGREPORT); - if (bugreportFile == null) { - // Should never happen, dumpstate always set the file. - Log.wtf(TAG, "Missing " + EXTRA_BUGREPORT + " on intent " + intent); - return; - } - final int max = intent.getIntExtra(EXTRA_MAX, -1); - final File screenshotFile = getFileExtra(intent, EXTRA_SCREENSHOT); - final String shareTitle = intent.getStringExtra(EXTRA_TITLE); - final String shareDescription = intent.getStringExtra(EXTRA_DESCRIPTION); - onBugreportFinished(id, bugreportFile, screenshotFile, shareTitle, shareDescription, max); - } - - /** - * Handles the onfinish() call by BugreportCallbackImpl using the id - */ - private void onBugreportFinished(int id) { - BugreportInfo info = getInfo(id); - final File bugreportFile = info.bugreportFile; - final int max = -1; // this is to log metrics for dumpstate duration. - File screenshotFile = info.screenshotFiles.isEmpty() - ? null : info.screenshotFiles.get(0); - // If the screenshot file did not get populated implies this type of bugreport does not - // need the screenshot file; setting the file to null so that empty file doesnt get shared - if (screenshotFile != null && screenshotFile.length() == 0) { - if (screenshotFile.delete()) { - Log.d(TAG, "screenshot file deleted successfully."); - } - info.screenshotFiles.remove(0); - // TODO(b/136066578): Will soon stop using the below variable as it is a no-op in - // API flow and the screenshotFile value is read from info.screenshotFiles - screenshotFile = null; - } - // TODO: Since we are passing id to the function, it should be able to find the info linked - // to the id and therefore use the value of shareTitle and shareDescription. - onBugreportFinished(id, bugreportFile, screenshotFile, info.shareTitle, - info.shareDescription, max); - } - - - /** * Wraps up bugreport generation and triggers a notification to share the bugreport. */ - private void onBugreportFinished(int id, File bugreportFile, @Nullable File screenshotFile, - String shareTitle, String shareDescription, int max) { - mInfoDialog.onBugreportFinished(); - BugreportInfo info = getInfo(id); - if (info == null) { - // Happens when BUGREPORT_FINISHED was received without a BUGREPORT_STARTED first. - Log.v(TAG, "Creating info for untracked ID " + id); - info = new BugreportInfo(mContext, id); - DumpstateListener dumpstateListener = new DumpstateListener(info); - mBugreportInfos.put(id, info); - } - if (!mUsingBugreportApi) { - // Rename in API flow happens before sending the broadcast for remote bugreport (to - // handle renaming before sending broadcasts) - info.renameScreenshots(mScreenshotsDir); - // API workflow already has the bugreport file. This was required in legacy flow, when - // FINISHED broadcast would send the final bugreport files. - // TODO(b/136066578): Change function definition to not include bugreport/screenshot - // file in params - info.bugreportFile = bugreportFile; - if (screenshotFile != null) { - info.addScreenshot(screenshotFile); - } - } - - if (max != -1) { - MetricsLogger.histogram(this, "dumpstate_duration", max); - info.max = max; - } - - if (!TextUtils.isEmpty(shareTitle)) { - info.title = shareTitle; - if (!TextUtils.isEmpty(shareDescription)) { - info.shareDescription= shareDescription; - } - Log.d(TAG, "Bugreport title is " + info.title + "," - + " shareDescription is " + info.shareDescription); - } + private void onBugreportFinished(BugreportInfo info) { + Log.d(TAG, "Bugreport finished with title: " + info.title + + " and shareDescription: " + info.shareDescription); info.finished = true; // Stop running on foreground, otherwise share notification cannot be dismissed. - stopForegroundWhenDone(id); + stopForegroundWhenDone(info.id); triggerLocalNotification(mContext, info); } @@ -1214,7 +1034,11 @@ public class BugreportProgressService extends Service { /** * Build {@link Intent} that can be used to share the given bugreport. */ - private static Intent buildSendIntent(Context context, BugreportInfo info) { + private static Intent buildSendIntent(Context context, BugreportInfo info, + File screenshotsDir) { + // Rename files (if required) before sharing + info.renameBugreportFile(); + info.renameScreenshots(screenshotsDir); // Files are kept on private storage, so turn into Uris that we can // grant temporary permissions for. final Uri bugreportUri; @@ -1300,7 +1124,7 @@ public class BugreportProgressService extends Service { addDetailsToZipFile(info); - final Intent sendIntent = buildSendIntent(mContext, info); + final Intent sendIntent = buildSendIntent(mContext, info, mScreenshotsDir); if (sendIntent == null) { Log.w(TAG, "Stopping progres on ID " + id + " because share intent could not be built"); synchronized (mLock) { @@ -1628,12 +1452,11 @@ public class BugreportProgressService extends Service { } String action = intent.getAction(); if (action == null) { - // Happens when BugreportReceiver calls startService... + // Happens when startService is called... action = "no action"; } final StringBuilder buffer = new StringBuilder(action).append(" extras: "); addExtra(buffer, intent, EXTRA_ID); - addExtra(buffer, intent, EXTRA_PID); addExtra(buffer, intent, EXTRA_MAX); addExtra(buffer, intent, EXTRA_NAME); addExtra(buffer, intent, EXTRA_DESCRIPTION); @@ -1678,15 +1501,6 @@ public class BugreportProgressService extends Service { } /** - * Updates the system property used by {@code dumpstate} to rename the final bugreport files. - */ - private boolean setBugreportNameProperty(int pid, String name) { - Log.d(TAG, "Updating bugreport name to " + name); - final String key = DUMPSTATE_PREFIX + pid + NAME_SUFFIX; - return setSystemProperty(key, name); - } - - /** * Updates the user-provided details of a bugreport. */ private void updateBugreportInfo(int id, String name, String title, String description) { @@ -1770,30 +1584,6 @@ public class BugreportProgressService extends Service { private AlertDialog mDialog; private Button mOkButton; private int mId; - private int mPid; - - /** - * Last "committed" value of the bugreport name. - * <p> - * Once initially set, it's only updated when user clicks the OK button. - */ - private String mSavedName; - - /** - * Last value of the bugreport name as entered by the user. - * <p> - * Every time it's changed the equivalent system property is changed as well, but if the - * user clicks CANCEL, the old value (stored on {@code mSavedName} is restored. - * <p> - * This logic handles the corner-case scenario where {@code dumpstate} finishes after the - * user changed the name but didn't clicked OK yet (for example, because the user is typing - * the description). The only drawback is that if the user changes the name while - * {@code dumpstate} is running but clicks CANCEL after it finishes, then the final name - * will be the one that has been canceled. But when {@code dumpstate} finishes the {code - * name} UI is disabled and the old name restored anyways, so the user will be "alerted" of - * such drawback. - */ - private String mTempName; /** * Sets its internal state and displays the dialog. @@ -1813,18 +1603,6 @@ public class BugreportProgressService extends Service { mInfoName = (EditText) view.findViewById(R.id.name); mInfoTitle = (EditText) view.findViewById(R.id.title); mInfoDescription = (EditText) view.findViewById(R.id.description); - - mInfoName.setOnFocusChangeListener(new OnFocusChangeListener() { - - @Override - public void onFocusChange(View v, boolean hasFocus) { - if (hasFocus) { - return; - } - sanitizeName(info); - } - }); - mDialog = new AlertDialog.Builder(themedContext) .setView(view) .setTitle(dialogTitle) @@ -1839,15 +1617,6 @@ public class BugreportProgressService extends Service { { MetricsLogger.action(context, MetricsEvent.ACTION_BUGREPORT_DETAILS_CANCELED); - if (!mTempName.equals(mSavedName)) { - // Must restore bugreport's name since it was changed - // before user clicked OK. - if (mUsingBugreportApi) { - info.name = mSavedName; - } else { - setBugreportNameProperty(mPid, mSavedName); - } - } } }) .create(); @@ -1866,9 +1635,7 @@ public class BugreportProgressService extends Service { } // Then set fields. - mSavedName = mTempName = info.name; mId = info.id; - mPid = info.pid; if (!TextUtils.isEmpty(info.name)) { mInfoName.setText(info.name); } @@ -1895,7 +1662,7 @@ public class BugreportProgressService extends Service { @Override public void onClick(View view) { MetricsLogger.action(context, MetricsEvent.ACTION_BUGREPORT_DETAILS_SAVED); - sanitizeName(info); + sanitizeName(info.name); final String name = mInfoName.getText().toString(); final String title = mInfoTitle.getText().toString(); final String description = mInfoDescription.getText().toString(); @@ -1911,9 +1678,9 @@ public class BugreportProgressService extends Service { * Sanitizes the user-provided value for the {@code name} field, automatically replacing * invalid characters if necessary. */ - private void sanitizeName(BugreportInfo info) { + private void sanitizeName(String savedName) { String name = mInfoName.getText().toString(); - if (name.equals(mTempName)) { + if (name.equals(savedName)) { if (DEBUG) Log.v(TAG, "name didn't change, no need to sanitize: " + name); return; } @@ -1933,28 +1700,6 @@ public class BugreportProgressService extends Service { name = safeName.toString(); mInfoName.setText(name); } - mTempName = name; - if (mUsingBugreportApi) { - info.name = name; - } else { - // Must update system property for the cases where dumpstate finishes - // while the user is still entering other fields (like title or - // description) - setBugreportNameProperty(mPid, name); - } - } - - /** - * Notifies the dialog that the bugreport has finished so it disables the {@code name} - * field. - * <p>Once the bugreport is finished dumpstate has already generated the final files, so - * changing the name would have no effect. - */ - void onBugreportFinished() { - if (mInfoName != null) { - mInfoName.setEnabled(false); - mInfoName.setText(mSavedName); - } } void cancel() { @@ -1976,18 +1721,10 @@ public class BugreportProgressService extends Service { int id; /** - * {@code pid} of the {@code dumpstate} process generating the bugreport. - * pid is unused in the API flow - * TODO(b/136066578): Remove pid - */ - final int pid; - - /** * Prefix name of the bugreport, this is uneditable. * The baseName consists of the string "bugreport" + deviceName + buildID * This will end with the string "wifi"/"telephony" for wifi/telephony bugreports. * Bugreport zip file name = "<baseName>-<name>.zip" - * Bugreport png file name = "<baseName>-<name>.png" */ String baseName; @@ -1998,6 +1735,12 @@ public class BugreportProgressService extends Service { String name; /** + * Initial value of the field name. This is required to rename the files later on, as they + * are created using initial value of name. + */ + String initialName; + + /** * User-provided, one-line summary of the bug; when set, will be used as the subject * of the {@link Intent#ACTION_SEND_MULTIPLE} intent. */ @@ -2063,12 +1806,6 @@ public class BugreportProgressService extends Service { boolean finished; /** - * Whether this bugreport is using API workflow. - * TODO(b/136066578): Remove when legacy bugreport methods are removed - */ - boolean usingApi; - - /** * Whether the details entries have been added to the bugreport yet. */ boolean addingDetailsToZip; @@ -2092,42 +1829,18 @@ public class BugreportProgressService extends Service { int type; /** - * Constructor for tracked bugreports - typically called upon receiving BUGREPORT_STARTED. - */ - BugreportInfo(Context context, int id, int pid, String name, int max) { - // bugreports triggered by STARTED broadcast do not use callback functions, - // onFinished() callback method is the only function where type is used. - // Set type to -1 as it is unused in this workflow. - // This constructor will soon be removed. - this(context, pid, "" /* dumpstate handles basename */, name, max, null, null, -1, - false); - this.id = id; - } - - /** * Constructor for tracked bugreports - typically called upon receiving BUGREPORT_REQUESTED. */ - BugreportInfo(Context context, int pid, String baseName, String name, int max, + BugreportInfo(Context context, String baseName, String name, int max, @Nullable String shareTitle, @Nullable String shareDescription, - @BugreportParams.BugreportMode int type, boolean usingApi) { + @BugreportParams.BugreportMode int type) { this.context = context; - this.pid = pid; - this.name = name; + this.name = this.initialName = name; this.max = this.realMax = max; this.shareTitle = shareTitle == null ? "" : shareTitle; this.shareDescription = shareDescription == null ? "" : shareDescription; this.type = type; this.baseName = baseName; - this.usingApi = usingApi; - } - - /** - * Constructor for untracked bugreports - typically called upon receiving BUGREPORT_FINISHED - * without a previous call to BUGREPORT_STARTED. - */ - BugreportInfo(Context context, int id) { - this(context, id, id, null, 0); - this.finished = true; } ParcelFileDescriptor createBugreportFd() { @@ -2136,17 +1849,24 @@ public class BugreportProgressService extends Service { } ParcelFileDescriptor createScreenshotFd() { - File screenshotFile = new File(BUGREPORT_DIR, getFileName(this, ".png")); + File screenshotFile = new File(BUGREPORT_DIR, getScreenshotName("default")); addScreenshot(screenshotFile); return createReadWriteFile(screenshotFile); } /** - * Gets the name for next screenshot file. + * Gets the name for next user triggered screenshot file. */ String getPathNextScreenshot() { screenshotCounter ++; - return "screenshot-" + pid + "-" + screenshotCounter + ".png"; + return getScreenshotName(Integer.toString(screenshotCounter)); + } + + /** + * Gets the name for screenshot file based on the suffix that is passed. + */ + String getScreenshotName(String suffix) { + return "screenshot-" + initialName + "-" + suffix + ".png"; } /** @@ -2157,7 +1877,8 @@ public class BugreportProgressService extends Service { } /** - * Rename all screenshots files so that they contain the user-generated name instead of pid. + * Rename all screenshots files so that they contain the new {@code name} instead of the + * {@code initialName} if user has changed it. */ void renameScreenshots(File screenshotDir) { if (TextUtils.isEmpty(name)) { @@ -2166,22 +1887,21 @@ public class BugreportProgressService extends Service { final List<File> renamedFiles = new ArrayList<>(screenshotFiles.size()); for (File oldFile : screenshotFiles) { final String oldName = oldFile.getName(); - final String newName; - if (usingApi) { - newName = getFileName(this, ".png"); - } else { - newName = oldName.replaceFirst(Integer.toString(pid), name); - } + final String newName = oldName.replaceFirst(initialName, name); final File newFile; if (!newName.equals(oldName)) { final File renamedFile = new File(screenshotDir, newName); Log.d(TAG, "Renaming screenshot file " + oldFile + " to " + renamedFile); newFile = oldFile.renameTo(renamedFile) ? renamedFile : oldFile; } else { - Log.w(TAG, "Name didn't change: " + oldName); // Shouldn't happen. + Log.w(TAG, "Name didn't change: " + oldName); newFile = oldFile; } - renamedFiles.add(newFile); + if (newFile.length() > 0) { + renamedFiles.add(newFile); + } else if (newFile.delete()) { + Log.d(TAG, "screenshot file: " + newFile + "deleted successfully."); + } } screenshotFiles = renamedFiles; } @@ -2215,11 +1935,10 @@ public class BugreportProgressService extends Service { final StringBuilder builder = new StringBuilder() .append("\tid: ").append(id) - .append(", pid: ").append(pid) .append(", baseName: ").append(baseName) .append(", name: ").append(name) + .append(", initialName: ").append(initialName) .append(", finished: ").append(finished) - .append(", usingApi: ").append(usingApi) .append("\n\ttitle: ").append(title) .append("\n\tdescription: "); if (description == null) { @@ -2250,9 +1969,9 @@ public class BugreportProgressService extends Service { protected BugreportInfo(Parcel in) { context = null; id = in.readInt(); - pid = in.readInt(); baseName = in.readString(); name = in.readString(); + initialName = in.readString(); title = in.readString(); description = in.readString(); max = in.readInt(); @@ -2269,7 +1988,6 @@ public class BugreportProgressService extends Service { } finished = in.readInt() == 1; - usingApi = in.readInt() == 1; screenshotCounter = in.readInt(); shareDescription = in.readString(); shareTitle = in.readString(); @@ -2278,9 +1996,9 @@ public class BugreportProgressService extends Service { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(id); - dest.writeInt(pid); dest.writeString(baseName); dest.writeString(name); + dest.writeString(initialName); dest.writeString(title); dest.writeString(description); dest.writeInt(max); @@ -2297,7 +2015,6 @@ public class BugreportProgressService extends Service { } dest.writeInt(finished ? 1 : 0); - dest.writeInt(usingApi ? 1 : 0); dest.writeInt(screenshotCounter); dest.writeString(shareDescription); dest.writeString(shareTitle); @@ -2333,80 +2050,6 @@ public class BugreportProgressService extends Service { } - private final class DumpstateListener extends IDumpstateListener.Stub - implements DeathRecipient { - - private final BugreportInfo info; - private IDumpstateToken token; - - DumpstateListener(BugreportInfo info) { - this.info = info; - } - - /** - * Connects to the {@code dumpstate} binder to receive updates. - */ - boolean connect() { - if (token != null) { - Log.d(TAG, "connect(): " + info.id + " already connected"); - return true; - } - final IBinder service = ServiceManager.getService("dumpstate"); - if (service == null) { - Log.d(TAG, "dumpstate service not bound yet"); - return true; - } - final IDumpstate dumpstate = IDumpstate.Stub.asInterface(service); - try { - token = dumpstate.setListener("Shell", this, /* perSectionDetails= */ false); - if (token != null) { - token.asBinder().linkToDeath(this, 0); - } - } catch (Exception e) { - Log.e(TAG, "Could not set dumpstate listener: " + e); - } - return token != null; - } - - @Override - public void binderDied() { - if (!info.finished) { - // TODO: linkToDeath() might be called BEFORE Shell received the - // BUGREPORT_FINISHED broadcast, in which case the statements below - // spam logcat (but are harmless). - // The right, long-term solution is to provide an onFinished() callback - // on IDumpstateListener and call it instead of using a broadcast. - Log.w(TAG, "Dumpstate process died:\n" + info); - synchronized (mLock) { - stopProgressLocked(info.id); - } - } - token.asBinder().unlinkToDeath(this, 0); - } - - @Override - public void onProgress(int progress) throws RemoteException { - synchronized (mLock) { - checkProgressUpdatedLocked(info, progress); - } - } - - @Override - public void onError(int errorCode) throws RemoteException { - // TODO(b/111441001): implement - } - - @Override - public void onFinished() throws RemoteException { - // TODO(b/111441001): implement - } - - public void dump(String prefix, PrintWriter pw) { - pw.print(prefix); pw.print("token: "); pw.println(token); - } - - } - @GuardedBy("mLock") private void checkProgressUpdatedLocked(BugreportInfo info, int progress) { if (progress > CAPPED_PROGRESS) { @@ -2418,11 +2061,11 @@ public class BugreportProgressService extends Service { private void updateProgressInfo(BugreportInfo info, int progress, int max) { if (DEBUG) { if (progress != info.progress) { - Log.v(TAG, "Updating progress for PID " + info.pid + "(id: " + info.id + Log.v(TAG, "Updating progress for name " + info.name + "(id: " + info.id + ") from " + info.progress + " to " + progress); } if (max != info.max) { - Log.v(TAG, "Updating max progress for PID " + info.pid + "(id: " + info.id + Log.v(TAG, "Updating max progress for name " + info.name + "(id: " + info.id + ") from " + info.max + " to " + max); } } diff --git a/packages/Shell/src/com/android/shell/BugreportReceiver.java b/packages/Shell/src/com/android/shell/BugreportReceiver.java deleted file mode 100644 index 15ce90fa6418..000000000000 --- a/packages/Shell/src/com/android/shell/BugreportReceiver.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (C) 2013 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.shell; - -import static com.android.shell.BugreportProgressService.EXTRA_BUGREPORT; -import static com.android.shell.BugreportProgressService.EXTRA_ORIGINAL_INTENT; -import static com.android.shell.BugreportProgressService.INTENT_BUGREPORT_FINISHED; -import static com.android.shell.BugreportProgressService.getFileExtra; -import static com.android.shell.BugreportProgressService.dumpIntent; - -import java.io.File; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.os.AsyncTask; -import android.os.FileUtils; -import android.text.format.DateUtils; -import android.util.Log; - -/** - * Receiver that handles finished bugreports, usually by attaching them to an - * {@link Intent#ACTION_SEND_MULTIPLE}. - */ -public class BugreportReceiver extends BroadcastReceiver { - private static final String TAG = "BugreportReceiver"; - - /** - * Always keep the newest 8 bugreport files. - */ - private static final int MIN_KEEP_COUNT = 8; - - /** - * Always keep bugreports taken in the last week. - */ - private static final long MIN_KEEP_AGE = DateUtils.WEEK_IN_MILLIS; - - @Override - public void onReceive(Context context, Intent intent) { - Log.d(TAG, "onReceive(): " + dumpIntent(intent)); - // Clean up older bugreports in background - cleanupOldFiles(this, intent, INTENT_BUGREPORT_FINISHED, MIN_KEEP_COUNT, MIN_KEEP_AGE); - - // Delegate intent handling to service. - Intent serviceIntent = new Intent(context, BugreportProgressService.class); - serviceIntent.putExtra(EXTRA_ORIGINAL_INTENT, intent); - context.startService(serviceIntent); - } - - static void cleanupOldFiles(BroadcastReceiver br, Intent intent, String expectedAction, - final int minCount, final long minAge) { - if (!expectedAction.equals(intent.getAction())) { - return; - } - final File bugreportFile = getFileExtra(intent, EXTRA_BUGREPORT); - if (bugreportFile == null || !bugreportFile.exists()) { - Log.e(TAG, "Not deleting old files because file " + bugreportFile + " doesn't exist"); - return; - } - final PendingResult result = br.goAsync(); - new AsyncTask<Void, Void, Void>() { - @Override - protected Void doInBackground(Void... params) { - try { - FileUtils.deleteOlderFiles(bugreportFile.getParentFile(), minCount, minAge); - } catch (RuntimeException e) { - Log.e(TAG, "RuntimeException deleting old files", e); - } - result.finish(); - return null; - } - }.execute(); - } -} diff --git a/packages/Shell/src/com/android/shell/RemoteBugreportReceiver.java b/packages/Shell/src/com/android/shell/RemoteBugreportReceiver.java deleted file mode 100644 index 634c3b47c787..000000000000 --- a/packages/Shell/src/com/android/shell/RemoteBugreportReceiver.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.shell; - -import static com.android.shell.BugreportProgressService.EXTRA_BUGREPORT; -import static com.android.shell.BugreportProgressService.INTENT_REMOTE_BUGREPORT_FINISHED; -import static com.android.shell.BugreportProgressService.getFileExtra; -import static com.android.shell.BugreportProgressService.getUri; -import static com.android.shell.BugreportReceiver.cleanupOldFiles; - -import java.io.File; - -import android.app.admin.DevicePolicyManager; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.os.UserHandle; -import android.text.format.DateUtils; - -/** - * Receiver that handles finished remote bugreports, by re-sending - * the intent with appended bugreport zip file URI. - * - * <p> Remote bugreport never contains a screenshot. - */ -public class RemoteBugreportReceiver extends BroadcastReceiver { - - private static final String BUGREPORT_MIMETYPE = "application/vnd.android.bugreport"; - - /** Always keep just the last remote bugreport's files around. */ - private static final int REMOTE_BUGREPORT_FILES_AMOUNT = 3; - - /** Always keep remote bugreport files created in the last day. */ - private static final long MIN_KEEP_AGE = DateUtils.DAY_IN_MILLIS; - - @Override - public void onReceive(Context context, Intent intent) { - cleanupOldFiles(this, intent, INTENT_REMOTE_BUGREPORT_FINISHED, - REMOTE_BUGREPORT_FILES_AMOUNT, MIN_KEEP_AGE); - - final File bugreportFile = getFileExtra(intent, EXTRA_BUGREPORT); - final Uri bugreportUri = getUri(context, bugreportFile); - final String bugreportHash = intent.getStringExtra( - DevicePolicyManager.EXTRA_REMOTE_BUGREPORT_HASH); - - final Intent newIntent = new Intent(DevicePolicyManager.ACTION_REMOTE_BUGREPORT_DISPATCH); - newIntent.setDataAndType(bugreportUri, BUGREPORT_MIMETYPE); - newIntent.putExtra(DevicePolicyManager.EXTRA_REMOTE_BUGREPORT_HASH, bugreportHash); - context.sendBroadcastAsUser(newIntent, UserHandle.SYSTEM, - android.Manifest.permission.DUMP); - } -} diff --git a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java index 3a71632cf1ca..bb298e937fbb 100644 --- a/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java +++ b/packages/Shell/tests/src/com/android/shell/BugreportReceiverTest.java @@ -29,10 +29,8 @@ import static com.android.shell.BugreportProgressService.EXTRA_BUGREPORT; import static com.android.shell.BugreportProgressService.EXTRA_ID; import static com.android.shell.BugreportProgressService.EXTRA_MAX; import static com.android.shell.BugreportProgressService.EXTRA_NAME; -import static com.android.shell.BugreportProgressService.EXTRA_PID; import static com.android.shell.BugreportProgressService.EXTRA_SCREENSHOT; import static com.android.shell.BugreportProgressService.INTENT_BUGREPORT_FINISHED; -import static com.android.shell.BugreportProgressService.INTENT_BUGREPORT_STARTED; import static com.android.shell.BugreportProgressService.SCREENSHOT_DELAY_SECONDS; import static org.junit.Assert.assertEquals; @@ -145,6 +143,10 @@ public class BugreportReceiverTest { private static final String TITLE2 = "Master of the Universe"; private static final String DESCRIPTION = "One's description..."; private static final String DESCRIPTION2 = "...is another's treasure."; + // TODO(b/143130523): Fix (update) tests and add to presubmit + private static final String EXTRA_PID = "android.intent.extra.PID"; + private static final String INTENT_BUGREPORT_STARTED = + "com.android.internal.intent.action.BUGREPORT_STARTED"; private static final String NO_DESCRIPTION = null; private static final String NO_NAME = null; diff --git a/packages/SystemUI/res/layout/qqs_media_panel.xml b/packages/SystemUI/res/layout/qqs_media_panel.xml new file mode 100644 index 000000000000..1189371fc7f1 --- /dev/null +++ b/packages/SystemUI/res/layout/qqs_media_panel.xml @@ -0,0 +1,100 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<!-- Layout for QQS media controls --> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/qqs_media_controls" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:gravity="center" + android:padding="10dp" + > + <!-- Top line: icon + artist name --> + <LinearLayout + android:orientation="horizontal" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:clipChildren="false" + android:gravity="center" + > + <com.android.internal.widget.CachingIconView + android:id="@+id/icon" + android:layout_width="15dp" + android:layout_height="15dp" + android:layout_marginEnd="5dp" + /> + <TextView + android:id="@+id/header_title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:fontFamily="@*android:string/config_headlineFontFamilyMedium" + android:singleLine="true" + /> + </LinearLayout> + + <!-- Second line: song name --> + <TextView + android:id="@+id/header_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:singleLine="true" + android:fontFamily="@*android:string/config_bodyFontFamily" + android:gravity="center"/> + + <!-- Bottom section: controls --> + <LinearLayout + android:id="@+id/media_actions" + android:orientation="horizontal" + android:layoutDirection="ltr" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center" + > + <ImageButton + style="@android:style/Widget.Material.Button.Borderless.Small" + android:layout_width="48dp" + android:layout_height="48dp" + android:padding="8dp" + android:layout_marginEnd="2dp" + android:gravity="center" + android:visibility="gone" + android:id="@+id/action0" + /> + <ImageButton + style="@android:style/Widget.Material.Button.Borderless.Small" + android:layout_width="48dp" + android:layout_height="48dp" + android:padding="8dp" + android:layout_marginEnd="2dp" + android:gravity="center" + android:visibility="gone" + android:id="@+id/action1" + /> + <ImageButton + style="@android:style/Widget.Material.Button.Borderless.Small" + android:layout_width="48dp" + android:layout_height="48dp" + android:padding="8dp" + android:layout_marginEnd="2dp" + android:gravity="center" + android:visibility="gone" + android:id="@+id/action2" + /> + </LinearLayout> +</LinearLayout> diff --git a/packages/SystemUI/res/layout/qs_media_panel.xml b/packages/SystemUI/res/layout/qs_media_panel.xml new file mode 100644 index 000000000000..dd422766c153 --- /dev/null +++ b/packages/SystemUI/res/layout/qs_media_panel.xml @@ -0,0 +1,124 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<!-- Layout for media controls inside QSPanel carousel --> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/qs_media_controls" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:gravity="center_horizontal|fill_vertical" + android:padding="10dp" + > + + <!-- placeholder for notification header --> + <LinearLayout + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:id="@+id/header" + android:padding="3dp" + android:layout_marginEnd="-12dp" + /> + + <!-- Top line: artist name --> + <LinearLayout + android:orientation="horizontal" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center" + > + <TextView + android:id="@+id/header_title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:fontFamily="@*android:string/config_headlineFontFamilyMedium" + android:singleLine="true" + /> + </LinearLayout> + + <!-- Second line: song name --> + <TextView + android:id="@+id/header_text" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:singleLine="true" + android:fontFamily="@*android:string/config_bodyFontFamily" + android:gravity="center"/> + + <!-- Bottom section: controls --> + <LinearLayout + android:id="@+id/media_actions" + android:orientation="horizontal" + android:layoutDirection="ltr" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center" + > + <ImageButton + style="@android:style/Widget.Material.Button.Borderless.Small" + android:layout_width="48dp" + android:layout_height="48dp" + android:padding="8dp" + android:layout_marginEnd="2dp" + android:gravity="center" + android:visibility="gone" + android:id="@+id/action0" + /> + <ImageButton + style="@android:style/Widget.Material.Button.Borderless.Small" + android:layout_width="48dp" + android:layout_height="48dp" + android:padding="8dp" + android:layout_marginEnd="2dp" + android:gravity="center" + android:visibility="gone" + android:id="@+id/action1" + /> + <ImageButton + style="@android:style/Widget.Material.Button.Borderless.Small" + android:layout_width="48dp" + android:layout_height="48dp" + android:padding="8dp" + android:layout_marginEnd="2dp" + android:gravity="center" + android:visibility="gone" + android:id="@+id/action2" + /> + <ImageButton + style="@android:style/Widget.Material.Button.Borderless.Small" + android:layout_width="48dp" + android:layout_height="48dp" + android:padding="8dp" + android:layout_marginEnd="2dp" + android:gravity="center" + android:visibility="gone" + android:id="@+id/action3" + /> + <ImageButton + style="@android:style/Widget.Material.Button.Borderless.Small" + android:layout_width="48dp" + android:layout_height="48dp" + android:padding="8dp" + android:layout_marginEnd="2dp" + android:gravity="center" + android:visibility="gone" + android:id="@+id/action4" + /> + </LinearLayout> +</LinearLayout> diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml index ed18dc728402..e99b91787072 100644 --- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml +++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml @@ -43,7 +43,7 @@ <com.android.systemui.qs.QuickQSPanel android:id="@+id/quick_qs_panel" android:layout_width="match_parent" - android:layout_height="48dp" + android:layout_height="wrap_content" android:layout_below="@id/quick_qs_status_icons" android:layout_marginStart="@dimen/qs_header_tile_margin_horizontal" android:layout_marginEnd="@dimen/qs_header_tile_margin_horizontal" diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index d722d618e416..14371fe4b41b 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1164,4 +1164,11 @@ <!-- Size of the RAT type for CellularTile --> <dimen name="celltile_rat_type_size">10sp</dimen> + + <dimen name="new_qs_vertical_margin">8dp</dimen> + + <!-- Size of media cards in the QSPanel carousel --> + <dimen name="qs_media_height">150dp</dimen> + <dimen name="qs_media_width">350dp</dimen> + <dimen name="qs_media_padding">8dp</dimen> </resources> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java index ebac3d9da5e1..caee8ccb6970 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java @@ -131,8 +131,7 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout protected void verifyPasswordAndUnlock() { if (mDismissing) return; // already verified but haven't been dismissed; don't do it again. - final LockscreenCredential password = - LockscreenCredential.createPassword(getPasswordText()); + final LockscreenCredential password = getEnteredCredential(); setPasswordEntryInputEnabled(false); if (mPendingLockCheck != null) { mPendingLockCheck.cancel(false); @@ -223,7 +222,7 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout } protected abstract void resetPasswordText(boolean animate, boolean announce); - protected abstract CharSequence getPasswordText(); + protected abstract LockscreenCredential getEnteredCredential(); protected abstract void setPasswordEntryEnabled(boolean enabled); protected abstract void setPasswordEntryInputEnabled(boolean enabled); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java index 12c9fc9ce536..f8f3dc8d6ecd 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java @@ -36,6 +36,7 @@ import android.view.inputmethod.InputMethodSubtype; import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; +import com.android.internal.widget.LockscreenCredential; import com.android.internal.widget.TextViewInputDisabler; import com.android.systemui.R; @@ -243,8 +244,8 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView } @Override - protected CharSequence getPasswordText() { - return mPasswordEntry.getText(); + protected LockscreenCredential getEnteredCredential() { + return LockscreenCredential.createPasswordOrNone(mPasswordEntry.getText()); } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java index 8e9df5563123..c67deccb1f62 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java @@ -23,6 +23,7 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; +import com.android.internal.widget.LockscreenCredential; import com.android.systemui.R; /** @@ -167,8 +168,8 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView } @Override - protected CharSequence getPasswordText() { - return mPasswordEntry.getText(); + protected LockscreenCredential getEnteredCredential() { + return LockscreenCredential.createPinOrNone(mPasswordEntry.getText()); } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index ac5e255289cf..27410be09db0 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -39,7 +39,6 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STR import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; import static com.android.systemui.DejankUtils.whitelistIpcs; -import static com.android.systemui.Dependency.MAIN_LOOPER_NAME; import android.annotation.AnyThread; import android.annotation.MainThread; @@ -101,6 +100,7 @@ import com.android.internal.widget.LockPatternUtils; import com.android.settingslib.WirelessUtils; import com.android.systemui.DejankUtils; import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.MainLooper; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.statusbar.phone.KeyguardBypassController; @@ -118,7 +118,6 @@ import java.util.TimeZone; import java.util.function.Consumer; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Singleton; /** @@ -1500,7 +1499,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { @VisibleForTesting @Inject - protected KeyguardUpdateMonitor(Context context, @Named(MAIN_LOOPER_NAME) Looper mainLooper) { + protected KeyguardUpdateMonitor(Context context, @MainLooper Looper mainLooper) { mContext = context; mSubscriptionManager = SubscriptionManager.from(context); mDeviceProvisioned = isDeviceProvisionedInSettingsDb(); diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index ff8a932a6a92..486d02c207db 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -40,6 +40,10 @@ import com.android.systemui.assist.AssistManager; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.dagger.qualifiers.BgHandler; +import com.android.systemui.dagger.qualifiers.BgLooper; +import com.android.systemui.dagger.qualifiers.MainHandler; +import com.android.systemui.dagger.qualifiers.MainLooper; import com.android.systemui.dock.DockManager; import com.android.systemui.fragments.FragmentService; import com.android.systemui.keyguard.ScreenLifecycle; @@ -148,16 +152,16 @@ public class Dependency { /** * Key for getting a the main looper. */ - public static final String MAIN_LOOPER_NAME = "main_looper"; + private static final String MAIN_LOOPER_NAME = "main_looper"; /** * Key for getting a background Looper for background work. */ - public static final String BG_LOOPER_NAME = "background_looper"; + private static final String BG_LOOPER_NAME = "background_looper"; /** * Key for getting a background Handler for background work. */ - public static final String BG_HANDLER_NAME = "background_handler"; + private static final String BG_HANDLER_NAME = "background_handler"; /** * Key for getting a Handler for receiving time tick broadcasts on. */ @@ -165,7 +169,7 @@ public class Dependency { /** * Generic handler on the main thread. */ - public static final String MAIN_HANDLER_NAME = "main_handler"; + private static final String MAIN_HANDLER_NAME = "main_handler"; /** * An email address to send memory leak reports to by default. @@ -300,10 +304,10 @@ public class Dependency { @Inject Lazy<AutoHideController> mAutoHideController; @Inject Lazy<ForegroundServiceNotificationListener> mForegroundServiceNotificationListener; @Inject Lazy<PrivacyItemController> mPrivacyItemController; - @Inject @Named(BG_LOOPER_NAME) Lazy<Looper> mBgLooper; - @Inject @Named(BG_HANDLER_NAME) Lazy<Handler> mBgHandler; - @Inject @Named(MAIN_LOOPER_NAME) Lazy<Looper> mMainLooper; - @Inject @Named(MAIN_HANDLER_NAME) Lazy<Handler> mMainHandler; + @Inject @BgLooper Lazy<Looper> mBgLooper; + @Inject @BgHandler Lazy<Handler> mBgHandler; + @Inject @MainLooper Lazy<Looper> mMainLooper; + @Inject @MainHandler Lazy<Handler> mMainHandler; @Inject @Named(TIME_TICK_HANDLER_NAME) Lazy<Handler> mTimeTickHandler; @Nullable @Inject @Named(LEAK_REPORT_EMAIL_NAME) Lazy<String> mLeakReportEmail; diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java index aa13fa834f56..746515a816b3 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java @@ -27,6 +27,8 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.app.AppComponentFactory; +import com.android.systemui.dagger.ContextComponentHelper; + import javax.inject.Inject; /** diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java index 91776a335f0d..022bf06838a6 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java @@ -34,6 +34,7 @@ import android.util.ArraySet; import android.util.Log; import android.util.TimingsTraceLog; +import com.android.systemui.dagger.ContextComponentHelper; import com.android.systemui.plugins.OverlayPlugin; import com.android.systemui.plugins.PluginListener; import com.android.systemui.shared.plugins.PluginManager; diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index 11db481a425b..0a547b6bf051 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -27,6 +27,9 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.ViewMediatorCallback; +import com.android.systemui.dagger.DaggerSystemUIRootComponent; +import com.android.systemui.dagger.DependencyProvider; +import com.android.systemui.dagger.SystemUIRootComponent; import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -107,7 +110,7 @@ public class SystemUIFactory { protected SystemUIRootComponent buildSystemUIRootComponent(Context context) { return DaggerSystemUIRootComponent.builder() - .dependencyProvider(new com.android.systemui.DependencyProvider()) + .dependencyProvider(new DependencyProvider()) .contextHolder(new ContextHolder(context)) .build(); } diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java index ef171d305d28..f616d57e90aa 100644 --- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java @@ -16,8 +16,6 @@ package com.android.systemui.appops; -import static com.android.systemui.Dependency.BG_LOOPER_NAME; - import android.app.AppOpsManager; import android.content.Context; import android.content.pm.PackageManager; @@ -31,6 +29,7 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dumpable; +import com.android.systemui.dagger.qualifiers.BgLooper; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -39,7 +38,6 @@ import java.util.List; import java.util.Set; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Singleton; /** @@ -79,7 +77,7 @@ public class AppOpsControllerImpl implements AppOpsController, }; @Inject - public AppOpsControllerImpl(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper) { + public AppOpsControllerImpl(Context context, @BgLooper Looper bgLooper) { this(context, bgLooper, new PermissionFlagsCache(context)); } diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt index 5e977b4684dc..ff4711cb208a 100644 --- a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt +++ b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt @@ -26,13 +26,12 @@ import android.os.UserHandle import android.util.Log import android.util.SparseArray import com.android.internal.annotations.VisibleForTesting -import com.android.systemui.Dependency.BG_LOOPER_NAME -import com.android.systemui.Dependency.MAIN_HANDLER_NAME import com.android.systemui.Dumpable +import com.android.systemui.dagger.qualifiers.BgLooper +import com.android.systemui.dagger.qualifiers.MainHandler import java.io.FileDescriptor import java.io.PrintWriter import javax.inject.Inject -import javax.inject.Named import javax.inject.Singleton data class ReceiverData( @@ -61,8 +60,8 @@ private const val DEBUG = false @Singleton open class BroadcastDispatcher @Inject constructor ( private val context: Context, - @Named(MAIN_HANDLER_NAME) private val mainHandler: Handler, - @Named(BG_LOOPER_NAME) private val bgLooper: Looper + @MainHandler private val mainHandler: Handler, + @BgLooper private val bgLooper: Looper ) : Dumpable { // Only modify in BG thread diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java index 914258f48b46..db85fa0a3203 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java @@ -17,7 +17,6 @@ package com.android.systemui.classifier; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_MANAGER_ENABLED; -import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; import android.content.Context; import android.hardware.SensorManager; @@ -31,6 +30,7 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.Dependency; import com.android.systemui.classifier.brightline.BrightLineFalsingManager; import com.android.systemui.classifier.brightline.FalsingDataProvider; +import com.android.systemui.dagger.qualifiers.MainHandler; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.FalsingPlugin; import com.android.systemui.plugins.PluginListener; @@ -41,7 +41,6 @@ import com.android.systemui.util.sensors.ProximitySensor; import java.io.PrintWriter; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Singleton; /** @@ -62,7 +61,7 @@ public class FalsingManagerProxy implements FalsingManager { @Inject FalsingManagerProxy(Context context, PluginManager pluginManager, - @Named(MAIN_HANDLER_NAME) Handler handler, + @MainHandler Handler handler, ProximitySensor proximitySensor, DeviceConfigProxy deviceConfig) { mProximitySensor = proximitySensor; diff --git a/packages/SystemUI/src/com/android/systemui/ActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/ActivityBinder.java index 2c8a67270d94..4be610fcd9ee 100644 --- a/packages/SystemUI/src/com/android/systemui/ActivityBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ActivityBinder.java @@ -14,10 +14,11 @@ * limitations under the License. */ -package com.android.systemui; +package com.android.systemui.dagger; import android.app.Activity; +import com.android.systemui.ForegroundServicesDialog; import com.android.systemui.tuner.TunerActivity; import dagger.Binds; diff --git a/packages/SystemUI/src/com/android/systemui/ComponentBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/ComponentBinder.java index 3b35c61e8eb2..4e4c06e9d447 100644 --- a/packages/SystemUI/src/com/android/systemui/ComponentBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ComponentBinder.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui; +package com.android.systemui.dagger; import dagger.Binds; import dagger.Module; diff --git a/packages/SystemUI/src/com/android/systemui/ContextComponentHelper.java b/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentHelper.java index 2cf0f8dafcad..d6d1e418240a 100644 --- a/packages/SystemUI/src/com/android/systemui/ContextComponentHelper.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentHelper.java @@ -14,11 +14,13 @@ * limitations under the License. */ -package com.android.systemui; +package com.android.systemui.dagger; import android.app.Activity; import android.app.Service; +import com.android.systemui.SystemUI; + /** * Interface necessary to make Dagger happy. See {@link ContextComponentResolver}. */ diff --git a/packages/SystemUI/src/com/android/systemui/ContextComponentResolver.java b/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentResolver.java index 995263240e2d..d7822c9fc6da 100644 --- a/packages/SystemUI/src/com/android/systemui/ContextComponentResolver.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ContextComponentResolver.java @@ -14,11 +14,13 @@ * limitations under the License. */ -package com.android.systemui; +package com.android.systemui.dagger; import android.app.Activity; import android.app.Service; +import com.android.systemui.SystemUI; + import java.util.Map; import javax.inject.Inject; diff --git a/packages/SystemUI/src/com/android/systemui/DependencyBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java index f9f0f1bad2fa..6674c12ab613 100644 --- a/packages/SystemUI/src/com/android/systemui/DependencyBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyBinder.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,8 +14,9 @@ * limitations under the License. */ -package com.android.systemui; +package com.android.systemui.dagger; +import com.android.systemui.ActivityStarterDelegate; import com.android.systemui.appops.AppOpsController; import com.android.systemui.appops.AppOpsControllerImpl; import com.android.systemui.classifier.FalsingManagerProxy; diff --git a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java index 8c1f8ac91916..87434f344729 100644 --- a/packages/SystemUI/src/com/android/systemui/DependencyProvider.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,51 +14,35 @@ * limitations under the License. */ -package com.android.systemui; +package com.android.systemui.dagger; -import static com.android.systemui.Dependency.BG_HANDLER_NAME; -import static com.android.systemui.Dependency.BG_LOOPER_NAME; -import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; -import static com.android.systemui.Dependency.MAIN_LOOPER_NAME; import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import android.annotation.Nullable; -import android.app.ActivityManager; -import android.app.AlarmManager; -import android.app.IActivityManager; import android.app.INotificationManager; -import android.app.IWallpaperManager; -import android.app.WallpaperManager; import android.content.Context; -import android.content.res.Resources; -import android.hardware.SensorPrivacyManager; import android.hardware.display.AmbientDisplayConfiguration; import android.hardware.display.NightDisplayListener; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; -import android.os.PowerManager; import android.os.Process; import android.os.ServiceManager; -import android.os.UserHandle; import android.util.DisplayMetrics; import android.view.IWindowManager; -import android.view.WindowManager; -import android.view.WindowManagerGlobal; import com.android.internal.logging.MetricsLogger; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.widget.LockPatternUtils; -import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.systemui.dagger.qualifiers.BgHandler; +import com.android.systemui.dagger.qualifiers.BgLooper; +import com.android.systemui.dagger.qualifiers.MainHandler; +import com.android.systemui.dagger.qualifiers.MainLooper; import com.android.systemui.doze.AlwaysOnDisplayPolicy; import com.android.systemui.plugins.PluginInitializerImpl; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.shared.plugins.PluginManagerImpl; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.DevicePolicyManagerWrapper; -import com.android.systemui.shared.system.PackageManagerWrapper; import com.android.systemui.statusbar.NavigationBarController; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.phone.AutoHideController; @@ -70,11 +54,7 @@ import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.util.leak.LeakDetector; -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; - import javax.inject.Named; -import javax.inject.Qualifier; import javax.inject.Singleton; import dagger.Module; @@ -82,16 +62,14 @@ import dagger.Provides; /** * Provides dependencies for the root component of sysui injection. + * + * Only SystemUI owned classes and instances should go in here. Other, framework-owned classes + * should go in {@link SystemServicesModule}. + * * See SystemUI/docs/dagger.md */ @Module public class DependencyProvider { - @Qualifier - @Documented - @Retention(RUNTIME) - public @interface MainResources { - // TODO: use attribute to get other, non-main resources? - } @Singleton @Provides @@ -104,7 +82,7 @@ public class DependencyProvider { @Singleton @Provides - @Named(BG_LOOPER_NAME) + @BgLooper public Looper provideBgLooper() { HandlerThread thread = new HandlerThread("SysUiBg", Process.THREAD_PRIORITY_BACKGROUND); @@ -113,29 +91,34 @@ public class DependencyProvider { } /** Main Looper */ - @Singleton @Provides - @Named(MAIN_LOOPER_NAME) + @MainLooper public Looper provideMainLooper() { return Looper.getMainLooper(); } @Singleton @Provides - @Named(BG_HANDLER_NAME) - public Handler provideBgHandler(@Named(BG_LOOPER_NAME) Looper bgLooper) { + @BgHandler + public Handler provideBgHandler(@BgLooper Looper bgLooper) { return new Handler(bgLooper); } @Singleton @Provides - @Named(MAIN_HANDLER_NAME) - public Handler provideMainHandler(@Named(MAIN_LOOPER_NAME) Looper mainLooper) { + @MainHandler + public Handler provideMainHandler(@MainLooper Looper mainLooper) { return new Handler(mainLooper); } /** */ @Provides + public AmbientDisplayConfiguration provideAmbientDispalyConfiguration(Context context) { + return new AmbientDisplayConfiguration(context); + } + + /** */ + @Provides public Handler provideHandler() { return new Handler(); } @@ -148,23 +131,10 @@ public class DependencyProvider { @Singleton @Provides - @Nullable - public LocalBluetoothManager provideLocalBluetoothController(Context context, - @Named(BG_HANDLER_NAME) Handler bgHandler) { - return LocalBluetoothManager.create(context, bgHandler, - UserHandle.ALL); - } - - @Singleton - @Provides - public MetricsLogger provideMetricsLogger() { - return new MetricsLogger(); - } - - @Singleton - @Provides - public IWindowManager provideIWindowManager() { - return WindowManagerGlobal.getWindowManagerService(); + // Single instance of DisplayMetrics, gets updated by StatusBar, but can be used + // anywhere it is needed. + public DisplayMetrics provideDisplayMetrics() { + return new DisplayMetrics(); } @Singleton @@ -184,29 +154,21 @@ public class DependencyProvider { @Singleton @Provides - // Single instance of DisplayMetrics, gets updated by StatusBar, but can be used - // anywhere it is needed. - public DisplayMetrics provideDisplayMetrics() { - return new DisplayMetrics(); - } + public LeakDetector provideLeakDetector() { + return LeakDetector.create(); - @Singleton - @Provides - public SensorPrivacyManager provideSensorPrivacyManager(Context context) { - return context.getSystemService(SensorPrivacyManager.class); } @Singleton @Provides - public LeakDetector provideLeakDetector() { - return LeakDetector.create(); - + public MetricsLogger provideMetricsLogger() { + return new MetricsLogger(); } @Singleton @Provides public NightDisplayListener provideNightDisplayListener(Context context, - @Named(BG_HANDLER_NAME) Handler bgHandler) { + @BgHandler Handler bgHandler) { return new NightDisplayListener(context, bgHandler); } @@ -219,7 +181,7 @@ public class DependencyProvider { @Singleton @Provides public NavigationBarController provideNavigationBarController(Context context, - @Named(MAIN_HANDLER_NAME) Handler mainHandler) { + @MainHandler Handler mainHandler) { return new NavigationBarController(context, mainHandler); } @@ -232,7 +194,7 @@ public class DependencyProvider { @Singleton @Provides public AutoHideController provideAutoHideController(Context context, - @Named(MAIN_HANDLER_NAME) Handler mainHandler, + @MainHandler Handler mainHandler, NotificationRemoteInputManager notificationRemoteInputManager, IWindowManager iWindowManager) { return new AutoHideController(context, mainHandler, notificationRemoteInputManager, @@ -253,25 +215,12 @@ public class DependencyProvider { @Singleton @Provides - public PackageManagerWrapper providePackageManagerWrapper() { - return PackageManagerWrapper.getInstance(); - } - - @Singleton - @Provides public DeviceProvisionedController provideDeviceProvisionedController(Context context, - @Named(MAIN_HANDLER_NAME) Handler mainHandler) { + @MainHandler Handler mainHandler) { return new DeviceProvisionedControllerImpl(context, mainHandler); } /** */ - @Singleton - @Provides - public AlarmManager provideAlarmManager(Context context) { - return context.getSystemService(AlarmManager.class); - } - - /** */ @Provides public LockPatternUtils provideLockPatternUtils(Context context) { return new LockPatternUtils(context); @@ -279,52 +228,7 @@ public class DependencyProvider { /** */ @Provides - public AmbientDisplayConfiguration provideAmbientDispalyConfiguration(Context context) { - return new AmbientDisplayConfiguration(context); - } - - /** */ - @Provides public AlwaysOnDisplayPolicy provideAlwaysOnDisplayPolicy(Context context) { return new AlwaysOnDisplayPolicy(context); } - - /** */ - @Provides - public PowerManager providePowerManager(Context context) { - return context.getSystemService(PowerManager.class); - } - - /** */ - @Provides - @MainResources - public Resources provideResources(Context context) { - return context.getResources(); - } - - /** */ - @Provides - @Nullable - public IWallpaperManager provideIWallpaperManager() { - return IWallpaperManager.Stub.asInterface( - ServiceManager.getService(Context.WALLPAPER_SERVICE)); - } - - /** */ - @Provides - public WallpaperManager providesWallpaperManager(Context context) { - return (WallpaperManager) context.getSystemService(Context.WALLPAPER_SERVICE); - } - - /** */ - @Provides - public WindowManager providesWindowManager(Context context) { - return context.getSystemService(WindowManager.class); - } - - /** */ - @Provides - public IActivityManager providesIActivityManager() { - return ActivityManager.getService(); - } } diff --git a/packages/SystemUI/src/com/android/systemui/ServiceBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/ServiceBinder.java index c11236ebae53..1f2c0a18f928 100644 --- a/packages/SystemUI/src/com/android/systemui/ServiceBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ServiceBinder.java @@ -14,10 +14,11 @@ * limitations under the License. */ -package com.android.systemui; +package com.android.systemui.dagger; import android.app.Service; +import com.android.systemui.ImageWallpaper; import com.android.systemui.doze.DozeService; import com.android.systemui.keyguard.KeyguardService; diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java new file mode 100644 index 000000000000..891bf615b8f4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemServicesModule.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.dagger; + +import android.annotation.Nullable; +import android.annotation.SuppressLint; +import android.app.ActivityManager; +import android.app.AlarmManager; +import android.app.IActivityManager; +import android.app.IWallpaperManager; +import android.app.WallpaperManager; +import android.content.Context; +import android.content.res.Resources; +import android.hardware.SensorPrivacyManager; +import android.os.Handler; +import android.os.PowerManager; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.view.IWindowManager; +import android.view.WindowManager; +import android.view.WindowManagerGlobal; + +import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.systemui.dagger.qualifiers.BgHandler; +import com.android.systemui.dagger.qualifiers.MainResources; +import com.android.systemui.shared.system.PackageManagerWrapper; + +import javax.inject.Singleton; + +import dagger.Module; +import dagger.Provides; + +/** + * Provides Non-SystemUI, Framework-Owned instances to the dependency graph. + */ +@Module +public class SystemServicesModule { + + @Singleton + @Provides + static AlarmManager provideAlarmManager(Context context) { + return context.getSystemService(AlarmManager.class); + } + + @Singleton + @Provides + static IActivityManager provideIActivityManager() { + return ActivityManager.getService(); + } + + @Provides + @Nullable + static IWallpaperManager provideIWallPaperManager() { + return IWallpaperManager.Stub.asInterface( + ServiceManager.getService(Context.WALLPAPER_SERVICE)); + } + + @Singleton + @Provides + static IWindowManager provideIWindowManager() { + return WindowManagerGlobal.getWindowManagerService(); + } + + @SuppressLint("MissingPermission") + @Singleton + @Provides + @Nullable + static LocalBluetoothManager provideLocalBluetoothController(Context context, + @BgHandler Handler bgHandler) { + return LocalBluetoothManager.create(context, bgHandler, UserHandle.ALL); + } + + @Singleton + @Provides + static PackageManagerWrapper providePackageManagerWrapper() { + return PackageManagerWrapper.getInstance(); + } + + /** */ + @Singleton + @Provides + static PowerManager providePowerManager(Context context) { + return context.getSystemService(PowerManager.class); + } + + @Provides + @MainResources + static Resources provideResources(Context context) { + return context.getResources(); + } + + @Singleton + @Provides + static SensorPrivacyManager provideSensorPrivacyManager(Context context) { + return context.getSystemService(SensorPrivacyManager.class); + } + + @Provides + static WallpaperManager provideWallpaperManager(Context context) { + return (WallpaperManager) context.getSystemService(Context.WALLPAPER_SERVICE); + } + + @Singleton + @Provides + static WindowManager provideWindowManager(Context context) { + return context.getSystemService(WindowManager.class); + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java index a5a55983fe51..49cd414f9d9e 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java @@ -14,8 +14,9 @@ * limitations under the License. */ -package com.android.systemui; +package com.android.systemui.dagger; +import com.android.systemui.SystemUI; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.pip.PipUI; import com.android.systemui.power.PowerUI; diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java index 176bcbfd9500..c95b50b195b3 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIDefaultModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java @@ -14,13 +14,14 @@ * limitations under the License. */ -package com.android.systemui; +package com.android.systemui.dagger; import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME; import static com.android.systemui.Dependency.LEAK_REPORT_EMAIL_NAME; import androidx.annotation.Nullable; +import com.android.systemui.SystemUI; import com.android.systemui.dock.DockManager; import com.android.systemui.dock.DockManagerImpl; import com.android.systemui.power.EnhancedEstimates; diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 4520a1a6a037..30f13979e66e 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui; +package com.android.systemui.dagger; import android.annotation.Nullable; import android.content.Context; diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java index bcbe672fbe97..113c9c845d95 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIRootComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java @@ -14,12 +14,15 @@ * limitations under the License. */ -package com.android.systemui; +package com.android.systemui.dagger; import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME; import android.content.ContentProvider; +import com.android.systemui.Dependency; +import com.android.systemui.SystemUIAppComponentFactory; +import com.android.systemui.SystemUIFactory; import com.android.systemui.fragments.FragmentService; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.util.InjectionInflationController; @@ -36,6 +39,7 @@ import dagger.Component; @Component(modules = { DependencyProvider.class, DependencyBinder.class, + SystemServicesModule.class, SystemUIFactory.ContextHolder.class, SystemUIModule.class, SystemUIDefaultModule.class}) diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/BgHandler.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/BgHandler.java new file mode 100644 index 000000000000..bc6b83ba1def --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/BgHandler.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.dagger.qualifiers; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Qualifier; + +@Qualifier +@Documented +@Retention(RUNTIME) +public @interface BgHandler { +} diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/BgLooper.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/BgLooper.java new file mode 100644 index 000000000000..2aadda1215d5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/BgLooper.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.dagger.qualifiers; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Qualifier; + +@Qualifier +@Documented +@Retention(RUNTIME) +public @interface BgLooper { +} diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/MainHandler.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/MainHandler.java new file mode 100644 index 000000000000..79661fa4a738 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/MainHandler.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.dagger.qualifiers; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Qualifier; + +@Qualifier +@Documented +@Retention(RUNTIME) +public @interface MainHandler { +} diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/MainLooper.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/MainLooper.java new file mode 100644 index 000000000000..750d7d72035c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/MainLooper.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.dagger.qualifiers; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Qualifier; + +@Qualifier +@Documented +@Retention(RUNTIME) +public @interface MainLooper { +} diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/MainResources.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/MainResources.java new file mode 100644 index 000000000000..3daeda550b4c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/MainResources.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.dagger.qualifiers; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Qualifier; + +@Qualifier +@Documented +@Retention(RUNTIME) +public @interface MainResources { + // TODO: use attribute to get other, non-main resources? +} diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java index 1a6bd60816b3..d1047e216ec4 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java @@ -68,6 +68,12 @@ public interface DozeHost { */ void prepareForGentleSleep(Runnable onDisplayOffCallback); + /** + * Cancel pending {@code onDisplayOffCallback} callback. + * @see #prepareForGentleSleep(Runnable) + */ + void cancelGentleSleep(); + void onIgnoreTouchWhilePulsing(boolean ignore); /** diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java index 95c42fcd175c..e1b4f3122861 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java @@ -71,7 +71,7 @@ public class DozeScreenState implements DozeMachine.Part { @Override public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) { int screenState = newState.screenState(mParameters); - mDozeHost.prepareForGentleSleep(null); + mDozeHost.cancelGentleSleep(); if (newState == DozeMachine.State.FINISH) { // Make sure not to apply the screen state after DozeService was destroyed. diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java index b4cc571be061..1b4857ebb209 100644 --- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java +++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java @@ -22,7 +22,7 @@ import android.view.View; import com.android.systemui.ConfigurationChangedReceiver; import com.android.systemui.Dumpable; -import com.android.systemui.SystemUIRootComponent; +import com.android.systemui.dagger.SystemUIRootComponent; import com.android.systemui.qs.QSFragment; import com.android.systemui.statusbar.phone.NavigationBarFragment; diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt index 631b8b7a14a0..22fb4c0dbdb5 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt @@ -22,25 +22,20 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter -import android.os.Handler -import android.os.Looper -import android.os.Message -import android.os.UserHandle -import android.os.UserManager +import android.os.* import android.provider.DeviceConfig import com.android.internal.annotations.VisibleForTesting import com.android.internal.config.sysui.SystemUiDeviceConfigFlags -import com.android.systemui.Dependency.BG_HANDLER_NAME -import com.android.systemui.Dependency.MAIN_HANDLER_NAME +import com.android.systemui.Dumpable import com.android.systemui.R import com.android.systemui.appops.AppOpItem import com.android.systemui.appops.AppOpsController -import com.android.systemui.Dumpable +import com.android.systemui.dagger.qualifiers.BgHandler +import com.android.systemui.dagger.qualifiers.MainHandler import java.io.FileDescriptor import java.io.PrintWriter import java.lang.ref.WeakReference import javax.inject.Inject -import javax.inject.Named import javax.inject.Singleton fun isPermissionsHubEnabled() = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, @@ -50,8 +45,8 @@ fun isPermissionsHubEnabled() = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_P class PrivacyItemController @Inject constructor( val context: Context, private val appOpsController: AppOpsController, - @Named(MAIN_HANDLER_NAME) private val uiHandler: Handler, - @Named(BG_HANDLER_NAME) private val bgHandler: Handler + @MainHandler private val uiHandler: Handler, + @BgHandler private val bgHandler: Handler ) : Dumpable { @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt new file mode 100644 index 000000000000..f710f7fc47e2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs + +import android.content.Context +import android.content.res.Configuration +import android.view.View +import android.view.ViewGroup +import com.android.systemui.R +import com.android.systemui.qs.TileLayout.exactly + +class DoubleLineTileLayout(context: Context) : ViewGroup(context), QSPanel.QSTileLayout { + + protected val mRecords = ArrayList<QSPanel.TileRecord>() + private var _listening = false + private var smallTileSize = 0 + private val twoLineHeight + get() = smallTileSize * 2 + cellMarginVertical + private var cellMarginHorizontal = 0 + private var cellMarginVertical = 0 + + init { + isFocusableInTouchMode = true + clipChildren = false + clipToPadding = false + + updateResources() + } + + override fun addTile(tile: QSPanel.TileRecord) { + mRecords.add(tile) + tile.tile.setListening(this, _listening) + addTileView(tile) + } + + protected fun addTileView(tile: QSPanel.TileRecord) { + addView(tile.tileView) + } + + override fun removeTile(tile: QSPanel.TileRecord) { + mRecords.remove(tile) + tile.tile.setListening(this, false) + removeView(tile.tileView) + } + + override fun removeAllViews() { + mRecords.forEach { it.tile.setListening(this, false) } + mRecords.clear() + super.removeAllViews() + } + + override fun getOffsetTop(tile: QSPanel.TileRecord?) = top + + override fun updateResources(): Boolean { + with(mContext.resources) { + smallTileSize = getDimensionPixelSize(R.dimen.qs_quick_tile_size) + cellMarginHorizontal = getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal) + cellMarginVertical = getDimensionPixelSize(R.dimen.new_qs_vertical_margin) + } + requestLayout() + return false + } + + override fun setListening(listening: Boolean) { + if (_listening == listening) return + _listening = listening + for (record in mRecords) { + record.tile.setListening(this, listening) + } + } + + override fun getNumVisibleTiles() = mRecords.size + + override fun onConfigurationChanged(newConfig: Configuration) { + super.onConfigurationChanged(newConfig) + updateResources() + } + + override fun onFinishInflate() { + updateResources() + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + var previousView: View = this + var tiles = 0 + + mRecords.forEach { + val tileView = it.tileView + if (tileView.visibility != View.GONE) { + tileView.updateAccessibilityOrder(previousView) + previousView = tileView + tiles++ + tileView.measure(exactly(smallTileSize), exactly(smallTileSize)) + } + } + + val height = twoLineHeight + val columns = tiles / 2 + val width = paddingStart + paddingEnd + + columns * smallTileSize + + (columns - 1) * cellMarginHorizontal + setMeasuredDimension(width, height) + } + + override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) { + val tiles = mRecords.filter { it.tileView.visibility != View.GONE } + tiles.forEachIndexed { + index, tile -> + val column = index % (tiles.size / 2) + val left = getLeftForColumn(column) + val top = if (index < tiles.size / 2) 0 else getTopBottomRow() + tile.tileView.layout(left, top, left + smallTileSize, top + smallTileSize) + } + } + + private fun getLeftForColumn(column: Int) = column * (smallTileSize + cellMarginHorizontal) + + private fun getTopBottomRow() = smallTileSize + cellMarginVertical +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java index 9221b6852112..a267bbb92ee7 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java @@ -14,6 +14,7 @@ package com.android.systemui.qs; +import android.provider.Settings; import android.util.Log; import android.view.View; import android.view.View.OnAttachStateChangeListener; @@ -267,6 +268,17 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha mAllViews.add(tileView); count++; } + + + int flag = Settings.System.getInt(mQsPanel.getContext().getContentResolver(), + "qs_media_player", 0); + if (flag == 1) { + View qsMediaView = mQsPanel.getMediaPanel(); + View qqsMediaView = mQuickQsPanel.getMediaPlayer().getView(); + translationXBuilder.addFloat(qsMediaView, "alpha", 0, 1); + translationXBuilder.addFloat(qqsMediaView, "alpha", 1, 0); + } + if (mAllowFancy) { // Make brightness appear static position and alpha in through second half. View brightness = mQsPanel.getBrightnessView(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java b/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java index b9f3a7fcc63b..5742787b39bc 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSCarrierGroup.java @@ -17,9 +17,7 @@ package com.android.systemui.qs; import static com.android.systemui.Dependency.BG_HANDLER; -import static com.android.systemui.Dependency.BG_HANDLER_NAME; import static com.android.systemui.Dependency.MAIN_LOOPER; -import static com.android.systemui.Dependency.MAIN_LOOPER_NAME; import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT; import android.annotation.MainThread; @@ -41,6 +39,8 @@ import androidx.annotation.VisibleForTesting; import com.android.keyguard.CarrierTextController; import com.android.systemui.Dependency; import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.BgHandler; +import com.android.systemui.dagger.qualifiers.MainLooper; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.statusbar.policy.NetworkController; @@ -77,8 +77,8 @@ public class QSCarrierGroup extends LinearLayout implements @Inject public QSCarrierGroup(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs, NetworkController networkController, ActivityStarter activityStarter, - @Named(BG_HANDLER_NAME) Handler handler, - @Named(MAIN_LOOPER_NAME) Looper looper) { + @BgHandler Handler handler, + @MainLooper Looper looper) { super(context, attrs); mNetworkController = networkController; mActivityStarter = activityStarter; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java new file mode 100644 index 000000000000..af418f6308a0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/QSMediaPlayer.java @@ -0,0 +1,301 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs; + +import android.app.Notification; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.res.ColorStateList; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; +import android.graphics.drawable.Icon; +import android.graphics.drawable.RippleDrawable; +import android.media.MediaMetadata; +import android.media.session.MediaController; +import android.media.session.MediaSession; +import android.media.session.PlaybackState; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.RemoteViews; +import android.widget.TextView; + +import androidx.core.graphics.drawable.RoundedBitmapDrawable; +import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory; + +import com.android.settingslib.media.MediaOutputSliceConstants; +import com.android.systemui.Dependency; +import com.android.systemui.R; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.statusbar.MediaTransferManager; + +/** + * Single media player for carousel in QSPanel + */ +public class QSMediaPlayer { + + private static final String TAG = "QSMediaPlayer"; + + private Context mContext; + private LinearLayout mMediaNotifView; + private MediaSession.Token mToken; + private MediaController mController; + private int mWidth; + private int mHeight; + + /** + * + * @param context + * @param parent + * @param width + * @param height + */ + public QSMediaPlayer(Context context, ViewGroup parent, int width, int height) { + mContext = context; + LayoutInflater inflater = LayoutInflater.from(mContext); + mMediaNotifView = (LinearLayout) inflater.inflate(R.layout.qs_media_panel, parent, false); + + mWidth = width; + mHeight = height; + } + + public View getView() { + return mMediaNotifView; + } + + /** + * + * @param token token for this media session + * @param icon app notification icon + * @param iconColor foreground color (for text, icons) + * @param bgColor background color + * @param actionsContainer a LinearLayout containing the media action buttons + * @param notif + */ + public void setMediaSession(MediaSession.Token token, Icon icon, int iconColor, int bgColor, + View actionsContainer, Notification notif) { + Log.d(TAG, "got media session: " + token); + mToken = token; + mController = new MediaController(mContext, token); + MediaMetadata mMediaMetadata = mController.getMetadata(); + Notification.Builder builder = Notification.Builder.recoverBuilder(mContext, notif); + + // Album art + addAlbumArtBackground(mMediaMetadata, bgColor, mWidth, mHeight); + + // Reuse notification header instead of reimplementing everything + RemoteViews headerRemoteView = builder.makeNotificationHeader(); + LinearLayout headerView = mMediaNotifView.findViewById(R.id.header); + View result = headerRemoteView.apply(mContext, headerView); + result.setPadding(0, 0, 0, 0); + LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, 75); + result.setLayoutParams(lp); + headerView.removeAllViews(); + headerView.addView(result); + + View seamless = headerView.findViewById(com.android.internal.R.id.media_seamless); + seamless.setVisibility(View.VISIBLE); + + // App icon + ImageView appIcon = headerView.findViewById(com.android.internal.R.id.icon); + Drawable iconDrawable = icon.loadDrawable(mContext); + iconDrawable.setTint(iconColor); + appIcon.setImageDrawable(iconDrawable); + + // App title + TextView appName = headerView.findViewById(com.android.internal.R.id.app_name_text); + String appNameString = builder.loadHeaderAppName(); + appName.setText(appNameString); + appName.setTextColor(iconColor); + + // Action + mMediaNotifView.setOnClickListener(v -> { + try { + notif.contentIntent.send(); + // Also close shade + mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); + } catch (PendingIntent.CanceledException e) { + Log.e(TAG, "Pending intent was canceled"); + e.printStackTrace(); + } + }); + + // Separator + TextView separator = headerView.findViewById(com.android.internal.R.id.header_text_divider); + separator.setTextColor(iconColor); + + // Album name + TextView albumName = headerView.findViewById(com.android.internal.R.id.header_text); + String albumString = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_ALBUM); + if (!albumString.isEmpty()) { + albumName.setText(albumString); + albumName.setTextColor(iconColor); + albumName.setVisibility(View.VISIBLE); + separator.setVisibility(View.VISIBLE); + } else { + albumName.setVisibility(View.GONE); + separator.setVisibility(View.GONE); + } + + // Transfer chip + MediaTransferManager mediaTransferManager = new MediaTransferManager(mContext); + View transferBackgroundView = headerView.findViewById( + com.android.internal.R.id.media_seamless); + LinearLayout viewLayout = (LinearLayout) transferBackgroundView; + RippleDrawable bkgDrawable = (RippleDrawable) viewLayout.getBackground(); + GradientDrawable rect = (GradientDrawable) bkgDrawable.getDrawable(0); + rect.setStroke(2, iconColor); + rect.setColor(bgColor); + ImageView transferIcon = headerView.findViewById( + com.android.internal.R.id.media_seamless_image); + transferIcon.setBackgroundColor(bgColor); + transferIcon.setImageTintList(ColorStateList.valueOf(iconColor)); + TextView transferText = headerView.findViewById( + com.android.internal.R.id.media_seamless_text); + transferText.setTextColor(iconColor); + + ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class); + transferBackgroundView.setOnClickListener(v -> { + final Intent intent = new Intent() + .setAction(MediaOutputSliceConstants.ACTION_MEDIA_OUTPUT); + mActivityStarter.startActivity(intent, false, true /* dismissShade */, + Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + }); + + // Artist name + TextView artistText = mMediaNotifView.findViewById(R.id.header_title); + String artistName = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_ARTIST); + artistText.setText(artistName); + artistText.setTextColor(iconColor); + + // Song name + TextView titleText = mMediaNotifView.findViewById(R.id.header_text); + String songName = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_TITLE); + titleText.setText(songName); + titleText.setTextColor(iconColor); + + // Media controls + LinearLayout parentActionsLayout = (LinearLayout) actionsContainer; + final int[] actionIds = { + R.id.action0, + R.id.action1, + R.id.action2, + R.id.action3, + R.id.action4 + }; + final int[] notifActionIds = { + com.android.internal.R.id.action0, + com.android.internal.R.id.action1, + com.android.internal.R.id.action2, + com.android.internal.R.id.action3, + com.android.internal.R.id.action4 + }; + for (int i = 0; i < parentActionsLayout.getChildCount() && i < actionIds.length; i++) { + ImageButton thisBtn = mMediaNotifView.findViewById(actionIds[i]); + ImageButton thatBtn = parentActionsLayout.findViewById(notifActionIds[i]); + if (thatBtn == null || thatBtn.getDrawable() == null) { + thisBtn.setVisibility(View.GONE); + continue; + } + + Drawable thatIcon = thatBtn.getDrawable(); + thisBtn.setImageDrawable(thatIcon.mutate()); + thisBtn.setVisibility(View.VISIBLE); + thisBtn.setOnClickListener(v -> { + Log.d(TAG, "clicking on other button"); + thatBtn.performClick(); + }); + } + } + + public MediaSession.Token getMediaSessionToken() { + return mToken; + } + + public String getMediaPlayerPackage() { + return mController.getPackageName(); + } + + /** + * Check whether the media controlled by this player is currently playing + * @return whether it is playing, or false if no controller information + */ + public boolean isPlaying() { + if (mController == null) { + return false; + } + + PlaybackState state = mController.getPlaybackState(); + if (state == null) { + return false; + } + + return (state.getState() == PlaybackState.STATE_PLAYING); + } + + private void addAlbumArtBackground(MediaMetadata metadata, int bgColor, int width, int height) { + Bitmap albumArt = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART); + if (albumArt != null) { + + Bitmap original = albumArt.copy(Bitmap.Config.ARGB_8888, true); + Bitmap scaled = scaleBitmap(original, width, height); + Canvas canvas = new Canvas(scaled); + + // Add translucent layer over album art to improve contrast + Paint p = new Paint(); + p.setStyle(Paint.Style.FILL); + p.setColor(bgColor); + p.setAlpha(200); + canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), p); + + RoundedBitmapDrawable roundedDrawable = RoundedBitmapDrawableFactory.create( + mContext.getResources(), scaled); + roundedDrawable.setCornerRadius(20); + + mMediaNotifView.setBackground(roundedDrawable); + } else { + Log.e(TAG, "No album art available"); + } + } + + private Bitmap scaleBitmap(Bitmap original, int width, int height) { + Bitmap cropped = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(cropped); + + float scale = (float) cropped.getWidth() / (float) original.getWidth(); + float dy = (cropped.getHeight() - original.getHeight() * scale) / 2.0f; + Matrix transformation = new Matrix(); + transformation.postTranslate(0, dy); + transformation.preScale(scale, scale); + + Paint paint = new Paint(); + paint.setFilterBitmap(true); + canvas.drawBitmap(original, transformation, paint); + + return cropped; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 2e24403d460f..20600596c3a1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -24,16 +24,22 @@ import android.content.ComponentName; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; +import android.graphics.drawable.Icon; +import android.media.session.MediaSession; import android.metrics.LogMaker; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.provider.Settings; +import android.service.notification.StatusBarNotification; import android.service.quicksettings.Tile; import android.util.AttributeSet; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; import android.widget.FrameLayout; +import android.widget.HorizontalScrollView; import android.widget.LinearLayout; import com.android.internal.logging.MetricsLogger; @@ -82,6 +88,9 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class); private final QSTileRevealController mQsTileRevealController; + private final LinearLayout mMediaCarousel; + private final ArrayList<QSMediaPlayer> mMediaPlayers = new ArrayList<>(); + protected boolean mExpanded; protected boolean mListening; @@ -140,6 +149,27 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne addDivider(); + // Add media carousel + int flag = Settings.System.getInt(context.getContentResolver(), "qs_media_player", 0); + if (flag == 1) { + HorizontalScrollView mediaScrollView = new HorizontalScrollView(mContext); + mediaScrollView.setHorizontalScrollBarEnabled(false); + int playerHeight = (int) getResources().getDimension(R.dimen.qs_media_height); + int padding = (int) getResources().getDimension(R.dimen.qs_media_padding); + LayoutParams lpView = new LayoutParams(LayoutParams.MATCH_PARENT, playerHeight); + lpView.setMarginStart(padding); + lpView.setMarginEnd(padding); + addView(mediaScrollView, lpView); + + LayoutParams lpCarousel = new LayoutParams(LayoutParams.MATCH_PARENT, + LayoutParams.WRAP_CONTENT); + mMediaCarousel = new LinearLayout(mContext); + mMediaCarousel.setOrientation(LinearLayout.HORIZONTAL); + mediaScrollView.addView(mMediaCarousel, lpCarousel); + } else { + mMediaCarousel = null; + } + mFooter = new QSSecurityFooter(this, context); addView(mFooter.getView()); @@ -159,6 +189,72 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne } + /** + * Add or update a player for the associated media session + * @param token + * @param icon + * @param iconColor + * @param bgColor + * @param actionsContainer + * @param notif + */ + public void addMediaSession(MediaSession.Token token, Icon icon, int iconColor, int bgColor, + View actionsContainer, StatusBarNotification notif) { + int flag = Settings.System.getInt(mContext.getContentResolver(), "qs_media_player", 0); + if (flag != 1) { + // Shouldn't happen, but just in case + Log.e(TAG, "Tried to add media session without player!"); + return; + } + QSMediaPlayer player = null; + String packageName = notif.getPackageName(); + for (QSMediaPlayer p : mMediaPlayers) { + if (p.getMediaSessionToken().equals(token)) { + Log.d(TAG, "a player for this session already exists"); + player = p; + break; + } + + if (packageName.equals(p.getMediaPlayerPackage())) { + Log.d(TAG, "found an old session for this app"); + player = p; + break; + } + } + + int playerHeight = (int) getResources().getDimension(R.dimen.qs_media_height); + int playerWidth = (int) getResources().getDimension(R.dimen.qs_media_width); + int padding = (int) getResources().getDimension(R.dimen.qs_media_padding); + LayoutParams lp = new LayoutParams(playerWidth, ViewGroup.LayoutParams.MATCH_PARENT); + lp.setMarginStart(padding); + lp.setMarginEnd(padding); + + if (player == null) { + Log.d(TAG, "creating new player"); + + player = new QSMediaPlayer(mContext, this, playerWidth, playerHeight); + + if (player.isPlaying()) { + mMediaCarousel.addView(player.getView(), 0, lp); // add in front + } else { + mMediaCarousel.addView(player.getView(), lp); // add at end + } + } else if (player.isPlaying()) { + // move it to the front + mMediaCarousel.removeView(player.getView()); + mMediaCarousel.addView(player.getView(), 0, lp); + } + + Log.d(TAG, "setting player session"); + player.setMediaSession(token, icon, iconColor, bgColor, actionsContainer, + notif.getNotification()); + mMediaPlayers.add(player); + } + + protected View getMediaPanel() { + return mMediaCarousel; + } + protected void addDivider() { mDivider = LayoutInflater.from(mContext).inflate(R.layout.qs_divider, this, false); mDivider.setBackgroundColor(Utils.applyAlpha(mDivider.getAlpha(), diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java index 1e763cf79240..b395c3c336d3 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java @@ -30,11 +30,12 @@ import android.service.quicksettings.Tile; import android.text.TextUtils; import android.util.Log; -import com.android.systemui.Dependency; import com.android.systemui.DumpController; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.SysUiServiceProvider; +import com.android.systemui.dagger.qualifiers.BgLooper; +import com.android.systemui.dagger.qualifiers.MainHandler; import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.qs.QSFactory; import com.android.systemui.plugins.qs.QSTile; @@ -61,7 +62,6 @@ import java.util.List; import java.util.function.Predicate; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Provider; import javax.inject.Singleton; @@ -94,8 +94,8 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D public QSTileHost(Context context, StatusBarIconController iconController, QSFactoryImpl defaultFactory, - @Named(Dependency.MAIN_HANDLER_NAME) Handler mainHandler, - @Named(Dependency.BG_LOOPER_NAME) Looper bgLooper, + @MainHandler Handler mainHandler, + @BgLooper Looper bgLooper, PluginManager pluginManager, TunerService tunerService, Provider<AutoTileManager> autoTiles, diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java new file mode 100644 index 000000000000..ae66cd576765 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSMediaPlayer.java @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs; + +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; +import android.media.MediaMetadata; +import android.media.session.MediaController; +import android.media.session.MediaSession; +import android.media.session.PlaybackState; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.core.graphics.drawable.RoundedBitmapDrawable; +import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory; + +import com.android.systemui.R; + +/** + * QQS mini media player + */ +public class QuickQSMediaPlayer { + + private static final String TAG = "QQSMediaPlayer"; + + private Context mContext; + private LinearLayout mMediaNotifView; + private MediaSession.Token mToken; + private MediaController mController; + + /** + * + * @param context + * @param parent + */ + public QuickQSMediaPlayer(Context context, ViewGroup parent) { + mContext = context; + LayoutInflater inflater = LayoutInflater.from(mContext); + mMediaNotifView = (LinearLayout) inflater.inflate(R.layout.qqs_media_panel, parent, false); + } + + public View getView() { + return mMediaNotifView; + } + + /** + * + * @param token token for this media session + * @param icon app notification icon + * @param iconColor foreground color (for text, icons) + * @param bgColor background color + * @param actionsContainer a LinearLayout containing the media action buttons + */ + public void setMediaSession(MediaSession.Token token, Icon icon, int iconColor, int bgColor, + View actionsContainer) { + Log.d(TAG, "Setting media session: " + token); + mToken = token; + mController = new MediaController(mContext, token); + MediaMetadata mMediaMetadata = mController.getMetadata(); + + // Album art + addAlbumArtBackground(mMediaMetadata, bgColor); + + // App icon + ImageView appIcon = mMediaNotifView.findViewById(R.id.icon); + Drawable iconDrawable = icon.loadDrawable(mContext); + iconDrawable.setTint(iconColor); + appIcon.setImageDrawable(iconDrawable); + + // Artist name + TextView appText = mMediaNotifView.findViewById(R.id.header_title); + String artistName = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_ARTIST); + appText.setText(artistName); + appText.setTextColor(iconColor); + + // Song name + TextView titleText = mMediaNotifView.findViewById(R.id.header_text); + String songName = mMediaMetadata.getString(MediaMetadata.METADATA_KEY_TITLE); + titleText.setText(songName); + titleText.setTextColor(iconColor); + + // Action buttons + LinearLayout parentActionsLayout = (LinearLayout) actionsContainer; + final int[] actionIds = {R.id.action0, R.id.action1, R.id.action2}; + + // TODO some apps choose different buttons to show in compact mode + final int[] notifActionIds = { + com.android.internal.R.id.action1, + com.android.internal.R.id.action2, + com.android.internal.R.id.action3 + }; + for (int i = 0; i < parentActionsLayout.getChildCount() && i < actionIds.length; i++) { + ImageButton thisBtn = mMediaNotifView.findViewById(actionIds[i]); + ImageButton thatBtn = parentActionsLayout.findViewById(notifActionIds[i]); + if (thatBtn == null || thatBtn.getDrawable() == null) { + thisBtn.setVisibility(View.GONE); + continue; + } + + Drawable thatIcon = thatBtn.getDrawable(); + thisBtn.setImageDrawable(thatIcon.mutate()); + thisBtn.setVisibility(View.VISIBLE); + + thisBtn.setOnClickListener(v -> { + Log.d(TAG, "clicking on other button"); + thatBtn.performClick(); + }); + } + } + + public MediaSession.Token getMediaSessionToken() { + return mToken; + } + + /** + * Check whether the media controlled by this player is currently playing + * @return whether it is playing, or false if no controller information + */ + public boolean isPlaying() { + if (mController == null) { + return false; + } + + PlaybackState state = mController.getPlaybackState(); + if (state == null) { + return false; + } + + return (state.getState() == PlaybackState.STATE_PLAYING); + } + + private void addAlbumArtBackground(MediaMetadata metadata, int bgColor) { + Bitmap albumArt = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART); + if (albumArt != null) { + Rect bounds = new Rect(); + mMediaNotifView.getBoundsOnScreen(bounds); + int width = bounds.width(); + int height = bounds.height(); + + Bitmap original = albumArt.copy(Bitmap.Config.ARGB_8888, true); + Bitmap scaled = scaleBitmap(original, width, height); + Canvas canvas = new Canvas(scaled); + + // Add translucent layer over album art to improve contrast + Paint p = new Paint(); + p.setStyle(Paint.Style.FILL); + p.setColor(bgColor); + p.setAlpha(200); + canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), p); + + RoundedBitmapDrawable roundedDrawable = RoundedBitmapDrawableFactory.create( + mContext.getResources(), scaled); + roundedDrawable.setCornerRadius(20); + + mMediaNotifView.setBackground(roundedDrawable); + } else { + Log.e(TAG, "No album art available"); + } + } + + private Bitmap scaleBitmap(Bitmap original, int width, int height) { + Bitmap cropped = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(cropped); + + float scale = (float) cropped.getWidth() / (float) original.getWidth(); + float dy = (cropped.getHeight() - original.getHeight() * scale) / 2.0f; + Matrix transformation = new Matrix(); + transformation.postTranslate(0, dy); + transformation.preScale(scale, scale); + + Paint paint = new Paint(); + paint.setFilterBitmap(true); + canvas.drawBitmap(original, transformation, paint); + + return cropped; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java index 85aafa06961a..dcd4633a79d2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java @@ -21,6 +21,7 @@ import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEX import android.content.Context; import android.content.res.Configuration; import android.graphics.Rect; +import android.provider.Settings; import android.util.AttributeSet; import android.view.Gravity; import android.view.View; @@ -55,6 +56,7 @@ public class QuickQSPanel extends QSPanel { private boolean mDisabledByPolicy; private int mMaxTiles; protected QSPanel mFullPanel; + private QuickQSMediaPlayer mMediaPlayer; @Inject public QuickQSPanel(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs, @@ -69,11 +71,43 @@ public class QuickQSPanel extends QSPanel { } removeView((View) mTileLayout); } - sDefaultMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns); - mTileLayout = new HeaderTileLayout(context); - mTileLayout.setListening(mListening); - addView((View) mTileLayout, 0 /* Between brightness and footer */); - super.setPadding(0, 0, 0, 0); + + int flag = Settings.System.getInt(context.getContentResolver(), "qs_media_player", 0); + if (flag == 1) { + LinearLayout mHorizontalLinearLayout = new LinearLayout(mContext); + mHorizontalLinearLayout.setOrientation(LinearLayout.HORIZONTAL); + mHorizontalLinearLayout.setClipChildren(false); + mHorizontalLinearLayout.setClipToPadding(false); + + LayoutParams lp = new LayoutParams(0, LayoutParams.MATCH_PARENT, 1); + + mTileLayout = new DoubleLineTileLayout(context); + lp.setMarginEnd(10); + lp.setMarginStart(0); + mHorizontalLinearLayout.addView((View) mTileLayout, lp); + + mMediaPlayer = new QuickQSMediaPlayer(mContext, mHorizontalLinearLayout); + + lp.setMarginEnd(0); + lp.setMarginStart(10); + mHorizontalLinearLayout.addView(mMediaPlayer.getView(), lp); + + sDefaultMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns); + + mTileLayout.setListening(mListening); + addView(mHorizontalLinearLayout, 0 /* Between brightness and footer */); + super.setPadding(0, 0, 0, 0); + } else { + sDefaultMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns); + mTileLayout = new HeaderTileLayout(context); + mTileLayout.setListening(mListening); + addView((View) mTileLayout, 0 /* Between brightness and footer */); + super.setPadding(0, 0, 0, 0); + } + } + + public QuickQSMediaPlayer getMediaPlayer() { + return mMediaPlayer; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index 4013586d4197..592e3881ea97 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -392,9 +392,15 @@ public class QuickStatusBarHeader extends RelativeLayout implements mSystemIconsView.setLayoutParams(mSystemIconsView.getLayoutParams()); FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams(); + + int flag = Settings.System.getInt(mContext.getContentResolver(), "qs_media_player", 0); if (mQsDisabled) { lp.height = resources.getDimensionPixelSize( com.android.internal.R.dimen.quick_qs_offset_height); + } else if (flag == 1) { + lp.height = Math.max(getMinimumHeight(), + resources.getDimensionPixelSize( + com.android.internal.R.dimen.quick_qs_total_height_with_media)); } else { lp.height = Math.max(getMinimumHeight(), resources.getDimensionPixelSize( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java index 0679595cef23..341c49a87156 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java @@ -16,18 +16,17 @@ package com.android.systemui.statusbar; -import static com.android.systemui.Dependency.BG_HANDLER_NAME; - import android.annotation.NonNull; import android.os.Handler; import android.os.HandlerExecutor; import android.provider.DeviceConfig; import android.util.ArrayMap; +import com.android.systemui.dagger.qualifiers.BgHandler; + import java.util.Map; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Singleton; /** @@ -50,8 +49,7 @@ public class FeatureFlags { private final Map<String, Boolean> mCachedDeviceConfigFlags = new ArrayMap<>(); @Inject - public FeatureFlags( - @Named(BG_HANDLER_NAME) Handler bgHandler) { + public FeatureFlags(@BgHandler Handler bgHandler) { DeviceConfig.addOnPropertiesChangedListener( "systemui", new HandlerExecutor(bgHandler), diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java index 275475d6d72c..1f389049f423 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java @@ -18,7 +18,6 @@ package com.android.systemui.statusbar; import static android.view.Display.DEFAULT_DISPLAY; -import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; import static com.android.systemui.SysUiServiceProvider.getComponent; import android.content.Context; @@ -38,6 +37,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.RegisterStatusBarResult; import com.android.systemui.Dependency; import com.android.systemui.assist.AssistHandleViewController; +import com.android.systemui.dagger.qualifiers.MainHandler; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.statusbar.CommandQueue.Callbacks; import com.android.systemui.statusbar.phone.AutoHideController; @@ -48,7 +48,6 @@ import com.android.systemui.statusbar.phone.NavigationBarView; import com.android.systemui.statusbar.policy.BatteryController; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Singleton; @@ -67,7 +66,7 @@ public class NavigationBarController implements Callbacks { SparseArray<NavigationBarFragment> mNavigationBars = new SparseArray<>(); @Inject - public NavigationBarController(Context context, @Named(MAIN_HANDLER_NAME) Handler handler) { + public NavigationBarController(Context context, @MainHandler Handler handler) { mContext = context; mHandler = handler; mDisplayManager = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java index 23968d59d58f..c838ac5315a7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java @@ -17,8 +17,6 @@ package com.android.systemui.statusbar; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY; -import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; - import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; @@ -52,6 +50,7 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.Dumpable; import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.MainHandler; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; @@ -69,7 +68,6 @@ import java.util.Objects; import java.util.Set; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Singleton; import dagger.Lazy; @@ -262,7 +260,7 @@ public class NotificationRemoteInputManager implements Dumpable { NotificationEntryManager notificationEntryManager, Lazy<ShadeController> shadeController, StatusBarStateController statusBarStateController, - @Named(MAIN_HANDLER_NAME) Handler mainHandler) { + @MainHandler Handler mainHandler) { mContext = context; mLockscreenUserManager = lockscreenUserManager; mSmartReplyController = smartReplyController; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java index c2bb5b7693c2..d6b87afc53b9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java @@ -16,19 +16,19 @@ package com.android.systemui.statusbar; -import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; - import android.content.Context; import android.content.res.Resources; import android.os.Handler; import android.os.Trace; import android.os.UserHandle; +import android.provider.Settings; import android.util.Log; import android.view.View; import android.view.ViewGroup; import com.android.systemui.R; import com.android.systemui.bubbles.BubbleController; +import com.android.systemui.dagger.qualifiers.MainHandler; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotificationEntryManager; @@ -47,7 +47,6 @@ import java.util.List; import java.util.Stack; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Singleton; import dagger.Lazy; @@ -88,6 +87,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle private final BubbleController mBubbleController; private final DynamicPrivacyController mDynamicPrivacyController; private final KeyguardBypassController mBypassController; + private final Context mContext; private NotificationPresenter mPresenter; private NotificationListContainer mListContainer; @@ -99,8 +99,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle private boolean mIsHandleDynamicPrivacyChangeScheduled; @Inject - public NotificationViewHierarchyManager(Context context, - @Named(MAIN_HANDLER_NAME) Handler mainHandler, + public NotificationViewHierarchyManager(Context context, @MainHandler Handler mainHandler, NotificationLockscreenUserManager notificationLockscreenUserManager, NotificationGroupManager groupManager, VisualStabilityManager visualStabilityManager, @@ -110,6 +109,7 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle KeyguardBypassController bypassController, BubbleController bubbleController, DynamicPrivacyController privacyController) { + mContext = context; mHandler = mainHandler; mLockscreenUserManager = notificationLockscreenUserManager; mBypassController = bypassController; @@ -146,7 +146,11 @@ public class NotificationViewHierarchyManager implements DynamicPrivacyControlle final int N = activeNotifications.size(); for (int i = 0; i < N; i++) { NotificationEntry ent = activeNotifications.get(i); + int flag = Settings.System.getInt(mContext.getContentResolver(), + "qs_media_player", 0); + boolean hideMedia = (flag == 1); if (ent.isRowDismissed() || ent.isRowRemoved() + || (ent.isMediaNotification() && hideMedia) || mBubbleController.isBubbleNotificationSuppressedFromShade(ent.getKey())) { // we don't want to update removed notifications because they could // temporarily become children if they were isolated before. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java index 6fe4abee6133..1daf48492ea0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java @@ -16,8 +16,6 @@ package com.android.systemui.statusbar.notification; -import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; - import android.os.Handler; import android.os.SystemClock; import android.view.View; @@ -25,6 +23,7 @@ import android.view.View; import androidx.collection.ArraySet; import com.android.systemui.Dumpable; +import com.android.systemui.dagger.qualifiers.MainHandler; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -35,7 +34,6 @@ import java.io.PrintWriter; import java.util.ArrayList; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Singleton; /** @@ -64,8 +62,7 @@ public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpabl @Inject public VisualStabilityManager( - NotificationEntryManager notificationEntryManager, - @Named(MAIN_HANDLER_NAME) Handler handler) { + NotificationEntryManager notificationEntryManager, @MainHandler Handler handler) { mHandler = handler; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java index 5d5c09e98f11..9bc0ca440893 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java @@ -28,6 +28,7 @@ import android.media.session.MediaSession; import android.media.session.PlaybackState; import android.metrics.LogMaker; import android.os.Handler; +import android.provider.Settings; import android.text.format.DateUtils; import android.view.LayoutInflater; import android.view.View; @@ -41,9 +42,12 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.widget.MediaNotificationView; import com.android.systemui.Dependency; +import com.android.systemui.qs.QSPanel; +import com.android.systemui.qs.QuickQSPanel; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.TransformableView; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; +import com.android.systemui.statusbar.phone.StatusBarWindowController; import java.util.Timer; import java.util.TimerTask; @@ -178,6 +182,26 @@ public class NotificationMediaTemplateViewWrapper extends NotificationTemplateVi final MediaSession.Token token = mRow.getEntry().getSbn().getNotification().extras .getParcelable(Notification.EXTRA_MEDIA_SESSION); + int flag = Settings.System.getInt(mContext.getContentResolver(), "qs_media_player", 0); + if (flag == 1) { + StatusBarWindowController ctrl = Dependency.get(StatusBarWindowController.class); + QuickQSPanel panel = ctrl.getStatusBarView().findViewById( + com.android.systemui.R.id.quick_qs_panel); + panel.getMediaPlayer().setMediaSession(token, + mRow.getStatusBarNotification().getNotification().getSmallIcon(), + getNotificationHeader().getOriginalIconColor(), + mRow.getCurrentBackgroundTint(), + mActions); + QSPanel bigPanel = ctrl.getStatusBarView().findViewById( + com.android.systemui.R.id.quick_settings_panel); + bigPanel.addMediaSession(token, + mRow.getStatusBarNotification().getNotification().getSmallIcon(), + getNotificationHeader().getOriginalIconColor(), + mRow.getCurrentBackgroundTint(), + mActions, + mRow.getStatusBarNotification()); + } + boolean showCompactSeekbar = mMediaManager.getShowCompactMediaSeekbar(); if (token == null || (COMPACT_MEDIA_TAG.equals(mView.getTag()) && !showCompactSeekbar)) { if (mSeekBarView != null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java index 008464e543a0..f9b936763308 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java @@ -16,8 +16,6 @@ package com.android.systemui.statusbar.phone; -import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; - import android.content.Context; import android.os.Handler; import android.os.RemoteException; @@ -25,10 +23,10 @@ import android.util.Log; import android.view.IWindowManager; import android.view.MotionEvent; +import com.android.systemui.dagger.qualifiers.MainHandler; import com.android.systemui.statusbar.NotificationRemoteInputManager; import javax.inject.Inject; -import javax.inject.Named; /** A controller to control all auto-hide things. */ public class AutoHideController { @@ -54,7 +52,7 @@ public class AutoHideController { }; @Inject - public AutoHideController(Context context, @Named(MAIN_HANDLER_NAME) Handler handler, + public AutoHideController(Context context, @MainHandler Handler handler, NotificationRemoteInputManager notificationRemoteInputManager, IWindowManager iWindowManager) { mHandler = handler; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java index 007c50c8765d..837517e2cc9b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java @@ -21,7 +21,7 @@ import android.os.Handler; import android.provider.Settings.Secure; import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.Dependency; +import com.android.systemui.dagger.qualifiers.BgHandler; import com.android.systemui.qs.AutoAddTracker; import com.android.systemui.qs.QSTileHost; import com.android.systemui.qs.SecureSetting; @@ -33,7 +33,6 @@ import com.android.systemui.statusbar.policy.HotspotController; import com.android.systemui.statusbar.policy.HotspotController.Callback; import javax.inject.Inject; -import javax.inject.Named; /** * Manages which tiles should be automatically added to QS. @@ -58,7 +57,7 @@ public class AutoTileManager { @Inject public AutoTileManager(Context context, AutoAddTracker autoAddTracker, QSTileHost host, - @Named(Dependency.BG_HANDLER_NAME) Handler handler, + @BgHandler Handler handler, HotspotController hotspotController, DataSaverController dataSaverController, ManagedProfileController managedProfileController, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java index 28dac87c92cf..50d33a70fed5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java @@ -25,8 +25,8 @@ import android.provider.Settings; import android.util.MathUtils; import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.DependencyProvider; import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.MainResources; import com.android.systemui.doze.AlwaysOnDisplayPolicy; import com.android.systemui.doze.DozeScreenState; import com.android.systemui.tuner.TunerService; @@ -59,7 +59,7 @@ public class DozeParameters implements TunerService.Tunable, @Inject protected DozeParameters( - @DependencyProvider.MainResources Resources resources, + @MainResources Resources resources, AmbientDisplayConfiguration ambientDisplayConfiguration, AlwaysOnDisplayPolicy alwaysOnDisplayPolicy, PowerManager powerManager, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java index c9c38a0ef5f5..1c8e832d03d2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java @@ -54,9 +54,7 @@ import com.android.systemui.R; import com.android.systemui.bubbles.BubbleController; import com.android.systemui.model.SysUiState; import com.android.systemui.recents.OverviewProxyService; -import com.android.systemui.shared.system.PinnedStackListenerForwarder.PinnedStackListener; import com.android.systemui.shared.system.QuickStepContract; -import com.android.systemui.shared.system.WindowManagerWrapper; import java.io.PrintWriter; import java.util.concurrent.Executor; @@ -70,15 +68,6 @@ public class EdgeBackGestureHandler implements DisplayListener { private static final int MAX_LONG_PRESS_TIMEOUT = SystemProperties.getInt( "gestures.back_timeout", 250); - private final PinnedStackListener mImeChangedListener = new PinnedStackListener() { - @Override - public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { - // No need to thread jump, assignments are atomic - mImeHeight = imeVisible ? imeHeight : 0; - // TODO: Probably cancel any existing gesture - } - }; - private ISystemGestureExclusionListener mGestureExclusionListener = new ISystemGestureExclusionListener.Stub() { @Override @@ -126,8 +115,6 @@ public class EdgeBackGestureHandler implements DisplayListener { private boolean mInRejectedExclusion = false; private boolean mIsOnLeftEdge; - private int mImeHeight = 0; - private boolean mIsAttached; private boolean mIsGesturalModeEnabled; private boolean mIsEnabled; @@ -227,7 +214,6 @@ public class EdgeBackGestureHandler implements DisplayListener { } if (!mIsEnabled) { - WindowManagerWrapper.getInstance().removePinnedStackListener(mImeChangedListener); mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this); try { @@ -244,7 +230,6 @@ public class EdgeBackGestureHandler implements DisplayListener { mContext.getMainThreadHandler()); try { - WindowManagerWrapper.getInstance().addPinnedStackListener(mImeChangedListener); WindowManagerGlobal.getWindowManagerService() .registerSystemGestureExclusionListener( mGestureExclusionListener, mDisplayId); @@ -301,11 +286,6 @@ public class EdgeBackGestureHandler implements DisplayListener { } private boolean isWithinTouchRegion(int x, int y) { - // Disallow if over the IME - if (y > (mDisplaySize.y - Math.max(mImeHeight, mNavBarHeight))) { - return false; - } - // Disallow if too far from the edge if (x > mEdgeWidth + mLeftInset && x < (mDisplaySize.x - mEdgeWidth - mRightInset)) { return false; @@ -483,7 +463,6 @@ public class EdgeBackGestureHandler implements DisplayListener { pw.println(" mInRejectedExclusion" + mInRejectedExclusion); pw.println(" mExcludeRegion=" + mExcludeRegion); pw.println(" mUnrestrictedExcludeRegion=" + mUnrestrictedExcludeRegion); - pw.println(" mImeHeight=" + mImeHeight); pw.println(" mIsAttached=" + mIsAttached); pw.println(" mEdgeWidth=" + mEdgeWidth); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index 89051cda15ab..30fe68a28ef2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -770,6 +770,11 @@ public class NotificationPanelView extends PanelView implements int sideMargin = res.getDimensionPixelOffset(R.dimen.notification_side_paddings); int topMargin = res.getDimensionPixelOffset(com.android.internal.R.dimen.quick_qs_total_height); + int flag = Settings.System.getInt(mContext.getContentResolver(), "qs_media_player", 0); + if (flag == 1) { + topMargin = res.getDimensionPixelOffset( + com.android.internal.R.dimen.quick_qs_total_height_with_media); + } lp = (FrameLayout.LayoutParams) mPluginFrame.getLayoutParams(); if (lp.width != qsWidth || lp.gravity != panelGravity || lp.leftMargin != sideMargin || lp.rightMargin != sideMargin || lp.topMargin != topMargin) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index c50b1bf2dc20..6064fbedf63d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -42,10 +42,10 @@ import com.android.internal.util.function.TriConsumer; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.DejankUtils; -import com.android.systemui.DependencyProvider; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.dagger.qualifiers.MainResources; import com.android.systemui.statusbar.ScrimView; import com.android.systemui.statusbar.notification.stack.ViewState; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -188,7 +188,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo @Inject public ScrimController(LightBarController lightBarController, DozeParameters dozeParameters, AlarmManager alarmManager, KeyguardStateController keyguardStateController, - @DependencyProvider.MainResources Resources resources, + @MainResources Resources resources, DelayedWakeLock.Builder delayedWakeLockBuilder, Handler handler, KeyguardUpdateMonitor keyguardUpdateMonitor, SysuiColorExtractor sysuiColorExtractor) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java index e059715986dc..13055ffb2f77 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java @@ -39,15 +39,20 @@ public enum ScrimState { @Override public void prepare(ScrimState previousState) { mFrontTint = Color.BLACK; - mBehindTint = previousState.mBehindTint; + mBehindTint = Color.BLACK; mBubbleTint = previousState.mBubbleTint; mFrontAlpha = 1f; - mBehindAlpha = previousState.mBehindAlpha; + mBehindAlpha = 1f; mBubbleAlpha = previousState.mBubbleAlpha; mAnimationDuration = ScrimController.ANIMATION_DURATION_LONG; } + + @Override + public boolean isLowPowerState() { + return true; + } }, /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 2963c9469fb2..97e09dccdc41 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -4323,13 +4323,21 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void prepareForGentleSleep(Runnable onDisplayOffCallback) { - if (onDisplayOffCallback != null) { + if (mPendingScreenOffCallback != null) { Log.w(TAG, "Overlapping onDisplayOffCallback. Ignoring previous one."); } mPendingScreenOffCallback = onDisplayOffCallback; updateScrimController(); } + @Override + public void cancelGentleSleep() { + mPendingScreenOffCallback = null; + if (mScrimController.getState() == ScrimState.OFF) { + updateScrimController(); + } + } + /** * When the dozing host is waiting for scrims to fade out to change the display state. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java index 0a2fb2e783a9..76683b6a5e6a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java @@ -16,9 +16,6 @@ package com.android.systemui.statusbar.policy; -import static com.android.systemui.Dependency.BG_LOOPER_NAME; -import static com.android.systemui.Dependency.MAIN_LOOPER_NAME; - import android.annotation.Nullable; import android.app.ActivityManager; import android.bluetooth.BluetoothAdapter; @@ -36,6 +33,8 @@ import com.android.settingslib.bluetooth.BluetoothCallback; import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; +import com.android.systemui.dagger.qualifiers.BgLooper; +import com.android.systemui.dagger.qualifiers.MainLooper; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -46,7 +45,6 @@ import java.util.List; import java.util.WeakHashMap; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Singleton; /** @@ -74,9 +72,8 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa /** */ @Inject - public BluetoothControllerImpl(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper, - @Named(MAIN_LOOPER_NAME) Looper mainLooper, - @Nullable LocalBluetoothManager localBluetoothManager) { + public BluetoothControllerImpl(Context context, @BgLooper Looper bgLooper, + @MainLooper Looper mainLooper, @Nullable LocalBluetoothManager localBluetoothManager) { mLocalBluetoothManager = localBluetoothManager; mBgHandler = new Handler(bgLooper); mHandler = new H(mainLooper); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java index 089d5c924de8..0a40b3c4831c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.java @@ -14,8 +14,6 @@ package com.android.systemui.statusbar.policy; -import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; - import android.app.ActivityManager; import android.content.ContentResolver; import android.content.Context; @@ -26,12 +24,12 @@ import android.provider.Settings.Global; import android.provider.Settings.Secure; import android.util.Log; +import com.android.systemui.dagger.qualifiers.MainHandler; import com.android.systemui.settings.CurrentUserTracker; import java.util.ArrayList; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Singleton; /** @@ -51,8 +49,7 @@ public class DeviceProvisionedControllerImpl extends CurrentUserTracker implemen /** */ @Inject - public DeviceProvisionedControllerImpl(Context context, - @Named(MAIN_HANDLER_NAME) Handler mainHandler) { + public DeviceProvisionedControllerImpl(Context context, @MainHandler Handler mainHandler) { super(context); mContext = context; mContentResolver = context.getContentResolver(); 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 1c6d12f15e4c..8f201c793258 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java @@ -16,8 +16,6 @@ package com.android.systemui.statusbar.policy; -import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; - import android.app.ActivityManager; import android.content.Context; import android.net.ConnectivityManager; @@ -26,12 +24,13 @@ import android.os.Handler; import android.os.UserManager; import android.util.Log; +import com.android.systemui.dagger.qualifiers.MainHandler; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Singleton; /** @@ -56,7 +55,7 @@ public class HotspotControllerImpl implements HotspotController, WifiManager.Sof /** */ @Inject - public HotspotControllerImpl(Context context, @Named(MAIN_HANDLER_NAME) Handler mainHandler) { + public HotspotControllerImpl(Context context, @MainHandler Handler mainHandler) { mContext = context; mConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java index 683cdbb326dc..5a97eedd9b2c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java @@ -17,7 +17,6 @@ package com.android.systemui.statusbar.policy; import static com.android.settingslib.Utils.updateLocationEnabled; -import static com.android.systemui.Dependency.BG_LOOPER_NAME; import android.app.ActivityManager; import android.app.AppOpsManager; @@ -36,13 +35,13 @@ import android.provider.Settings; import androidx.annotation.VisibleForTesting; +import com.android.systemui.dagger.qualifiers.BgLooper; import com.android.systemui.util.Utils; import java.util.ArrayList; import java.util.List; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Singleton; /** @@ -66,7 +65,7 @@ public class LocationControllerImpl extends BroadcastReceiver implements Locatio private final H mHandler = new H(); @Inject - public LocationControllerImpl(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper) { + public LocationControllerImpl(Context context, @BgLooper Looper bgLooper) { mContext = context; // Register to listen for changes in location settings. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index 7b5d48947498..60784c91ee62 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -24,7 +24,6 @@ import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_OU import static android.telephony.PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE; import static com.android.internal.telephony.PhoneConstants.MAX_PHONE_COUNT_DUAL_SIM; -import static com.android.systemui.Dependency.BG_LOOPER_NAME; import android.content.BroadcastReceiver; import android.content.Context; @@ -65,6 +64,7 @@ import com.android.systemui.ConfigurationChangedReceiver; import com.android.systemui.DemoMode; import com.android.systemui.Dumpable; import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.BgLooper; import com.android.systemui.settings.CurrentUserTracker; import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; import com.android.systemui.statusbar.policy.MobileSignalController.MobileIconGroup; @@ -81,7 +81,6 @@ import java.util.Locale; import java.util.Map; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Singleton; /** Platform implementation of the network controller. **/ @@ -170,7 +169,7 @@ public class NetworkControllerImpl extends BroadcastReceiver * Construct this controller object and register for updates. */ @Inject - public NetworkControllerImpl(Context context, @Named(BG_LOOPER_NAME) Looper bgLooper, + public NetworkControllerImpl(Context context, @BgLooper Looper bgLooper, DeviceProvisionedController deviceProvisionedController) { this(context, (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE), (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE), diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java index d88ae78c5afb..6edd75b20fed 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java @@ -15,8 +15,6 @@ */ package com.android.systemui.statusbar.policy; -import static com.android.systemui.Dependency.BG_HANDLER_NAME; - import android.app.ActivityManager; import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; @@ -50,6 +48,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.net.LegacyVpnInfo; import com.android.internal.net.VpnConfig; import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.BgHandler; import com.android.systemui.settings.CurrentUserTracker; import java.io.FileDescriptor; @@ -57,7 +56,6 @@ import java.io.PrintWriter; import java.util.ArrayList; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Singleton; /** @@ -102,7 +100,7 @@ public class SecurityControllerImpl extends CurrentUserTracker implements Securi /** */ @Inject - public SecurityControllerImpl(Context context, @Named(BG_HANDLER_NAME) Handler bgHandler) { + public SecurityControllerImpl(Context context, @BgHandler Handler bgHandler) { this(context, bgHandler, null); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java index 655c29cbf687..347d3009c3ec 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyConstants.java @@ -16,8 +16,6 @@ package com.android.systemui.statusbar.policy; -import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; - import android.app.RemoteInput; import android.content.Context; import android.content.res.Resources; @@ -30,9 +28,9 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.MainHandler; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Singleton; @Singleton @@ -67,7 +65,7 @@ public final class SmartReplyConstants { private final KeyValueListParser mParser = new KeyValueListParser(','); @Inject - public SmartReplyConstants(@Named(MAIN_HANDLER_NAME) Handler handler, Context context) { + public SmartReplyConstants(@MainHandler Handler handler, Context context) { mHandler = handler; mContext = context; final Resources resources = mContext.getResources(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java index bcfbdacf9ea8..f2d2faed6a42 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java @@ -18,7 +18,6 @@ package com.android.systemui.statusbar.policy; import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import static com.android.systemui.DejankUtils.whitelistIpcs; -import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; import android.app.ActivityManager; import android.app.Dialog; @@ -58,6 +57,7 @@ import com.android.systemui.Prefs; import com.android.systemui.Prefs.Key; import com.android.systemui.R; import com.android.systemui.SystemUISecondaryUserService; +import com.android.systemui.dagger.qualifiers.MainHandler; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.qs.DetailAdapter; import com.android.systemui.qs.tiles.UserDetailView; @@ -70,7 +70,6 @@ import java.util.ArrayList; import java.util.List; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Singleton; /** @@ -110,7 +109,7 @@ public class UserSwitcherController implements Dumpable { @Inject public UserSwitcherController(Context context, KeyguardStateController keyguardStateController, - @Named(MAIN_HANDLER_NAME) Handler handler, ActivityStarter activityStarter) { + @MainHandler Handler handler, ActivityStarter activityStarter) { mContext = context; if (!UserManager.isGuestUserEphemeral()) { mGuestResumeSessionReceiver.register(context); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java index 29c42d24b3b4..1c7a1951b27d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java @@ -16,8 +16,6 @@ package com.android.systemui.statusbar.policy; -import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; - import android.app.ActivityManager; import android.app.AlarmManager; import android.app.NotificationManager; @@ -41,6 +39,7 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.Dumpable; +import com.android.systemui.dagger.qualifiers.MainHandler; import com.android.systemui.qs.GlobalSetting; import com.android.systemui.settings.CurrentUserTracker; import com.android.systemui.util.Utils; @@ -51,7 +50,6 @@ import java.util.ArrayList; import java.util.Objects; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Singleton; /** Platform implementation of the zen mode controller. **/ @@ -79,7 +77,7 @@ public class ZenModeControllerImpl extends CurrentUserTracker private NotificationManager.Policy mConsolidatedNotificationPolicy; @Inject - public ZenModeControllerImpl(Context context, @Named(MAIN_HANDLER_NAME) Handler handler) { + public ZenModeControllerImpl(Context context, @MainHandler Handler handler) { super(context); mContext = context; mModeSetting = new GlobalSetting(mContext, handler, Global.ZEN_MODE) { diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java index a55e2cf38e39..2d6027cb324d 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java @@ -15,8 +15,6 @@ */ package com.android.systemui.tuner; -import static com.android.systemui.Dependency.MAIN_HANDLER_NAME; - import android.app.ActivityManager; import android.content.ContentResolver; import android.content.Context; @@ -36,6 +34,7 @@ import android.util.ArraySet; import com.android.internal.util.ArrayUtils; import com.android.systemui.DejankUtils; import com.android.systemui.DemoMode; +import com.android.systemui.dagger.qualifiers.MainHandler; import com.android.systemui.qs.QSTileHost; import com.android.systemui.settings.CurrentUserTracker; import com.android.systemui.statusbar.phone.StatusBarIconController; @@ -46,7 +45,6 @@ import java.util.HashSet; import java.util.Set; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Singleton; @@ -83,7 +81,7 @@ public class TunerServiceImpl extends TunerService { /** */ @Inject - public TunerServiceImpl(Context context, @Named(MAIN_HANDLER_NAME) Handler mainHandler, + public TunerServiceImpl(Context context, @MainHandler Handler mainHandler, LeakDetector leakDetector) { mContext = context; mContentResolver = mContext.getContentResolver(); diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java index 7e801da9cd1b..5ed027d37def 100644 --- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java +++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java @@ -26,7 +26,7 @@ import android.view.View; import com.android.keyguard.KeyguardClockSwitch; import com.android.keyguard.KeyguardMessageArea; import com.android.keyguard.KeyguardSliceView; -import com.android.systemui.SystemUIRootComponent; +import com.android.systemui.dagger.SystemUIRootComponent; import com.android.systemui.qs.QSCarrierGroup; import com.android.systemui.qs.QSFooterImpl; import com.android.systemui.qs.QSPanel; diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java index 63db755ef09d..bff405c0bee6 100644 --- a/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java +++ b/packages/SystemUI/src/com/android/systemui/util/leak/GarbageMonitor.java @@ -20,7 +20,6 @@ import static android.service.quicksettings.Tile.STATE_ACTIVE; import static android.telephony.ims.feature.ImsFeature.STATE_UNAVAILABLE; import static com.android.internal.logging.MetricsLogger.VIEW_UNKNOWN; -import static com.android.systemui.Dependency.BG_LOOPER_NAME; import android.annotation.Nullable; import android.app.ActivityManager; @@ -48,6 +47,7 @@ import android.util.LongSparseArray; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.SystemUI; +import com.android.systemui.dagger.qualifiers.BgLooper; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.qs.QSHost; @@ -59,7 +59,6 @@ import java.util.ArrayList; import java.util.List; import javax.inject.Inject; -import javax.inject.Named; import javax.inject.Singleton; /** @@ -110,7 +109,7 @@ public class GarbageMonitor implements Dumpable { @Inject public GarbageMonitor( Context context, - @Named(BG_LOOPER_NAME) Looper bgLooper, + @BgLooper Looper bgLooper, LeakDetector leakDetector, LeakReporter leakReporter) { mContext = context.getApplicationContext(); diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java index fdffc4338843..a96977a338a9 100644 --- a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java +++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java @@ -25,8 +25,8 @@ import android.os.Handler; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.DependencyProvider; import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.MainResources; import java.util.ArrayList; import java.util.List; @@ -65,7 +65,7 @@ public class ProximitySensor { }; @Inject - public ProximitySensor(@DependencyProvider.MainResources Resources resources, + public ProximitySensor(@MainResources Resources resources, AsyncSensorManager sensorManager) { mSensorManager = sensorManager; Sensor sensor = findBrightnessSensor(resources); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index 1b132b962493..85c247e11f94 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -64,6 +64,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.stubbing.Answer; +import java.util.Arrays; import java.util.Collections; import java.util.HashSet; @@ -249,7 +250,7 @@ public class ScrimControllerTest extends SysuiTestCase { finishAnimationsImmediately(); assertScrimAlpha(OPAQUE /* front */, - SEMI_TRANSPARENT /* back */, + OPAQUE /* back */, TRANSPARENT /* bubble */); assertScrimTint(true /* front */, @@ -858,6 +859,22 @@ public class ScrimControllerTest extends SysuiTestCase { mScrimForBubble.getDefaultFocusHighlightEnabled()); } + @Test + public void testIsLowPowerMode() { + HashSet<ScrimState> lowPowerModeStates = new HashSet<>(Arrays.asList( + ScrimState.OFF, ScrimState.AOD, ScrimState.PULSING)); + HashSet<ScrimState> regularStates = new HashSet<>(Arrays.asList( + ScrimState.UNINITIALIZED, ScrimState.KEYGUARD, ScrimState.BOUNCER, + ScrimState.BOUNCER_SCRIMMED, ScrimState.BRIGHTNESS_MIRROR, ScrimState.UNLOCKED, + ScrimState.BUBBLE_EXPANDED)); + + for (ScrimState state : ScrimState.values()) { + if (!lowPowerModeStates.contains(state) && !regularStates.contains(state)) { + Assert.fail("Scrim state not whitelisted nor blacklisted as low power mode"); + } + } + } + private void assertScrimTint(boolean front, boolean behind, boolean bubble) { Assert.assertEquals("Tint test failed at state " + mScrimController.getState() + " with scrim: " + getScrimName(mScrimInFront) + " and tint: " diff --git a/services/art-profile b/services/art-profile index 8b911a24b5cc..a0338d55c55f 100644 --- a/services/art-profile +++ b/services/art-profile @@ -21148,7 +21148,6 @@ HPLcom/android/server/wm/RemoteAnimationController$FinishedCallback;->release()V HPLcom/android/server/wm/RemoteAnimationController;->lambda$goodToGo$1$RemoteAnimationController([Landroid/view/RemoteAnimationTarget;[Landroid/view/RemoteAnimationTarget;)V HPLcom/android/server/wm/RunningTasks;->getTasks(ILjava/util/List;IILjava/util/ArrayList;IZZLandroid/util/ArraySet;)V HPLcom/android/server/wm/SplashScreenStartingData;->createStartingSurface(Lcom/android/server/wm/ActivityRecord;)Lcom/android/server/policy/WindowManagerPolicy$StartingSurface; -HPLcom/android/server/wm/SurfaceAnimator$Animatable;->shouldDeferAnimationFinish(Ljava/lang/Runnable;)Z HPLcom/android/server/wm/SurfaceAnimator;->lambda$getFinishedCallback$0$SurfaceAnimator(Lcom/android/server/wm/AnimationAdapter;Ljava/lang/Runnable;)V HPLcom/android/server/wm/Task;->positionChildAt(ILcom/android/server/wm/ActivityRecord;Z)V HPLcom/android/server/wm/TaskRecord;->handlesOrientationChangeFromDescendant()Z @@ -21191,7 +21190,6 @@ HPLcom/android/server/wm/DisplayRotation;->updateRotationUnchecked(Z)Z HPLcom/android/server/wm/RemoteAnimationController;->createRemoteAnimationRecord(Lcom/android/server/wm/ActivityRecord;Landroid/graphics/Point;Landroid/graphics/Rect;Landroid/graphics/Rect;)Lcom/android/server/wm/RemoteAnimationController$RemoteAnimationRecord; HPLcom/android/server/wm/RemoteAnimationController;->linkToDeathOfRunner()V HPLcom/android/server/wm/RemoteAnimationController;->writeStartDebugStatement()V -HPLcom/android/server/wm/SurfaceAnimator$Animatable;->shouldDeferAnimationFinish(Ljava/lang/Runnable;)Z HPLcom/android/server/wm/TaskRecord;->setMinDimensions(Landroid/content/pm/ActivityInfo;)V HPLcom/android/server/wm/WallpaperAnimationAdapter;->startWallpaperAnimations(Lcom/android/server/wm/WindowManagerService;JJLjava/util/function/Consumer;Ljava/util/ArrayList;)[Landroid/view/RemoteAnimationTarget; HPLcom/android/server/wm/WindowStateAnimator;->finishDrawingLocked(Landroid/view/SurfaceControl$Transaction;)Z @@ -21206,13 +21204,152 @@ HPLcom/android/server/statusbar/StatusBarManagerService;->setSystemUiVisibility( HPLcom/android/server/wm/AnimationAdapter;->writeToProto(Landroid/util/proto/ProtoOutputStream;J)V HPLcom/android/server/wm/RemoteAnimationController$RemoteAnimationAdapterWrapper;->dump(Ljava/io/PrintWriter;Ljava/lang/String;)V HPLcom/android/server/wm/RemoteAnimationController$RemoteAnimationRecord;-><init>(Lcom/android/server/wm/RemoteAnimationController;Lcom/android/server/wm/ActivityRecord;Landroid/graphics/Point;Landroid/graphics/Rect;Landroid/graphics/Rect;)V -HPLcom/android/server/wm/SurfaceAnimator$Animatable;->shouldDeferAnimationFinish(Ljava/lang/Runnable;)Z HPLcom/android/server/wm/WindowProcessController;->setBoundClientUids(Landroid/util/ArraySet;)V HPLcom/google/android/startop/iorap/IorapForwardingService$AppLaunchObserver;->lambda$onActivityLaunched$2$IorapForwardingService$AppLaunchObserver([BILcom/google/android/startop/iorap/IIorap;)V HPLcom/google/android/startop/iorap/IorapForwardingService$AppLaunchObserver;->lambda$onActivityLaunchFinished$4$IorapForwardingService$AppLaunchObserver([BJLcom/google/android/startop/iorap/IIorap;)V HPLcom/google/android/startop/iorap/IorapForwardingService$AppLaunchObserver;->lambda$onIntentStarted$0$IorapForwardingService$AppLaunchObserver(Landroid/content/Intent;JLcom/google/android/startop/iorap/IIorap;)V HPLcom/android/server/statusbar/StatusBarManagerService;->updateUiVisibilityLocked(IIIIILandroid/graphics/Rect;Landroid/graphics/Rect;Z)V HPLcom/android/server/wm/AnimationAdapter;->writeToProto(Landroid/util/proto/ProtoOutputStream;J)V -HPLcom/android/server/wm/SurfaceAnimator$Animatable;->shouldDeferAnimationFinish(Ljava/lang/Runnable;)Z HPLcom/google/android/startop/iorap/IIorap$Stub$Proxy;->onAppLaunchEvent(Lcom/google/android/startop/iorap/RequestId;Lcom/google/android/startop/iorap/AppLaunchEvent;)V HPLcom/google/android/startop/iorap/RequestId;->nextValueForSequence()Lcom/google/android/startop/iorap/RequestId; +HPLcom/android/server/AlarmManagerService;->decrementAlarmCount(II)V +HPLcom/android/server/am/ActivityManagerService$4;->newArray(I)[Landroid/content/IntentFilter; +HPLcom/android/server/am/ActivityManagerService$PidMap;->remove(Lcom/android/server/am/ProcessRecord;)V +HPLcom/android/server/am/ActivityManagerService;->updateOomAdjLocked(Ljava/lang/String;)V +HPLcom/android/server/am/ActivityManagerShellCommand$1;-><init>(Lcom/android/server/am/ActivityManagerShellCommand;)V +HPLcom/android/server/am/ActivityManagerShellCommand;-><init>(Lcom/android/server/am/ActivityManagerService;Z)V +HPLcom/android/server/am/ActivityManagerShellCommand;->runStartActivity(Ljava/io/PrintWriter;)I +HPLcom/android/server/am/HostingRecord;-><init>(Ljava/lang/String;Landroid/content/ComponentName;I)V +HPLcom/android/server/am/PendingIntentController;->registerIntentSenderCancelListener(Landroid/content/IIntentSender;Lcom/android/internal/os/IResultReceiver;)V +HPLcom/android/server/appprediction/-$$Lambda$AppPredictionManagerService$PredictionManagerServiceStub$vSY20eQq5y5FXrxhhqOTcEmezTs;-><init>(Landroid/app/prediction/AppPredictionSessionId;)V +HPLcom/android/server/appprediction/-$$Lambda$RemoteAppPredictionService$9DCowUTEF8fYuBlWGxOmP5hTAWA;-><init>(Landroid/app/prediction/AppPredictionSessionId;)V +HPLcom/android/server/appprediction/RemoteAppPredictionService;->lambda$requestPredictionUpdate$6(Landroid/app/prediction/AppPredictionSessionId;Landroid/service/appprediction/IPredictionService;)V +HPLcom/android/server/autofill/-$$Lambda$AutofillManagerService$1$1-WNu3tTkxodB_LsZ7dGIlvrPN0;->visit(Ljava/lang/Object;)V +HPLcom/android/server/autofill/ui/-$$Lambda$AutoFillUI$56AC3ykfo4h_e2LSjdkJ3XQn370;-><init>(Lcom/android/server/autofill/ui/AutoFillUI;Lcom/android/server/autofill/ui/AutoFillUI$AutoFillUiCallback;)V +HPLcom/android/server/contentsuggestions/-$$Lambda$RemoteContentSuggestionsService$Enqw46SYVKFK9F2xX4qUcIu5_3I;->run(Landroid/os/IInterface;)V +HPLcom/android/server/contentsuggestions/-$$Lambda$RemoteContentSuggestionsService$eoGnQ2MDLLnW1UBX6wxNE1VBLAk;->run(Landroid/os/IInterface;)V +HPLcom/android/server/contentsuggestions/-$$Lambda$RemoteContentSuggestionsService$VKh1DoMPNSPjPfnVGdsInmxuqzc;->run(Landroid/os/IInterface;)V +HPLcom/android/server/contentsuggestions/-$$Lambda$RemoteContentSuggestionsService$yUTbcaYlZCYTmagCkNJ3i2VCkY4;->run(Landroid/os/IInterface;)V +HPLcom/android/server/contentsuggestions/RemoteContentSuggestionsService;->getServiceInterface(Landroid/os/IBinder;)Landroid/os/IInterface; +HPLcom/android/server/display/AutomaticBrightnessController;->updateAutoBrightness(ZZ)V +HPLcom/android/server/display/DisplayPowerController$BrightnessReason;->setModifier(I)V +HPLcom/android/server/display/DisplayPowerController$BrightnessReason;->setReason(I)V +HPLcom/android/server/display/DisplayPowerController$BrightnessReason;->toString(I)Ljava/lang/String; +HPLcom/android/server/display/DisplayPowerController$BrightnessReason;->toString()Ljava/lang/String; +HPLcom/android/server/media/projection/MediaProjectionManagerService$1;->onProcessDied(II)V +HPLcom/android/server/net/NetworkPolicyManagerService;->removeUidStateUL(I)Z +HPLcom/android/server/pm/permission/PermissionManagerService;->addOnPermissionsChangeListener(Landroid/permission/IOnPermissionsChangeListener;)V +HPLcom/android/server/protolog/ProtoLogImpl;->getSingleInstance()Lcom/android/server/protolog/ProtoLogImpl; +HPLcom/android/server/soundtrigger/SoundTriggerHelper$PowerSaveModeListener;-><init>(Lcom/android/server/soundtrigger/SoundTriggerHelper;)V +HPLcom/android/server/statusbar/-$$Lambda$StatusBarManagerService$uF0ibEnnXe7Lxunxb98QQLJjgZM;->run()V +HPLcom/android/server/statusbar/StatusBarManagerService$UiState;->setSystemUiState(IIILandroid/graphics/Rect;Landroid/graphics/Rect;Z)V +HPLcom/android/server/statusbar/StatusBarManagerService$UiState;->systemUiStateEquals(IIILandroid/graphics/Rect;Landroid/graphics/Rect;Z)Z +HPLcom/android/server/usage/UsageStatsDatabase;->filterStats(Lcom/android/server/usage/IntervalStats;)V +HPLcom/android/server/VibratorService$VibrationInfo;-><init>(JLandroid/os/VibrationEffect;Landroid/os/VibrationEffect;Landroid/media/AudioAttributes;ILjava/lang/String;Ljava/lang/String;)V +HPLcom/android/server/VibratorService;->getAppOpMode(Lcom/android/server/VibratorService$Vibration;)I +HPLcom/android/server/VibratorService;->getCurrentIntensityLocked(Lcom/android/server/VibratorService$Vibration;)I +HPLcom/android/server/VibratorService;->isAllowedToVibrateLocked(Lcom/android/server/VibratorService$Vibration;)Z +HPLcom/android/server/VibratorService;->vibrate(ILjava/lang/String;Landroid/os/VibrationEffect;Landroid/media/AudioAttributes;Ljava/lang/String;Landroid/os/IBinder;)V +HPLcom/android/server/wm/-$$Lambda$ActivityRecord$g49I60MBbnNkxHlgA-NR7ALwWTQ;->apply(Ljava/lang/Object;)Z +HPLcom/android/server/wm/-$$Lambda$ActivityRecord$pBc6yUdDV5IrUd9vt6oCz6QzpiE;->accept(Ljava/lang/Object;)V +HPLcom/android/server/wm/-$$Lambda$BEx3OWenCvYAaV5h_J2ZkZXhEcY;->accept(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)V +HPLcom/android/server/wm/-$$Lambda$DisplayPolicy$Zbxkj4wIhcDki6VwBh1kWmSmxqM;-><init>(Lcom/android/server/wm/DisplayPolicy;IIILandroid/graphics/Rect;Landroid/graphics/Rect;ZLcom/android/server/wm/WindowState;I[Lcom/android/internal/view/AppearanceRegion;ZZ)V +HPLcom/android/server/wm/-$$Lambda$DisplayPolicy$Zbxkj4wIhcDki6VwBh1kWmSmxqM;->run()V +HPLcom/android/server/wm/-$$Lambda$TaskChangeNotificationController$UexNbaqPy0mc3VxTw2coCctHho8;->accept(Landroid/app/ITaskStackListener;Landroid/os/Message;)V +HPLcom/android/server/wm/ActivityMetricsLogger;->allWindowsDrawn()Z +HPLcom/android/server/wm/ActivityRecord;->createAnimationBoundsLayer(Landroid/view/SurfaceControl$Transaction;)Landroid/view/SurfaceControl; +HPLcom/android/server/wm/ActivityRecord;->destroyed(Ljava/lang/String;)V +HPLcom/android/server/wm/ActivityRecord;->forAllWindowsUnchecked(Lcom/android/internal/util/ToBooleanFunction;Z)Z +HPLcom/android/server/wm/ActivityRecord;->getTopFullscreenWindow()Lcom/android/server/wm/WindowState; +HPLcom/android/server/wm/ActivityRecord;->loadAnimation(Landroid/view/WindowManager$LayoutParams;IZZ)Landroid/view/animation/Animation; +HPLcom/android/server/wm/ActivityRecord;->notifyAppStopped()V +HPLcom/android/server/wm/ActivityRecord;->onFirstWindowDrawn(Lcom/android/server/wm/WindowState;Lcom/android/server/wm/WindowStateAnimator;)V +HPLcom/android/server/wm/ActivityRecord;->onRemovedFromDisplay()V +HPLcom/android/server/wm/ActivityRecord;->removeDeadWindows()V +HPLcom/android/server/wm/ActivityRecord;->setClientHidden(Z)V +HPLcom/android/server/wm/ActivityRecord;->stopFreezingScreenLocked(Z)V +HPLcom/android/server/wm/ActivityStack;->removeTimeoutsForActivity(Lcom/android/server/wm/ActivityRecord;)V +HPLcom/android/server/wm/ActivityStarter;->recycleTask(Lcom/android/server/wm/TaskRecord;Lcom/android/server/wm/ActivityRecord;Lcom/android/server/wm/ActivityRecord;[Lcom/android/server/wm/ActivityRecord;)I +HPLcom/android/server/wm/ActivityTaskManagerService;->getTaskSnapshot(IZZ)Landroid/app/ActivityManager$TaskSnapshot; +HPLcom/android/server/wm/AnimationAdapter;->writeToProto(Landroid/util/proto/ProtoOutputStream;J)V +HPLcom/android/server/wm/DisplayContent;->getWindowCornerRadius()F +HPLcom/android/server/wm/DisplayPolicy;->convertNonDecorInsetsToStableInsets(Landroid/graphics/Rect;I)V +HPLcom/android/server/wm/DisplayPolicy;->getInsetsPolicy()Lcom/android/server/wm/InsetsPolicy; +HPLcom/android/server/wm/DisplayPolicy;->updateLightStatusBarAppearanceLw(ILcom/android/server/wm/WindowState;Lcom/android/server/wm/WindowState;)I +HPLcom/android/server/wm/InsetsPolicy;->getInsetsForDispatch(Lcom/android/server/wm/WindowState;)Landroid/view/InsetsState; +HPLcom/android/server/wm/InsetsPolicy;->isHidden(I)Z +HPLcom/android/server/wm/InsetsPolicy;->updateBarControlTarget(Lcom/android/server/wm/WindowState;)V +HPLcom/android/server/wm/Task;->getTopFullscreenActivity()Lcom/android/server/wm/ActivityRecord; +HPLcom/android/server/wm/TaskRecord;->removeTaskActivitiesLocked(Ljava/lang/String;)V +HPLcom/android/server/wm/Task;->removeChild(Lcom/android/server/wm/ActivityRecord;)V +HPLcom/android/server/wm/TaskSnapshotCache$CacheEntry;-><init>(Landroid/app/ActivityManager$TaskSnapshot;Lcom/android/server/wm/ActivityRecord;)V +HPLcom/android/server/wm/TaskSnapshotController;->createTaskSnapshot(Lcom/android/server/wm/Task;F)Landroid/view/SurfaceControl$ScreenshotGraphicBuffer; +HPLcom/android/server/wm/TaskSnapshotController;->findAppTokenForSnapshot(Lcom/android/server/wm/Task;)Lcom/android/server/wm/ActivityRecord; +HPLcom/android/server/wm/TaskSnapshotPersister$DeleteWriteQueueItem;-><init>(Lcom/android/server/wm/TaskSnapshotPersister;II)V +HPLcom/android/server/wm/TaskSnapshotPersister$StoreWriteQueueItem;-><init>(Lcom/android/server/wm/TaskSnapshotPersister;IILandroid/app/ActivityManager$TaskSnapshot;)V +HPLcom/android/server/wm/TaskSnapshotPersister$StoreWriteQueueItem;->onQueuedLocked()V +HPLcom/android/server/wm/WallpaperAnimationAdapter;->lambda$startWallpaperAnimations$0(JJLjava/util/function/Consumer;Ljava/util/ArrayList;Ljava/util/ArrayList;Lcom/android/server/wm/WallpaperWindowToken;)V +HPLcom/android/server/wm/WallpaperAnimationAdapter;->startWallpaperAnimations(Lcom/android/server/wm/WindowManagerService;JJLjava/util/function/Consumer;Ljava/util/ArrayList;)[Landroid/view/RemoteAnimationTarget; +HPLcom/android/server/wm/WindowAnimationSpec;->findTranslateAnimation(Landroid/view/animation/Animation;)Landroid/view/animation/TranslateAnimation; +HPLcom/android/server/wm/WindowProcessControllerMap;->removeProcessFromUidMap(Lcom/android/server/wm/WindowProcessController;)V +HPLcom/android/server/wm/WindowProcessController;->updateProcessInfo(ZZZ)V +HPLcom/android/server/wm/WindowState$DeathRecipient;-><init>(Lcom/android/server/wm/WindowState;Lcom/android/server/wm/WindowState$1;)V +HPLcom/android/server/wm/WindowState$WindowId;-><init>(Lcom/android/server/wm/WindowState;Lcom/android/server/wm/WindowState$1;)V +HPLcom/google/android/startop/iorap/AppLaunchEvent$ActivityLaunched;->writeToParcelImpl(Landroid/os/Parcel;I)V +HPLcom/google/android/startop/iorap/AppLaunchEvent$ActivityLaunchFinished;->writeToParcelImpl(Landroid/os/Parcel;I)V +HPLcom/google/android/startop/iorap/AppLaunchEvent$IntentStarted;->writeToParcelImpl(Landroid/os/Parcel;I)V +HPLcom/google/android/startop/iorap/AppLaunchEvent;->getTypeIndex()I +HPLcom/android/server/pm/PackageManagerService;->access$7600(Lcom/android/server/pm/PackageManagerService;Landroid/content/Intent;Ljava/lang/String;IIZI)Landroid/content/pm/ResolveInfo; +HPLcom/android/server/pm/PackageManagerService;->access$8100(Lcom/android/server/pm/PackageManagerService;II)Z +HPLcom/android/server/wm/ActivityRecord$Token;->access$100(Lcom/android/server/wm/ActivityRecord$Token;Lcom/android/server/wm/ActivityRecord;)V +HPLcom/android/server/wm/RemoteAnimationController$RemoteAnimationAdapterWrapper;->access$000(Lcom/android/server/wm/RemoteAnimationController$RemoteAnimationAdapterWrapper;)Lcom/android/server/wm/SurfaceAnimator$OnAnimationFinishedCallback; +HPLcom/android/server/wm/RemoteAnimationController$RemoteAnimationAdapterWrapper;->access$400(Lcom/android/server/wm/RemoteAnimationController$RemoteAnimationAdapterWrapper;)Landroid/graphics/Point; +HPLcom/android/server/wm/RemoteAnimationController$RemoteAnimationAdapterWrapper;->access$500(Lcom/android/server/wm/RemoteAnimationController$RemoteAnimationAdapterWrapper;)Landroid/graphics/Rect; +HPLcom/android/server/wm/RemoteAnimationController;->access$100(Lcom/android/server/wm/RemoteAnimationController;)V +HPLcom/android/server/wm/RemoteAnimationController;->access$300(Lcom/android/server/wm/RemoteAnimationController;)Landroid/view/RemoteAnimationAdapter; +HPLcom/android/server/wm/TaskPersister$TaskWriteQueueItem;->access$200(Lcom/android/server/wm/TaskPersister$TaskWriteQueueItem;)Lcom/android/server/wm/TaskRecord; +HPLcom/android/server/wm/TaskPersister;->access$000(I)Ljava/io/File; +HPLcom/android/server/wm/WindowContainer;->access$100(Lcom/android/server/wm/WindowContainer;)Landroid/util/Pools$SynchronizedPool; +HPLcom/android/server/wm/WindowManagerService;->access$1600(Lcom/android/server/wm/WindowManagerService;Landroid/os/IBinder;)V +HPLcom/android/server/pm/ShortcutUser;->logSharingShortcutStats(Lcom/android/internal/logging/MetricsLogger;)V +HPLcom/android/server/statusbar/StatusBarManagerService;->lambda$topAppWindowChanged$1$StatusBarManagerService(IZZ)V +HPLcom/android/server/wm/ActivityRecord$Token;->attach(Lcom/android/server/wm/ActivityRecord;)V +HPLcom/android/server/wm/AnimationAdapter;->writeToProto(Landroid/util/proto/ProtoOutputStream;J)V +HPLcom/android/server/wm/DisplayPolicy;->lambda$updateSystemUiVisibilityLw$10$DisplayPolicy(IIILandroid/graphics/Rect;Landroid/graphics/Rect;ZLcom/android/server/wm/WindowState;I[Lcom/android/internal/view/AppearanceRegion;ZZ)V +HPLcom/android/server/wm/InsetsSourceProvider;->hasWindow()Z +HPLcom/android/server/wm/InsetsStateController;->onBarControlTargetChanged(Lcom/android/server/wm/InsetsControlTarget;Lcom/android/server/wm/InsetsControlTarget;Lcom/android/server/wm/InsetsControlTarget;Lcom/android/server/wm/InsetsControlTarget;)V +HPLcom/android/server/wm/InsetsStateController;->peekSourceProvider(I)Lcom/android/server/wm/InsetsSourceProvider; +HPLcom/android/server/wm/TaskPersister$TaskWriteQueueItem;->access$200(Lcom/android/server/wm/TaskPersister$TaskWriteQueueItem;)Lcom/android/server/wm/TaskRecord; +HPLcom/android/server/wm/WindowContainer;->access$100(Lcom/android/server/wm/WindowContainer;)Landroid/util/Pools$SynchronizedPool; +HPLcom/android/server/-$$Lambda$AlarmManagerService$2$Eo-D98J-N9R2METkD-12gPs320c;-><init>(Lcom/android/server/AlarmManagerService$2;Landroid/app/IAlarmCompleteListener;)V +HPLcom/android/server/AlarmManagerService;->access$100(Lcom/android/server/AlarmManagerService;)Lcom/android/server/AlarmManagerService$Injector; +HPLcom/android/server/AlarmManagerService;->access$2102(Lcom/android/server/AlarmManagerService;J)J +HPLcom/android/server/AlarmManagerService;->access$2202(Lcom/android/server/AlarmManagerService;J)J +HPLcom/android/server/AlarmManagerService;->access$2300(Lcom/android/server/AlarmManagerService;)V +HPLcom/android/server/AlarmManagerService;->access$2400(Lcom/android/server/AlarmManagerService;Lcom/android/server/AlarmManagerService$Alarm;)Z +HPLcom/android/server/AlarmManagerService;->isExemptFromAppStandby(Lcom/android/server/AlarmManagerService$Alarm;)Z +HPLcom/android/server/appwidget/AppWidgetServiceImpl$HostId;-><init>(IILjava/lang/String;)V +HPLcom/android/server/content/ContentService$ObserverCall;->run()V +HPLcom/android/server/statusbar/-$$Lambda$StatusBarManagerService$uF0ibEnnXe7Lxunxb98QQLJjgZM;-><init>(Lcom/android/server/statusbar/StatusBarManagerService;IZZ)V +HPLcom/android/server/statusbar/StatusBarManagerService$1;->topAppWindowChanged(IZZ)V +HPLcom/android/server/statusbar/StatusBarManagerService$UiState;->access$1600(Lcom/android/server/statusbar/StatusBarManagerService$UiState;Z)V +HPLcom/android/server/statusbar/StatusBarManagerService$UiState;->access$1700(Lcom/android/server/statusbar/StatusBarManagerService$UiState;Z)V +HPLcom/android/server/statusbar/StatusBarManagerService$UiState;->setFullscreen(Z)V +HPLcom/android/server/statusbar/StatusBarManagerService$UiState;->setImmersive(Z)V +HPLcom/android/server/statusbar/StatusBarManagerService;->access$600(Lcom/android/server/statusbar/StatusBarManagerService;IZZ)V +HPLcom/android/server/statusbar/StatusBarManagerService;->enforceStatusBar()V +HPLcom/android/server/statusbar/StatusBarManagerService;->getUiState(I)Lcom/android/server/statusbar/StatusBarManagerService$UiState; +HPLcom/android/server/statusbar/StatusBarManagerService;->topAppWindowChanged(IZZ)V +HPLcom/android/server/wm/InsetsStateController;->onControlFakeTargetChanged(ILcom/android/server/wm/InsetsControlTarget;)V +HPLcom/android/server/wm/LocalAnimationAdapter$AnimationSpec;->writeToProto(Landroid/util/proto/ProtoOutputStream;J)V +HPLcom/android/server/wm/LocalAnimationAdapter;->writeToProto(Landroid/util/proto/ProtoOutputStream;)V +HPLcom/android/server/wm/PersisterQueue;->access$100(Lcom/android/server/wm/PersisterQueue;)Ljava/util/ArrayList; +HPLcom/android/server/wm/PersisterQueue;->access$200(Lcom/android/server/wm/PersisterQueue;)Ljava/util/ArrayList; +HPLcom/android/server/wm/PersisterQueue;->access$300(Lcom/android/server/wm/PersisterQueue;)V +HPLcom/android/server/wm/TaskSnapshotPersister;->access$100(Lcom/android/server/wm/TaskSnapshotPersister;)Ljava/lang/Object; +HPLcom/android/server/wm/TaskSnapshotPersister;->access$200(Lcom/android/server/wm/TaskSnapshotPersister;)Z +HPLcom/android/server/wm/TaskSnapshotPersister;->access$300(Lcom/android/server/wm/TaskSnapshotPersister;)Ljava/util/ArrayDeque; +HPLcom/android/server/wm/TaskSnapshotPersister;->access$402(Lcom/android/server/wm/TaskSnapshotPersister;Z)Z +HPLcom/android/server/wm/WindowAnimationSpec;->findTranslateAnimation(Landroid/view/animation/Animation;)Landroid/view/animation/TranslateAnimation; +HPLcom/android/server/wm/WindowAnimationSpec;->writeToProtoInner(Landroid/util/proto/ProtoOutputStream;)V +HPLcom/android/server/wm/WindowContainer;->access$100(Lcom/android/server/wm/WindowContainer;)Landroid/util/Pools$SynchronizedPool; diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java index 7d129eaa5390..1fc47516864b 100644 --- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java @@ -197,8 +197,20 @@ final class SaveUi { if ((type & SaveInfo.SAVE_DATA_TYPE_ADDRESS) != 0) { types.add(context.getString(R.string.autofill_save_type_address)); } - if ((type & SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD) != 0) { + + // fallback to generic card type if set multiple types + final int cardTypeMask = SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD + | SaveInfo.SAVE_DATA_TYPE_DEBIT_CARD + | SaveInfo.SAVE_DATA_TYPE_PAYMENT_CARD; + final int count = Integer.bitCount(type & cardTypeMask); + if (count > 1 || (type & SaveInfo.SAVE_DATA_TYPE_GENERIC_CARD) != 0) { + types.add(context.getString(R.string.autofill_save_type_generic_card)); + } else if ((type & SaveInfo.SAVE_DATA_TYPE_PAYMENT_CARD) != 0) { + types.add(context.getString(R.string.autofill_save_type_payment_card)); + } else if ((type & SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD) != 0) { types.add(context.getString(R.string.autofill_save_type_credit_card)); + } else if ((type & SaveInfo.SAVE_DATA_TYPE_DEBIT_CARD) != 0) { + types.add(context.getString(R.string.autofill_save_type_debit_card)); } if ((type & SaveInfo.SAVE_DATA_TYPE_USERNAME) != 0) { types.add(context.getString(R.string.autofill_save_type_username)); @@ -269,7 +281,9 @@ final class SaveUi { noButton.setOnClickListener((v) -> mListener.onCancel(info.getNegativeActionListener())); final TextView yesButton = view.findViewById(R.id.autofill_save_yes); - if (isUpdate) { + if (info.getPositiveActionStyle() == SaveInfo.POSITIVE_BUTTON_STYLE_CONTINUE) { + yesButton.setText(R.string.autofill_continue_yes); + } else if (isUpdate) { yesButton.setText(R.string.autofill_update_yes); } yesButton.setOnClickListener((v) -> mListener.onSave()); diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index 1d22b824aff9..ad3f5020031d 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -1000,6 +1000,12 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void onReportLocation(Location location) { + // likelihood of a 0,0 bug is far greater than this being a valid location + if (!isMock() && location.getLatitude() == 0 && location.getLongitude() == 0) { + Slog.w(TAG, "blocking 0,0 location from " + mName + " provider"); + return; + } + synchronized (mLock) { handleLocationChangedLocked(location, this); } diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java index 854f16aeb54e..9ac9955493cc 100644 --- a/services/core/java/com/android/server/compat/PlatformCompat.java +++ b/services/core/java/com/android/server/compat/PlatformCompat.java @@ -19,7 +19,7 @@ package com.android.server.compat; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; -import android.os.Process; +import android.os.UserHandle; import android.util.Slog; import android.util.StatsLog; @@ -54,8 +54,8 @@ public class PlatformCompat extends IPlatformCompat.Stub { } @Override - public void reportChangeByPackageName(long changeId, String packageName) { - ApplicationInfo appInfo = getApplicationInfo(packageName); + public void reportChangeByPackageName(long changeId, String packageName, int userId) { + ApplicationInfo appInfo = getApplicationInfo(packageName, userId); if (appInfo == null) { return; } @@ -80,8 +80,8 @@ public class PlatformCompat extends IPlatformCompat.Stub { } @Override - public boolean isChangeEnabledByPackageName(long changeId, String packageName) { - ApplicationInfo appInfo = getApplicationInfo(packageName); + public boolean isChangeEnabledByPackageName(long changeId, String packageName, int userId) { + ApplicationInfo appInfo = getApplicationInfo(packageName, userId); if (appInfo == null) { return true; } @@ -96,7 +96,8 @@ public class PlatformCompat extends IPlatformCompat.Stub { } boolean enabled = true; for (String packageName : packages) { - enabled = enabled && isChangeEnabledByPackageName(changeId, packageName); + enabled = enabled && isChangeEnabledByPackageName(changeId, packageName, + UserHandle.getUserId(uid)); } return enabled; } @@ -127,10 +128,9 @@ public class PlatformCompat extends IPlatformCompat.Stub { mChangeReporter.resetReportedChanges(appInfo.uid); } - private ApplicationInfo getApplicationInfo(String packageName) { + private ApplicationInfo getApplicationInfo(String packageName, int userId) { try { - return mContext.getPackageManager().getApplicationInfoAsUser(packageName, 0, - Process.myUid()); + return mContext.getPackageManager().getApplicationInfoAsUser(packageName, 0, userId); } catch (PackageManager.NameNotFoundException e) { Slog.e(TAG, "No installed package " + packageName); } diff --git a/services/core/java/com/android/server/compat/PlatformCompatNative.java b/services/core/java/com/android/server/compat/PlatformCompatNative.java index 839967139baa..85dfbf411667 100644 --- a/services/core/java/com/android/server/compat/PlatformCompatNative.java +++ b/services/core/java/com/android/server/compat/PlatformCompatNative.java @@ -29,8 +29,8 @@ public class PlatformCompatNative extends IPlatformCompatNative.Stub { } @Override - public void reportChangeByPackageName(long changeId, String packageName) { - mPlatformCompat.reportChangeByPackageName(changeId, packageName); + public void reportChangeByPackageName(long changeId, String packageName, int userId) { + mPlatformCompat.reportChangeByPackageName(changeId, packageName, userId); } @Override @@ -39,8 +39,8 @@ public class PlatformCompatNative extends IPlatformCompatNative.Stub { } @Override - public boolean isChangeEnabledByPackageName(long changeId, String packageName) { - return mPlatformCompat.isChangeEnabledByPackageName(changeId, packageName); + public boolean isChangeEnabledByPackageName(long changeId, String packageName, int userId) { + return mPlatformCompat.isChangeEnabledByPackageName(changeId, packageName, userId); } @Override diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 34fb64190e8e..9bbe62839db7 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -23,8 +23,11 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD; +import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD_OR_PIN; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN; +import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN; import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback; +import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_ENABLED_BY_DEFAULT; import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_ENABLED_KEY; import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_HANDLE_KEY; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT; @@ -101,7 +104,6 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.EventLog; -import android.util.Log; import android.util.Slog; import android.util.SparseArray; @@ -116,7 +118,6 @@ import com.android.internal.util.Preconditions; import com.android.internal.widget.ICheckCredentialProgressCallback; import com.android.internal.widget.ILockSettings; import com.android.internal.widget.LockPatternUtils; -import com.android.internal.widget.LockPatternUtils.CredentialType; import com.android.internal.widget.LockSettingsInternal; import com.android.internal.widget.LockscreenCredential; import com.android.internal.widget.VerifyCredentialResponse; @@ -178,7 +179,6 @@ public class LockSettingsService extends ILockSettings.Stub { private static final int PROFILE_KEY_IV_SIZE = 12; private static final String SEPARATE_PROFILE_CHALLENGE_KEY = "lockscreen.profilechallenge"; - private static final int SYNTHETIC_PASSWORD_ENABLED_BY_DEFAULT = 1; private static final String PREV_SYNTHETIC_PASSWORD_HANDLE_KEY = "prev-sp-handle"; private static final String SYNTHETIC_PASSWORD_UPDATE_TIME_KEY = "sp-handle-ts"; @@ -317,14 +317,34 @@ public class LockSettingsService extends ILockSettings.Stub { } } + private LockscreenCredential generateRandomProfilePassword() { + byte[] randomLockSeed = new byte[] {}; + try { + randomLockSeed = SecureRandom.getInstance("SHA1PRNG").generateSeed(40); + char[] newPasswordChars = HexEncoding.encode(randomLockSeed); + byte[] newPassword = new byte[newPasswordChars.length]; + for (int i = 0; i < newPasswordChars.length; i++) { + newPassword[i] = (byte) newPasswordChars[i]; + } + LockscreenCredential credential = + LockscreenCredential.createManagedPassword(newPassword); + Arrays.fill(newPasswordChars, '\u0000'); + Arrays.fill(newPassword, (byte) 0); + Arrays.fill(randomLockSeed, (byte) 0); + return credential; + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException("Fail to generate profile password", e); + } + } + /** * Tie managed profile to primary profile if it is in unified mode and not tied before. * * @param managedUserId Managed profile user Id * @param managedUserPassword Managed profile original password (when it has separated lock). - * NULL when it does not have a separated lock before. */ - public void tieManagedProfileLockIfNecessary(int managedUserId, byte[] managedUserPassword) { + public void tieManagedProfileLockIfNecessary(int managedUserId, + LockscreenCredential managedUserPassword) { if (DEBUG) Slog.v(TAG, "Check child profile lock for user: " + managedUserId); // Only for managed profile if (!mUserManager.getUserInfo(managedUserId).isManagedProfile()) { @@ -356,27 +376,10 @@ public class LockSettingsService extends ILockSettings.Stub { return; } if (DEBUG) Slog.v(TAG, "Tie managed profile to parent now!"); - byte[] randomLockSeed = new byte[] {}; - try { - randomLockSeed = SecureRandom.getInstance("SHA1PRNG").generateSeed(40); - char[] newPasswordChars = HexEncoding.encode(randomLockSeed); - byte[] newPassword = new byte[newPasswordChars.length]; - for (int i = 0; i < newPasswordChars.length; i++) { - newPassword[i] = (byte) newPasswordChars[i]; - } - Arrays.fill(newPasswordChars, '\u0000'); - final int quality = DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC; - setLockCredentialInternal(newPassword, CREDENTIAL_TYPE_PASSWORD, managedUserPassword, - quality, managedUserId, false, /* isLockTiedToParent= */ true); - // We store a private credential for the managed user that's unlocked by the primary - // account holder's credential. As such, the user will never be prompted to enter this - // password directly, so we always store a password. - setLong(LockPatternUtils.PASSWORD_TYPE_KEY, quality, managedUserId); - tieProfileLockToParent(managedUserId, newPassword); - Arrays.fill(newPassword, (byte) 0); - } catch (NoSuchAlgorithmException e) { - Slog.e(TAG, "Fail to tie managed profile", e); - // Nothing client can do to fix this issue, so we do not throw exception out + try (LockscreenCredential unifiedProfilePassword = generateRandomProfilePassword()) { + setLockCredentialInternal(unifiedProfilePassword, managedUserPassword, managedUserId, + false, /* isLockTiedToParent= */ true); + tieProfileLockToParent(managedUserId, unifiedProfilePassword); } } @@ -512,6 +515,10 @@ public class LockSettingsService extends ILockSettings.Stub { } } + public int settingsGlobalGetInt(ContentResolver contentResolver, String keyName, + int defaultValue) { + return Settings.Global.getInt(contentResolver, keyName, defaultValue); + } } public LockSettingsService(Context context) { @@ -682,7 +689,7 @@ public class LockSettingsService extends ILockSettings.Stub { hideEncryptionNotification(new UserHandle(userId)); if (mUserManager.getUserInfo(userId).isManagedProfile()) { - tieManagedProfileLockIfNecessary(userId, null); + tieManagedProfileLockIfNecessary(userId, LockscreenCredential.createNone()); } // If the user doesn't have a credential, try and derive their secret for the @@ -704,10 +711,9 @@ public class LockSettingsService extends ILockSettings.Stub { } final long handle = getSyntheticPasswordHandleLocked(userId); - final byte[] noCredential = null; AuthenticationResult result = - mSpManager.unwrapPasswordBasedSyntheticPassword( - getGateKeeperService(), handle, noCredential, userId, null); + mSpManager.unwrapPasswordBasedSyntheticPassword(getGateKeeperService(), + handle, LockscreenCredential.createNone(), userId, null); if (result.authToken != null) { Slog.i(TAG, "Retrieved auth token for user " + userId); onAuthTokenKnownForUser(userId, result.authToken); @@ -870,25 +876,6 @@ public class LockSettingsService extends ILockSettings.Stub { final List<UserInfo> users = mUserManager.getUsers(); for (int i = 0; i < users.size(); i++) { final UserInfo userInfo = users.get(i); - if (userInfo.isManagedProfile() && mStorage.hasChildProfileLock(userInfo.id)) { - // When managed profile has a unified lock, the password quality stored has 2 - // possibilities only. - // 1). PASSWORD_QUALITY_UNSPECIFIED, which is upgraded from dp2, and we are - // going to set it back to PASSWORD_QUALITY_ALPHANUMERIC. - // 2). PASSWORD_QUALITY_ALPHANUMERIC, which is the actual password quality for - // unified lock. - final long quality = getLong(LockPatternUtils.PASSWORD_TYPE_KEY, - DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userInfo.id); - if (quality == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { - // Only possible when it's upgraded from nyc dp3 - Slog.i(TAG, "Migrated tied profile lock type"); - setLong(LockPatternUtils.PASSWORD_TYPE_KEY, - DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC, userInfo.id); - } else if (quality != DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC) { - // It should not happen - Slog.e(TAG, "Invalid tied profile lock type: " + quality); - } - } try { final String alias = LockPatternUtils.PROFILE_KEY_NAME_ENCRYPT + userInfo.id; java.security.KeyStore keyStore = @@ -1031,21 +1018,22 @@ public class LockSettingsService extends ILockSettings.Stub { @Override public void setSeparateProfileChallengeEnabled(int userId, boolean enabled, - byte[] managedUserPassword) { + LockscreenCredential managedUserPassword) { checkWritePermission(userId); if (!mLockPatternUtils.hasSecureLockScreen()) { throw new UnsupportedOperationException( "This operation requires secure lock screen feature."); } synchronized (mSeparateChallengeLock) { - setSeparateProfileChallengeEnabledLocked(userId, enabled, managedUserPassword); + setSeparateProfileChallengeEnabledLocked(userId, enabled, managedUserPassword != null + ? managedUserPassword : LockscreenCredential.createNone()); } notifySeparateProfileChallengeChanged(userId); } @GuardedBy("mSeparateChallengeLock") private void setSeparateProfileChallengeEnabledLocked(@UserIdInt int userId, - boolean enabled, byte[] managedUserPassword) { + boolean enabled, LockscreenCredential managedUserPassword) { final boolean old = getBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, false, userId); setBoolean(SEPARATE_PROFILE_CHALLENGE_KEY, enabled, userId); try { @@ -1083,6 +1071,10 @@ public class LockSettingsService extends ILockSettings.Stub { @Override public void setLong(String key, long value, int userId) { checkWritePermission(userId); + setLongUnchecked(key, value, userId); + } + + private void setLongUnchecked(String key, long value, int userId) { setStringUnchecked(key, userId, Long.toString(value)); } @@ -1112,6 +1104,10 @@ public class LockSettingsService extends ILockSettings.Stub { @Override public long getLong(String key, long defaultValue, int userId) { checkReadPermission(key, userId); + return getLongUnchecked(key, defaultValue, userId); + } + + private long getLongUnchecked(String key, long defaultValue, int userId) { String value = getStringUnchecked(key, null, userId); return TextUtils.isEmpty(value) ? defaultValue : Long.parseLong(value); } @@ -1122,7 +1118,7 @@ public class LockSettingsService extends ILockSettings.Stub { return getStringUnchecked(key, defaultValue, userId); } - public String getStringUnchecked(String key, String defaultValue, int userId) { + private String getStringUnchecked(String key, String defaultValue, int userId) { if (Settings.Secure.LOCK_PATTERN_ENABLED.equals(key)) { long ident = Binder.clearCallingIdentity(); try { @@ -1131,9 +1127,8 @@ public class LockSettingsService extends ILockSettings.Stub { Binder.restoreCallingIdentity(ident); } } - if (userId == USER_FRP) { - return getFrpStringUnchecked(key); + return null; } if (LockPatternUtils.LEGACY_LOCK_PATTERN_ENABLED.equals(key)) { @@ -1143,51 +1138,81 @@ public class LockSettingsService extends ILockSettings.Stub { return mStorage.readKeyValue(key, defaultValue, userId); } - private String getFrpStringUnchecked(String key) { - if (LockPatternUtils.PASSWORD_TYPE_KEY.equals(key)) { - return String.valueOf(readFrpPasswordQuality()); - } - return null; + private void setKeyguardStoredQuality(int quality, int userId) { + if (DEBUG) Slog.d(TAG, "setKeyguardStoredQuality: user=" + userId + " quality=" + quality); + setLongUnchecked(LockPatternUtils.PASSWORD_TYPE_KEY, quality, userId); } - private int readFrpPasswordQuality() { - return mStorage.readPersistentDataBlock().qualityForUi; + private int getKeyguardStoredQuality(int userId) { + return (int) getLongUnchecked(LockPatternUtils.PASSWORD_TYPE_KEY, + DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userId); } @Override - public boolean havePassword(int userId) { + public int getCredentialType(int userId) { checkPasswordHavePermission(userId); + return getCredentialTypeInternal(userId); + } + + // TODO: this is a hot path, can we optimize it? + /** + * Returns the credential type of the user, can be one of {@link #CREDENTIAL_TYPE_NONE}, + * {@link #CREDENTIAL_TYPE_PATTERN}, {@link #CREDENTIAL_TYPE_PIN} and + * {@link #CREDENTIAL_TYPE_PASSWORD} + */ + public int getCredentialTypeInternal(int userId) { + if (userId == USER_FRP) { + return getFrpCredentialType(); + } synchronized (mSpManager) { if (isSyntheticPasswordBasedCredentialLocked(userId)) { final long handle = getSyntheticPasswordHandleLocked(userId); - return mSpManager.getCredentialType(handle, userId) == CREDENTIAL_TYPE_PASSWORD; + int rawType = mSpManager.getCredentialType(handle, userId); + if (rawType != CREDENTIAL_TYPE_PASSWORD_OR_PIN) { + return rawType; + } + return pinOrPasswordQualityToCredentialType(getKeyguardStoredQuality(userId)); } } - // Do we need a permissions check here? - return mStorage.hasPassword(userId); + // Intentional duplication of the getKeyguardStoredQuality() call above since this is a + // unlikely code path (device with pre-synthetic password credential). We want to skip + // calling getKeyguardStoredQuality whenever possible. + final int savedQuality = getKeyguardStoredQuality(userId); + if (savedQuality == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING + && mStorage.hasPattern(userId)) { + return CREDENTIAL_TYPE_PATTERN; + } + if (savedQuality >= DevicePolicyManager.PASSWORD_QUALITY_NUMERIC + && mStorage.hasPassword(userId)) { + return pinOrPasswordQualityToCredentialType(savedQuality); + } + return CREDENTIAL_TYPE_NONE; } - @Override - public boolean havePattern(int userId) { - checkPasswordHavePermission(userId); - synchronized (mSpManager) { - if (isSyntheticPasswordBasedCredentialLocked(userId)) { - final long handle = getSyntheticPasswordHandleLocked(userId); - return mSpManager.getCredentialType(handle, userId) == CREDENTIAL_TYPE_PATTERN; - } + private int getFrpCredentialType() { + PersistentData data = mStorage.readPersistentDataBlock(); + if (data.type != PersistentData.TYPE_SP && data.type != PersistentData.TYPE_SP_WEAVER) { + return CREDENTIAL_TYPE_NONE; + } + int credentialType = SyntheticPasswordManager.getFrpCredentialType(data.payload); + if (credentialType != CREDENTIAL_TYPE_PASSWORD_OR_PIN) { + return credentialType; } - // Do we need a permissions check here? - return mStorage.hasPattern(userId); + return pinOrPasswordQualityToCredentialType(data.qualityForUi); } - private boolean isUserSecure(int userId) { - synchronized (mSpManager) { - if (isSyntheticPasswordBasedCredentialLocked(userId)) { - final long handle = getSyntheticPasswordHandleLocked(userId); - return mSpManager.getCredentialType(handle, userId) != CREDENTIAL_TYPE_NONE; - } + private static int pinOrPasswordQualityToCredentialType(int quality) { + if (LockPatternUtils.isQualityAlphabeticPassword(quality)) { + return CREDENTIAL_TYPE_PASSWORD; + } + if (LockPatternUtils.isQualityNumericPin(quality)) { + return CREDENTIAL_TYPE_PIN; } - return mStorage.hasCredential(userId); + throw new IllegalArgumentException("Quality is neither Pin nor password: " + quality); + } + + private boolean isUserSecure(int userId) { + return getCredentialTypeInternal(userId) != CREDENTIAL_TYPE_NONE; } private void setKeystorePassword(byte[] password, int userHandle) { @@ -1205,8 +1230,8 @@ public class LockSettingsService extends ILockSettings.Stub { ks.unlock(userHandle, passwordString); } - @VisibleForTesting - protected byte[] getDecryptedPasswordForTiedProfile(int userId) + @VisibleForTesting /** Note: this method is overridden in unit tests */ + protected LockscreenCredential getDecryptedPasswordForTiedProfile(int userId) throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException, @@ -1230,7 +1255,10 @@ public class LockSettingsService extends ILockSettings.Stub { cipher.init(Cipher.DECRYPT_MODE, decryptionKey, new GCMParameterSpec(128, iv)); decryptionResult = cipher.doFinal(encryptedPassword); - return decryptionResult; + LockscreenCredential credential = LockscreenCredential.createManagedPassword( + decryptionResult); + Arrays.fill(decryptionResult, (byte) 0); + return credential; } private void unlockChildProfile(int profileHandle, boolean ignoreUserNotAuthenticated, @@ -1238,7 +1266,6 @@ public class LockSettingsService extends ILockSettings.Stub { @Nullable ArrayList<PendingResetLockout> resetLockouts) { try { doVerifyCredential(getDecryptedPasswordForTiedProfile(profileHandle), - CREDENTIAL_TYPE_PASSWORD, challengeType, challenge, profileHandle, null /* progressCallback */, resetLockouts); } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException @@ -1276,17 +1303,17 @@ public class LockSettingsService extends ILockSettings.Stub { final IProgressListener listener = new IProgressListener.Stub() { @Override public void onStarted(int id, Bundle extras) throws RemoteException { - Log.d(TAG, "unlockUser started"); + Slog.d(TAG, "unlockUser started"); } @Override public void onProgress(int id, int progress, Bundle extras) throws RemoteException { - Log.d(TAG, "unlockUser progress " + progress); + Slog.d(TAG, "unlockUser progress " + progress); } @Override public void onFinished(int id, Bundle extras) throws RemoteException { - Log.d(TAG, "unlockUser finished"); + Slog.d(TAG, "unlockUser finished"); latch.countDown(); } }; @@ -1351,11 +1378,11 @@ public class LockSettingsService extends ILockSettings.Stub { && mUserManager.isUserRunning(userInfo.id); } - private Map<Integer, byte[]> getDecryptedPasswordsForAllTiedProfiles(int userId) { + private Map<Integer, LockscreenCredential> getDecryptedPasswordsForAllTiedProfiles(int userId) { if (mUserManager.getUserInfo(userId).isManagedProfile()) { return null; } - Map<Integer, byte[]> result = new ArrayMap<Integer, byte[]>(); + Map<Integer, LockscreenCredential> result = new ArrayMap<>(); final List<UserInfo> profiles = mUserManager.getProfiles(userId); final int size = profiles.size(); for (int i = 0; i < size; i++) { @@ -1393,7 +1420,7 @@ public class LockSettingsService extends ILockSettings.Stub { * terminates when the user is a managed profile. */ private void synchronizeUnifiedWorkChallengeForProfiles(int userId, - Map<Integer, byte[]> profilePasswordMap) { + Map<Integer, LockscreenCredential> profilePasswordMap) { if (mUserManager.getUserInfo(userId).isManagedProfile()) { return; } @@ -1408,20 +1435,23 @@ public class LockSettingsService extends ILockSettings.Stub { continue; } if (isSecure) { - tieManagedProfileLockIfNecessary(managedUserId, null); + tieManagedProfileLockIfNecessary(managedUserId, + LockscreenCredential.createNone()); } else { // We use cached work profile password computed before clearing the parent's // credential, otherwise they get lost - if (profilePasswordMap != null && profilePasswordMap.containsKey(managedUserId)) { - setLockCredentialInternal(null, CREDENTIAL_TYPE_NONE, + if (profilePasswordMap != null + && profilePasswordMap.containsKey(managedUserId)) { + setLockCredentialInternal(LockscreenCredential.createNone(), profilePasswordMap.get(managedUserId), - DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, managedUserId, + managedUserId, false, /* isLockTiedToParent= */ true); } else { Slog.wtf(TAG, "clear tied profile challenges, but no password supplied."); - // Supplying null here would lead to untrusted credential change - setLockCredentialInternal(null, CREDENTIAL_TYPE_NONE, null, - DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, managedUserId, + // Attempt an untrusted reset by supplying an empty credential. + setLockCredentialInternal(LockscreenCredential.createNone(), + LockscreenCredential.createNone(), + managedUserId, true, /* isLockTiedToParent= */ true); } mStorage.removeChildProfileLock(managedUserId); @@ -1445,8 +1475,7 @@ public class LockSettingsService extends ILockSettings.Stub { * Send credentials for user {@code userId} to {@link RecoverableKeyStoreManager} during an * unlock operation. */ - private void sendCredentialsOnUnlockIfRequired( - int credentialType, @NonNull byte[] credential, int userId) { + private void sendCredentialsOnUnlockIfRequired(LockscreenCredential credential, int userId) { // Don't send credentials during the factory reset protection flow. if (userId == USER_FRP) { return; @@ -1459,10 +1488,12 @@ public class LockSettingsService extends ILockSettings.Stub { return; } + // RecoverableKeyStoreManager expects null for empty credential. + final byte[] secret = credential.isNone() ? null : credential.getCredential(); // Send credentials for the user and any child profiles that share its lock screen. for (int profileId : getProfilesWithSameLockScreen(userId)) { mRecoverableKeyStoreManager.lockScreenSecretAvailable( - credentialType, credential, profileId); + credential.getType(), secret, profileId); } } @@ -1471,7 +1502,7 @@ public class LockSettingsService extends ILockSettings.Stub { * credentials are set/changed. */ private void sendCredentialsOnChangeIfRequired( - int credentialType, byte[] credential, int userId, boolean isLockTiedToParent) { + LockscreenCredential credential, int userId, boolean isLockTiedToParent) { // A profile whose lock screen is being tied to its parent's will either have a randomly // generated credential (creation) or null (removal). We rely on the parent to send its // credentials for the profile in both cases as it stores the unified lock credential. @@ -1479,10 +1510,12 @@ public class LockSettingsService extends ILockSettings.Stub { return; } + // RecoverableKeyStoreManager expects null for empty credential. + final byte[] secret = credential.isNone() ? null : credential.getCredential(); // Send credentials for the user and any child profiles that share its lock screen. for (int profileId : getProfilesWithSameLockScreen(userId)) { mRecoverableKeyStoreManager.lockScreenSecretChanged( - credentialType, credential, profileId); + credential.getType(), secret, profileId); } } @@ -1505,9 +1538,8 @@ public class LockSettingsService extends ILockSettings.Stub { // This method should be called by LockPatternUtil only, all internal methods in this class // should call setLockCredentialInternal. @Override - public boolean setLockCredential(byte[] credential, int type, - byte[] savedCredential, int requestedQuality, int userId, - boolean allowUntrustedChange) { + public boolean setLockCredential(LockscreenCredential credential, + LockscreenCredential savedCredential, int userId, boolean allowUntrustedChange) { if (!mLockPatternUtils.hasSecureLockScreen()) { throw new UnsupportedOperationException( @@ -1515,11 +1547,11 @@ public class LockSettingsService extends ILockSettings.Stub { } checkWritePermission(userId); synchronized (mSeparateChallengeLock) { - if (!setLockCredentialInternal(credential, type, savedCredential, requestedQuality, + if (!setLockCredentialInternal(credential, savedCredential, userId, allowUntrustedChange, /* isLockTiedToParent= */ false)) { return false; } - setSeparateProfileChallengeEnabledLocked(userId, true, null); + setSeparateProfileChallengeEnabledLocked(userId, true, /* unused */ null); notifyPasswordChanged(userId); } if (mUserManager.getUserInfo(userId).isManagedProfile()) { @@ -1531,51 +1563,44 @@ public class LockSettingsService extends ILockSettings.Stub { } /** + * @param savedCredential if the user is a managed profile with unified challenge and + * savedCredential is empty, LSS will try to re-derive the profile password internally. + * TODO (b/80170828): Fix this so profile password is always passed in. * @param isLockTiedToParent is {@code true} if {@code userId} is a profile and its new * credentials are being tied to its parent's credentials. */ - private boolean setLockCredentialInternal(byte[] credential, @CredentialType int credentialType, - byte[] savedCredential, int requestedQuality, int userId, boolean allowUntrustedChange, - boolean isLockTiedToParent) { - // Normalize savedCredential and credential such that empty string is always represented - // as null. - if (savedCredential == null || savedCredential.length == 0) { - savedCredential = null; - } - if (credential == null || credential.length == 0) { - credential = null; - } + private boolean setLockCredentialInternal(LockscreenCredential credential, + LockscreenCredential savedCredential, int userId, + boolean allowUntrustedChange, boolean isLockTiedToParent) { + Preconditions.checkNotNull(credential); + Preconditions.checkNotNull(savedCredential); synchronized (mSpManager) { if (isSyntheticPasswordBasedCredentialLocked(userId)) { - return spBasedSetLockCredentialInternalLocked(credential, credentialType, - savedCredential, requestedQuality, userId, allowUntrustedChange, - isLockTiedToParent); + return spBasedSetLockCredentialInternalLocked(credential, savedCredential, userId, + allowUntrustedChange, isLockTiedToParent); } } - if (credentialType == CREDENTIAL_TYPE_NONE) { - if (credential != null) { - Slog.wtf(TAG, "CredentialType is none, but credential is non-null."); - } + if (credential.isNone()) { clearUserKeyProtection(userId); gateKeeperClearSecureUserId(userId); mStorage.writeCredentialHash(CredentialHash.createEmptyHash(), userId); + // Still update PASSWORD_TYPE_KEY if we are running in pre-synthetic password code path, + // since it forms part of the state that determines the credential type + // @see getCredentialTypeInternal + setKeyguardStoredQuality(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userId); setKeystorePassword(null, userId); fixateNewestUserKeyAuth(userId); synchronizeUnifiedWorkChallengeForProfiles(userId, null); - setUserPasswordMetrics(CREDENTIAL_TYPE_NONE, null, userId); - sendCredentialsOnChangeIfRequired( - credentialType, credential, userId, isLockTiedToParent); + setUserPasswordMetrics(LockscreenCredential.createNone(), userId); + sendCredentialsOnChangeIfRequired(credential, userId, isLockTiedToParent); return true; } - if (credential == null) { - throw new IllegalArgumentException("Null credential with mismatched credential type"); - } CredentialHash currentHandle = mStorage.readCredentialHash(userId); if (isManagedProfileWithUnifiedLock(userId)) { // get credential from keystore when managed profile has unified lock - if (savedCredential == null) { + if (savedCredential.isNone()) { try { savedCredential = getDecryptedPasswordForTiedProfile(userId); } catch (FileNotFoundException e) { @@ -1589,47 +1614,50 @@ public class LockSettingsService extends ILockSettings.Stub { } } else { if (currentHandle.hash == null) { - if (savedCredential != null) { + if (!savedCredential.isNone()) { Slog.w(TAG, "Saved credential provided, but none stored"); } - savedCredential = null; + savedCredential.close(); + savedCredential = LockscreenCredential.createNone(); } } synchronized (mSpManager) { if (shouldMigrateToSyntheticPasswordLocked(userId)) { - initializeSyntheticPasswordLocked(currentHandle.hash, savedCredential, - currentHandle.type, requestedQuality, userId); - return spBasedSetLockCredentialInternalLocked(credential, credentialType, - savedCredential, requestedQuality, userId, allowUntrustedChange, - isLockTiedToParent); + initializeSyntheticPasswordLocked(currentHandle.hash, savedCredential, userId); + return spBasedSetLockCredentialInternalLocked(credential, savedCredential, userId, + allowUntrustedChange, isLockTiedToParent); } } if (DEBUG) Slog.d(TAG, "setLockCredentialInternal: user=" + userId); - byte[] enrolledHandle = enrollCredential(currentHandle.hash, savedCredential, credential, - userId); + byte[] enrolledHandle = enrollCredential(currentHandle.hash, + savedCredential.getCredential(), credential.getCredential(), userId); if (enrolledHandle == null) { Slog.w(TAG, String.format("Failed to enroll %s: incorrect credential", - credentialType == CREDENTIAL_TYPE_PASSWORD ? "password" : "pattern")); + credential.isPattern() ? "pattern" : "password")); return false; } - CredentialHash willStore = CredentialHash.create(enrolledHandle, credentialType); + CredentialHash willStore = CredentialHash.create(enrolledHandle, credential.getType()); mStorage.writeCredentialHash(willStore, userId); + // Still update PASSWORD_TYPE_KEY if we are running in pre-synthetic password code path, + // since it forms part of the state that determines the credential type + // @see getCredentialTypeInternal + setKeyguardStoredQuality( + LockPatternUtils.credentialTypeToPasswordQuality(credential.getType()), userId); // push new secret and auth token to vold GateKeeperResponse gkResponse; try { gkResponse = getGateKeeperService().verifyChallenge(userId, 0, willStore.hash, - credential); + credential.getCredential()); } catch (RemoteException e) { throw new IllegalStateException("Failed to verify current credential", e); } setUserKeyProtection(userId, credential, convertResponse(gkResponse)); fixateNewestUserKeyAuth(userId); // Refresh the auth token - doVerifyCredential(credential, credentialType, CHALLENGE_FROM_CALLER, 0, userId, + doVerifyCredential(credential, CHALLENGE_FROM_CALLER, 0, userId, null /* progressCallback */); synchronizeUnifiedWorkChallengeForProfiles(userId, null); - sendCredentialsOnChangeIfRequired( - credentialType, credential, userId, isLockTiedToParent); + sendCredentialsOnChangeIfRequired(credential, userId, isLockTiedToParent); return true; } @@ -1637,8 +1665,8 @@ public class LockSettingsService extends ILockSettings.Stub { return VerifyCredentialResponse.fromGateKeeperResponse(gateKeeperResponse); } - @VisibleForTesting - protected void tieProfileLockToParent(int userId, byte[] password) { + @VisibleForTesting /** Note: this method is overridden in unit tests */ + protected void tieProfileLockToParent(int userId, LockscreenCredential password) { if (DEBUG) Slog.v(TAG, "tieProfileLockToParent for user: " + userId); byte[] encryptionResult; byte[] iv; @@ -1673,7 +1701,7 @@ public class LockSettingsService extends ILockSettings.Stub { KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_GCM + "/" + KeyProperties.ENCRYPTION_PADDING_NONE); cipher.init(Cipher.ENCRYPT_MODE, keyStoreEncryptionKey); - encryptionResult = cipher.doFinal(password); + encryptionResult = cipher.doFinal(password.getCredential()); iv = cipher.getIV(); } finally { // The original key can now be discarded. @@ -1728,7 +1756,8 @@ public class LockSettingsService extends ILockSettings.Stub { addUserKeyAuth(userId, null, key); } - private void setUserKeyProtection(int userId, byte[] credential, VerifyCredentialResponse vcr) { + private void setUserKeyProtection(int userId, LockscreenCredential credential, + VerifyCredentialResponse vcr) { if (DEBUG) Slog.d(TAG, "setUserKeyProtection: user=" + userId); if (vcr == null) { throw new IllegalArgumentException("Null response verifying a credential we just set"); @@ -1749,7 +1778,7 @@ public class LockSettingsService extends ILockSettings.Stub { addUserKeyAuth(userId, null, null); } - private static byte[] secretFromCredential(byte[] credential) { + private static byte[] secretFromCredential(LockscreenCredential credential) { try { MessageDigest digest = MessageDigest.getInstance("SHA-512"); // Personalize the hash @@ -1757,7 +1786,7 @@ public class LockSettingsService extends ILockSettings.Stub { // Pad it to the block size of the hash function personalization = Arrays.copyOf(personalization, 128); digest.update(personalization); - digest.update(credential); + digest.update(credential.getCredential()); return digest.digest(); } catch (NoSuchAlgorithmException e) { throw new IllegalStateException("NoSuchAlgorithmException for SHA-512"); @@ -1768,7 +1797,7 @@ public class LockSettingsService extends ILockSettings.Stub { try { return mStorageManager.isUserKeyUnlocked(userId); } catch (RemoteException e) { - Log.e(TAG, "failed to check user key locked state", e); + Slog.e(TAG, "failed to check user key locked state", e); return false; } } @@ -1815,7 +1844,7 @@ public class LockSettingsService extends ILockSettings.Stub { checkWritePermission(userId); if (DEBUG) Slog.v(TAG, "Reset keystore for user: " + userId); int managedUserId = -1; - byte[] managedUserDecryptedPassword = null; + LockscreenCredential managedUserDecryptedPassword = null; final List<UserInfo> profiles = mUserManager.getProfiles(userId); for (UserInfo pi : profiles) { // Unlock managed profile with unified lock @@ -1852,30 +1881,30 @@ public class LockSettingsService extends ILockSettings.Stub { tieProfileLockToParent(managedUserId, managedUserDecryptedPassword); } } - if (managedUserDecryptedPassword != null && managedUserDecryptedPassword.length > 0) { - Arrays.fill(managedUserDecryptedPassword, (byte) 0); + if (managedUserDecryptedPassword != null) { + managedUserDecryptedPassword.zeroize(); } } @Override - public VerifyCredentialResponse checkCredential(byte[] credential, int type, int userId, + public VerifyCredentialResponse checkCredential(LockscreenCredential credential, int userId, ICheckCredentialProgressCallback progressCallback) { checkPasswordReadPermission(userId); - return doVerifyCredential(credential, type, CHALLENGE_NONE, 0, userId, progressCallback); + return doVerifyCredential(credential, CHALLENGE_NONE, 0, userId, progressCallback); } @Override - public VerifyCredentialResponse verifyCredential(byte[] credential, int type, long challenge, - int userId) { + public VerifyCredentialResponse verifyCredential(LockscreenCredential credential, + long challenge, int userId) { checkPasswordReadPermission(userId); - return doVerifyCredential(credential, type, CHALLENGE_FROM_CALLER, challenge, userId, + return doVerifyCredential(credential, CHALLENGE_FROM_CALLER, challenge, userId, null /* progressCallback */); } - private VerifyCredentialResponse doVerifyCredential(byte[] credential, int credentialType, + private VerifyCredentialResponse doVerifyCredential(LockscreenCredential credential, @ChallengeType int challengeType, long challenge, int userId, ICheckCredentialProgressCallback progressCallback) { - return doVerifyCredential(credential, credentialType, challengeType, challenge, userId, + return doVerifyCredential(credential, challengeType, challenge, userId, progressCallback, null /* resetLockouts */); } @@ -1883,25 +1912,25 @@ public class LockSettingsService extends ILockSettings.Stub { * Verify user credential and unlock the user. Fix pattern bug by deprecating the old base zero * format. */ - private VerifyCredentialResponse doVerifyCredential(byte[] credential, int credentialType, + private VerifyCredentialResponse doVerifyCredential(LockscreenCredential credential, @ChallengeType int challengeType, long challenge, int userId, ICheckCredentialProgressCallback progressCallback, @Nullable ArrayList<PendingResetLockout> resetLockouts) { - if (credential == null || credential.length == 0) { + if (credential == null || credential.isNone()) { throw new IllegalArgumentException("Credential can't be null or empty"); } - if (userId == USER_FRP && Settings.Global.getInt(mContext.getContentResolver(), + if (userId == USER_FRP && mInjector.settingsGlobalGetInt(mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0) != 0) { Slog.e(TAG, "FRP credential can only be verified prior to provisioning."); return VerifyCredentialResponse.ERROR; } VerifyCredentialResponse response = null; - response = spBasedDoVerifyCredential(credential, credentialType, challengeType, challenge, + response = spBasedDoVerifyCredential(credential, challengeType, challenge, userId, progressCallback, resetLockouts); // The user employs synthetic password based credential. if (response != null) { if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) { - sendCredentialsOnUnlockIfRequired(credentialType, credential, userId); + sendCredentialsOnUnlockIfRequired(credential, userId); } return response; } @@ -1912,9 +1941,9 @@ public class LockSettingsService extends ILockSettings.Stub { } final CredentialHash storedHash = mStorage.readCredentialHash(userId); - if (storedHash.type != credentialType) { + if (!credential.checkAgainstStoredType(storedHash.type)) { Slog.wtf(TAG, "doVerifyCredential type mismatch with stored credential??" - + " stored: " + storedHash.type + " passed in: " + credentialType); + + " stored: " + storedHash.type + " passed in: " + credential.getType()); return VerifyCredentialResponse.ERROR; } @@ -1929,7 +1958,7 @@ public class LockSettingsService extends ILockSettings.Stub { } @Override - public VerifyCredentialResponse verifyTiedProfileChallenge(byte[] credential, int type, + public VerifyCredentialResponse verifyTiedProfileChallenge(LockscreenCredential credential, long challenge, int userId) { checkPasswordReadPermission(userId); if (!isManagedProfileWithUnifiedLock(userId)) { @@ -1939,7 +1968,6 @@ public class LockSettingsService extends ILockSettings.Stub { // Unlock parent by using parent's challenge final VerifyCredentialResponse parentResponse = doVerifyCredential( credential, - type, CHALLENGE_FROM_CALLER, challenge, parentProfileId, @@ -1952,7 +1980,6 @@ public class LockSettingsService extends ILockSettings.Stub { try { // Unlock work profile, and work profile with unified lock must use password only return doVerifyCredential(getDecryptedPasswordForTiedProfile(userId), - CREDENTIAL_TYPE_PASSWORD, CHALLENGE_FROM_CALLER, challenge, userId, null /* progressCallback */); @@ -1971,15 +1998,14 @@ public class LockSettingsService extends ILockSettings.Stub { * hash to GK. */ private VerifyCredentialResponse verifyCredential(int userId, CredentialHash storedHash, - byte[] credential, @ChallengeType int challengeType, long challenge, + LockscreenCredential credential, @ChallengeType int challengeType, long challenge, ICheckCredentialProgressCallback progressCallback) { - if ((storedHash == null || storedHash.hash.length == 0) - && (credential == null || credential.length == 0)) { + if ((storedHash == null || storedHash.hash.length == 0) && credential.isNone()) { // don't need to pass empty credentials to GateKeeper return VerifyCredentialResponse.OK; } - if (storedHash == null || credential == null || credential.length == 0) { + if (storedHash == null || storedHash.hash.length == 0 || credential.isNone()) { return VerifyCredentialResponse.ERROR; } @@ -1989,8 +2015,8 @@ public class LockSettingsService extends ILockSettings.Stub { GateKeeperResponse gateKeeperResponse; try { - gateKeeperResponse = getGateKeeperService() - .verifyChallenge(userId, challenge, storedHash.hash, credential); + gateKeeperResponse = getGateKeeperService().verifyChallenge( + userId, challenge, storedHash.hash, credential.getCredential()); } catch (RemoteException e) { Slog.e(TAG, "gatekeeper verify failed", e); gateKeeperResponse = GateKeeperResponse.ERROR; @@ -2006,11 +2032,11 @@ public class LockSettingsService extends ILockSettings.Stub { try { progressCallback.onCredentialVerified(); } catch (RemoteException e) { - Log.w(TAG, "progressCallback throws exception", e); + Slog.w(TAG, "progressCallback throws exception", e); } } - setUserPasswordMetrics(storedHash.type, credential, userId); - unlockKeystore(credential, userId); + setUserPasswordMetrics(credential, userId); + unlockKeystore(credential.getCredential(), userId); Slog.i(TAG, "Unlocking user " + userId + " with token length " + response.getPayload().length); @@ -2019,27 +2045,22 @@ public class LockSettingsService extends ILockSettings.Stub { if (isManagedProfileWithSeparatedLock(userId)) { setDeviceUnlockedForUser(userId); } - int reEnrollQuality = storedHash.type == CREDENTIAL_TYPE_PATTERN - ? DevicePolicyManager.PASSWORD_QUALITY_SOMETHING - : DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC - /* TODO(roosa): keep the same password quality */; if (shouldReEnroll) { - setLockCredentialInternal(credential, storedHash.type, credential, - reEnrollQuality, userId, false, /* isLockTiedToParent= */ false); + setLockCredentialInternal(credential, credential, + userId, false, /* isLockTiedToParent= */ false); } else { // Now that we've cleared of all required GK migration, let's do the final // migration to synthetic password. synchronized (mSpManager) { if (shouldMigrateToSyntheticPasswordLocked(userId)) { AuthenticationToken auth = initializeSyntheticPasswordLocked( - storedHash.hash, credential, storedHash.type, reEnrollQuality, - userId); + storedHash.hash, credential, userId); activateEscrowTokens(auth, userId); } } } // Use credentials to create recoverable keystore snapshot. - sendCredentialsOnUnlockIfRequired(storedHash.type, credential, userId); + sendCredentialsOnUnlockIfRequired(credential, userId); } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) { if (response.getTimeout() > 0) { @@ -2055,12 +2076,9 @@ public class LockSettingsService extends ILockSettings.Stub { * when the user is authenticating or when a new password is being set. In comparison, * {@link #notifyPasswordChanged} only needs to be called when the user changes the password. */ - private void setUserPasswordMetrics(@CredentialType int credentialType, byte[] password, - @UserIdInt int userHandle) { + private void setUserPasswordMetrics(LockscreenCredential password, @UserIdInt int userHandle) { synchronized (this) { - mUserPasswordMetrics.put(userHandle, - PasswordMetrics.computeForCredential( - LockscreenCredential.createRaw(credentialType, password))); + mUserPasswordMetrics.put(userHandle, PasswordMetrics.computeForCredential(password)); } } @@ -2089,6 +2107,14 @@ public class LockSettingsService extends ILockSettings.Stub { }); } + private LockscreenCredential createPattern(String patternString) { + final byte[] patternBytes = patternString.getBytes(); + LockscreenCredential pattern = LockscreenCredential.createPattern( + LockPatternUtils.byteArrayToPattern(patternBytes)); + Arrays.fill(patternBytes, (byte) 0); + return pattern; + } + @Override public boolean checkVoldPassword(int userId) { if (!mFirstCallToVold) { @@ -2119,30 +2145,34 @@ public class LockSettingsService extends ILockSettings.Stub { } finally { Binder.restoreCallingIdentity(identity); } - if (password == null) { + if (TextUtils.isEmpty(password)) { return false; } try { - if (mLockPatternUtils.isLockPatternEnabled(userId)) { - if (checkCredential(password.getBytes(), CREDENTIAL_TYPE_PATTERN, - userId, null /* progressCallback */) - .getResponseCode() == GateKeeperResponse.RESPONSE_OK) { - return true; - } - } - } catch (Exception e) { - } - - try { - if (mLockPatternUtils.isLockPasswordEnabled(userId)) { - if (checkCredential(password.getBytes(), CREDENTIAL_TYPE_PASSWORD, - userId, null /* progressCallback */) + final LockscreenCredential credential; + switch (getCredentialTypeInternal(userId)) { + case CREDENTIAL_TYPE_PATTERN: + credential = createPattern(password); + break; + case CREDENTIAL_TYPE_PIN: + credential = LockscreenCredential.createPin(password); + break; + case CREDENTIAL_TYPE_PASSWORD: + credential = LockscreenCredential.createPassword(password); + break; + default: + credential = null; + Slog.e(TAG, "Unknown credential type"); + } + + if (credential != null + && checkCredential(credential, userId, null /* progressCallback */) .getResponseCode() == GateKeeperResponse.RESPONSE_OK) { - return true; - } + return true; } } catch (Exception e) { + Slog.e(TAG, "checkVoldPassword failed: ", e); } return false; @@ -2524,7 +2554,7 @@ public class LockSettingsService extends ILockSettings.Stub { @GuardedBy("mSpManager") @VisibleForTesting protected AuthenticationToken initializeSyntheticPasswordLocked(byte[] credentialHash, - byte[] credential, int credentialType, int requestedQuality, int userId) { + LockscreenCredential credential, int userId) { Slog.i(TAG, "Initialize SyntheticPassword for user: " + userId); final AuthenticationToken auth = mSpManager.newSyntheticPasswordAndSid( getGateKeeperService(), credentialHash, credential, userId); @@ -2534,8 +2564,8 @@ public class LockSettingsService extends ILockSettings.Stub { return null; } long handle = mSpManager.createPasswordBasedSyntheticPassword(getGateKeeperService(), - credential, credentialType, auth, requestedQuality, userId); - if (credential != null) { + credential, auth, userId); + if (!credential.isNone()) { if (credentialHash == null) { // Since when initializing SP, we didn't provide an existing password handle // for it to migrate SID, we need to create a new SID for the user. @@ -2567,6 +2597,13 @@ public class LockSettingsService extends ILockSettings.Stub { } + @VisibleForTesting + boolean isSyntheticPasswordBasedCredential(int userId) { + synchronized (mSpManager) { + return isSyntheticPasswordBasedCredentialLocked(userId); + } + } + private boolean isSyntheticPasswordBasedCredentialLocked(int userId) { if (userId == USER_FRP) { final int type = mStorage.readPersistentDataBlock().type; @@ -2592,8 +2629,8 @@ public class LockSettingsService extends ILockSettings.Stub { setLong(SYNTHETIC_PASSWORD_ENABLED_KEY, 1, UserHandle.USER_SYSTEM); } - private VerifyCredentialResponse spBasedDoVerifyCredential(byte[] userCredential, - @CredentialType int credentialType, @ChallengeType int challengeType, long challenge, + private VerifyCredentialResponse spBasedDoVerifyCredential(LockscreenCredential userCredential, + @ChallengeType int challengeType, long challenge, int userId, ICheckCredentialProgressCallback progressCallback, @Nullable ArrayList<PendingResetLockout> resetLockouts) { @@ -2601,9 +2638,6 @@ public class LockSettingsService extends ILockSettings.Stub { Slog.d(TAG, "spBasedDoVerifyCredential: user=" + userId + " challengeType=" + challengeType + " hasEnrolledBiometrics=" + hasEnrolledBiometrics); - if (credentialType == CREDENTIAL_TYPE_NONE) { - userCredential = null; - } final PackageManager pm = mContext.getPackageManager(); // TODO: When lockout is handled under the HAL for all biometrics (fingerprint), @@ -2625,17 +2659,13 @@ public class LockSettingsService extends ILockSettings.Stub { } if (userId == USER_FRP) { return mSpManager.verifyFrpCredential(getGateKeeperService(), - userCredential, credentialType, progressCallback); + userCredential, progressCallback); } long handle = getSyntheticPasswordHandleLocked(userId); authResult = mSpManager.unwrapPasswordBasedSyntheticPassword( getGateKeeperService(), handle, userCredential, userId, progressCallback); - if (authResult.credentialType != credentialType) { - Slog.e(TAG, "Credential type mismatch."); - return VerifyCredentialResponse.ERROR; - } response = authResult.gkResponse; // credential has matched if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) { @@ -2653,7 +2683,7 @@ public class LockSettingsService extends ILockSettings.Stub { } if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) { - setUserPasswordMetrics(credentialType, userCredential, userId); + setUserPasswordMetrics(userCredential, userId); unlockKeystore(authResult.authToken.deriveKeyStorePassword(), userId); // Do resetLockout / revokeChallenge when all profiles are unlocked @@ -2700,14 +2730,13 @@ public class LockSettingsService extends ILockSettings.Stub { * added back when new password is set in future. */ @GuardedBy("mSpManager") - private long setLockCredentialWithAuthTokenLocked(byte[] credential, - @CredentialType int credentialType, AuthenticationToken auth, int requestedQuality, - int userId) { + private long setLockCredentialWithAuthTokenLocked(LockscreenCredential credential, + AuthenticationToken auth, int userId) { if (DEBUG) Slog.d(TAG, "setLockCredentialWithAuthTokenLocked: user=" + userId); long newHandle = mSpManager.createPasswordBasedSyntheticPassword(getGateKeeperService(), - credential, credentialType, auth, requestedQuality, userId); - final Map<Integer, byte[]> profilePasswords; - if (credential != null) { + credential, auth, userId); + final Map<Integer, LockscreenCredential> profilePasswords; + if (!credential.isNone()) { // not needed by synchronizeUnifiedWorkChallengeForProfiles() profilePasswords = null; @@ -2748,11 +2777,11 @@ public class LockSettingsService extends ILockSettings.Stub { setSyntheticPasswordHandleLocked(newHandle, userId); synchronizeUnifiedWorkChallengeForProfiles(userId, profilePasswords); - setUserPasswordMetrics(credentialType, credential, userId); + setUserPasswordMetrics(credential, userId); if (profilePasswords != null) { - for (Map.Entry<Integer, byte[]> entry : profilePasswords.entrySet()) { - Arrays.fill(entry.getValue(), (byte) 0); + for (Map.Entry<Integer, LockscreenCredential> entry : profilePasswords.entrySet()) { + entry.getValue().zeroize(); } } @@ -2838,12 +2867,17 @@ public class LockSettingsService extends ILockSettings.Stub { }; } + /** + * @param savedCredential if the user is a managed profile with unified challenge and + * savedCredential is empty, LSS will try to re-derive the profile password internally. + * TODO (b/80170828): Fix this so profile password is always passed in. + */ @GuardedBy("mSpManager") - private boolean spBasedSetLockCredentialInternalLocked(byte[] credential, int credentialType, - byte[] savedCredential, int requestedQuality, int userId, + private boolean spBasedSetLockCredentialInternalLocked(LockscreenCredential credential, + LockscreenCredential savedCredential, int userId, boolean allowUntrustedChange, boolean isLockTiedToParent) { if (DEBUG) Slog.d(TAG, "spBasedSetLockCredentialInternalLocked: user=" + userId); - if (isManagedProfileWithUnifiedLock(userId)) { + if (savedCredential.isNone() && isManagedProfileWithUnifiedLock(userId)) { // get credential from keystore when managed profile has unified lock try { savedCredential = getDecryptedPasswordForTiedProfile(userId); @@ -2863,9 +2897,8 @@ public class LockSettingsService extends ILockSettings.Stub { AuthenticationToken auth = authResult.authToken; // If existing credential is provided, the existing credential must match. - if (savedCredential != null && auth == null) { - Slog.w(TAG, String.format("Failed to enroll %s: incorrect credential", - credentialType == CREDENTIAL_TYPE_PASSWORD ? "password" : "pattern")); + if (!savedCredential.isNone() && auth == null) { + Slog.w(TAG, "Failed to enroll: incorrect credential"); return false; } boolean untrustedReset = false; @@ -2898,8 +2931,7 @@ public class LockSettingsService extends ILockSettings.Stub { // setLockCredentialWithAuthTokenLocked next mSpManager.newSidForUser(getGateKeeperService(), auth, userId); } - setLockCredentialWithAuthTokenLocked(credential, credentialType, auth, requestedQuality, - userId); + setLockCredentialWithAuthTokenLocked(credential, auth, userId); mSpManager.destroyPasswordBasedSyntheticPassword(handle, userId); } else { throw new IllegalStateException( @@ -2908,7 +2940,7 @@ public class LockSettingsService extends ILockSettings.Stub { // requestedQuality, userId) instead if we still allow untrusted reset that changes // synthetic password. That would invalidate existing escrow tokens though. } - sendCredentialsOnChangeIfRequired(credentialType, credential, userId, isLockTiedToParent); + sendCredentialsOnChangeIfRequired(credential, userId, isLockTiedToParent); return true; } @@ -2919,11 +2951,8 @@ public class LockSettingsService extends ILockSettings.Stub { * If user is a managed profile with unified challenge, currentCredential is ignored. */ @Override - public byte[] getHashFactor(byte[] currentCredential, int userId) { + public byte[] getHashFactor(LockscreenCredential currentCredential, int userId) { checkPasswordReadPermission(userId); - if (currentCredential == null || currentCredential.length == 0) { - currentCredential = null; - } if (isManagedProfileWithUnifiedLock(userId)) { try { currentCredential = getDecryptedPasswordForTiedProfile(userId); @@ -2957,13 +2986,12 @@ public class LockSettingsService extends ILockSettings.Stub { AuthenticationToken auth = null; if (!isUserSecure(userId)) { if (shouldMigrateToSyntheticPasswordLocked(userId)) { - auth = initializeSyntheticPasswordLocked(null, null, - CREDENTIAL_TYPE_NONE, - DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userId); + auth = initializeSyntheticPasswordLocked( + /* credentialHash */ null, LockscreenCredential.createNone(), userId); } else /* isSyntheticPasswordBasedCredentialLocked(userId) */ { long pwdHandle = getSyntheticPasswordHandleLocked(userId); auth = mSpManager.unwrapPasswordBasedSyntheticPassword(getGateKeeperService(), - pwdHandle, null, userId, null).authToken; + pwdHandle, LockscreenCredential.createNone(), userId, null).authToken; } } if (isSyntheticPasswordBasedCredentialLocked(userId)) { @@ -3023,21 +3051,21 @@ public class LockSettingsService extends ILockSettings.Stub { } } - private boolean setLockCredentialWithToken(byte[] credential, int type, long tokenHandle, - byte[] token, int requestedQuality, int userId) { + private boolean setLockCredentialWithToken(LockscreenCredential credential, long tokenHandle, + byte[] token, int userId) { boolean result; synchronized (mSpManager) { if (!mSpManager.hasEscrowData(userId)) { throw new SecurityException("Escrow token is disabled on the current user"); } - result = setLockCredentialWithTokenInternalLocked(credential, type, tokenHandle, token, - requestedQuality, userId); + result = setLockCredentialWithTokenInternalLocked( + credential, tokenHandle, token, userId); } if (result) { synchronized (mSeparateChallengeLock) { - setSeparateProfileChallengeEnabledLocked(userId, true, null); + setSeparateProfileChallengeEnabledLocked(userId, true, /* unused */ null); } - if (credential == null) { + if (credential.isNone()) { // If clearing credential, unlock the user manually in order to progress user start // Call unlockUser() on a handler thread so no lock is held (either by LSS or by // the caller like DPMS), otherwise it can lead to deadlock. @@ -3050,8 +3078,8 @@ public class LockSettingsService extends ILockSettings.Stub { } @GuardedBy("mSpManager") - private boolean setLockCredentialWithTokenInternalLocked(byte[] credential, int type, - long tokenHandle, byte[] token, int requestedQuality, int userId) { + private boolean setLockCredentialWithTokenInternalLocked(LockscreenCredential credential, + long tokenHandle, byte[] token, int userId) { final AuthenticationResult result; result = mSpManager.unwrapTokenBasedSyntheticPassword( getGateKeeperService(), tokenHandle, token, userId); @@ -3066,11 +3094,8 @@ public class LockSettingsService extends ILockSettings.Stub { + "verification."); return false; } - // TODO: refactor usage of PASSWORD_TYPE_KEY b/65239740 - setLong(LockPatternUtils.PASSWORD_TYPE_KEY, requestedQuality, userId); long oldHandle = getSyntheticPasswordHandleLocked(userId); - setLockCredentialWithAuthTokenLocked(credential, type, result.authToken, - requestedQuality, userId); + setLockCredentialWithAuthTokenLocked(credential, result.authToken, userId); mSpManager.destroyPasswordBasedSyntheticPassword(oldHandle, userId); onAuthTokenKnownForUser(userId, result.authToken); @@ -3132,11 +3157,10 @@ public class LockSettingsService extends ILockSettings.Stub { } // It's OK to dump the password type since anyone with physical access can just // observe it from the keyguard directly. - pw.println("PasswordType: " + getLong(LockPatternUtils.PASSWORD_TYPE_KEY, 0, userId)); - pw.println("hasPassword: " + havePassword(userId)); - pw.println("hasPattern: " + havePattern(userId)); // print raw credential type instead? + pw.println("Quality: " + getKeyguardStoredQuality(userId)); + pw.println("CredentialType: " + getCredentialTypeInternal(userId)); pw.println("SeparateChallenge: " + getSeparateProfileChallengeEnabled(userId)); - pw.println(String.format("metrics: %s", + pw.println(String.format("Metrics: %s", getUserPasswordMetrics(userId) != null ? "known" : "unknown")); pw.decreaseIndent(); } @@ -3298,14 +3322,14 @@ public class LockSettingsService extends ILockSettings.Stub { } @Override - public boolean setLockCredentialWithToken(byte[] credential, int type, - long tokenHandle, byte[] token, int requestedQuality, int userId) { + public boolean setLockCredentialWithToken(LockscreenCredential credential, long tokenHandle, + byte[] token, int userId) { if (!mLockPatternUtils.hasSecureLockScreen()) { throw new UnsupportedOperationException( "This operation requires secure lock screen feature."); } - return LockSettingsService.this.setLockCredentialWithToken(credential, type, - tokenHandle, token, requestedQuality, userId); + return LockSettingsService.this.setLockCredentialWithToken( + credential, tokenHandle, token, userId); } @Override diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java index 0a8e5bd7ad4b..71c7b23a3a0d 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java @@ -243,9 +243,7 @@ class LockSettingsShellCommand extends ShellCommand { } private boolean checkCredential() { - final boolean havePassword = mLockPatternUtils.isLockPasswordEnabled(mCurrentUserId); - final boolean havePattern = mLockPatternUtils.isLockPatternEnabled(mCurrentUserId); - if (havePassword || havePattern) { + if (mLockPatternUtils.isSecure(mCurrentUserId)) { if (mLockPatternUtils.isManagedProfileWithUnifiedChallenge(mCurrentUserId)) { getOutPrintWriter().println("Profile uses unified challenge"); return false; diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java index f4cad634ac1a..3dab3ce7116c 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java @@ -30,7 +30,6 @@ import android.os.Environment; import android.os.UserHandle; import android.os.UserManager; import android.util.ArrayMap; -import android.util.Log; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; @@ -214,7 +213,7 @@ class LockSettingsStorage { private CredentialHash readPasswordHashIfExists(int userId) { byte[] stored = readFile(getLockPasswordFilename(userId)); if (!ArrayUtils.isEmpty(stored)) { - return new CredentialHash(stored, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD); + return new CredentialHash(stored, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD_OR_PIN); } return null; } @@ -270,10 +269,6 @@ class LockSettingsStorage { return hasFile(getLockPatternFilename(userId)); } - public boolean hasCredential(int userId) { - return hasPassword(userId) || hasPattern(userId); - } - private boolean hasFile(String name) { byte[] contents = readFile(name); return contents != null && contents.length > 0; @@ -360,11 +355,15 @@ class LockSettingsStorage { public void writeCredentialHash(CredentialHash hash, int userId) { byte[] patternHash = null; byte[] passwordHash = null; - - if (hash.type == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD) { + if (hash.type == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD_OR_PIN + || hash.type == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD + || hash.type == LockPatternUtils.CREDENTIAL_TYPE_PIN) { passwordHash = hash.hash; } else if (hash.type == LockPatternUtils.CREDENTIAL_TYPE_PATTERN) { patternHash = hash.hash; + } else { + Preconditions.checkArgument(hash.type == LockPatternUtils.CREDENTIAL_TYPE_NONE, + "Unknown credential type"); } writeFile(getLockPasswordFilename(userId), passwordHash); writeFile(getLockPatternFilename(userId), patternHash); @@ -523,8 +522,8 @@ class LockSettingsStorage { mCache.clear(); } - @Nullable - public PersistentDataBlockManagerInternal getPersistentDataBlock() { + @Nullable @VisibleForTesting + PersistentDataBlockManagerInternal getPersistentDataBlockManager() { if (mPersistentDataBlockManagerInternal == null) { mPersistentDataBlockManagerInternal = LocalServices.getService(PersistentDataBlockManagerInternal.class); @@ -534,7 +533,7 @@ class LockSettingsStorage { public void writePersistentDataBlock(int persistentType, int userId, int qualityForUi, byte[] payload) { - PersistentDataBlockManagerInternal persistentDataBlock = getPersistentDataBlock(); + PersistentDataBlockManagerInternal persistentDataBlock = getPersistentDataBlockManager(); if (persistentDataBlock == null) { return; } @@ -543,7 +542,7 @@ class LockSettingsStorage { } public PersistentData readPersistentDataBlock() { - PersistentDataBlockManagerInternal persistentDataBlock = getPersistentDataBlock(); + PersistentDataBlockManagerInternal persistentDataBlock = getPersistentDataBlockManager(); if (persistentDataBlock == null) { return PersistentData.NONE; } @@ -699,7 +698,7 @@ class LockSettingsStorage { } if (upgradeVersion != DATABASE_VERSION) { - Log.w(TAG, "Failed to upgrade database!"); + Slog.w(TAG, "Failed to upgrade database!"); } } } diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java index 955a9aa8d0de..53d922b6da9f 100644 --- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java +++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java @@ -20,7 +20,6 @@ import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChang import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.admin.DevicePolicyManager; import android.content.Context; import android.content.pm.UserInfo; import android.hardware.weaver.V1_0.IWeaver; @@ -35,13 +34,13 @@ import android.security.Scrypt; import android.service.gatekeeper.GateKeeperResponse; import android.service.gatekeeper.IGateKeeperService; import android.util.ArrayMap; -import android.util.Log; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.internal.widget.ICheckCredentialProgressCallback; import com.android.internal.widget.LockPatternUtils; +import com.android.internal.widget.LockscreenCredential; import com.android.internal.widget.VerifyCredentialResponse; import com.android.server.locksettings.LockSettingsStorage.PersistentData; @@ -138,7 +137,6 @@ public class SyntheticPasswordManager { static class AuthenticationResult { public AuthenticationToken authToken; public VerifyCredentialResponse gkResponse; - public int credentialType; } static class AuthenticationToken { @@ -220,7 +218,7 @@ public class SyntheticPasswordManager { byte scryptN; byte scryptR; byte scryptP; - public int passwordType; + public int credentialType; byte[] salt; // For GateKeeper-based credential, this is the password handle returned by GK, // for weaver-based credential, this is empty. @@ -231,7 +229,7 @@ public class SyntheticPasswordManager { result.scryptN = PASSWORD_SCRYPT_N; result.scryptR = PASSWORD_SCRYPT_R; result.scryptP = PASSWORD_SCRYPT_P; - result.passwordType = passwordType; + result.credentialType = passwordType; result.salt = secureRandom(PASSWORD_SALT_LENGTH); return result; } @@ -241,7 +239,7 @@ public class SyntheticPasswordManager { ByteBuffer buffer = ByteBuffer.allocate(data.length); buffer.put(data, 0, data.length); buffer.flip(); - result.passwordType = buffer.getInt(); + result.credentialType = buffer.getInt(); result.scryptN = buffer.get(); result.scryptR = buffer.get(); result.scryptP = buffer.get(); @@ -263,7 +261,7 @@ public class SyntheticPasswordManager { ByteBuffer buffer = ByteBuffer.allocate(Integer.BYTES + 3 * Byte.BYTES + Integer.BYTES + salt.length + Integer.BYTES + (passwordHandle != null ? passwordHandle.length : 0)); - buffer.putInt(passwordType); + buffer.putInt(credentialType); buffer.put(scryptN); buffer.put(scryptR); buffer.put(scryptP); @@ -366,11 +364,11 @@ public class SyntheticPasswordManager { try { int writeStatus = mWeaver.write(slot, toByteArrayList(key), toByteArrayList(value)); if (writeStatus != WeaverStatus.OK) { - Log.e(TAG, "weaver write failed, slot: " + slot + " status: " + writeStatus); + Slog.e(TAG, "weaver write failed, slot: " + slot + " status: " + writeStatus); return null; } } catch (RemoteException e) { - Log.e(TAG, "weaver write failed", e); + Slog.e(TAG, "weaver write failed", e); return null; } return value; @@ -401,31 +399,31 @@ public class SyntheticPasswordManager { break; case WeaverReadStatus.THROTTLE: response[0] = new VerifyCredentialResponse(readResponse.timeout); - Log.e(TAG, "weaver read failed (THROTTLE), slot: " + slot); + Slog.e(TAG, "weaver read failed (THROTTLE), slot: " + slot); break; case WeaverReadStatus.INCORRECT_KEY: if (readResponse.timeout == 0) { response[0] = VerifyCredentialResponse.ERROR; - Log.e(TAG, "weaver read failed (INCORRECT_KEY), slot: " + slot); + Slog.e(TAG, "weaver read failed (INCORRECT_KEY), slot: " + slot); } else { response[0] = new VerifyCredentialResponse(readResponse.timeout); - Log.e(TAG, "weaver read failed (INCORRECT_KEY/THROTTLE), slot: " + Slog.e(TAG, "weaver read failed (INCORRECT_KEY/THROTTLE), slot: " + slot); } break; case WeaverReadStatus.FAILED: response[0] = VerifyCredentialResponse.ERROR; - Log.e(TAG, "weaver read failed (FAILED), slot: " + slot); + Slog.e(TAG, "weaver read failed (FAILED), slot: " + slot); break; default: response[0] = VerifyCredentialResponse.ERROR; - Log.e(TAG, "weaver read unknown status " + status + ", slot: " + slot); + Slog.e(TAG, "weaver read unknown status " + status + ", slot: " + slot); break; } }); } catch (RemoteException e) { response[0] = VerifyCredentialResponse.ERROR; - Log.e(TAG, "weaver read failed, slot: " + slot, e); + Slog.e(TAG, "weaver read failed, slot: " + slot, e); } return response[0]; } @@ -437,13 +435,20 @@ public class SyntheticPasswordManager { } } - public int getCredentialType(long handle, int userId) { + int getCredentialType(long handle, int userId) { byte[] passwordData = loadState(PASSWORD_DATA_NAME, handle, userId); if (passwordData == null) { - Log.w(TAG, "getCredentialType: encountered empty password data for user " + userId); + Slog.w(TAG, "getCredentialType: encountered empty password data for user " + userId); + return LockPatternUtils.CREDENTIAL_TYPE_NONE; + } + return PasswordData.fromBytes(passwordData).credentialType; + } + + static int getFrpCredentialType(byte[] payload) { + if (payload == null) { return LockPatternUtils.CREDENTIAL_TYPE_NONE; } - return PasswordData.fromBytes(passwordData).passwordType; + return PasswordData.fromBytes(payload).credentialType; } /** @@ -469,17 +474,18 @@ public class SyntheticPasswordManager { * */ public AuthenticationToken newSyntheticPasswordAndSid(IGateKeeperService gatekeeper, - byte[] hash, byte[] credential, int userId) { + byte[] hash, LockscreenCredential credential, int userId) { AuthenticationToken result = AuthenticationToken.create(); GateKeeperResponse response; if (hash != null) { try { - response = gatekeeper.enroll(userId, hash, credential, result.deriveGkPassword()); + response = gatekeeper.enroll(userId, hash, credential.getCredential(), + result.deriveGkPassword()); } catch (RemoteException e) { throw new IllegalStateException("Failed to enroll credential duing SP init", e); } if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) { - Log.w(TAG, "Fail to migrate SID, assuming no SID, user " + userId); + Slog.w(TAG, "Fail to migrate SID, assuming no SID, user " + userId); clearSidForUser(userId); } else { saveSyntheticPasswordHandle(response.getPayload(), userId); @@ -504,7 +510,7 @@ public class SyntheticPasswordManager { throw new IllegalStateException("Failed to create new SID for user", e); } if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) { - Log.e(TAG, "Fail to create new SID for user " + userId); + Slog.e(TAG, "Fail to create new SID for user " + userId); return; } saveSyntheticPasswordHandle(response.getPayload(), userId); @@ -561,7 +567,7 @@ public class SyntheticPasswordManager { buffer.put(data, 0, data.length); buffer.flip(); if (buffer.get() != WEAVER_VERSION) { - Log.e(TAG, "Invalid weaver slot version of handle " + handle); + Slog.e(TAG, "Invalid weaver slot version of handle " + handle); return INVALID_WEAVER_SLOT; } return buffer.getInt(); @@ -580,11 +586,11 @@ public class SyntheticPasswordManager { if (slot != INVALID_WEAVER_SLOT) { Set<Integer> usedSlots = getUsedWeaverSlots(); if (!usedSlots.contains(slot)) { - Log.i(TAG, "Destroy weaver slot " + slot + " for user " + userId); + Slog.i(TAG, "Destroy weaver slot " + slot + " for user " + userId); weaverEnroll(slot, null, null); mPasswordSlotManager.markSlotDeleted(slot); } else { - Log.w(TAG, "Skip destroying reused weaver slot " + slot + " for user " + userId); + Slog.w(TAG, "Skip destroying reused weaver slot " + slot + " for user " + userId); } } } @@ -638,15 +644,9 @@ public class SyntheticPasswordManager { * @throw IllegalStateException if creation fails. */ public long createPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper, - byte[] credential, int credentialType, AuthenticationToken authToken, - int requestedQuality, int userId) { - if (credential == null || credentialType == LockPatternUtils.CREDENTIAL_TYPE_NONE) { - credentialType = LockPatternUtils.CREDENTIAL_TYPE_NONE; - credential = DEFAULT_PASSWORD; - } - + LockscreenCredential credential, AuthenticationToken authToken, int userId) { long handle = generateHandle(); - PasswordData pwd = PasswordData.create(credentialType); + PasswordData pwd = PasswordData.create(credential.getType()); byte[] pwdToken = computePasswordToken(credential, pwd); final long sid; final byte[] applicationId; @@ -654,7 +654,7 @@ public class SyntheticPasswordManager { if (isWeaverAvailable()) { // Weaver based user password int weaverSlot = getNextAvailableWeaverSlot(); - Log.i(TAG, "Weaver enroll password to slot " + weaverSlot + " for user " + userId); + Slog.i(TAG, "Weaver enroll password to slot " + weaverSlot + " for user " + userId); byte[] weaverSecret = weaverEnroll(weaverSlot, passwordTokenToWeaverKey(pwdToken), null); if (weaverSecret == null) { @@ -663,7 +663,8 @@ public class SyntheticPasswordManager { } saveWeaverSlot(weaverSlot, handle, userId); mPasswordSlotManager.markSlotInUse(weaverSlot); - synchronizeWeaverFrpPassword(pwd, requestedQuality, userId, weaverSlot); + // No need to pass in quality since the credential type already encodes sufficient info + synchronizeWeaverFrpPassword(pwd, 0, userId, weaverSlot); pwd.passwordHandle = null; sid = GateKeeper.INVALID_SECURE_USER_ID; @@ -674,7 +675,7 @@ public class SyntheticPasswordManager { try { gatekeeper.clearSecureUserId(fakeUid(userId)); } catch (RemoteException ignore) { - Log.w(TAG, "Failed to clear SID from gatekeeper"); + Slog.w(TAG, "Failed to clear SID from gatekeeper"); } // GateKeeper based user password GateKeeperResponse response; @@ -692,7 +693,8 @@ public class SyntheticPasswordManager { sid = sidFromPasswordHandle(pwd.passwordHandle); applicationId = transformUnderSecdiscardable(pwdToken, createSecdiscardable(handle, userId)); - synchronizeFrpPassword(pwd, requestedQuality, userId); + // No need to pass in quality since the credential type already encodes sufficient info + synchronizeFrpPassword(pwd, 0, userId); } saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId); @@ -702,7 +704,7 @@ public class SyntheticPasswordManager { } public VerifyCredentialResponse verifyFrpCredential(IGateKeeperService gatekeeper, - byte[] userCredential, int credentialType, + LockscreenCredential userCredential, ICheckCredentialProgressCallback progressCallback) { PersistentData persistentData = mStorage.readPersistentDataBlock(); if (persistentData.type == PersistentData.TYPE_SP) { @@ -714,7 +716,7 @@ public class SyntheticPasswordManager { response = gatekeeper.verifyChallenge(fakeUid(persistentData.userId), 0 /* challenge */, pwd.passwordHandle, passwordTokenToGkInput(pwdToken)); } catch (RemoteException e) { - Log.e(TAG, "FRP verifyChallenge failed", e); + Slog.e(TAG, "FRP verifyChallenge failed", e); return VerifyCredentialResponse.ERROR; } return VerifyCredentialResponse.fromGateKeeperResponse(response); @@ -725,7 +727,7 @@ public class SyntheticPasswordManager { return weaverVerify(weaverSlot, passwordTokenToWeaverKey(pwdToken)).stripPayload(); } else { - Log.e(TAG, "persistentData.type must be TYPE_SP or TYPE_SP_WEAVER, but is " + Slog.e(TAG, "persistentData.type must be TYPE_SP or TYPE_SP_WEAVER, but is " + persistentData.type); return VerifyCredentialResponse.ERROR; } @@ -733,11 +735,11 @@ public class SyntheticPasswordManager { public void migrateFrpPasswordLocked(long handle, UserInfo userInfo, int requestedQuality) { - if (mStorage.getPersistentDataBlock() != null + if (mStorage.getPersistentDataBlockManager() != null && LockPatternUtils.userOwnsFrpCredential(mContext, userInfo)) { PasswordData pwd = PasswordData.fromBytes(loadState(PASSWORD_DATA_NAME, handle, userInfo.id)); - if (pwd.passwordType != LockPatternUtils.CREDENTIAL_TYPE_NONE) { + if (pwd.credentialType != LockPatternUtils.CREDENTIAL_TYPE_NONE) { int weaverSlot = loadWeaverSlot(handle, userInfo.id); if (weaverSlot != INVALID_WEAVER_SLOT) { synchronizeWeaverFrpPassword(pwd, requestedQuality, userInfo.id, weaverSlot); @@ -750,10 +752,10 @@ public class SyntheticPasswordManager { private void synchronizeFrpPassword(PasswordData pwd, int requestedQuality, int userId) { - if (mStorage.getPersistentDataBlock() != null + if (mStorage.getPersistentDataBlockManager() != null && LockPatternUtils.userOwnsFrpCredential(mContext, mUserManager.getUserInfo(userId))) { - if (pwd.passwordType != LockPatternUtils.CREDENTIAL_TYPE_NONE) { + if (pwd.credentialType != LockPatternUtils.CREDENTIAL_TYPE_NONE) { mStorage.writePersistentDataBlock(PersistentData.TYPE_SP, userId, requestedQuality, pwd.toBytes()); } else { @@ -764,10 +766,10 @@ public class SyntheticPasswordManager { private void synchronizeWeaverFrpPassword(PasswordData pwd, int requestedQuality, int userId, int weaverSlot) { - if (mStorage.getPersistentDataBlock() != null + if (mStorage.getPersistentDataBlockManager() != null && LockPatternUtils.userOwnsFrpCredential(mContext, mUserManager.getUserInfo(userId))) { - if (pwd.passwordType != LockPatternUtils.CREDENTIAL_TYPE_NONE) { + if (pwd.credentialType != LockPatternUtils.CREDENTIAL_TYPE_NONE) { mStorage.writePersistentDataBlock(PersistentData.TYPE_SP_WEAVER, weaverSlot, requestedQuality, pwd.toBytes()); } else { @@ -829,14 +831,14 @@ public class SyntheticPasswordManager { return false; } if (!loadEscrowData(authToken, userId)) { - Log.w(TAG, "User is not escrowable"); + Slog.w(TAG, "User is not escrowable"); return false; } if (isWeaverAvailable()) { int slot = getNextAvailableWeaverSlot(); - Log.i(TAG, "Weaver enroll token to slot " + slot + " for user " + userId); + Slog.i(TAG, "Weaver enroll token to slot " + slot + " for user " + userId); if (weaverEnroll(slot, null, tokenData.weaverSecret) == null) { - Log.e(TAG, "Failed to enroll weaver secret when activating token"); + Slog.e(TAG, "Failed to enroll weaver secret when activating token"); return false; } saveWeaverSlot(slot, handle, userId); @@ -881,18 +883,20 @@ public class SyntheticPasswordManager { * Decrypt a synthetic password by supplying the user credential and corresponding password * blob handle generated previously. If the decryption is successful, initiate a GateKeeper * verification to referesh the SID & Auth token maintained by the system. - * Note: the credential type is not validated here since there are call sites where the type is - * unknown. Caller might choose to validate it by examining AuthenticationResult.credentialType */ public AuthenticationResult unwrapPasswordBasedSyntheticPassword(IGateKeeperService gatekeeper, - long handle, byte[] credential, int userId, + long handle, @NonNull LockscreenCredential credential, int userId, ICheckCredentialProgressCallback progressCallback) { - if (credential == null) { - credential = DEFAULT_PASSWORD; - } AuthenticationResult result = new AuthenticationResult(); PasswordData pwd = PasswordData.fromBytes(loadState(PASSWORD_DATA_NAME, handle, userId)); - result.credentialType = pwd.passwordType; + + if (!credential.checkAgainstStoredType(pwd.credentialType)) { + Slog.e(TAG, String.format("Credential type mismatch: expected %d actual %d", + pwd.credentialType, credential.getType())); + result.gkResponse = VerifyCredentialResponse.ERROR; + return result; + } + byte[] pwdToken = computePasswordToken(credential, pwd); final byte[] applicationId; @@ -901,7 +905,7 @@ public class SyntheticPasswordManager { if (weaverSlot != INVALID_WEAVER_SLOT) { // Weaver based user password if (!isWeaverAvailable()) { - Log.e(TAG, "No weaver service to unwrap password based SP"); + Slog.e(TAG, "No weaver service to unwrap password based SP"); result.gkResponse = VerifyCredentialResponse.ERROR; return result; } @@ -918,7 +922,7 @@ public class SyntheticPasswordManager { response = gatekeeper.verifyChallenge(fakeUid(userId), 0L, pwd.passwordHandle, gkPwdToken); } catch (RemoteException e) { - Log.e(TAG, "gatekeeper verify failed", e); + Slog.e(TAG, "gatekeeper verify failed", e); result.gkResponse = VerifyCredentialResponse.ERROR; return result; } @@ -931,21 +935,19 @@ public class SyntheticPasswordManager { reenrollResponse = gatekeeper.enroll(fakeUid(userId), pwd.passwordHandle, gkPwdToken, gkPwdToken); } catch (RemoteException e) { - Log.w(TAG, "Fail to invoke gatekeeper.enroll", e); + Slog.w(TAG, "Fail to invoke gatekeeper.enroll", e); reenrollResponse = GateKeeperResponse.ERROR; // continue the flow anyway } if (reenrollResponse.getResponseCode() == GateKeeperResponse.RESPONSE_OK) { pwd.passwordHandle = reenrollResponse.getPayload(); + // Use the reenrollment opportunity to update credential type + // (getting rid of CREDENTIAL_TYPE_PASSWORD_OR_PIN) + pwd.credentialType = credential.getType(); saveState(PASSWORD_DATA_NAME, pwd.toBytes(), handle, userId); - synchronizeFrpPassword(pwd, - pwd.passwordType == LockPatternUtils.CREDENTIAL_TYPE_PATTERN - ? DevicePolicyManager.PASSWORD_QUALITY_SOMETHING - : DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC - /* TODO(roosa): keep the same password quality */, - userId); + synchronizeFrpPassword(pwd, 0, userId); } else { - Log.w(TAG, "Fail to re-enroll user password for user " + userId); + Slog.w(TAG, "Fail to re-enroll user password for user " + userId); // continue the flow anyway } } @@ -966,7 +968,7 @@ public class SyntheticPasswordManager { try { progressCallback.onCredentialVerified(); } catch (RemoteException e) { - Log.w(TAG, "progressCallback throws exception", e); + Slog.w(TAG, "progressCallback throws exception", e); } } result.authToken = unwrapSyntheticPasswordBlob(handle, SYNTHETIC_PASSWORD_PASSWORD_BASED, @@ -989,14 +991,14 @@ public class SyntheticPasswordManager { int slotId = loadWeaverSlot(handle, userId); if (slotId != INVALID_WEAVER_SLOT) { if (!isWeaverAvailable()) { - Log.e(TAG, "No weaver service to unwrap token based SP"); + Slog.e(TAG, "No weaver service to unwrap token based SP"); result.gkResponse = VerifyCredentialResponse.ERROR; return result; } VerifyCredentialResponse response = weaverVerify(slotId, null); if (response.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK || response.getPayload() == null) { - Log.e(TAG, "Failed to retrieve weaver secret when unwrapping token"); + Slog.e(TAG, "Failed to retrieve weaver secret when unwrapping token"); result.gkResponse = VerifyCredentialResponse.ERROR; return result; } @@ -1043,13 +1045,13 @@ public class SyntheticPasswordManager { Arrays.copyOfRange(blob, 2, blob.length), applicationId); } if (secret == null) { - Log.e(TAG, "Fail to decrypt SP for user " + userId); + Slog.e(TAG, "Fail to decrypt SP for user " + userId); return null; } AuthenticationToken result = new AuthenticationToken(version); if (type == SYNTHETIC_PASSWORD_TOKEN_BASED) { if (!loadEscrowData(result, userId)) { - Log.e(TAG, "User is not escrowable: " + userId); + Slog.e(TAG, "User is not escrowable: " + userId); return null; } result.recreate(secret); @@ -1057,7 +1059,7 @@ public class SyntheticPasswordManager { result.syntheticPassword = new String(secret); } if (version == SYNTHETIC_PASSWORD_VERSION_V1) { - Log.i(TAG, "Upgrade v1 SP blob for user " + userId + ", type = " + type); + Slog.i(TAG, "Upgrade v1 SP blob for user " + userId + ", type = " + type); createSyntheticPasswordBlob(handle, type, result, applicationId, sid, userId); } return result; @@ -1084,7 +1086,7 @@ public class SyntheticPasswordManager { response = gatekeeper.verifyChallenge(userId, challenge, spHandle, auth.deriveGkPassword()); } catch (RemoteException e) { - Log.e(TAG, "Fail to verify with gatekeeper " + userId, e); + Slog.e(TAG, "Fail to verify with gatekeeper " + userId, e); return VerifyCredentialResponse.ERROR; } int responseCode = response.getResponseCode(); @@ -1095,7 +1097,7 @@ public class SyntheticPasswordManager { response = gatekeeper.enroll(userId, spHandle, spHandle, auth.deriveGkPassword()); } catch (RemoteException e) { - Log.e(TAG, "Failed to invoke gatekeeper.enroll", e); + Slog.e(TAG, "Failed to invoke gatekeeper.enroll", e); response = GateKeeperResponse.ERROR; } if (response.getResponseCode() == GateKeeperResponse.RESPONSE_OK) { @@ -1105,7 +1107,7 @@ public class SyntheticPasswordManager { return verifyChallenge(gatekeeper, auth, challenge, userId); } else { // Fall through, return result from the previous verification attempt. - Log.w(TAG, "Fail to re-enroll SP handle for user " + userId); + Slog.w(TAG, "Fail to re-enroll SP handle for user " + userId); } } return result; @@ -1225,7 +1227,8 @@ public class SyntheticPasswordManager { return String.format("%s%x", LockPatternUtils.SYNTHETIC_PASSWORD_KEY_PREFIX, handle); } - private byte[] computePasswordToken(byte[] password, PasswordData data) { + private byte[] computePasswordToken(LockscreenCredential credential, PasswordData data) { + final byte[] password = credential.isNone() ? DEFAULT_PASSWORD : credential.getCredential(); return scrypt(password, data.salt, 1 << data.scryptN, 1 << data.scryptR, 1 << data.scryptP, PASSWORD_TOKEN_LENGTH); } diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java index 5676da213dd2..29338ba06dc2 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncTask.java @@ -18,7 +18,6 @@ package com.android.server.locksettings.recoverablekeystore; import static android.security.keystore.recovery.KeyChainProtectionParams.TYPE_LOCKSCREEN; -import android.annotation.Nullable; import android.content.Context; import android.os.RemoteException; import android.security.Scrypt; @@ -193,6 +192,7 @@ public class KeySyncTask implements Runnable { private boolean isCustomLockScreen() { return mCredentialType != LockPatternUtils.CREDENTIAL_TYPE_NONE && mCredentialType != LockPatternUtils.CREDENTIAL_TYPE_PATTERN + && mCredentialType != LockPatternUtils.CREDENTIAL_TYPE_PIN && mCredentialType != LockPatternUtils.CREDENTIAL_TYPE_PASSWORD; } @@ -345,7 +345,7 @@ public class KeySyncTask implements Runnable { } KeyChainProtectionParams keyChainProtectionParams = new KeyChainProtectionParams.Builder() .setUserSecretType(TYPE_LOCKSCREEN) - .setLockScreenUiFormat(getUiFormat(mCredentialType, mCredential)) + .setLockScreenUiFormat(getUiFormat(mCredentialType)) .setKeyDerivationParams(keyDerivationParams) .setSecret(new byte[0]) .build(); @@ -449,11 +449,10 @@ public class KeySyncTask implements Runnable { * @return The format - either pattern, pin, or password. */ @VisibleForTesting - @KeyChainProtectionParams.LockScreenUiFormat static int getUiFormat( - int credentialType, byte[] credential) { + @KeyChainProtectionParams.LockScreenUiFormat static int getUiFormat(int credentialType) { if (credentialType == LockPatternUtils.CREDENTIAL_TYPE_PATTERN) { return KeyChainProtectionParams.UI_FORMAT_PATTERN; - } else if (isPin(credential)) { + } else if (credentialType == LockPatternUtils.CREDENTIAL_TYPE_PIN) { return KeyChainProtectionParams.UI_FORMAT_PIN; } else { return KeyChainProtectionParams.UI_FORMAT_PASSWORD; @@ -472,23 +471,6 @@ public class KeySyncTask implements Runnable { } /** - * Returns {@code true} if {@code credential} looks like a pin. - */ - @VisibleForTesting - static boolean isPin(@Nullable byte[] credential) { - if (credential == null) { - return false; - } - int length = credential.length; - for (int i = 0; i < length; i++) { - if (!Character.isDigit((char) credential[i])) { - return false; - } - } - return true; - } - - /** * Hashes {@code credentials} with the given {@code salt}. * * @return The SHA-256 hash. @@ -541,6 +523,7 @@ public class KeySyncTask implements Runnable { } private boolean shouldUseScryptToHashCredential() { - return mCredentialType == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD; + return mCredentialType == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD + || mCredentialType == LockPatternUtils.CREDENTIAL_TYPE_PIN; } } diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java index 90a36723de4d..c963f799245f 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/TestOnlyInsecureCertificateHelper.java @@ -97,7 +97,8 @@ public class TestOnlyInsecureCertificateHelper { if (credential == null) { return false; } - if (credentialType != LockPatternUtils.CREDENTIAL_TYPE_PASSWORD) { + if (credentialType != LockPatternUtils.CREDENTIAL_TYPE_PASSWORD + && credentialType != LockPatternUtils.CREDENTIAL_TYPE_PIN) { return false; } byte[] insecurePasswordPrefixBytes = diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java index 714bbb97c90d..9a1b30dc2b0b 100644 --- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java +++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java @@ -25,7 +25,6 @@ import android.os.Binder; import android.os.BugreportParams; import android.os.IDumpstate; import android.os.IDumpstateListener; -import android.os.IDumpstateToken; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; @@ -64,13 +63,6 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub { @Override @RequiresPermission(android.Manifest.permission.DUMP) - public IDumpstateToken setListener(String name, IDumpstateListener listener, - boolean getSectionDetails) { - throw new UnsupportedOperationException("setListener is not allowed on this service"); - } - - @Override - @RequiresPermission(android.Manifest.permission.DUMP) public void startBugreport(int callingUidUnused, String callingPackage, FileDescriptor bugreportFd, FileDescriptor screenshotFd, int bugreportMode, IDumpstateListener listener) { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 0a4415bf0436..104ce1cbd5cd 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -1562,8 +1562,8 @@ public class PackageManagerService extends IPackageManager.Stub } // Send broadcasts for (int i = 0; i < size; i++) { - sendPackageChangedBroadcast(packages[i], true, components[i], uids[i], - null); + sendPackageChangedBroadcast(packages[i], true /* dontKillApp */, + components[i], uids[i], null /* reason */); } Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); break; @@ -2049,7 +2049,7 @@ public class PackageManagerService extends IPackageManager.Stub for (int i = 0; i < res.libraryConsumers.size(); i++) { PackageParser.Package pkg = res.libraryConsumers.get(i); // send broadcast that all consumers of the static shared library have changed - sendPackageChangedBroadcast(pkg.packageName, false /*killFlag*/, + sendPackageChangedBroadcast(pkg.packageName, false /* dontKillApp */, new ArrayList<>(Collections.singletonList(pkg.packageName)), pkg.applicationInfo.uid, null); } @@ -19999,7 +19999,7 @@ public class PackageManagerService extends IPackageManager.Stub } private void sendPackageChangedBroadcast(String packageName, - boolean killFlag, ArrayList<String> componentNames, int packageUid, + boolean dontKillApp, ArrayList<String> componentNames, int packageUid, String reason) { if (DEBUG_INSTALL) Log.v(TAG, "Sending package changed: package=" + packageName + " components=" @@ -20009,7 +20009,7 @@ public class PackageManagerService extends IPackageManager.Stub String nameList[] = new String[componentNames.size()]; componentNames.toArray(nameList); extras.putStringArray(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST, nameList); - extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, killFlag); + extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, dontKillApp); extras.putInt(Intent.EXTRA_UID, packageUid); if (reason != null) { extras.putString(Intent.EXTRA_REASON, reason); @@ -20282,7 +20282,7 @@ public class PackageManagerService extends IPackageManager.Stub return; } sendPackageChangedBroadcast(pkg.packageName, - false /* killFlag */, + true /* dontKillApp */, new ArrayList<>(Collections.singletonList(pkg.packageName)), pkg.applicationInfo.uid, Intent.ACTION_OVERLAY_CHANGED); diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 2593c38180e2..5e1d93fdda3d 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -386,6 +386,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { BurnInProtectionHelper mBurnInProtectionHelper; private DisplayFoldController mDisplayFoldController; AppOpsManager mAppOpsManager; + PackageManager mPackageManager; private boolean mHasFeatureAuto; private boolean mHasFeatureWatch; private boolean mHasFeatureLeanback; @@ -1555,10 +1556,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { private void launchAllAppsAction() { Intent intent = new Intent(Intent.ACTION_ALL_APPS); if (mHasFeatureLeanback) { - final PackageManager pm = mContext.getPackageManager(); Intent intentLauncher = new Intent(Intent.ACTION_MAIN); intentLauncher.addCategory(Intent.CATEGORY_HOME); - ResolveInfo resolveInfo = pm.resolveActivityAsUser(intentLauncher, + ResolveInfo resolveInfo = mPackageManager.resolveActivityAsUser(intentLauncher, PackageManager.MATCH_SYSTEM_ONLY, mCurrentUserId); if (resolveInfo != null) { @@ -1753,10 +1753,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class); mAppOpsManager = mContext.getSystemService(AppOpsManager.class); mDisplayManager = mContext.getSystemService(DisplayManager.class); - mHasFeatureWatch = mContext.getPackageManager().hasSystemFeature(FEATURE_WATCH); - mHasFeatureLeanback = mContext.getPackageManager().hasSystemFeature(FEATURE_LEANBACK); - mHasFeatureAuto = mContext.getPackageManager().hasSystemFeature(FEATURE_AUTOMOTIVE); - mHasFeatureHdmiCec = mContext.getPackageManager().hasSystemFeature(FEATURE_HDMI_CEC); + mPackageManager = mContext.getPackageManager(); + mHasFeatureWatch = mPackageManager.hasSystemFeature(FEATURE_WATCH); + mHasFeatureLeanback = mPackageManager.hasSystemFeature(FEATURE_LEANBACK); + mHasFeatureAuto = mPackageManager.hasSystemFeature(FEATURE_AUTOMOTIVE); + mHasFeatureHdmiCec = mPackageManager.hasSystemFeature(FEATURE_HDMI_CEC); mAccessibilityShortcutController = new AccessibilityShortcutController(mContext, new Handler(), mCurrentUserId); mLogger = new MetricsLogger(); @@ -1994,7 +1995,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } mShortPressOnWindowBehavior = SHORT_PRESS_WINDOW_NOTHING; - if (mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) { + if (mPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) { mShortPressOnWindowBehavior = SHORT_PRESS_WINDOW_PICTURE_IN_PICTURE; } } @@ -2138,7 +2139,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { ApplicationInfo appInfo; try { - appInfo = mContext.getPackageManager().getApplicationInfoAsUser( + appInfo = mPackageManager.getApplicationInfoAsUser( attrs.packageName, 0 /* flags */, UserHandle.getUserId(callingUid)); @@ -4914,7 +4915,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { @Override public void run() { if (mBootMsgDialog == null) { int theme; - if (mContext.getPackageManager().hasSystemFeature(FEATURE_LEANBACK)) { + if (mPackageManager.hasSystemFeature(FEATURE_LEANBACK)) { theme = com.android.internal.R.style.Theme_Leanback_Dialog_Alert; } else { theme = 0; @@ -4943,7 +4944,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { return true; } }; - if (mContext.getPackageManager().isDeviceUpgrading()) { + if (mPackageManager.isDeviceUpgrading()) { mBootMsgDialog.setTitle(R.string.android_upgrading_title); } else { mBootMsgDialog.setTitle(R.string.android_start_title); @@ -5203,7 +5204,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } ActivityInfo ai = null; - ResolveInfo info = mContext.getPackageManager().resolveActivityAsUser( + ResolveInfo info = mPackageManager.resolveActivityAsUser( intent, PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA, mCurrentUserId); diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java index edf0cbfe459a..b67d9b285acd 100644 --- a/services/core/java/com/android/server/power/Notifier.java +++ b/services/core/java/com/android/server/power/Notifier.java @@ -94,16 +94,16 @@ public class Notifier { private static final int MSG_PROFILE_TIMED_OUT = 5; private static final int MSG_WIRED_CHARGING_STARTED = 6; - private static final long[] WIRELESS_VIBRATION_TIME = { + private static final long[] CHARGING_VIBRATION_TIME = { 40, 40, 40, 40, 40, 40, 40, 40, 40, // ramp-up sampling rate = 40ms 40, 40, 40, 40, 40, 40, 40 // ramp-down sampling rate = 40ms }; - private static final int[] WIRELESS_VIBRATION_AMPLITUDE = { + private static final int[] CHARGING_VIBRATION_AMPLITUDE = { 1, 4, 11, 25, 44, 67, 91, 114, 123, // ramp-up amplitude (from 0 to 50%) 103, 79, 55, 34, 17, 7, 2 // ramp-up amplitude }; - private static final VibrationEffect WIRELESS_CHARGING_VIBRATION_EFFECT = - VibrationEffect.createWaveform(WIRELESS_VIBRATION_TIME, WIRELESS_VIBRATION_AMPLITUDE, + private static final VibrationEffect CHARGING_VIBRATION_EFFECT = + VibrationEffect.createWaveform(CHARGING_VIBRATION_TIME, CHARGING_VIBRATION_AMPLITUDE, -1); private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) @@ -130,6 +130,10 @@ public class Notifier { // True if the device should suspend when the screen is off due to proximity. private final boolean mSuspendWhenScreenOffDueToProximityConfig; + // True if the device should show the wireless charging animation when the device + // begins charging wirelessly + private final boolean mShowWirelessChargingAnimationConfig; + // The current interactive state. This is set as soon as an interactive state // transition begins so as to capture the reason that it happened. At some point // this state will propagate to the pending state then eventually to the @@ -182,6 +186,8 @@ public class Notifier { mSuspendWhenScreenOffDueToProximityConfig = context.getResources().getBoolean( com.android.internal.R.bool.config_suspendWhenScreenOffDueToProximity); + mShowWirelessChargingAnimationConfig = context.getResources().getBoolean( + com.android.internal.R.bool.config_showBuiltinWirelessChargingAnim); // Initialize interactive state for battery stats. try { @@ -755,35 +761,45 @@ public class Notifier { } }; - /** - * If enabled, plays a sound and/or vibration when wireless or non-wireless charging has started - */ - private void playChargingStartedFeedback(@UserIdInt int userId) { - playChargingStartedVibration(userId); + private void playChargingStartedFeedback(@UserIdInt int userId, boolean wireless) { + if (!isChargingFeedbackEnabled(userId)) { + return; + } + + // vibrate + final boolean vibrate = Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.CHARGING_VIBRATION_ENABLED, 1, userId) != 0; + if (vibrate) { + mVibrator.vibrate(CHARGING_VIBRATION_EFFECT, VIBRATION_ATTRIBUTES); + } + + // play sound final String soundPath = Settings.Global.getString(mContext.getContentResolver(), - Settings.Global.CHARGING_STARTED_SOUND); - if (isChargingFeedbackEnabled(userId) && soundPath != null) { - final Uri soundUri = Uri.parse("file://" + soundPath); - if (soundUri != null) { - final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri); - if (sfx != null) { - sfx.setStreamType(AudioManager.STREAM_SYSTEM); - sfx.play(); - } + wireless ? Settings.Global.WIRELESS_CHARGING_STARTED_SOUND + : Settings.Global.CHARGING_STARTED_SOUND); + final Uri soundUri = Uri.parse("file://" + soundPath); + if (soundUri != null) { + final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri); + if (sfx != null) { + sfx.setStreamType(AudioManager.STREAM_SYSTEM); + sfx.play(); } } } private void showWirelessChargingStarted(int batteryLevel, @UserIdInt int userId) { - playChargingStartedFeedback(userId); - if (mStatusBarManagerInternal != null) { + // play sounds + haptics + playChargingStartedFeedback(userId, true /* wireless */); + + // show animation + if (mShowWirelessChargingAnimationConfig && mStatusBarManagerInternal != null) { mStatusBarManagerInternal.showChargingAnimation(batteryLevel); } mSuspendBlocker.release(); } private void showWiredChargingStarted(@UserIdInt int userId) { - playChargingStartedFeedback(userId); + playChargingStartedFeedback(userId, false /* wireless */); mSuspendBlocker.release(); } @@ -791,14 +807,6 @@ public class Notifier { mTrustManager.setDeviceLockedForUser(userId, true /*locked*/); } - private void playChargingStartedVibration(@UserIdInt int userId) { - final boolean vibrateEnabled = Settings.Secure.getIntForUser(mContext.getContentResolver(), - Settings.Secure.CHARGING_VIBRATION_ENABLED, 1, userId) != 0; - if (vibrateEnabled && isChargingFeedbackEnabled(userId)) { - mVibrator.vibrate(WIRELESS_CHARGING_VIBRATION_EFFECT, VIBRATION_ATTRIBUTES); - } - } - private boolean isChargingFeedbackEnabled(@UserIdInt int userId) { final boolean enabled = Settings.Secure.getIntForUser(mContext.getContentResolver(), Settings.Secure.CHARGING_SOUNDS_ENABLED, 1, userId) != 0; diff --git a/services/core/java/com/android/server/twilight/TwilightService.java b/services/core/java/com/android/server/twilight/TwilightService.java index e4cb19e3eb21..e72ba8d9f01b 100644 --- a/services/core/java/com/android/server/twilight/TwilightService.java +++ b/services/core/java/com/android/server/twilight/TwilightService.java @@ -17,6 +17,7 @@ package com.android.server.twilight; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.AlarmManager; import android.content.BroadcastReceiver; import android.content.Context; @@ -160,8 +161,13 @@ public final class TwilightService extends SystemService // Request the device's location immediately if a previous location isn't available. if (mLocationManager.getLastLocation() == null) { if (mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) { - mLocationManager.requestSingleUpdate( - LocationManager.NETWORK_PROVIDER, this, Looper.getMainLooper()); + mLocationManager.getCurrentLocation( + LocationManager.NETWORK_PROVIDER, null, getContext().getMainExecutor(), + this::onLocationChanged); + } else if (mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) { + mLocationManager.getCurrentLocation( + LocationManager.GPS_PROVIDER, null, getContext().getMainExecutor(), + this::onLocationChanged); } } @@ -218,12 +224,7 @@ public final class TwilightService extends SystemService for (int i = mListeners.size() - 1; i >= 0; --i) { final TwilightListener listener = mListeners.keyAt(i); final Handler handler = mListeners.valueAt(i); - handler.post(new Runnable() { - @Override - public void run() { - listener.onTwilightStateChanged(state); - } - }); + handler.post(() -> listener.onTwilightStateChanged(state)); } } } @@ -243,12 +244,8 @@ public final class TwilightService extends SystemService } @Override - public void onLocationChanged(Location location) { - // Location providers may erroneously return (0.0, 0.0) when they fail to determine the - // device's location. These location updates can be safely ignored since the chance of a - // user actually being at these coordinates is quite low. - if (location != null - && !(location.getLongitude() == 0.0 && location.getLatitude() == 0.0)) { + public void onLocationChanged(@Nullable Location location) { + if (location != null) { Slog.d(TAG, "onLocationChanged:" + " provider=" + location.getProvider() + " accuracy=" + location.getAccuracy() diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index e6c6b12e18c6..9d41d97bc1ed 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -187,9 +187,22 @@ class ActivityMetricsLogger { private int startingWindowDelayMs = INVALID_DELAY; private int bindApplicationDelayMs = INVALID_DELAY; private int reason = APP_TRANSITION_TIMEOUT; - private boolean loggedWindowsDrawn; + // TODO(b/132736359) The number may need to consider the visibility change. + private int numUndrawnActivities = 1; private boolean loggedStartingWindowDrawn; private boolean launchTraceActive; + + /** + * Remembers the latest launched activity to represent the final transition. This also + * increments the number of activities that should be drawn, so a consecutive launching + * sequence can be coalesced as one event. + */ + void setLatestLaunchedActivity(ActivityRecord r) { + if (launchedActivity == r) { + return; + } + launchedActivity = r; + } } final class WindowingModeTransitionInfoSnapshot { @@ -400,7 +413,7 @@ class ActivityMetricsLogger { // the other attributes. // Coalesce multiple (trampoline) activities from a single sequence together. - info.launchedActivity = launchedActivity; + info.setLatestLaunchedActivity(launchedActivity); return; } @@ -422,7 +435,7 @@ class ActivityMetricsLogger { // A new launch sequence [with the windowingMode] has begun. // Start tracking it. final WindowingModeTransitionInfo newInfo = new WindowingModeTransitionInfo(); - newInfo.launchedActivity = launchedActivity; + newInfo.setLatestLaunchedActivity(launchedActivity); newInfo.currentTransitionProcessRunning = processRunning; newInfo.startResult = resultCode; mWindowingModeTransitionInfo.put(windowingMode, newInfo); @@ -448,11 +461,11 @@ class ActivityMetricsLogger { if (DEBUG_METRICS) Slog.i(TAG, "notifyWindowsDrawn windowingMode=" + windowingMode); final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(windowingMode); - if (info == null || info.loggedWindowsDrawn) { + if (info == null || info.numUndrawnActivities == 0) { return null; } info.windowsDrawnDelayMs = calculateDelay(timestampNs); - info.loggedWindowsDrawn = true; + info.numUndrawnActivities--; final WindowingModeTransitionInfoSnapshot infoSnapshot = new WindowingModeTransitionInfoSnapshot(info); if (allWindowsDrawn() && mLoggedTransitionStarting) { @@ -594,9 +607,10 @@ class ActivityMetricsLogger { } } - private boolean allWindowsDrawn() { + @VisibleForTesting + boolean allWindowsDrawn() { for (int index = mWindowingModeTransitionInfo.size() - 1; index >= 0; index--) { - if (!mWindowingModeTransitionInfo.valueAt(index).loggedWindowsDrawn) { + if (mWindowingModeTransitionInfo.valueAt(index).numUndrawnActivities != 0) { return false; } } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 208f54c146ae..a783ee9d4e44 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -876,6 +876,8 @@ public class DisplayPolicy { if (canToastShowWhenLocked(callingPid)) { attrs.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; } + // Toasts can't be clickable + attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; break; } @@ -3241,7 +3243,8 @@ public class DisplayPolicy { statusBar.topAppWindowChanged(displayId, isFullscreen, isImmersive); // TODO(b/118118435): Remove this after removing system UI visibilities. - mDisplayContent.statusBarVisibilityChanged(visibility); + mDisplayContent.statusBarVisibilityChanged( + visibility & ~(View.STATUS_BAR_UNHIDE | View.NAVIGATION_BAR_UNHIDE)); } }); return diff; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index c4adc035d613..5e49c7a9e5d1 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -168,6 +168,7 @@ import android.database.ContentObserver; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.Color; +import android.location.LocationManager; import android.media.AudioManager; import android.media.IAudioService; import android.net.ConnectivityManager; @@ -1959,6 +1960,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return mContext.getSystemService(ConnectivityManager.class); } + LocationManager getLocationManager() { + return mContext.getSystemService(LocationManager.class); + } + IWindowManager getIWindowManager() { return IWindowManager.Stub .asInterface(ServiceManager.getService(Context.WINDOW_SERVICE)); @@ -4178,7 +4183,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private boolean passwordQualityInvocationOrderCheckEnabled(String packageName, int userId) { try { return mIPlatformCompat.isChangeEnabledByPackageName(ADMIN_APP_PASSWORD_COMPLEXITY, - packageName); + packageName, userId); } catch (RemoteException e) { Log.e(LOG_TAG, "Failed to get a response from PLATFORM_COMPAT_SERVICE", e); } @@ -10899,6 +10904,36 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override + public void setLocationEnabled(ComponentName who, boolean locationEnabled) { + Preconditions.checkNotNull(who, "ComponentName is null"); + int userId = mInjector.userHandleGetCallingUserId(); + + synchronized (getLockObject()) { + getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + + if (!isDeviceOwner(who, userId) && !isCurrentUserDemo()) { + throw new SecurityException( + "Permission denial: Profile owners cannot update location settings"); + } + } + + long ident = mInjector.binderClearCallingIdentity(); + try { + mInjector.getLocationManager().setLocationEnabledForUser( + locationEnabled, UserHandle.of(userId)); + DevicePolicyEventLogger + .createEvent(DevicePolicyEnums.SET_SECURE_SETTING) + .setAdmin(who) + .setStrings(Settings.Secure.LOCATION_MODE, Integer.toString( + locationEnabled ? Settings.Secure.LOCATION_MODE_ON + : Settings.Secure.LOCATION_MODE_OFF)) + .write(); + } finally { + mInjector.binderRestoreCallingIdentity(ident); + } + } + + @Override public boolean setTime(ComponentName who, long millis) { Preconditions.checkNotNull(who, "ComponentName is null in setTime"); enforceDeviceOwner(who); diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java index 537287d18cca..98630576ed66 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java @@ -52,6 +52,7 @@ import android.test.AndroidTestCase; import com.android.internal.widget.ILockSettings; import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockSettingsInternal; +import com.android.internal.widget.LockscreenCredential; import com.android.server.LocalServices; import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager; import com.android.server.wm.WindowManagerInternal; @@ -104,6 +105,7 @@ public abstract class BaseLockSettingsServiceTests extends AndroidTestCase { FaceManager mFaceManager; PackageManager mPackageManager; protected boolean mHasSecureLockScreen; + FakeSettings mSettings; @Override protected void setUp() throws Exception { @@ -125,6 +127,7 @@ public abstract class BaseLockSettingsServiceTests extends AndroidTestCase { mFingerprintManager = mock(FingerprintManager.class); mFaceManager = mock(FaceManager.class); mPackageManager = mock(PackageManager.class); + mSettings = new FakeSettings(); LocalServices.removeServiceForTest(LockSettingsInternal.class); LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class); @@ -162,7 +165,7 @@ public abstract class BaseLockSettingsServiceTests extends AndroidTestCase { mService = new LockSettingsServiceTestable(mContext, mLockPatternUtils, mStorage, mGateKeeperService, mKeyStore, setUpStorageManagerMock(), mActivityManager, mSpManager, mAuthSecretService, mGsiService, mRecoverableKeyStoreManager, - mUserManagerInternal, mDeviceStateCache); + mUserManagerInternal, mDeviceStateCache, mSettings); when(mUserManager.getUserInfo(eq(PRIMARY_USER_ID))).thenReturn(PRIMARY_USER_INFO); mPrimaryUserProfiles.add(PRIMARY_USER_INFO); installChildProfile(MANAGED_PROFILE_USER_ID); @@ -195,6 +198,7 @@ public abstract class BaseLockSettingsServiceTests extends AndroidTestCase { mockBiometricsHardwareFingerprintsAndTemplates(PRIMARY_USER_ID); mockBiometricsHardwareFingerprintsAndTemplates(MANAGED_PROFILE_USER_ID); + mSettings.setDeviceProvisioned(true); mLocalService = LocalServices.getService(LockSettingsInternal.class); } @@ -307,4 +311,22 @@ public abstract class BaseLockSettingsServiceTests extends AndroidTestCase { protected static void assertArrayNotEquals(byte[] expected, byte[] actual) { assertFalse(Arrays.equals(expected, actual)); } + + protected LockscreenCredential newPassword(String password) { + return LockscreenCredential.createPasswordOrNone(password); + } + + protected LockscreenCredential newPin(String pin) { + return LockscreenCredential.createPinOrNone(pin); + } + + protected LockscreenCredential newPattern(String pattern) { + return LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern( + pattern.getBytes())); + } + + protected LockscreenCredential nonePassword() { + return LockscreenCredential.createNone(); + } + } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java index d2a914527880..5c54883c338f 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/CachedSyntheticPasswordTests.java @@ -15,9 +15,6 @@ */ package com.android.server.locksettings; -import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC; -import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; - import static com.android.server.testutils.TestUtils.assertExpectException; import static org.mockito.Mockito.anyInt; @@ -30,7 +27,7 @@ import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; -import com.android.internal.widget.LockPatternUtils; +import com.android.internal.widget.LockscreenCredential; import com.android.internal.widget.VerifyCredentialResponse; import org.mockito.ArgumentCaptor; @@ -59,56 +56,53 @@ public class CachedSyntheticPasswordTests extends SyntheticPasswordTests { } public void testSyntheticPasswordClearCredentialUntrusted() throws RemoteException { - final byte[] password = "testSyntheticPasswordClearCredential-password".getBytes(); - final byte[] newPassword = "testSyntheticPasswordClearCredential-newpassword".getBytes(); + final LockscreenCredential password = newPassword("password"); + final LockscreenCredential newPassword = newPassword("newpassword"); initializeCredentialUnderSP(password, PRIMARY_USER_ID); long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID); // clear password - mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, null, - PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID, true); + mService.setLockCredential(nonePassword(), password, PRIMARY_USER_ID, true); assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); // set a new password - mService.setLockCredential(newPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, - PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false); - assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(newPassword, - LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) + mService.setLockCredential(newPassword, nonePassword(), PRIMARY_USER_ID, + false); + assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( + newPassword, 0, PRIMARY_USER_ID) .getResponseCode()); assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); } public void testSyntheticPasswordChangeCredentialUntrusted() throws RemoteException { - final byte[] password = "testSyntheticPasswordClearCredential-password".getBytes(); - final byte[] newPassword = "testSyntheticPasswordClearCredential-newpassword".getBytes(); + final LockscreenCredential password = newPassword("password"); + final LockscreenCredential newPassword = newPassword("newpassword"); initializeCredentialUnderSP(password, PRIMARY_USER_ID); long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID); // Untrusted change password - mService.setLockCredential(newPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, - PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, true); + mService.setLockCredential(newPassword, nonePassword(), PRIMARY_USER_ID, + true); assertNotEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); // Verify the password - assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(newPassword, - LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID).getResponseCode()); + assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( + newPassword, 0, PRIMARY_USER_ID).getResponseCode()); } public void testUntrustedCredentialChangeMaintainsAuthSecret() throws RemoteException { - final byte[] password = - "testUntrustedCredentialChangeMaintainsAuthSecret-password".getBytes(); - final byte[] newPassword = - "testUntrustedCredentialChangeMaintainsAuthSecret-newpassword".getBytes(); + final LockscreenCredential password = newPassword("password"); + final LockscreenCredential newPassword = newPassword("newpassword"); initializeCredentialUnderSP(password, PRIMARY_USER_ID); // Untrusted change password - mService.setLockCredential(newPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, - PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, true); + mService.setLockCredential(newPassword, nonePassword(), PRIMARY_USER_ID, + true); // Verify the password - assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(newPassword, - LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) + assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( + newPassword, 0, PRIMARY_USER_ID) .getResponseCode()); // Ensure the same secret was passed each time @@ -118,31 +112,30 @@ public class CachedSyntheticPasswordTests extends SyntheticPasswordTests { } public void testUntrustedCredentialChangeBlockedIfSpNotCached() throws RemoteException { - final byte[] password = - "testUntrustedCredentialChangeBlockedIfSpNotCached-password".getBytes(); - final byte[] newPassword = - "testUntrustedCredentialChangeBlockedIfSpNotCached-newpassword".getBytes(); + final LockscreenCredential password = newPassword("password"); + final LockscreenCredential newPassword = newPassword("newpassword"); // Disable caching for this test enableSpCaching(false); initializeCredentialUnderSP(password, PRIMARY_USER_ID); + flushHandlerTasks(); + long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID); // Untrusted change password assertExpectException( IllegalStateException.class, /* messageRegex= */ "Untrusted credential reset not possible without cached SP", - () -> mService.setLockCredential(newPassword, - LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, - PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, true)); + () -> mService.setLockCredential(newPassword, nonePassword(), + PRIMARY_USER_ID, true)); assertEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); // Verify the new password doesn't work but the old one still does - assertEquals(VerifyCredentialResponse.RESPONSE_ERROR, mService.verifyCredential(newPassword, - LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) + assertEquals(VerifyCredentialResponse.RESPONSE_ERROR, mService.verifyCredential( + newPassword, 0, PRIMARY_USER_ID) .getResponseCode()); - assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(password, - LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) + assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( + password, 0, PRIMARY_USER_ID) .getResponseCode()); } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/FakeSettings.java b/services/tests/servicestests/src/com/android/server/locksettings/FakeSettings.java new file mode 100644 index 000000000000..70a927c216ec --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/locksettings/FakeSettings.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.locksettings; + +import android.provider.Settings; + +public class FakeSettings { + + private int mDeviceProvisioned; + + public void setDeviceProvisioned(boolean provisioned) { + mDeviceProvisioned = provisioned ? 1 : 0; + } + + public int globalGetInt(String keyName) { + switch (keyName) { + case Settings.Global.DEVICE_PROVISIONED: + return mDeviceProvisioned; + default: + throw new IllegalArgumentException("Unhandled global settings: " + keyName); + } + } +} diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java index fcd98e0742ea..7e7e170fee06 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java @@ -20,9 +20,11 @@ import static org.mockito.Mockito.mock; import android.app.IActivityManager; import android.app.admin.DeviceStateCache; +import android.content.ContentResolver; import android.content.Context; import android.hardware.authsecret.V1_0.IAuthSecret; import android.os.Handler; +import android.os.Parcel; import android.os.Process; import android.os.RemoteException; import android.os.UserManagerInternal; @@ -31,6 +33,7 @@ import android.security.KeyStore; import android.security.keystore.KeyPermanentlyInvalidatedException; import com.android.internal.widget.LockPatternUtils; +import com.android.internal.widget.LockscreenCredential; import com.android.server.ServiceThread; import com.android.server.locksettings.recoverablekeystore.RecoverableKeyStoreManager; @@ -50,12 +53,14 @@ public class LockSettingsServiceTestable extends LockSettingsService { private RecoverableKeyStoreManager mRecoverableKeyStoreManager; private UserManagerInternal mUserManagerInternal; private DeviceStateCache mDeviceStateCache; + private FakeSettings mSettings; public MockInjector(Context context, LockSettingsStorage storage, KeyStore keyStore, IActivityManager activityManager, LockPatternUtils lockPatternUtils, IStorageManager storageManager, SyntheticPasswordManager spManager, FakeGsiService gsiService, RecoverableKeyStoreManager recoverableKeyStoreManager, - UserManagerInternal userManagerInternal, DeviceStateCache deviceStateCache) { + UserManagerInternal userManagerInternal, DeviceStateCache deviceStateCache, + FakeSettings settings) { super(context); mLockSettingsStorage = storage; mKeyStore = keyStore; @@ -67,6 +72,7 @@ public class LockSettingsServiceTestable extends LockSettingsService { mRecoverableKeyStoreManager = recoverableKeyStoreManager; mUserManagerInternal = userManagerInternal; mDeviceStateCache = deviceStateCache; + mSettings = settings; } @Override @@ -119,6 +125,12 @@ public class LockSettingsServiceTestable extends LockSettingsService { } @Override + public int settingsGlobalGetInt(ContentResolver contentResolver, String keyName, + int defaultValue) { + return mSettings.globalGetInt(keyName); + } + + @Override public UserManagerInternal getUserManagerInternal() { return mUserManagerInternal; } @@ -144,27 +156,33 @@ public class LockSettingsServiceTestable extends LockSettingsService { } } + public MockInjector mInjector; + protected LockSettingsServiceTestable(Context context, LockPatternUtils lockPatternUtils, LockSettingsStorage storage, FakeGateKeeperService gatekeeper, KeyStore keystore, IStorageManager storageManager, IActivityManager mActivityManager, SyntheticPasswordManager spManager, IAuthSecret authSecretService, FakeGsiService gsiService, RecoverableKeyStoreManager recoverableKeyStoreManager, - UserManagerInternal userManagerInternal, DeviceStateCache deviceStateCache) { + UserManagerInternal userManagerInternal, DeviceStateCache deviceStateCache, + FakeSettings settings) { super(new MockInjector(context, storage, keystore, mActivityManager, lockPatternUtils, storageManager, spManager, gsiService, - recoverableKeyStoreManager, userManagerInternal, deviceStateCache)); + recoverableKeyStoreManager, userManagerInternal, deviceStateCache, settings)); mGateKeeperService = gatekeeper; mAuthSecretService = authSecretService; } @Override - protected void tieProfileLockToParent(int userId, byte[] password) { - mStorage.writeChildProfileLock(userId, password); + protected void tieProfileLockToParent(int userId, LockscreenCredential password) { + Parcel parcel = Parcel.obtain(); + parcel.writeParcelable(password, 0); + mStorage.writeChildProfileLock(userId, parcel.marshall()); + parcel.recycle(); } @Override - protected byte[] getDecryptedPasswordForTiedProfile(int userId) throws FileNotFoundException, - KeyPermanentlyInvalidatedException { + protected LockscreenCredential getDecryptedPasswordForTiedProfile(int userId) + throws FileNotFoundException, KeyPermanentlyInvalidatedException { byte[] storedData = mStorage.readChildProfileLock(userId); if (storedData == null) { throw new FileNotFoundException("Child profile lock file not found"); @@ -176,6 +194,13 @@ public class LockSettingsServiceTestable extends LockSettingsService { } catch (RemoteException e) { // shouldn't happen. } - return storedData; + Parcel parcel = Parcel.obtain(); + try { + parcel.unmarshall(storedData, 0, storedData.length); + parcel.setDataPosition(0); + return (LockscreenCredential) parcel.readParcelable(null); + } finally { + parcel.recycle(); + } } } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java index 5818133aa2a4..86ef31a392b5 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java @@ -16,14 +16,10 @@ package com.android.server.locksettings; -import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC; -import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX; -import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; -import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; - import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN; +import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; @@ -39,6 +35,7 @@ import android.service.gatekeeper.GateKeeperResponse; import androidx.test.filters.SmallTest; import com.android.internal.widget.LockPatternUtils; +import com.android.internal.widget.LockscreenCredential; import com.android.internal.widget.VerifyCredentialResponse; import com.android.server.locksettings.FakeGateKeeperService.VerifyHandle; import com.android.server.locksettings.LockSettingsStorage.CredentialHash; @@ -61,60 +58,51 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { } public void testCreatePasswordPrimaryUser() throws RemoteException { - testCreateCredential(PRIMARY_USER_ID, "password", CREDENTIAL_TYPE_PASSWORD, - PASSWORD_QUALITY_ALPHABETIC); + testCreateCredential(PRIMARY_USER_ID, newPassword("password")); } public void testCreatePasswordFailsWithoutLockScreen() throws RemoteException { - testCreateCredentialFailsWithoutLockScreen(PRIMARY_USER_ID, "password", - CREDENTIAL_TYPE_PASSWORD, PASSWORD_QUALITY_ALPHABETIC); + testCreateCredentialFailsWithoutLockScreen(PRIMARY_USER_ID, newPassword("password")); } public void testCreatePatternPrimaryUser() throws RemoteException { - testCreateCredential(PRIMARY_USER_ID, "123456789", CREDENTIAL_TYPE_PATTERN, - PASSWORD_QUALITY_SOMETHING); + testCreateCredential(PRIMARY_USER_ID, newPattern("123456789")); } public void testCreatePatternFailsWithoutLockScreen() throws RemoteException { - testCreateCredentialFailsWithoutLockScreen(PRIMARY_USER_ID, "123456789", - CREDENTIAL_TYPE_PATTERN, PASSWORD_QUALITY_SOMETHING); + testCreateCredentialFailsWithoutLockScreen(PRIMARY_USER_ID, newPattern("123456789")); } public void testChangePasswordPrimaryUser() throws RemoteException { - testChangeCredentials(PRIMARY_USER_ID, "78963214", CREDENTIAL_TYPE_PATTERN, - "asdfghjk", CREDENTIAL_TYPE_PASSWORD, PASSWORD_QUALITY_ALPHABETIC); + testChangeCredentials(PRIMARY_USER_ID, newPattern("78963214"), newPassword("asdfghjk")); } public void testChangePatternPrimaryUser() throws RemoteException { - testChangeCredentials(PRIMARY_USER_ID, "!£$%^&*(())", CREDENTIAL_TYPE_PASSWORD, - "1596321", CREDENTIAL_TYPE_PATTERN, PASSWORD_QUALITY_SOMETHING); + testChangeCredentials(PRIMARY_USER_ID, newPassword("!£$%^&*(())"), newPattern("1596321")); } public void testChangePasswordFailPrimaryUser() throws RemoteException { final long sid = 1234; - initializeStorageWithCredential(PRIMARY_USER_ID, "password", CREDENTIAL_TYPE_PASSWORD, sid); + initializeStorageWithCredential(PRIMARY_USER_ID, newPassword("password"), sid); - assertFalse(mService.setLockCredential("newpwd".getBytes(), CREDENTIAL_TYPE_PASSWORD, - "badpwd".getBytes(), PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false)); - assertVerifyCredentials(PRIMARY_USER_ID, "password", CREDENTIAL_TYPE_PASSWORD, sid); + assertFalse(mService.setLockCredential(newPassword("newpwd"), newPassword("badpwd"), + PRIMARY_USER_ID, false)); + assertVerifyCredentials(PRIMARY_USER_ID, newPassword("password"), sid); } public void testClearPasswordPrimaryUser() throws RemoteException { - final String PASSWORD = "password"; - initializeStorageWithCredential(PRIMARY_USER_ID, PASSWORD, CREDENTIAL_TYPE_PASSWORD, 1234); - assertTrue(mService.setLockCredential(null, CREDENTIAL_TYPE_NONE, PASSWORD.getBytes(), - PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID, false)); - assertFalse(mService.havePassword(PRIMARY_USER_ID)); - assertFalse(mService.havePattern(PRIMARY_USER_ID)); + initializeStorageWithCredential(PRIMARY_USER_ID, newPassword("password"), 1234); + assertTrue(mService.setLockCredential(nonePassword(), newPassword("password"), + PRIMARY_USER_ID, false)); + assertEquals(CREDENTIAL_TYPE_NONE, mService.getCredentialType(PRIMARY_USER_ID)); assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); } public void testManagedProfileUnifiedChallenge() throws RemoteException { - final String firstUnifiedPassword = "testManagedProfileUnifiedChallenge-pwd-1"; - final String secondUnifiedPassword = "testManagedProfileUnifiedChallenge-pwd-2"; - assertTrue(mService.setLockCredential(firstUnifiedPassword.getBytes(), - LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, - null, PASSWORD_QUALITY_COMPLEX, PRIMARY_USER_ID, false)); + final LockscreenCredential firstUnifiedPassword = newPassword("pwd-1"); + final LockscreenCredential secondUnifiedPassword = newPassword("pwd-2"); + assertTrue(mService.setLockCredential(firstUnifiedPassword, + nonePassword(), PRIMARY_USER_ID, false)); mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null); final long primarySid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID); final long profileSid = mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID); @@ -132,8 +120,8 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { mGateKeeperService.clearAuthToken(TURNED_OFF_PROFILE_USER_ID); // verify credential assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - firstUnifiedPassword.getBytes(), LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, - PRIMARY_USER_ID).getResponseCode()); + firstUnifiedPassword, 0, PRIMARY_USER_ID) + .getResponseCode()); // Verify that we have a new auth token for the profile assertNotNull(mGateKeeperService.getAuthToken(MANAGED_PROFILE_USER_ID)); @@ -148,16 +136,15 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { */ mStorageManager.setIgnoreBadUnlock(true); // Change primary password and verify that profile SID remains - assertTrue(mService.setLockCredential(secondUnifiedPassword.getBytes(), - LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, firstUnifiedPassword.getBytes(), - PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false)); + assertTrue(mService.setLockCredential( + secondUnifiedPassword, firstUnifiedPassword, PRIMARY_USER_ID, false)); mStorageManager.setIgnoreBadUnlock(false); assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID)); assertNull(mGateKeeperService.getAuthToken(TURNED_OFF_PROFILE_USER_ID)); // Clear unified challenge - assertTrue(mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, - secondUnifiedPassword.getBytes(), PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID, + assertTrue(mService.setLockCredential(nonePassword(), + secondUnifiedPassword, PRIMARY_USER_ID, false)); assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); assertEquals(0, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID)); @@ -165,19 +152,19 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { } public void testManagedProfileSeparateChallenge() throws RemoteException { - final String primaryPassword = "testManagedProfileSeparateChallenge-primary"; - final String profilePassword = "testManagedProfileSeparateChallenge-profile"; - assertTrue(mService.setLockCredential(primaryPassword.getBytes(), - LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, - PASSWORD_QUALITY_COMPLEX, PRIMARY_USER_ID, false)); + final LockscreenCredential primaryPassword = newPassword("primary"); + final LockscreenCredential profilePassword = newPassword("profile"); + assertTrue(mService.setLockCredential(primaryPassword, + nonePassword(), + PRIMARY_USER_ID, false)); /* Currently in LockSettingsService.setLockCredential, unlockUser() is called with the new * credential as part of verifyCredential() before the new credential is committed in * StorageManager. So we relax the check in our mock StorageManager to allow that. */ mStorageManager.setIgnoreBadUnlock(true); - assertTrue(mService.setLockCredential(profilePassword.getBytes(), - LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, - PASSWORD_QUALITY_COMPLEX, MANAGED_PROFILE_USER_ID, false)); + assertTrue(mService.setLockCredential(profilePassword, + nonePassword(), + MANAGED_PROFILE_USER_ID, false)); mStorageManager.setIgnoreBadUnlock(false); final long primarySid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID); @@ -190,81 +177,69 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { mGateKeeperService.clearAuthToken(MANAGED_PROFILE_USER_ID); // verify primary credential assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - primaryPassword.getBytes(), LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, - PRIMARY_USER_ID).getResponseCode()); + primaryPassword, 0, PRIMARY_USER_ID) + .getResponseCode()); assertNull(mGateKeeperService.getAuthToken(MANAGED_PROFILE_USER_ID)); // verify profile credential assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - profilePassword.getBytes(), LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, - MANAGED_PROFILE_USER_ID).getResponseCode()); + profilePassword, 0, MANAGED_PROFILE_USER_ID) + .getResponseCode()); assertNotNull(mGateKeeperService.getAuthToken(MANAGED_PROFILE_USER_ID)); assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID)); // Change primary credential and make sure we don't affect profile mStorageManager.setIgnoreBadUnlock(true); - assertTrue(mService.setLockCredential("pwd".getBytes(), - LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, - primaryPassword.getBytes(), PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false)); + assertTrue(mService.setLockCredential( + newPassword("pwd"), primaryPassword, PRIMARY_USER_ID, false)); mStorageManager.setIgnoreBadUnlock(false); assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - profilePassword.getBytes(), LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, - MANAGED_PROFILE_USER_ID).getResponseCode()); + profilePassword, 0, MANAGED_PROFILE_USER_ID) + .getResponseCode()); assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID)); } public void testSetLockCredential_forPrimaryUser_sendsCredentials() throws Exception { - final byte[] password = "password".getBytes(); - assertTrue(mService.setLockCredential( - password, - CREDENTIAL_TYPE_PASSWORD, - null, - PASSWORD_QUALITY_ALPHABETIC, + newPassword("password"), + nonePassword(), PRIMARY_USER_ID, false)); verify(mRecoverableKeyStoreManager) - .lockScreenSecretChanged(CREDENTIAL_TYPE_PASSWORD, password, PRIMARY_USER_ID); + .lockScreenSecretChanged(CREDENTIAL_TYPE_PASSWORD, "password".getBytes(), + PRIMARY_USER_ID); } public void testSetLockCredential_forProfileWithSeparateChallenge_sendsCredentials() throws Exception { - final byte[] pattern = "12345".getBytes(); - assertTrue(mService.setLockCredential( - pattern, - CREDENTIAL_TYPE_PATTERN, - null, - PASSWORD_QUALITY_SOMETHING, + newPattern("12345"), + nonePassword(), MANAGED_PROFILE_USER_ID, false)); verify(mRecoverableKeyStoreManager) - .lockScreenSecretChanged(CREDENTIAL_TYPE_PATTERN, pattern, MANAGED_PROFILE_USER_ID); + .lockScreenSecretChanged(CREDENTIAL_TYPE_PATTERN, "12345".getBytes(), + MANAGED_PROFILE_USER_ID); } public void testSetLockCredential_forProfileWithSeparateChallenge_updatesCredentials() throws Exception { - final String oldCredential = "12345"; - final byte[] newCredential = "newPassword".getBytes(); initializeStorageWithCredential( MANAGED_PROFILE_USER_ID, - oldCredential, - CREDENTIAL_TYPE_PATTERN, - PASSWORD_QUALITY_SOMETHING); + newPattern("12345"), + 1234); assertTrue(mService.setLockCredential( - newCredential, - CREDENTIAL_TYPE_PASSWORD, - oldCredential.getBytes(), - PASSWORD_QUALITY_ALPHABETIC, + newPassword("newPassword"), + newPattern("12345"), MANAGED_PROFILE_USER_ID, false)); verify(mRecoverableKeyStoreManager) - .lockScreenSecretChanged( - CREDENTIAL_TYPE_PASSWORD, newCredential, MANAGED_PROFILE_USER_ID); + .lockScreenSecretChanged(CREDENTIAL_TYPE_PASSWORD, "newPassword".getBytes(), + MANAGED_PROFILE_USER_ID); } public void testSetLockCredential_forProfileWithUnifiedChallenge_doesNotSendRandomCredential() @@ -272,10 +247,8 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null); assertTrue(mService.setLockCredential( - "12345".getBytes(), - CREDENTIAL_TYPE_PATTERN, - null, - PASSWORD_QUALITY_SOMETHING, + newPattern("12345"), + nonePassword(), PRIMARY_USER_ID, false)); @@ -287,40 +260,35 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { public void testSetLockCredential_forPrimaryUserWithUnifiedChallengeProfile_updatesBothCredentials() throws Exception { - final String oldCredential = "oldPassword"; - final byte[] newCredential = "newPassword".getBytes(); + final LockscreenCredential oldCredential = newPassword("oldPassword"); + final LockscreenCredential newCredential = newPassword("newPassword"); initializeStorageWithCredential( - PRIMARY_USER_ID, oldCredential, CREDENTIAL_TYPE_PASSWORD, 1234); + PRIMARY_USER_ID, oldCredential, 1234); mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null); assertTrue(mService.setLockCredential( newCredential, - CREDENTIAL_TYPE_PASSWORD, - oldCredential.getBytes(), - PASSWORD_QUALITY_ALPHABETIC, + oldCredential, PRIMARY_USER_ID, false)); verify(mRecoverableKeyStoreManager) - .lockScreenSecretChanged(CREDENTIAL_TYPE_PASSWORD, newCredential, PRIMARY_USER_ID); + .lockScreenSecretChanged(CREDENTIAL_TYPE_PASSWORD, newCredential.getCredential(), + PRIMARY_USER_ID); verify(mRecoverableKeyStoreManager) - .lockScreenSecretChanged( - CREDENTIAL_TYPE_PASSWORD, newCredential, MANAGED_PROFILE_USER_ID); + .lockScreenSecretChanged(CREDENTIAL_TYPE_PASSWORD, newCredential.getCredential(), + MANAGED_PROFILE_USER_ID); } public void testSetLockCredential_forPrimaryUserWithUnifiedChallengeProfile_removesBothCredentials() throws Exception { - final String oldCredential = "oldPassword"; - initializeStorageWithCredential( - PRIMARY_USER_ID, oldCredential, CREDENTIAL_TYPE_PASSWORD, 1234); + initializeStorageWithCredential(PRIMARY_USER_ID, newPassword("oldPassword"), 1234); mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null); assertTrue(mService.setLockCredential( - null, - CREDENTIAL_TYPE_NONE, - oldCredential.getBytes(), - PASSWORD_QUALITY_UNSPECIFIED, + nonePassword(), + newPassword("oldPassword"), PRIMARY_USER_ID, false)); @@ -331,17 +299,13 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { } public void testSetLockCredential_nullCredential_removeBiometrics() throws RemoteException { - final String oldCredential = "oldPassword"; - initializeStorageWithCredential( PRIMARY_USER_ID, - oldCredential, - CREDENTIAL_TYPE_PATTERN, - PASSWORD_QUALITY_SOMETHING); + newPattern("123654"), + 1234); mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null); - mService.setLockCredential(null, CREDENTIAL_TYPE_NONE, oldCredential.getBytes(), - PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID, false); + mService.setLockCredential(nonePassword(), newPattern("123654"), PRIMARY_USER_ID, false); // Verify fingerprint is removed verify(mFingerprintManager).remove(any(), eq(PRIMARY_USER_ID), any()); @@ -353,41 +317,33 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { public void testSetLockCredential_forUnifiedToSeparateChallengeProfile_sendsNewCredentials() throws Exception { - final String parentPassword = "parentPassword"; - final byte[] profilePassword = "profilePassword".getBytes(); - initializeStorageWithCredential( - PRIMARY_USER_ID, parentPassword, CREDENTIAL_TYPE_PASSWORD, 1234); + final LockscreenCredential parentPassword = newPassword("parentPassword"); + final LockscreenCredential profilePassword = newPassword("profilePassword"); + initializeStorageWithCredential(PRIMARY_USER_ID, parentPassword, 1234); mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null); assertTrue(mService.setLockCredential( profilePassword, - CREDENTIAL_TYPE_PASSWORD, - null, - PASSWORD_QUALITY_ALPHABETIC, + nonePassword(), MANAGED_PROFILE_USER_ID, false)); verify(mRecoverableKeyStoreManager) - .lockScreenSecretChanged( - CREDENTIAL_TYPE_PASSWORD, profilePassword, MANAGED_PROFILE_USER_ID); + .lockScreenSecretChanged(CREDENTIAL_TYPE_PASSWORD, profilePassword.getCredential(), + MANAGED_PROFILE_USER_ID); } public void testSetLockCredential_forSeparateToUnifiedChallengeProfile_doesNotSendRandomCredential() throws Exception { - final String parentPassword = "parentPassword"; - final String profilePassword = "12345"; - initializeStorageWithCredential( - PRIMARY_USER_ID, parentPassword, CREDENTIAL_TYPE_PASSWORD, 1234); + final LockscreenCredential parentPassword = newPassword("parentPassword"); + final LockscreenCredential profilePassword = newPattern("12345"); + initializeStorageWithCredential(PRIMARY_USER_ID, parentPassword, 1234); // Create and verify separate profile credentials. - testCreateCredential( - MANAGED_PROFILE_USER_ID, - profilePassword, - CREDENTIAL_TYPE_PATTERN, - PASSWORD_QUALITY_SOMETHING); + testCreateCredential(MANAGED_PROFILE_USER_ID, profilePassword); mService.setSeparateProfileChallengeEnabled( - MANAGED_PROFILE_USER_ID, false, profilePassword.getBytes()); + MANAGED_PROFILE_USER_ID, false, profilePassword); // Called once for setting the initial separate profile credentials and not again during // unification. @@ -396,132 +352,121 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { } public void testVerifyCredential_forPrimaryUser_sendsCredentials() throws Exception { - final String password = "password"; - initializeStorageWithCredential(PRIMARY_USER_ID, password, CREDENTIAL_TYPE_PASSWORD, 1234); + final LockscreenCredential password = newPassword("password"); + initializeStorageWithCredential(PRIMARY_USER_ID, password, 1234); reset(mRecoverableKeyStoreManager); - mService.verifyCredential( - password.getBytes(), CREDENTIAL_TYPE_PASSWORD, 1, PRIMARY_USER_ID); + mService.verifyCredential(password, 1, PRIMARY_USER_ID); verify(mRecoverableKeyStoreManager) .lockScreenSecretAvailable( - CREDENTIAL_TYPE_PASSWORD, password.getBytes(), PRIMARY_USER_ID); + CREDENTIAL_TYPE_PASSWORD, password.getCredential(), PRIMARY_USER_ID); } public void testVerifyCredential_forProfileWithSeparateChallenge_sendsCredentials() throws Exception { - final byte[] pattern = "12345".getBytes(); + final LockscreenCredential pattern = newPattern("12345"); assertTrue(mService.setLockCredential( pattern, - CREDENTIAL_TYPE_PATTERN, - null, - PASSWORD_QUALITY_SOMETHING, + nonePassword(), MANAGED_PROFILE_USER_ID, false)); reset(mRecoverableKeyStoreManager); - mService.verifyCredential(pattern, CREDENTIAL_TYPE_PATTERN, 1, MANAGED_PROFILE_USER_ID); + mService.verifyCredential(pattern, 1, MANAGED_PROFILE_USER_ID); verify(mRecoverableKeyStoreManager) .lockScreenSecretAvailable( - CREDENTIAL_TYPE_PATTERN, pattern, MANAGED_PROFILE_USER_ID); + CREDENTIAL_TYPE_PATTERN, pattern.getCredential(), MANAGED_PROFILE_USER_ID); } public void testVerifyCredential_forPrimaryUserWithUnifiedChallengeProfile_sendsCredentialsForBoth() throws Exception { - final String pattern = "12345"; - initializeStorageWithCredential(PRIMARY_USER_ID, pattern, CREDENTIAL_TYPE_PATTERN, 1234); + final LockscreenCredential pattern = newPattern("12345"); + initializeStorageWithCredential(PRIMARY_USER_ID, pattern, 1234); mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null); reset(mRecoverableKeyStoreManager); - mService.verifyCredential(pattern.getBytes(), CREDENTIAL_TYPE_PATTERN, 1, PRIMARY_USER_ID); + mService.verifyCredential(pattern, 1, PRIMARY_USER_ID); // Parent sends its credentials for both the parent and profile. verify(mRecoverableKeyStoreManager) .lockScreenSecretAvailable( - CREDENTIAL_TYPE_PATTERN, pattern.getBytes(), PRIMARY_USER_ID); + CREDENTIAL_TYPE_PATTERN, pattern.getCredential(), PRIMARY_USER_ID); verify(mRecoverableKeyStoreManager) .lockScreenSecretAvailable( - CREDENTIAL_TYPE_PATTERN, pattern.getBytes(), MANAGED_PROFILE_USER_ID); + CREDENTIAL_TYPE_PATTERN, pattern.getCredential(), MANAGED_PROFILE_USER_ID); // Profile doesn't send its own random credentials. verify(mRecoverableKeyStoreManager, never()) .lockScreenSecretAvailable( eq(CREDENTIAL_TYPE_PASSWORD), any(), eq(MANAGED_PROFILE_USER_ID)); } - private void testCreateCredential(int userId, String credential, int type, int quality) + private void testCreateCredential(int userId, LockscreenCredential credential) throws RemoteException { - assertTrue(mService.setLockCredential(credential.getBytes(), type, null, quality, - userId, false)); - assertVerifyCredentials(userId, credential, type, -1); + assertTrue(mService.setLockCredential(credential, nonePassword(), userId, false)); + assertVerifyCredentials(userId, credential, -1); } private void testCreateCredentialFailsWithoutLockScreen( - int userId, String credential, int type, int quality) throws RemoteException { + int userId, LockscreenCredential credential) throws RemoteException { mHasSecureLockScreen = false; try { - mService.setLockCredential(credential.getBytes(), type, null, quality, - userId, false); + mService.setLockCredential(credential, null, userId, false); fail("An exception should have been thrown."); } catch (UnsupportedOperationException e) { // Success - the exception was expected. } - assertFalse(mService.havePassword(userId)); - assertFalse(mService.havePattern(userId)); + assertEquals(CREDENTIAL_TYPE_NONE, mService.getCredentialType(userId)); } - private void testChangeCredentials(int userId, String newCredential, int newType, - String oldCredential, int oldType, int quality) throws RemoteException { + private void testChangeCredentials(int userId, LockscreenCredential newCredential, + LockscreenCredential oldCredential) throws RemoteException { final long sid = 1234; - initializeStorageWithCredential(userId, oldCredential, oldType, sid); - assertTrue(mService.setLockCredential(newCredential.getBytes(), newType, - oldCredential.getBytes(), quality, userId, false)); - assertVerifyCredentials(userId, newCredential, newType, sid); + initializeStorageWithCredential(userId, oldCredential, sid); + assertTrue(mService.setLockCredential(newCredential, oldCredential, userId, false)); + assertVerifyCredentials(userId, newCredential, sid); } - private void assertVerifyCredentials(int userId, String credential, int type, long sid) + private void assertVerifyCredentials(int userId, LockscreenCredential credential, long sid) throws RemoteException{ final long challenge = 54321; - VerifyCredentialResponse response = mService.verifyCredential(credential.getBytes(), - type, challenge, userId); + VerifyCredentialResponse response = mService.verifyCredential(credential, + challenge, userId); assertEquals(GateKeeperResponse.RESPONSE_OK, response.getResponseCode()); if (sid != -1) assertEquals(sid, mGateKeeperService.getSecureUserId(userId)); - final int incorrectType; - if (type == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD) { - assertTrue(mService.havePassword(userId)); - assertFalse(mService.havePattern(userId)); - incorrectType = LockPatternUtils.CREDENTIAL_TYPE_PATTERN; - } else if (type == LockPatternUtils.CREDENTIAL_TYPE_PATTERN){ - assertFalse(mService.havePassword(userId)); - assertTrue(mService.havePattern(userId)); - incorrectType = LockPatternUtils.CREDENTIAL_TYPE_PASSWORD; + if (credential.isPassword()) { + assertEquals(CREDENTIAL_TYPE_PASSWORD, mService.getCredentialType(userId)); + } else if (credential.isPin()) { + assertEquals(CREDENTIAL_TYPE_PIN, mService.getCredentialType(userId)); + } else if (credential.isPattern()) { + assertEquals(CREDENTIAL_TYPE_PATTERN, mService.getCredentialType(userId)); } else { - assertFalse(mService.havePassword(userId)); - assertFalse(mService.havePassword(userId)); - incorrectType = LockPatternUtils.CREDENTIAL_TYPE_PASSWORD; + assertEquals(CREDENTIAL_TYPE_NONE, mService.getCredentialType(userId)); } - // check for bad type - assertEquals(GateKeeperResponse.RESPONSE_ERROR, mService.verifyCredential( - credential.getBytes(), incorrectType, challenge, userId).getResponseCode()); // check for bad credential + final LockscreenCredential badCredential; + if (!credential.isNone()) { + badCredential = credential.duplicate(); + badCredential.getCredential()[0] ^= 1; + } else { + badCredential = LockscreenCredential.createPin("0"); + } assertEquals(GateKeeperResponse.RESPONSE_ERROR, mService.verifyCredential( - ("0" + credential).getBytes(), type, challenge, userId).getResponseCode()); + badCredential, challenge, userId).getResponseCode()); } - private void initializeStorageWithCredential(int userId, String credential, int type, long sid) - throws RemoteException { - byte[] credentialBytes = credential == null ? null : credential.getBytes(); - byte[] oldHash = new VerifyHandle(credential.getBytes(), sid).toBytes(); + private void initializeStorageWithCredential(int userId, LockscreenCredential credential, + long sid) throws RemoteException { + byte[] oldHash = new VerifyHandle(credential.getCredential(), sid).toBytes(); if (mService.shouldMigrateToSyntheticPasswordLocked(userId)) { - mService.initializeSyntheticPasswordLocked(oldHash, credentialBytes, type, - type == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD ? PASSWORD_QUALITY_ALPHABETIC - : PASSWORD_QUALITY_SOMETHING, userId); + mService.initializeSyntheticPasswordLocked(oldHash, credential, userId); } else { - if (type == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD) { + if (credential.isPassword() || credential.isPin()) { mStorage.writeCredentialHash(CredentialHash.create(oldHash, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD), userId); } else { diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java index b60111ea3333..16176c029f8c 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java @@ -88,6 +88,7 @@ public class LockSettingsShellCommandTest { @Test public void testWrongPassword() throws Exception { + when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true); when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false); when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true); when(mLockPatternUtils.checkCredential( @@ -101,6 +102,7 @@ public class LockSettingsShellCommandTest { @Test public void testChangePin() throws Exception { + when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true); when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false); when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true); when(mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId)).thenReturn( @@ -128,6 +130,7 @@ public class LockSettingsShellCommandTest { @Test public void testChangePassword() throws Exception { + when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true); when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false); when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true); when(mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId)).thenReturn( @@ -155,6 +158,7 @@ public class LockSettingsShellCommandTest { @Test public void testChangePattern() throws Exception { + when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true); when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true); when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false); when(mLockPatternUtils.checkCredential( @@ -181,6 +185,7 @@ public class LockSettingsShellCommandTest { @Test public void testClear() throws Exception { + when(mLockPatternUtils.isSecure(mUserId)).thenReturn(true); when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true); when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false); when(mLockPatternUtils.checkCredential( diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java index bc61c582303a..1581d9ac1811 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java @@ -16,20 +16,51 @@ package com.android.server.locksettings; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; + import android.content.Context; import com.android.server.PersistentDataBlockManagerInternal; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + import java.io.File; +import java.util.Arrays; public class LockSettingsStorageTestable extends LockSettingsStorage { public File mStorageDir; - public PersistentDataBlockManagerInternal mPersistentDataBlock; + public PersistentDataBlockManagerInternal mPersistentDataBlockManager; + private byte[] mPersistentData; public LockSettingsStorageTestable(Context context, File storageDir) { super(context); mStorageDir = storageDir; + mPersistentDataBlockManager = mock(PersistentDataBlockManagerInternal.class); + doAnswer(new Answer<Void>() { + @Override + public Void answer(InvocationOnMock invocation) throws Throwable { + byte[] handle = (byte[]) invocation.getArguments()[0]; + if (handle != null) { + mPersistentData = Arrays.copyOf(handle, handle.length); + } else { + mPersistentData = null; + } + return null; + } + }).when(mPersistentDataBlockManager).setFrpCredentialHandle(any()); + // For some reasons, simply mocking getFrpCredentialHandle() with + // when(mPersistentDataBlockManager.getFrpCredentialHandle()).thenReturn(mPersistentData) + // does not work, I had to use the long-winded way below. + doAnswer(new Answer<byte[]>() { + @Override + public byte[] answer(InvocationOnMock invocation) throws Throwable { + return mPersistentData; + } + }).when(mPersistentDataBlockManager).getFrpCredentialHandle(); } @Override @@ -57,8 +88,8 @@ public class LockSettingsStorageTestable extends LockSettingsStorage { } @Override - public PersistentDataBlockManagerInternal getPersistentDataBlock() { - return mPersistentDataBlock; + PersistentDataBlockManagerInternal getPersistentDataBlockManager() { + return mPersistentDataBlockManager; } private File makeDirs(File baseDir, String filePath) { diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java index cb5189712685..7a18431cb8e1 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java @@ -240,12 +240,12 @@ public class LockSettingsStorageTests extends AndroidTestCase { writePasswordBytes(PASSWORD_0, 10); writePatternBytes(PATTERN_0, 20); - assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, + assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD_OR_PIN, mStorage.readCredentialHash(10).type); assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PATTERN, mStorage.readCredentialHash(20).type); mStorage.clearCache(); - assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, + assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD_OR_PIN, mStorage.readCredentialHash(10).type); assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PATTERN, mStorage.readCredentialHash(20).type); @@ -352,20 +352,20 @@ public class LockSettingsStorageTests extends AndroidTestCase { } public void testPersistentDataBlock_unavailable() { - mStorage.mPersistentDataBlock = null; + mStorage.mPersistentDataBlockManager = null; assertSame(PersistentData.NONE, mStorage.readPersistentDataBlock()); } public void testPersistentDataBlock_empty() { - mStorage.mPersistentDataBlock = mock(PersistentDataBlockManagerInternal.class); + mStorage.mPersistentDataBlockManager = mock(PersistentDataBlockManagerInternal.class); assertSame(PersistentData.NONE, mStorage.readPersistentDataBlock()); } public void testPersistentDataBlock_withData() { - mStorage.mPersistentDataBlock = mock(PersistentDataBlockManagerInternal.class); - when(mStorage.mPersistentDataBlock.getFrpCredentialHandle()) + mStorage.mPersistentDataBlockManager = mock(PersistentDataBlockManagerInternal.class); + when(mStorage.mPersistentDataBlockManager.getFrpCredentialHandle()) .thenReturn(PersistentData.toBytes(PersistentData.TYPE_SP_WEAVER, SOME_USER_ID, DevicePolicyManager.PASSWORD_QUALITY_COMPLEX, PAYLOAD)); @@ -378,8 +378,8 @@ public class LockSettingsStorageTests extends AndroidTestCase { } public void testPersistentDataBlock_exception() { - mStorage.mPersistentDataBlock = mock(PersistentDataBlockManagerInternal.class); - when(mStorage.mPersistentDataBlock.getFrpCredentialHandle()) + mStorage.mPersistentDataBlockManager = mock(PersistentDataBlockManagerInternal.class); + when(mStorage.mPersistentDataBlockManager.getFrpCredentialHandle()) .thenThrow(new IllegalStateException("oops")); assertSame(PersistentData.NONE, mStorage.readPersistentDataBlock()); } @@ -453,7 +453,7 @@ public class LockSettingsStorageTests extends AndroidTestCase { private void assertPasswordBytes(byte[] password, int userId) { CredentialHash cred = mStorage.readCredentialHash(userId); - assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, cred.type); + assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD_OR_PIN, cred.type); assertArrayEquals(password, cred.hash); } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockscreenFrpTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockscreenFrpTest.java new file mode 100644 index 000000000000..df719b6d3d21 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockscreenFrpTest.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.locksettings; + +import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE; +import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD; +import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD_OR_PIN; +import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN; +import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN; +import static com.android.internal.widget.LockPatternUtils.USER_FRP; + +import android.app.admin.DevicePolicyManager; + +import com.android.internal.widget.VerifyCredentialResponse; +import com.android.server.locksettings.LockSettingsStorage.PersistentData; + + +/** Test setting a lockscreen credential and then verify it under USER_FRP */ +public class LockscreenFrpTest extends BaseLockSettingsServiceTests { + + @Override + public void setUp() throws Exception { + super.setUp(); + // FRP credential can only be verified prior to provisioning + mSettings.setDeviceProvisioned(false); + } + + public void testFrpCredential_setPin() { + mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID, false); + + assertEquals(CREDENTIAL_TYPE_PIN, mService.getCredentialType(USER_FRP)); + assertEquals(VerifyCredentialResponse.RESPONSE_OK, + mService.verifyCredential(newPin("1234"), 0, USER_FRP).getResponseCode()); + } + + public void testFrpCredential_setPattern() { + mService.setLockCredential(newPattern("4321"), nonePassword(), PRIMARY_USER_ID, false); + + assertEquals(CREDENTIAL_TYPE_PATTERN, mService.getCredentialType(USER_FRP)); + assertEquals(VerifyCredentialResponse.RESPONSE_OK, + mService.verifyCredential(newPattern("4321"), 0, USER_FRP).getResponseCode()); + } + + public void testFrpCredential_setPassword() { + mService.setLockCredential(newPassword("4321"), nonePassword(), PRIMARY_USER_ID, false); + + assertEquals(CREDENTIAL_TYPE_PASSWORD, mService.getCredentialType(USER_FRP)); + assertEquals(VerifyCredentialResponse.RESPONSE_OK, + mService.verifyCredential(newPassword("4321"), 0, USER_FRP).getResponseCode()); + } + + public void testFrpCredential_changeCredential() { + mService.setLockCredential(newPassword("1234"), nonePassword(), PRIMARY_USER_ID, false); + mService.setLockCredential(newPattern("5678"), newPassword("1234"), PRIMARY_USER_ID, false); + + assertEquals(CREDENTIAL_TYPE_PATTERN, mService.getCredentialType(USER_FRP)); + assertEquals(VerifyCredentialResponse.RESPONSE_OK, + mService.verifyCredential(newPattern("5678"), 0, USER_FRP).getResponseCode()); + } + + public void testFrpCredential_removeCredential() { + mService.setLockCredential(newPassword("1234"), nonePassword(), PRIMARY_USER_ID, false); + assertEquals(CREDENTIAL_TYPE_PASSWORD, mService.getCredentialType(USER_FRP)); + + mService.setLockCredential(nonePassword(), newPassword("1234"), PRIMARY_USER_ID, false); + assertEquals(CREDENTIAL_TYPE_NONE, mService.getCredentialType(USER_FRP)); + } + + public void testFrpCredential_cannotVerifyAfterProvsioning() { + mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID, false); + + mSettings.setDeviceProvisioned(true); + assertEquals(VerifyCredentialResponse.RESPONSE_ERROR, + mService.verifyCredential(newPin("1234"), 0, USER_FRP).getResponseCode()); + } + + public void testFrpCredential_legacyPinTypePersistentData() { + mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID, false); + PersistentData data = mStorage.readPersistentDataBlock(); + // Tweak the existing persistent data to make it look like one with legacy credential type + assertEquals(CREDENTIAL_TYPE_PIN, data.payload[3]); + data.payload[3] = CREDENTIAL_TYPE_PASSWORD_OR_PIN; + mStorage.writePersistentDataBlock(data.type, data.userId, + DevicePolicyManager.PASSWORD_QUALITY_NUMERIC, data.payload); + + assertEquals(CREDENTIAL_TYPE_PIN, mService.getCredentialType(USER_FRP)); + assertEquals(VerifyCredentialResponse.RESPONSE_OK, + mService.verifyCredential(newPin("1234"), 0, USER_FRP).getResponseCode()); + + } + + public void testFrpCredential_legacyPasswordTypePersistentData() { + mService.setLockCredential(newPassword("1234"), nonePassword(), PRIMARY_USER_ID, false); + PersistentData data = mStorage.readPersistentDataBlock(); + // Tweak the existing persistent data to make it look like one with legacy credential type + assertEquals(CREDENTIAL_TYPE_PASSWORD, data.payload[3]); + data.payload[3] = CREDENTIAL_TYPE_PASSWORD_OR_PIN; + mStorage.writePersistentDataBlock(data.type, data.userId, + DevicePolicyManager.PASSWORD_QUALITY_COMPLEX, data.payload); + + assertEquals(CREDENTIAL_TYPE_PASSWORD, mService.getCredentialType(USER_FRP)); + assertEquals(VerifyCredentialResponse.RESPONSE_OK, + mService.verifyCredential(newPin("1234"), 0, USER_FRP).getResponseCode()); + } +} diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java index 42ca42aecf70..89a279c566e0 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java @@ -16,11 +16,9 @@ package com.android.server.locksettings; -import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC; -import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; -import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; - +import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD; +import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD_OR_PIN; import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_ENABLED_KEY; import static com.android.internal.widget.LockPatternUtils.SYNTHETIC_PASSWORD_HANDLE_KEY; @@ -38,7 +36,6 @@ import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; -import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockscreenCredential; import com.android.internal.widget.VerifyCredentialResponse; import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult; @@ -72,15 +69,14 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { public void testPasswordBasedSyntheticPassword() throws RemoteException { final int USER_ID = 10; - final byte[] password = "user-password".getBytes(); - final byte[] badPassword = "bad-password".getBytes(); + final LockscreenCredential password = newPassword("user-password"); + final LockscreenCredential badPassword = newPassword("bad-password"); MockSyntheticPasswordManager manager = new MockSyntheticPasswordManager(mContext, mStorage, mGateKeeperService, mUserManager, mPasswordSlotManager); AuthenticationToken authToken = manager.newSyntheticPasswordAndSid(mGateKeeperService, null, null, USER_ID); long handle = manager.createPasswordBasedSyntheticPassword(mGateKeeperService, - password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, authToken, - PASSWORD_QUALITY_ALPHABETIC, USER_ID); + password, authToken, USER_ID); AuthenticationResult result = manager.unwrapPasswordBasedSyntheticPassword( mGateKeeperService, handle, password, USER_ID, null); @@ -105,97 +101,90 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { } public void testPasswordMigration() throws RemoteException { - final byte[] password = "testPasswordMigration-password".getBytes(); + final LockscreenCredential password = newPassword("testPasswordMigration-password"); disableSyntheticPassword(); - mService.setLockCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, - PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false); + assertTrue(mService.setLockCredential(password, nonePassword(), PRIMARY_USER_ID, false)); long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID); final byte[] primaryStorageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID); enableSyntheticPassword(); // Performs migration assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) + password, 0, PRIMARY_USER_ID) .getResponseCode()); assertEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); assertTrue(hasSyntheticPassword(PRIMARY_USER_ID)); // SP-based verification - assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(password, - LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) + assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( + password, 0, PRIMARY_USER_ID) .getResponseCode()); assertArrayNotEquals(primaryStorageKey, mStorageManager.getUserUnlockToken(PRIMARY_USER_ID)); } - protected void initializeCredentialUnderSP(byte[] password, int userId) throws RemoteException { + protected void initializeCredentialUnderSP(LockscreenCredential password, int userId) + throws RemoteException { enableSyntheticPassword(); - int quality = password != null ? PASSWORD_QUALITY_ALPHABETIC - : PASSWORD_QUALITY_UNSPECIFIED; - int type = password != null ? LockPatternUtils.CREDENTIAL_TYPE_PASSWORD - : LockPatternUtils.CREDENTIAL_TYPE_NONE; - mService.setLockCredential(password, type, null, quality, userId, false); + mService.setLockCredential(password, nonePassword(), userId, false); + assertEquals(CREDENTIAL_TYPE_PASSWORD, mService.getCredentialType(userId)); + assertTrue(mService.isSyntheticPasswordBasedCredential(userId)); } public void testSyntheticPasswordChangeCredential() throws RemoteException { - final byte[] password = "testSyntheticPasswordChangeCredential-password".getBytes(); - final byte[] newPassword = "testSyntheticPasswordChangeCredential-newpassword".getBytes(); + final LockscreenCredential password = newPassword("password"); + final LockscreenCredential newPassword = newPassword("newpassword"); initializeCredentialUnderSP(password, PRIMARY_USER_ID); long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID); - mService.setLockCredential(newPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, password, - PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false); + mService.setLockCredential(newPassword, password, PRIMARY_USER_ID, false); assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - newPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) + newPassword, 0, PRIMARY_USER_ID) .getResponseCode()); assertEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); } public void testSyntheticPasswordVerifyCredential() throws RemoteException { - final byte[] password = "testSyntheticPasswordVerifyCredential-password".getBytes(); - final byte[] badPassword = "testSyntheticPasswordVerifyCredential-badpassword".getBytes(); + LockscreenCredential password = newPassword("password"); + LockscreenCredential badPassword = newPassword("badpassword"); initializeCredentialUnderSP(password, PRIMARY_USER_ID); assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) + password, 0, PRIMARY_USER_ID) .getResponseCode()); assertEquals(VerifyCredentialResponse.RESPONSE_ERROR, mService.verifyCredential( - badPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) + badPassword, 0, PRIMARY_USER_ID) .getResponseCode()); } public void testSyntheticPasswordClearCredential() throws RemoteException { - final byte[] password = "testSyntheticPasswordClearCredential-password".getBytes(); - final byte[] badPassword = "testSyntheticPasswordClearCredential-newpassword".getBytes(); + LockscreenCredential password = newPassword("password"); + LockscreenCredential badPassword = newPassword("newpassword"); initializeCredentialUnderSP(password, PRIMARY_USER_ID); long sid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID); // clear password - mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, password, - PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID, false); + mService.setLockCredential(nonePassword(), password, PRIMARY_USER_ID, false); assertEquals(0 ,mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); // set a new password - mService.setLockCredential(badPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, - PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false); + mService.setLockCredential(badPassword, nonePassword(), + PRIMARY_USER_ID, false); assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - badPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) + badPassword, 0, PRIMARY_USER_ID) .getResponseCode()); assertNotEquals(sid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); } public void testSyntheticPasswordChangeCredentialKeepsAuthSecret() throws RemoteException { - final byte[] password = - "testSyntheticPasswordChangeCredentialKeepsAuthSecret-password".getBytes(); - final byte[] badPassword = - "testSyntheticPasswordChangeCredentialKeepsAuthSecret-new".getBytes(); + LockscreenCredential password = newPassword("password"); + LockscreenCredential badPassword = newPassword("new"); initializeCredentialUnderSP(password, PRIMARY_USER_ID); - mService.setLockCredential(badPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, password, - PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false); + mService.setLockCredential(badPassword, password, PRIMARY_USER_ID, false); assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - badPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) + badPassword, 0, PRIMARY_USER_ID) .getResponseCode()); // Check the same secret was passed each time @@ -205,41 +194,35 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { } public void testSyntheticPasswordVerifyPassesPrimaryUserAuthSecret() throws RemoteException { - final byte[] password = - "testSyntheticPasswordVerifyPassesPrimaryUserAuthSecret-password".getBytes(); - final byte[] newPassword = - "testSyntheticPasswordVerifyPassesPrimaryUserAuthSecret-new".getBytes(); + LockscreenCredential password = newPassword("password"); + LockscreenCredential newPassword = newPassword("new"); initializeCredentialUnderSP(password, PRIMARY_USER_ID); reset(mAuthSecretService); - assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(password, - LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) + assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( + password, 0, PRIMARY_USER_ID) .getResponseCode()); verify(mAuthSecretService).primaryUserCredential(any(ArrayList.class)); } public void testSecondaryUserDoesNotPassAuthSecret() throws RemoteException { - final byte[] password = "testSecondaryUserDoesNotPassAuthSecret-password".getBytes(); + LockscreenCredential password = newPassword("password"); initializeCredentialUnderSP(password, SECONDARY_USER_ID); assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, SECONDARY_USER_ID) + password, 0, SECONDARY_USER_ID) .getResponseCode()); verify(mAuthSecretService, never()).primaryUserCredential(any(ArrayList.class)); } public void testNoSyntheticPasswordOrCredentialDoesNotPassAuthSecret() throws RemoteException { - // Setting null doesn't create a synthetic password - initializeCredentialUnderSP(null, PRIMARY_USER_ID); - - reset(mAuthSecretService); mService.onUnlockUser(PRIMARY_USER_ID); flushHandlerTasks(); verify(mAuthSecretService, never()).primaryUserCredential(any(ArrayList.class)); } public void testSyntheticPasswordAndCredentialDoesNotPassAuthSecret() throws RemoteException { - final byte[] password = "passwordForASyntheticPassword".getBytes(); + LockscreenCredential password = newPassword("passwordForASyntheticPassword"); initializeCredentialUnderSP(password, PRIMARY_USER_ID); reset(mAuthSecretService); @@ -249,10 +232,9 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { } public void testSyntheticPasswordButNoCredentialPassesAuthSecret() throws RemoteException { - final byte[] password = "getASyntheticPassword".getBytes(); + LockscreenCredential password = newPassword("getASyntheticPassword"); initializeCredentialUnderSP(password, PRIMARY_USER_ID); - mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, password, - PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID, false); + mService.setLockCredential(nonePassword(), password, PRIMARY_USER_ID, false); reset(mAuthSecretService); mService.onUnlockUser(PRIMARY_USER_ID); @@ -261,15 +243,14 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { } public void testManagedProfileUnifiedChallengeMigration() throws RemoteException { - final byte[] UnifiedPassword = "testManagedProfileUnifiedChallengeMigration-pwd".getBytes(); + LockscreenCredential UnifiedPassword = newPassword("unified-pwd"); disableSyntheticPassword(); - mService.setLockCredential(UnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, - PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false); + mService.setLockCredential(UnifiedPassword, nonePassword(), PRIMARY_USER_ID, false); mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null); final long primarySid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID); final long profileSid = mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID); - final byte[] primaryStorageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID); - final byte[] profileStorageKey = mStorageManager.getUserUnlockToken(MANAGED_PROFILE_USER_ID); + byte[] primaryStorageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID); + byte[] profileStorageKey = mStorageManager.getUserUnlockToken(MANAGED_PROFILE_USER_ID); assertTrue(primarySid != 0); assertTrue(profileSid != 0); assertTrue(profileSid != primarySid); @@ -277,12 +258,12 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { // do migration enableSyntheticPassword(); assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - UnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) + UnifiedPassword, 0, PRIMARY_USER_ID) .getResponseCode()); // verify assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - UnifiedPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) + UnifiedPassword, 0, PRIMARY_USER_ID) .getResponseCode()); assertEquals(primarySid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID)); @@ -295,19 +276,16 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { } public void testManagedProfileSeparateChallengeMigration() throws RemoteException { - final byte[] primaryPassword = - "testManagedProfileSeparateChallengeMigration-primary".getBytes(); - final byte[] profilePassword = - "testManagedProfileSeparateChallengeMigration-profile".getBytes(); + LockscreenCredential primaryPassword = newPassword("primary"); + LockscreenCredential profilePassword = newPassword("profile"); disableSyntheticPassword(); - mService.setLockCredential(primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, - PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false); - mService.setLockCredential(profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, - PASSWORD_QUALITY_ALPHABETIC, MANAGED_PROFILE_USER_ID, false); + mService.setLockCredential(primaryPassword, nonePassword(), PRIMARY_USER_ID, false); + mService.setLockCredential(profilePassword, nonePassword(), + MANAGED_PROFILE_USER_ID, false); final long primarySid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID); final long profileSid = mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID); - final byte[] primaryStorageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID); - final byte[] profileStorageKey = mStorageManager.getUserUnlockToken(MANAGED_PROFILE_USER_ID); + byte[] primaryStorageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID); + byte[] profileStorageKey = mStorageManager.getUserUnlockToken(MANAGED_PROFILE_USER_ID); assertTrue(primarySid != 0); assertTrue(profileSid != 0); assertTrue(profileSid != primarySid); @@ -315,19 +293,19 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { // do migration enableSyntheticPassword(); assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) + primaryPassword, 0, PRIMARY_USER_ID) .getResponseCode()); assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, - 0, MANAGED_PROFILE_USER_ID).getResponseCode()); + profilePassword, 0, MANAGED_PROFILE_USER_ID) + .getResponseCode()); // verify assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) - .getResponseCode()); + primaryPassword, 0, PRIMARY_USER_ID) + .getResponseCode()); assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, - 0, MANAGED_PROFILE_USER_ID).getResponseCode()); + profilePassword, 0, MANAGED_PROFILE_USER_ID) + .getResponseCode()); assertEquals(primarySid, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID)); assertArrayNotEquals(primaryStorageKey, @@ -339,101 +317,92 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { } public void testTokenBasedResetPassword() throws RemoteException { - final byte[] password = "password".getBytes(); - final byte[] pattern = "123654".getBytes(); - final byte[] token = "some-high-entropy-secure-token".getBytes(); + LockscreenCredential password = newPassword("password"); + LockscreenCredential pattern = newPattern("123654"); + byte[] token = "some-high-entropy-secure-token".getBytes(); initializeCredentialUnderSP(password, PRIMARY_USER_ID); // Disregard any reportPasswordChanged() invocations as part of credential setup. flushHandlerTasks(); reset(mDevicePolicyManager); - final byte[] storageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID); + byte[] storageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID); assertFalse(mService.hasPendingEscrowToken(PRIMARY_USER_ID)); long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null); assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID)); assertTrue(mService.hasPendingEscrowToken(PRIMARY_USER_ID)); - mService.verifyCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, - PRIMARY_USER_ID).getResponseCode(); + mService.verifyCredential(password, 0, PRIMARY_USER_ID).getResponseCode(); assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID)); assertFalse(mService.hasPendingEscrowToken(PRIMARY_USER_ID)); - mLocalService.setLockCredentialWithToken(pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, - handle, token, PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID); + mLocalService.setLockCredentialWithToken(pattern, handle, token, PRIMARY_USER_ID); // Verify DPM gets notified about new device lock flushHandlerTasks(); - final PasswordMetrics metric = PasswordMetrics.computeForCredential( - LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern(pattern))); + final PasswordMetrics metric = PasswordMetrics.computeForCredential(pattern); assertEquals(metric, mService.getUserPasswordMetrics(PRIMARY_USER_ID)); verify(mDevicePolicyManager).reportPasswordChanged(PRIMARY_USER_ID); assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, 0, PRIMARY_USER_ID) + pattern, 0, PRIMARY_USER_ID) .getResponseCode()); assertArrayEquals(storageKey, mStorageManager.getUserUnlockToken(PRIMARY_USER_ID)); } public void testTokenBasedClearPassword() throws RemoteException { - final byte[] password = "password".getBytes(); - final byte[] pattern = "123654".getBytes(); - final byte[] token = "some-high-entropy-secure-token".getBytes(); + LockscreenCredential password = newPassword("password"); + LockscreenCredential pattern = newPattern("123654"); + byte[] token = "some-high-entropy-secure-token".getBytes(); initializeCredentialUnderSP(password, PRIMARY_USER_ID); - final byte[] storageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID); + byte[] storageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID); long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null); assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID)); - mService.verifyCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, - 0, PRIMARY_USER_ID).getResponseCode(); + mService.verifyCredential(password, 0, PRIMARY_USER_ID).getResponseCode(); assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID)); - mLocalService.setLockCredentialWithToken(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, - handle, token, PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID); + mLocalService.setLockCredentialWithToken(nonePassword(), handle, token, PRIMARY_USER_ID); flushHandlerTasks(); // flush the unlockUser() call before changing password again - mLocalService.setLockCredentialWithToken(pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, - handle, token, PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID); + mLocalService.setLockCredentialWithToken(pattern, handle, token, + PRIMARY_USER_ID); assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, 0, PRIMARY_USER_ID) + pattern, 0, PRIMARY_USER_ID) .getResponseCode()); assertArrayEquals(storageKey, mStorageManager.getUserUnlockToken(PRIMARY_USER_ID)); } public void testTokenBasedResetPasswordAfterCredentialChanges() throws RemoteException { - final byte[] password = "password".getBytes(); - final byte[] pattern = "123654".getBytes(); - final byte[] newPassword = "password".getBytes(); - final byte[] token = "some-high-entropy-secure-token".getBytes(); + LockscreenCredential password = newPassword("password"); + LockscreenCredential pattern = newPattern("123654"); + LockscreenCredential newPassword = newPassword("password"); + byte[] token = "some-high-entropy-secure-token".getBytes(); initializeCredentialUnderSP(password, PRIMARY_USER_ID); - final byte[] storageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID); + byte[] storageKey = mStorageManager.getUserUnlockToken(PRIMARY_USER_ID); long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null); assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID)); - mService.verifyCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, - 0, PRIMARY_USER_ID).getResponseCode(); + mService.verifyCredential(password, 0, PRIMARY_USER_ID).getResponseCode(); assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID)); - mService.setLockCredential(pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, password, - PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID, false); + mService.setLockCredential(pattern, password, PRIMARY_USER_ID, false); - mLocalService.setLockCredentialWithToken(newPassword, - LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, handle, token, - PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID); + mLocalService.setLockCredentialWithToken(newPassword, handle, token, PRIMARY_USER_ID); assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - newPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) + newPassword, 0, PRIMARY_USER_ID) .getResponseCode()); assertArrayEquals(storageKey, mStorageManager.getUserUnlockToken(PRIMARY_USER_ID)); } public void testEscrowTokenActivatedImmediatelyIfNoUserPasswordNeedsMigration() throws RemoteException { - final String token = "some-high-entropy-secure-token"; + final byte[] token = "some-high-entropy-secure-token".getBytes(); enableSyntheticPassword(); - long handle = mLocalService.addEscrowToken(token.getBytes(), PRIMARY_USER_ID, null); + long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null); assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID)); assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); assertTrue(hasSyntheticPassword(PRIMARY_USER_ID)); @@ -441,9 +410,15 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { public void testEscrowTokenActivatedImmediatelyIfNoUserPasswordNoMigration() throws RemoteException { - final String token = "some-high-entropy-secure-token"; - initializeCredentialUnderSP(null, PRIMARY_USER_ID); - long handle = mLocalService.addEscrowToken(token.getBytes(), PRIMARY_USER_ID, null); + final byte[] token = "some-high-entropy-secure-token".getBytes(); + // By first setting a password and then clearing it, we enter the state where SP is + // initialized but the user currently has no password + initializeCredentialUnderSP(newPassword("password"), PRIMARY_USER_ID); + assertTrue(mService.setLockCredential(nonePassword(), newPassword("password"), + PRIMARY_USER_ID, false)); + assertTrue(mService.isSyntheticPasswordBasedCredential(PRIMARY_USER_ID)); + + long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null); assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID)); assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID)); assertTrue(hasSyntheticPassword(PRIMARY_USER_ID)); @@ -451,12 +426,11 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { public void testEscrowTokenActivatedLaterWithUserPasswordNeedsMigration() throws RemoteException { - final byte[] token = "some-high-entropy-secure-token".getBytes(); - final byte[] password = "password".getBytes(); + byte[] token = "some-high-entropy-secure-token".getBytes(); + LockscreenCredential password = newPassword("password"); // Set up pre-SP user password disableSyntheticPassword(); - mService.setLockCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, - PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false); + mService.setLockCredential(password, nonePassword(), PRIMARY_USER_ID, false); enableSyntheticPassword(); long handle = mLocalService.addEscrowToken(token, PRIMARY_USER_ID, null); @@ -464,14 +438,14 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { assertFalse(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID)); // Activate token (password gets migrated to SP at the same time) assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) + password, 0, PRIMARY_USER_ID) .getResponseCode()); // Verify token is activated assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID)); } public void testEscrowTokenCannotBeActivatedOnUnmanagedUser() { - final byte[] token = "some-high-entropy-secure-token".getBytes(); + byte[] token = "some-high-entropy-secure-token".getBytes(); when(mUserManagerInternal.isDeviceManaged()).thenReturn(false); when(mUserManagerInternal.isUserManaged(PRIMARY_USER_ID)).thenReturn(false); when(mDeviceStateCache.isDeviceProvisioned()).thenReturn(true); @@ -483,9 +457,9 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { } public void testSetLockCredentialWithTokenFailsWithoutLockScreen() throws Exception { - final byte[] password = "password".getBytes(); - final byte[] pattern = "123654".getBytes(); - final byte[] token = "some-high-entropy-secure-token".getBytes(); + LockscreenCredential password = newPassword("password"); + LockscreenCredential pattern = newPattern("123654"); + byte[] token = "some-high-entropy-secure-token".getBytes(); mHasSecureLockScreen = false; enableSyntheticPassword(); @@ -493,57 +467,50 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { assertTrue(mLocalService.isEscrowTokenActive(handle, PRIMARY_USER_ID)); try { - mLocalService.setLockCredentialWithToken(password, - LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, handle, token, - PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID); + mLocalService.setLockCredentialWithToken(password, handle, token, PRIMARY_USER_ID); fail("An exception should have been thrown."); } catch (UnsupportedOperationException e) { // Success - the exception was expected. } - assertFalse(mService.havePassword(PRIMARY_USER_ID)); + assertEquals(CREDENTIAL_TYPE_NONE, mService.getCredentialType(PRIMARY_USER_ID)); try { - mLocalService.setLockCredentialWithToken(pattern, - LockPatternUtils.CREDENTIAL_TYPE_PATTERN, handle, token, - PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID); + mLocalService.setLockCredentialWithToken(pattern, handle, token, PRIMARY_USER_ID); fail("An exception should have been thrown."); } catch (UnsupportedOperationException e) { // Success - the exception was expected. } - assertFalse(mService.havePattern(PRIMARY_USER_ID)); + assertEquals(CREDENTIAL_TYPE_NONE, mService.getCredentialType(PRIMARY_USER_ID)); } public void testGetHashFactorPrimaryUser() throws RemoteException { - final byte[] password = "password".getBytes(); - mService.setLockCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, - PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false); - final byte[] hashFactor = mService.getHashFactor(password, PRIMARY_USER_ID); + LockscreenCredential password = newPassword("password"); + mService.setLockCredential(password, nonePassword(), PRIMARY_USER_ID, false); + byte[] hashFactor = mService.getHashFactor(password, PRIMARY_USER_ID); assertNotNull(hashFactor); - mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE, - password, PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID, false); - final byte[] newHashFactor = mService.getHashFactor(null, PRIMARY_USER_ID); + mService.setLockCredential(nonePassword(), password, PRIMARY_USER_ID, false); + byte[] newHashFactor = mService.getHashFactor(nonePassword(), PRIMARY_USER_ID); assertNotNull(newHashFactor); // Hash factor should never change after password change/removal assertArrayEquals(hashFactor, newHashFactor); } public void testGetHashFactorManagedProfileUnifiedChallenge() throws RemoteException { - final byte[] pattern = "1236".getBytes(); - mService.setLockCredential(pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, - null, PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID, false); + LockscreenCredential pattern = newPattern("1236"); + mService.setLockCredential(pattern, nonePassword(), PRIMARY_USER_ID, false); mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null); assertNotNull(mService.getHashFactor(null, MANAGED_PROFILE_USER_ID)); } public void testGetHashFactorManagedProfileSeparateChallenge() throws RemoteException { - final byte[] primaryPassword = "primary".getBytes(); - final byte[] profilePassword = "profile".getBytes(); - mService.setLockCredential(primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, - PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false); - mService.setLockCredential(profilePassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, - PASSWORD_QUALITY_ALPHABETIC, MANAGED_PROFILE_USER_ID, false); - assertNotNull(mService.getHashFactor(profilePassword, MANAGED_PROFILE_USER_ID)); + LockscreenCredential primaryPassword = newPassword("primary"); + LockscreenCredential profilePassword = newPassword("profile"); + mService.setLockCredential(primaryPassword, nonePassword(), PRIMARY_USER_ID, false); + mService.setLockCredential(profilePassword, nonePassword(), + MANAGED_PROFILE_USER_ID, false); + assertNotNull( + mService.getHashFactor(profilePassword, MANAGED_PROFILE_USER_ID)); } public void testPasswordData_serializeDeserialize() { @@ -551,7 +518,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { data.scryptN = 11; data.scryptR = 22; data.scryptP = 33; - data.passwordType = CREDENTIAL_TYPE_PASSWORD; + data.credentialType = CREDENTIAL_TYPE_PASSWORD; data.salt = PAYLOAD; data.passwordHandle = PAYLOAD2; @@ -560,7 +527,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { assertEquals(11, deserialized.scryptN); assertEquals(22, deserialized.scryptR); assertEquals(33, deserialized.scryptP); - assertEquals(CREDENTIAL_TYPE_PASSWORD, deserialized.passwordType); + assertEquals(CREDENTIAL_TYPE_PASSWORD, deserialized.credentialType); assertArrayEquals(PAYLOAD, deserialized.salt); assertArrayEquals(PAYLOAD2, deserialized.passwordHandle); } @@ -569,7 +536,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { // Test that we can deserialize existing PasswordData and don't inadvertently change the // wire format. byte[] serialized = new byte[] { - 0, 0, 0, 2, /* CREDENTIAL_TYPE_PASSWORD */ + 0, 0, 0, 2, /* CREDENTIAL_TYPE_PASSWORD_OR_PIN */ 11, /* scryptN */ 22, /* scryptR */ 33, /* scryptP */ @@ -583,7 +550,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { assertEquals(11, deserialized.scryptN); assertEquals(22, deserialized.scryptR); assertEquals(33, deserialized.scryptP); - assertEquals(CREDENTIAL_TYPE_PASSWORD, deserialized.passwordType); + assertEquals(CREDENTIAL_TYPE_PASSWORD_OR_PIN, deserialized.credentialType); assertArrayEquals(PAYLOAD, deserialized.salt); assertArrayEquals(PAYLOAD2, deserialized.passwordHandle); } @@ -591,11 +558,11 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { public void testGsiDisablesAuthSecret() throws RemoteException { mGsiService.setIsGsiRunning(true); - final byte[] password = "testGsiDisablesAuthSecret-password".getBytes(); + LockscreenCredential password = newPassword("testGsiDisablesAuthSecret-password"); initializeCredentialUnderSP(password, PRIMARY_USER_ID); assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential( - password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0, PRIMARY_USER_ID) + password, 0, PRIMARY_USER_ID) .getResponseCode()); verify(mAuthSecretService, never()).primaryUserCredential(any(ArrayList.class)); } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java index a992dd126f5d..7d3ec030e7a6 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncTaskTest.java @@ -23,6 +23,7 @@ import static android.security.keystore.recovery.KeyChainProtectionParams.UI_FOR import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN; +import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN; import static com.google.common.truth.Truth.assertThat; @@ -31,7 +32,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; @@ -162,21 +162,6 @@ public class KeySyncTaskTest { } @Test - public void isPin_isTrueForNumericString() { - assertTrue(KeySyncTask.isPin("3298432574398654376547".getBytes())); - } - - @Test - public void isPin_isFalseForStringContainingLetters() { - assertFalse(KeySyncTask.isPin("398i54369548654".getBytes())); - } - - @Test - public void isPin_isFalseForStringContainingSymbols() { - assertFalse(KeySyncTask.isPin("-3987543643".getBytes())); - } - - @Test public void hashCredentialsBySaltedSha256_returnsSameHashForSameCredentialsAndSalt() { String credentials = "password1234"; byte[] salt = randomBytes(16); @@ -221,19 +206,19 @@ public class KeySyncTaskTest { @Test public void getUiFormat_returnsPinIfPin() { assertEquals(UI_FORMAT_PIN, - KeySyncTask.getUiFormat(CREDENTIAL_TYPE_PASSWORD, "1234".getBytes())); + KeySyncTask.getUiFormat(CREDENTIAL_TYPE_PIN)); } @Test public void getUiFormat_returnsPasswordIfPassword() { assertEquals(UI_FORMAT_PASSWORD, - KeySyncTask.getUiFormat(CREDENTIAL_TYPE_PASSWORD, "1234a".getBytes())); + KeySyncTask.getUiFormat(CREDENTIAL_TYPE_PASSWORD)); } @Test public void getUiFormat_returnsPatternIfPattern() { assertEquals(UI_FORMAT_PATTERN, - KeySyncTask.getUiFormat(CREDENTIAL_TYPE_PATTERN, "1234".getBytes())); + KeySyncTask.getUiFormat(CREDENTIAL_TYPE_PATTERN)); } @@ -683,7 +668,7 @@ public class KeySyncTaskTest { mRecoverySnapshotStorage, mSnapshotListenersStorage, TEST_USER_ID, - CREDENTIAL_TYPE_PASSWORD, + CREDENTIAL_TYPE_PIN, /*credential=*/ pin.getBytes(), /*credentialUpdated=*/ false, mPlatformKeyManager, @@ -799,7 +784,7 @@ public class KeySyncTaskTest { mRecoverySnapshotStorage, mSnapshotListenersStorage, TEST_USER_ID, - /*credentialType=*/ 3, + /*credentialType=*/ 5, // Some invalid credential type value "12345".getBytes(), /*credentialUpdated=*/ false, mPlatformKeyManager, diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java index 2836e69f79da..03367db0c3e6 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java @@ -18,8 +18,6 @@ package com.android.server.wm; import static android.app.ActivityManager.START_SUCCESS; import static android.app.ActivityManager.START_TASK_TO_FRONT; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; @@ -63,10 +61,8 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase { private ActivityMetricsLaunchObserver mLaunchObserver; private ActivityMetricsLaunchObserverRegistry mLaunchObserverRegistry; - private ActivityStack mStack; - private TaskRecord mTask; - private ActivityRecord mActivityRecord; - private ActivityRecord mActivityRecordTrampoline; + private ActivityRecord mTrampolineActivity; + private ActivityRecord mTopActivity; @Before public void setUpAMLO() { @@ -80,15 +76,10 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase { // Sometimes we need an ActivityRecord for ActivityMetricsLogger to do anything useful. // This seems to be the easiest way to create an ActivityRecord. - mStack = new StackBuilder(mRootActivityContainer) - .setActivityType(ACTIVITY_TYPE_STANDARD) - .setWindowingMode(WINDOWING_MODE_FULLSCREEN) - .setOnTop(true) - .setCreateActivity(true) + mTrampolineActivity = new ActivityBuilder(mService).setCreateTask(true).build(); + mTopActivity = new ActivityBuilder(mService) + .setTask(mTrampolineActivity.getTaskRecord()) .build(); - mTask = mStack.topTask(); - mActivityRecord = mTask.getTopActivity(); - mActivityRecordTrampoline = new ActivityBuilder(mService).setTask(mTask).build(); } @After @@ -123,8 +114,7 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase { return verify(mock, timeout(TimeUnit.SECONDS.toMillis(5))); } - @Test - public void testOnIntentStarted() throws Exception { + private void onIntentStarted() { Intent intent = new Intent("action 1"); mActivityMetricsLogger.notifyActivityLaunching(intent); @@ -134,123 +124,120 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase { } @Test - public void testOnIntentFailed() throws Exception { - testOnIntentStarted(); - - ActivityRecord activityRecord = null; + public void testOnIntentFailed() { + onIntentStarted(); // Bringing an intent that's already running 'to front' is not considered // as an ACTIVITY_LAUNCHED state transition. mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT, - activityRecord); + null /* launchedActivity */); verifyAsync(mLaunchObserver).onIntentFailed(); verifyNoMoreInteractions(mLaunchObserver); } - @Test - public void testOnActivityLaunched() throws Exception { - testOnIntentStarted(); + private void onActivityLaunched() { + onIntentStarted(); - mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS, - mActivityRecord); + mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS, mTopActivity); - verifyAsync(mLaunchObserver).onActivityLaunched(eqProto(mActivityRecord), anyInt()); + verifyAsync(mLaunchObserver).onActivityLaunched(eqProto(mTopActivity), anyInt()); verifyNoMoreInteractions(mLaunchObserver); } @Test - public void testOnActivityLaunchFinished() throws Exception { - testOnActivityLaunched(); + public void testOnActivityLaunchFinished() { + onActivityLaunched(); - mActivityMetricsLogger.notifyTransitionStarting(new SparseIntArray(), - SystemClock.elapsedRealtimeNanos()); + mActivityMetricsLogger.notifyTransitionStarting(new SparseIntArray(), + SystemClock.elapsedRealtimeNanos()); - mActivityMetricsLogger.notifyWindowsDrawn(mActivityRecord.getWindowingMode(), - SystemClock.elapsedRealtimeNanos()); + notifyWindowsDrawn(mTopActivity); - verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mActivityRecord), anyLong()); - verifyNoMoreInteractions(mLaunchObserver); + verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mTopActivity), anyLong()); + verifyNoMoreInteractions(mLaunchObserver); } @Test - public void testOnActivityLaunchCancelled() throws Exception { - testOnActivityLaunched(); + public void testOnActivityLaunchCancelled() { + onActivityLaunched(); - mActivityRecord.mDrawn = true; + mTopActivity.mDrawn = true; - // Cannot time already-visible activities. - mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT, mActivityRecord); + // Cannot time already-visible activities. + mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT, mTopActivity); - verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(mActivityRecord)); - verifyNoMoreInteractions(mLaunchObserver); + verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(mTopActivity)); + verifyNoMoreInteractions(mLaunchObserver); } @Test - public void testOnReportFullyDrawn() throws Exception { - testOnActivityLaunched(); + public void testOnReportFullyDrawn() { + onActivityLaunched(); - mActivityMetricsLogger.logAppTransitionReportedDrawn(mActivityRecord, false); + mActivityMetricsLogger.logAppTransitionReportedDrawn(mTopActivity, false); - verifyAsync(mLaunchObserver).onReportFullyDrawn(eqProto(mActivityRecord), anyLong()); + verifyAsync(mLaunchObserver).onReportFullyDrawn(eqProto(mTopActivity), anyLong()); verifyNoMoreInteractions(mLaunchObserver); } - @Test - public void testOnActivityLaunchedTrampoline() throws Exception { - testOnIntentStarted(); + private void onActivityLaunchedTrampoline() { + onIntentStarted(); - mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS, - mActivityRecord); + mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS, mTopActivity); - verifyAsync(mLaunchObserver).onActivityLaunched(eqProto(mActivityRecord), anyInt()); + verifyAsync(mLaunchObserver).onActivityLaunched(eqProto(mTopActivity), anyInt()); // A second, distinct, activity launch is coalesced into the the current app launch sequence - mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS, - mActivityRecordTrampoline); + mActivityMetricsLogger.notifyActivityLaunched(START_SUCCESS, mTrampolineActivity); verifyNoMoreInteractions(mLaunchObserver); } + private void notifyWindowsDrawn(ActivityRecord r) { + mActivityMetricsLogger.notifyWindowsDrawn(r.getWindowingMode(), + SystemClock.elapsedRealtimeNanos()); + } + @Test - public void testOnActivityLaunchFinishedTrampoline() throws Exception { - testOnActivityLaunchedTrampoline(); + public void testOnActivityLaunchFinishedTrampoline() { + onActivityLaunchedTrampoline(); + + mActivityMetricsLogger.notifyTransitionStarting(new SparseIntArray(), + SystemClock.elapsedRealtimeNanos()); - mActivityMetricsLogger.notifyTransitionStarting(new SparseIntArray(), - SystemClock.elapsedRealtimeNanos()); + notifyWindowsDrawn(mTrampolineActivity); - mActivityMetricsLogger.notifyWindowsDrawn(mActivityRecordTrampoline.getWindowingMode(), - SystemClock.elapsedRealtimeNanos()); + notifyWindowsDrawn(mTopActivity); - verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mActivityRecordTrampoline), - anyLong()); - verifyNoMoreInteractions(mLaunchObserver); + verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mTrampolineActivity), + anyLong()); + verifyNoMoreInteractions(mLaunchObserver); } @Test - public void testOnActivityLaunchCancelledTrampoline() throws Exception { - testOnActivityLaunchedTrampoline(); + public void testOnActivityLaunchCancelledTrampoline() { + onActivityLaunchedTrampoline(); - mActivityRecordTrampoline.mDrawn = true; + mTrampolineActivity.mDrawn = true; - // Cannot time already-visible activities. - mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT, - mActivityRecordTrampoline); + // Cannot time already-visible activities. + mActivityMetricsLogger.notifyActivityLaunched(START_TASK_TO_FRONT, mTrampolineActivity); - verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(mActivityRecordTrampoline)); - verifyNoMoreInteractions(mLaunchObserver); + verifyAsync(mLaunchObserver).onActivityLaunchCancelled(eqProto(mTrampolineActivity)); + verifyNoMoreInteractions(mLaunchObserver); } @Test - public void testActivityRecordProtoIsNotTooBig() throws Exception { + public void testActivityRecordProtoIsNotTooBig() { // The ActivityRecordProto must not be too big, otherwise converting it at runtime // will become prohibitively expensive. - assertWithMessage("mActivityRecord: %s", mActivityRecord). - that(activityRecordToProto(mActivityRecord).length). - isAtMost(ActivityMetricsLogger.LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE); + assertWithMessage("mTopActivity: %s", mTopActivity) + .that(activityRecordToProto(mTopActivity).length) + .isAtMost(ActivityMetricsLogger.LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE); - assertWithMessage("mActivityRecordTrampoline: %s", mActivityRecordTrampoline). - that(activityRecordToProto(mActivityRecordTrampoline).length). - isAtMost(ActivityMetricsLogger.LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE); + assertWithMessage("mTrampolineActivity: %s", mTrampolineActivity) + .that(activityRecordToProto(mTrampolineActivity).length) + .isAtMost(ActivityMetricsLogger.LAUNCH_OBSERVER_ACTIVITY_RECORD_PROTO_CHUNK_SIZE); } } diff --git a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java index 7449efda69ac..1fb6a563aa40 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppChangeTransitionTests.java @@ -60,11 +60,9 @@ public class AppChangeTransitionTests extends WindowTestsBase { private ActivityRecord mActivity; public void setUpOnDisplay(DisplayContent dc) { - mStack = createTaskStackOnDisplay(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, dc); - mTask = createTaskInStack(mStack, 0 /* userId */); - mActivity = WindowTestUtils.createTestActivityRecord(dc); - - mTask.addChild(mActivity, 0); + mActivity = createTestActivityRecord(dc, WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD); + mTask = mActivity.getTask(); + mStack = mTask.mStack; // Set a remote animator with snapshot disabled. Snapshots don't work in wmtests. RemoteAnimationDefinition definition = new RemoteAnimationDefinition(); @@ -167,7 +165,7 @@ public class AppChangeTransitionTests extends WindowTestsBase { // setup currently defaults to no snapshot. setUpOnDisplay(mDisplayContent); - mTask.setWindowingMode(WINDOWING_MODE_FREEFORM); + mTask.mTaskRecord.setWindowingMode(WINDOWING_MODE_FREEFORM); assertEquals(1, mDisplayContent.mChangingApps.size()); assertTrue(mActivity.isInChangeTransition()); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java index 5b9a785663e2..2fc03c7f13d3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackTests.java @@ -21,12 +21,15 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; @@ -119,7 +122,7 @@ public class TaskStackTests extends WindowTestsBase { @Test public void testRemoveContainer() { final TaskStack stack = createTaskStackOnDisplay(mDisplayContent); - final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stack); + final Task task = createTaskInStack(stack, 0 /* userId */); assertNotNull(stack); assertNotNull(task); @@ -135,10 +138,10 @@ public class TaskStackTests extends WindowTestsBase { @Test public void testRemoveContainer_deferRemoval() { final TaskStack stack = createTaskStackOnDisplay(mDisplayContent); - final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stack); + final Task task = createTaskInStack(stack, 0 /* userId */); // Stack removal is deferred if one of its child is animating. - task.setLocalIsAnimating(true); + doReturn(true).when(task).isSelfAnimating(); stack.removeIfPossible(); // For the case of deferred removal the task controller will still be connected to the its @@ -157,8 +160,7 @@ public class TaskStackTests extends WindowTestsBase { public void testReparent() { // Create first stack on primary display. final TaskStack stack1 = createTaskStackOnDisplay(mDisplayContent); - final WindowTestUtils.TestTask task1 = WindowTestUtils.createTestTask(stack1); - task1.mOnDisplayChangedCalled = false; + final Task task1 = createTaskInStack(stack1, 0 /* userId */); // Create second display and put second stack on it. final DisplayContent dc = createNewDisplay(); @@ -170,7 +172,7 @@ public class TaskStackTests extends WindowTestsBase { final int stack1PositionInParent = stack1.getParent().mChildren.indexOf(stack1); final int stack2PositionInParent = stack1.getParent().mChildren.indexOf(stack2); assertEquals(stack1PositionInParent, stack2PositionInParent + 1); - assertTrue(task1.mOnDisplayChangedCalled); + verify(task1, times(1)).onDisplayChanged(any()); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java index 2ba1834ea286..4dfa26644fa9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java @@ -16,11 +16,16 @@ package com.android.server.wm; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import android.graphics.Point; import android.graphics.Rect; @@ -45,7 +50,7 @@ public class TaskTests extends WindowTestsBase { @Test public void testRemoveContainer() { final TaskStack stackController1 = createTaskStackOnDisplay(mDisplayContent); - final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stackController1); + final Task task = createTaskInStack(stackController1, 0 /* userId */); final ActivityRecord activity = WindowTestUtils.createActivityRecordInTask(mDisplayContent, task); @@ -59,11 +64,11 @@ public class TaskTests extends WindowTestsBase { @Test public void testRemoveContainer_deferRemoval() { final TaskStack stackController1 = createTaskStackOnDisplay(mDisplayContent); - final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stackController1); + final Task task = createTaskInStack(stackController1, 0 /* userId */); final ActivityRecord activity = WindowTestUtils.createActivityRecordInTask(mDisplayContent, task); - task.mShouldDeferRemoval = true; + doReturn(true).when(task).shouldDeferRemoval(); task.removeIfPossible(); // For the case of deferred removal the task will still be connected to the its app token @@ -81,9 +86,9 @@ public class TaskTests extends WindowTestsBase { @Test public void testReparent() { final TaskStack stackController1 = createTaskStackOnDisplay(mDisplayContent); - final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stackController1); + final Task task = createTaskInStack(stackController1, 0 /* userId */); final TaskStack stackController2 = createTaskStackOnDisplay(mDisplayContent); - final WindowTestUtils.TestTask task2 = WindowTestUtils.createTestTask(stackController2); + final Task task2 = createTaskInStack(stackController2, 0 /* userId */); boolean gotException = false; try { @@ -104,34 +109,33 @@ public class TaskTests extends WindowTestsBase { task.reparent(stackController2, 0, false/* moveParents */); assertEquals(stackController2, task.getParent()); - assertEquals(0, task.positionInParent()); - assertEquals(1, task2.positionInParent()); + assertEquals(0, task.getParent().mChildren.indexOf(task)); + assertEquals(1, task2.getParent().mChildren.indexOf(task2)); } @Test public void testReparent_BetweenDisplays() { // Create first stack on primary display. final TaskStack stack1 = createTaskStackOnDisplay(mDisplayContent); - final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stack1); - task.mOnDisplayChangedCalled = false; + final Task task = createTaskInStack(stack1, 0 /* userId */); assertEquals(mDisplayContent, stack1.getDisplayContent()); // Create second display and put second stack on it. final DisplayContent dc = createNewDisplay(); final TaskStack stack2 = createTaskStackOnDisplay(dc); - final WindowTestUtils.TestTask task2 = WindowTestUtils.createTestTask(stack2); + final Task task2 = createTaskInStack(stack2, 0 /* userId */); // Reparent and check state task.reparent(stack2, 0, false /* moveParents */); assertEquals(stack2, task.getParent()); - assertEquals(0, task.positionInParent()); - assertEquals(1, task2.positionInParent()); - assertTrue(task.mOnDisplayChangedCalled); + assertEquals(0, task.getParent().mChildren.indexOf(task)); + assertEquals(1, task2.getParent().mChildren.indexOf(task2)); + verify(task, times(1)).onDisplayChanged(any()); } @Test public void testBounds() { final TaskStack stack1 = createTaskStackOnDisplay(mDisplayContent); - final WindowTestUtils.TestTask task = WindowTestUtils.createTestTask(stack1); + final Task task = createTaskInStack(stack1, 0 /* userId */); // Check that setting bounds also updates surface position Rect bounds = new Rect(10, 10, 100, 200); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java index 5c547c224225..8cd97cb8a344 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java @@ -16,24 +16,24 @@ package com.android.server.wm; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.view.DisplayCutout.BOUNDS_POSITION_TOP; import static android.view.DisplayCutout.fromBoundingRect; -import static android.view.WindowManager.LayoutParams.MATCH_PARENT; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; + import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; import android.content.res.Configuration; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; import android.view.DisplayInfo; import android.view.Gravity; -import android.view.IWindow; import android.view.WindowManager; import androidx.test.filters.FlakyTest; @@ -57,29 +57,11 @@ import org.mockito.Mockito; @RunWith(WindowTestRunner.class) public class WindowFrameTests extends WindowTestsBase { - private final IWindow mIWindow = new TestIWindow(); private final Rect mEmptyRect = new Rect(); private DisplayContent mTestDisplayContent; - static class FrameTestWindowState extends WindowState { - boolean mDockedResizingForTest = false; - FrameTestWindowState(WindowManagerService wm, IWindow iWindow, WindowToken windowToken, - WindowManager.LayoutParams attrs) { - super(wm, mock(Session.class), iWindow, windowToken, null, 0, 0, attrs, 0, 0, - false /* ownerCanAddInternalSystemWindow */); - } - - @Override - boolean isDockedResizing() { - return mDockedResizingForTest; - } - } - - TaskStack mStubStack; - @Before public void setUp() throws Exception { - mStubStack = mock(TaskStack.class); DisplayInfo testDisplayInfo = new DisplayInfo(mDisplayInfo); testDisplayInfo.displayCutout = null; mTestDisplayContent = createNewDisplay(testDisplayInfo); @@ -129,8 +111,7 @@ public class WindowFrameTests extends WindowTestsBase { expectedRect.bottom); } - private void assertPolicyCrop( - FrameTestWindowState w, int left, int top, int right, int bottom) { + private void assertPolicyCrop(WindowState w, int left, int top, int right, int bottom) { Rect policyCrop = new Rect(); w.calculatePolicyCrop(policyCrop); assertRect(policyCrop, left, top, right, bottom); @@ -139,7 +120,7 @@ public class WindowFrameTests extends WindowTestsBase { @Test public void testLayoutInFullscreenTaskInsets() { // fullscreen task doesn't use bounds for computeFrame - WindowState w = createWindow(MATCH_PARENT, MATCH_PARENT); + WindowState w = createWindow(); w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP; final int bottomContentInset = 100; @@ -196,7 +177,7 @@ public class WindowFrameTests extends WindowTestsBase { @Test public void testLayoutInFullscreenTaskNoInsets() { // fullscreen task doesn't use bounds for computeFrame - WindowState w = createWindow(MATCH_PARENT, MATCH_PARENT); + WindowState w = createWindow(); w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP; // With no insets or system decor all the frames incoming from PhoneWindowManager @@ -278,16 +259,16 @@ public class WindowFrameTests extends WindowTestsBase { final int logicalWidth = displayInfo.logicalWidth; final int logicalHeight = displayInfo.logicalHeight; - final int taskLeft = logicalWidth / 4; - final int taskTop = logicalHeight / 4; - final int taskRight = logicalWidth / 4 * 3; - final int taskBottom = logicalHeight / 4 * 3; - final Rect taskBounds = new Rect(taskLeft, taskTop, taskRight, taskBottom); - WindowState w = createWindow(MATCH_PARENT, MATCH_PARENT); + final Rect taskBounds = new Rect( + logicalWidth / 4, logicalHeight / 4, logicalWidth / 4 * 3, logicalHeight / 4 * 3); + WindowState w = createWindow(); final Task task = w.getTask(); // Use split-screen because it is non-fullscreen, but also not floating - task.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); - task.setBounds(taskBounds); + task.mTaskRecord.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); + task.mTaskRecord.setBounds(taskBounds); + // The bounds we are requesting might be different from what the system resolved based on + // other factors. + final Rect resolvedTaskBounds = task.getBounds(); w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP; final Rect pf = new Rect(0, 0, logicalWidth, logicalHeight); @@ -296,8 +277,8 @@ public class WindowFrameTests extends WindowTestsBase { w.computeFrameLw(); // For non fullscreen tasks the containing frame is based off the // task bounds not the parent frame. - assertFrame(w, taskLeft, taskTop, taskRight, taskBottom); - assertContentFrame(w, taskBounds); + assertEquals(resolvedTaskBounds, w.getFrameLw()); + assertContentFrame(w, resolvedTaskBounds); assertContentInset(w, 0, 0, 0, 0); pf.set(0, 0, logicalWidth, logicalHeight); @@ -307,36 +288,38 @@ public class WindowFrameTests extends WindowTestsBase { final Rect cf = new Rect(0, 0, cfRight, cfBottom); windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect); w.computeFrameLw(); - assertFrame(w, taskLeft, taskTop, taskRight, taskBottom); - int contentInsetRight = taskRight - cfRight; - int contentInsetBottom = taskBottom - cfBottom; + assertEquals(resolvedTaskBounds, w.getFrameLw()); + int contentInsetRight = resolvedTaskBounds.right - cfRight; + int contentInsetBottom = resolvedTaskBounds.bottom - cfBottom; assertContentInset(w, 0, 0, contentInsetRight, contentInsetBottom); - assertContentFrame(w, new Rect(taskLeft, taskTop, taskRight - contentInsetRight, - taskBottom - contentInsetBottom)); + assertContentFrame(w, new Rect(resolvedTaskBounds.left, resolvedTaskBounds.top, + resolvedTaskBounds.right - contentInsetRight, + resolvedTaskBounds.bottom - contentInsetBottom)); pf.set(0, 0, logicalWidth, logicalHeight); // If we set displayed bounds, the insets will be computed with the main task bounds // but the frame will be positioned according to the displayed bounds. final int insetLeft = logicalWidth / 5; final int insetTop = logicalHeight / 5; - final int insetRight = insetLeft + (taskRight - taskLeft); - final int insetBottom = insetTop + (taskBottom - taskTop); - task.setOverrideDisplayedBounds(taskBounds); - task.setBounds(insetLeft, insetTop, insetRight, insetBottom); + final int insetRight = insetLeft + (resolvedTaskBounds.right - resolvedTaskBounds.left); + final int insetBottom = insetTop + (resolvedTaskBounds.bottom - resolvedTaskBounds.top); + task.mTaskRecord.setDisplayedBounds(resolvedTaskBounds); + task.mTaskRecord.setBounds(insetLeft, insetTop, insetRight, insetBottom); windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect); w.computeFrameLw(); - assertFrame(w, taskLeft, taskTop, taskRight, taskBottom); + assertEquals(resolvedTaskBounds, w.getFrameLw()); contentInsetRight = insetRight - cfRight; contentInsetBottom = insetBottom - cfBottom; assertContentInset(w, 0, 0, contentInsetRight, contentInsetBottom); - assertContentFrame(w, new Rect(taskLeft, taskTop, taskRight - contentInsetRight, - taskBottom - contentInsetBottom)); + assertContentFrame(w, new Rect(resolvedTaskBounds.left, resolvedTaskBounds.top, + resolvedTaskBounds.right - contentInsetRight, + resolvedTaskBounds.bottom - contentInsetBottom)); } @Test @FlakyTest(bugId = 130388666) public void testCalculatePolicyCrop() { - final FrameTestWindowState w = createWindow(MATCH_PARENT, MATCH_PARENT); + final WindowState w = createWindow(); w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP; final DisplayInfo displayInfo = w.getDisplayContent().getDisplayInfo(); @@ -381,7 +364,7 @@ public class WindowFrameTests extends WindowTestsBase { // to the computed window frame. assertPolicyCrop(w, 0, 0, logicalWidth / 2, logicalHeight / 2); - w.mDockedResizingForTest = true; + doReturn(true).when(w).isDockedResizing(); // But if we are docked resizing it won't be, however we will still be // shrunk to the decor frame and the display. assertPolicyCrop(w, 0, 0, @@ -402,7 +385,7 @@ public class WindowFrameTests extends WindowTestsBase { final int taskRight = logicalWidth / 4 * 3; final int taskBottom = logicalHeight / 4 * 3; final Rect taskBounds = new Rect(taskLeft, taskTop, taskRight, taskBottom); - WindowState w = createWindow(MATCH_PARENT, MATCH_PARENT); + WindowState w = createWindow(); final Task task = w.getTask(); // Use split-screen because it is non-fullscreen, but also not floating task.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); @@ -440,7 +423,7 @@ public class WindowFrameTests extends WindowTestsBase { @FlakyTest(bugId = 130388666) public void testDisplayCutout() { // Regular fullscreen task and window - WindowState w = createWindow(MATCH_PARENT, MATCH_PARENT); + WindowState w = createWindow(); w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP; final Rect pf = new Rect(0, 0, 1000, 2000); @@ -464,7 +447,7 @@ public class WindowFrameTests extends WindowTestsBase { @FlakyTest(bugId = 130388666) public void testDisplayCutout_tempDisplayedBounds() { // Regular fullscreen task and window - WindowState w = createWindow(MATCH_PARENT, MATCH_PARENT); + WindowState w = createWindow(); final Task task = w.getTask(); task.setBounds(new Rect(0, 0, 1000, 2000)); task.setOverrideDisplayedBounds(new Rect(0, -500, 1000, 1500)); @@ -489,11 +472,12 @@ public class WindowFrameTests extends WindowTestsBase { @Test public void testFreeformContentInsets() { + removeGlobalMinSizeRestriction(); // fullscreen task doesn't use bounds for computeFrame - WindowState w = createWindow(MATCH_PARENT, MATCH_PARENT); + WindowState w = createWindow(); final Task task = w.getTask(); w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP; - task.setWindowingMode(WINDOWING_MODE_FREEFORM); + task.mTaskRecord.setWindowingMode(WINDOWING_MODE_FREEFORM); DisplayContent dc = mTestDisplayContent; dc.mInputMethodTarget = w; @@ -515,7 +499,7 @@ public class WindowFrameTests extends WindowTestsBase { // First check that it only gets moved up enough to show window. final Rect winRect = new Rect(200, 200, 300, 500); - task.setBounds(winRect); + task.mTaskRecord.setBounds(winRect); w.getWindowFrames().setFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect); w.computeFrameLw(); @@ -527,7 +511,7 @@ public class WindowFrameTests extends WindowTestsBase { // Now check that it won't get moved beyond the top and then has appropriate insets winRect.bottom = 600; - task.setBounds(winRect); + task.mTaskRecord.setBounds(winRect); w.setBounds(winRect); w.getWindowFrames().setFrames(pf, df, of, cf, vf, dcf, sf, mEmptyRect); w.computeFrameLw(); @@ -544,16 +528,9 @@ public class WindowFrameTests extends WindowTestsBase { assertEquals(winRect, w.getFrameLw()); } - private FrameTestWindowState createWindow(int width, int height) { - final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(TYPE_APPLICATION); - attrs.width = width; - attrs.height = height; - - ActivityRecord activity = createActivityRecord(mTestDisplayContent, - WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); - - FrameTestWindowState ws = new FrameTestWindowState(mWm, mIWindow, activity, attrs); - activity.addWindow(ws); + private WindowState createWindow() { + final WindowState ws = createWindow(null, TYPE_APPLICATION, mTestDisplayContent, "WindowFrameTests"); + spyOn(ws); return ws; } } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java index 4fbf820e339d..51daf6567a47 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestUtils.java @@ -33,16 +33,16 @@ import com.android.server.wm.ActivityTestsBase.ActivityBuilder; * A collection of static functions that provide access to WindowManager related test functionality. */ class WindowTestUtils { - private static int sNextTaskId = 0; /** Creates a {@link Task} and adds it to the specified {@link TaskStack}. */ - static Task createTaskInStack(WindowManagerService service, TaskStack stack, - int userId) { + static Task createTaskInStack(WindowManagerService service, TaskStack stack, int userId) { synchronized (service.mGlobalLock) { - final Task newTask = new Task(sNextTaskId++, stack, userId, service, 0, false, - new ActivityManager.TaskDescription(), null); - stack.addTask(newTask, POSITION_TOP); - return newTask; + final TaskRecord task = new ActivityTestsBase.TaskBuilder( + stack.mActivityStack.mStackSupervisor) + .setUserId(userId) + .setStack(stack.mActivityStack) + .build(); + return task.mTask; } } @@ -53,17 +53,33 @@ class WindowTestUtils { return activity; } + static ActivityRecord createTestActivityRecord(ActivityStack stack) { + synchronized (stack.mService.mGlobalLock) { + final ActivityRecord activity = new ActivityTestsBase.ActivityBuilder( + stack.mService) + .setStack(stack) + .setCreateTask(true) + .build(); + postCreateActivitySetup(activity, stack.mTaskStack.getDisplayContent()); + return activity; + } + } + static ActivityRecord createTestActivityRecord(DisplayContent dc) { synchronized (dc.mWmService.mGlobalLock) { final ActivityRecord activity = new ActivityBuilder(dc.mWmService.mAtmService).build(); - activity.onDisplayChanged(dc); - activity.setOccludesParent(true); - activity.setHidden(false); - activity.hiddenRequested = false; + postCreateActivitySetup(activity, dc); return activity; } } + private static void postCreateActivitySetup(ActivityRecord activity, DisplayContent dc) { + activity.onDisplayChanged(dc); + activity.setOccludesParent(true); + activity.setHidden(false); + activity.hiddenRequested = false; + } + static TestWindowToken createTestWindowToken(int type, DisplayContent dc) { return createTestWindowToken(type, dc, false /* persistOnEmpty */); } @@ -92,49 +108,6 @@ class WindowTestUtils { } } - /* Used so we can gain access to some protected members of the {@link Task} class */ - static class TestTask extends Task { - boolean mShouldDeferRemoval = false; - boolean mOnDisplayChangedCalled = false; - private boolean mIsAnimating = false; - - TestTask(int taskId, TaskStack stack, int userId, WindowManagerService service, - int resizeMode, boolean supportsPictureInPicture, - TaskRecord taskRecord) { - super(taskId, stack, userId, service, resizeMode, supportsPictureInPicture, - new ActivityManager.TaskDescription(), taskRecord); - stack.addTask(this, POSITION_TOP); - } - - boolean shouldDeferRemoval() { - return mShouldDeferRemoval; - } - - int positionInParent() { - return getParent().mChildren.indexOf(this); - } - - @Override - void onDisplayChanged(DisplayContent dc) { - super.onDisplayChanged(dc); - mOnDisplayChangedCalled = true; - } - - @Override - boolean isSelfAnimating() { - return mIsAnimating; - } - - void setLocalIsAnimating(boolean isAnimating) { - mIsAnimating = isAnimating; - } - } - - static TestTask createTestTask(TaskStack stack) { - return new TestTask(sNextTaskId++, stack, 0, stack.mWmService, RESIZE_MODE_UNRESIZEABLE, - false, mock(TaskRecord.class)); - } - /** Used to track resize reports. */ static class TestWindowState extends WindowState { boolean mResizeReported; diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index af9c51008091..780fed9805cb 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -211,11 +211,7 @@ class WindowTestsBase extends SystemServiceTestsBase { ActivityRecord createTestActivityRecord(DisplayContent dc, int windowingMode, int activityType) { final TaskStack stack = createTaskStackOnDisplay(windowingMode, activityType, dc); - final Task task = createTaskInStack(stack, 0 /* userId */); - final ActivityRecord activity = - WindowTestUtils.createTestActivityRecord(dc); - task.addChild(activity, 0); - return activity; + return WindowTestUtils.createTestActivityRecord(stack.mActivityStack); } WindowState createWindow(WindowState parent, int type, String name) { @@ -327,14 +323,14 @@ class WindowTestsBase extends SystemServiceTestsBase { TaskStack createTaskStackOnDisplay(int windowingMode, int activityType, DisplayContent dc) { synchronized (mWm.mGlobalLock) { - final Configuration overrideConfig = new Configuration(); - overrideConfig.windowConfiguration.setWindowingMode(windowingMode); - overrideConfig.windowConfiguration.setActivityType(activityType); - final int stackId = ++sNextStackId; - final TaskStack stack = new TaskStack(mWm, stackId, mock(ActivityStack.class)); - dc.setStackOnDisplay(stackId, true, stack); - stack.onRequestedOverrideConfigurationChanged(overrideConfig); - return stack; + final ActivityStack stack = new ActivityTestsBase.StackBuilder( + dc.mWmService.mAtmService.mRootActivityContainer) + .setDisplay(dc.mActivityDisplay) + .setWindowingMode(windowingMode) + .setActivityType(activityType) + .setCreateActivity(false) + .build(); + return stack.mTaskStack; } } @@ -386,4 +382,9 @@ class WindowTestsBase extends SystemServiceTestsBase { displayInfo.ownerUid = SYSTEM_UID; return createNewDisplay(displayInfo); } + + /** Sets the default minimum task size to 1 so that tests can use small task sizes */ + public void removeGlobalMinSizeRestriction() { + mWm.mAtmService.mRootActivityContainer.mDefaultMinSizeOfResizeableTaskDp = 1; + } } diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java index 3f348a4bda8c..60290e3b785d 100644 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -2049,6 +2049,10 @@ public final class Call { return "DISCONNECTING"; case STATE_SELECT_PHONE_ACCOUNT: return "SELECT_PHONE_ACCOUNT"; + case STATE_SIMULATED_RINGING: + return "SIMULATED_RINGING"; + case STATE_AUDIO_PROCESSING: + return "AUDIO_PROCESSING"; default: Log.w(Call.class, "Unknown state %d", state); return "UNKNOWN"; diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 06a737041f0d..f83520f1c7a7 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -10237,11 +10237,13 @@ public class TelephonyManager { /** * Action set from carrier signalling broadcast receivers to enable/disable radio - * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required + * Permissions {@link android.Manifest.permission.MODIFY_PHONE_STATE} is required. * @param subId the subscription ID that this action applies to. * @param enabled control enable or disable radio. * @hide */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void carrierActionSetRadioEnabled(int subId, boolean enabled) { try { ITelephony service = getITelephony(); @@ -10256,11 +10258,13 @@ public class TelephonyManager { /** * Action set from carrier signalling broadcast receivers to start/stop reporting default * network available events - * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required + * Permissions {@link android.Manifest.permission.MODIFY_PHONE_STATE} is required. * @param subId the subscription ID that this action applies to. * @param report control start/stop reporting network status. * @hide */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void carrierActionReportDefaultNetworkStatus(int subId, boolean report) { try { ITelephony service = getITelephony(); @@ -10274,10 +10278,12 @@ public class TelephonyManager { /** * Action set from carrier signalling broadcast receivers to reset all carrier actions - * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required + * Permissions {@link android.Manifest.permission.MODIFY_PHONE_STATE} is required. * @param subId the subscription ID that this action applies to. * @hide */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void carrierActionResetAll(int subId) { try { ITelephony service = getITelephony(); diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 39e00cc50f98..f79a5c654a63 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -1778,21 +1778,6 @@ interface ITelephony { boolean isInEmergencySmsMode(); /** - * Get a list of SMS apps on a user. - */ - String[] getSmsApps(int userId); - - /** - * Get the default SMS app on a given user. - */ - String getDefaultSmsApp(int userId); - - /** - * Set the default SMS app to a given package on a given user. - */ - void setDefaultSmsApp(int userId, String packageName); - - /** * Return the modem radio power state for slot index. * */ diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java index 0b557942c87b..62ba95dc806b 100644 --- a/wifi/java/android/net/wifi/WifiInfo.java +++ b/wifi/java/android/net/wifi/WifiInfo.java @@ -16,6 +16,7 @@ package android.net.wifi; +import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.Nullable; import android.annotation.SystemApi; @@ -27,6 +28,8 @@ import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.net.Inet4Address; import java.net.InetAddress; import java.net.UnknownHostException; @@ -34,7 +37,7 @@ import java.util.EnumMap; import java.util.Locale; /** - * Describes the state of any Wifi connection that is active or + * Describes the state of any Wi-Fi connection that is active or * is in the process of being set up. */ public class WifiInfo implements Parcelable { @@ -96,6 +99,47 @@ public class WifiInfo implements Parcelable { private int mRssi; /** + * Wi-Fi unknown technology + */ + public static final int WIFI_TECHNOLOGY_UNKNOWN = 0; + + /** + * Wi-Fi 802.11a/b/g + */ + public static final int WIFI_TECHNOLOGY_LEGACY = 1; + + /** + * Wi-Fi 802.11n + */ + public static final int WIFI_TECHNOLOGY_11N = 4; + + /** + * Wi-Fi 802.11ac + */ + public static final int WIFI_TECHNOLOGY_11AC = 5; + + /** + * Wi-Fi 802.11ax + */ + public static final int WIFI_TECHNOLOGY_11AX = 6; + + /** @hide */ + @IntDef(prefix = { "WIFI_TECHNOLOGY_" }, value = { + WIFI_TECHNOLOGY_UNKNOWN, + WIFI_TECHNOLOGY_LEGACY, + WIFI_TECHNOLOGY_11N, + WIFI_TECHNOLOGY_11AC, + WIFI_TECHNOLOGY_11AX + }) + @Retention(RetentionPolicy.SOURCE) + public @interface WifiTechnology{} + + /** + * Wi-Fi technology for the connection + */ + private @WifiTechnology int mWifiTechnology; + + /** * The unit in which links speeds are expressed. */ public static final String LINK_SPEED_UNITS = "Mbps"; @@ -286,6 +330,7 @@ public class WifiInfo implements Parcelable { txSuccessRate = source.txSuccessRate; rxSuccessRate = source.rxSuccessRate; score = source.score; + mWifiTechnology = source.mWifiTechnology; } } @@ -374,6 +419,22 @@ public class WifiInfo implements Parcelable { } /** + * Sets the Wi-Fi technology + * @hide + */ + public void setWifiTechnology(@WifiTechnology int wifiTechnology) { + mWifiTechnology = wifiTechnology; + } + + /** + * Get connection Wi-Fi technology + * @return the connection Wi-Fi technology + */ + public @WifiTechnology int getWifiTechnology() { + return mWifiTechnology; + } + + /** * Returns the current link speed in {@link #LINK_SPEED_UNITS}. * @return the link speed or {@link #LINK_SPEED_UNKNOWN} if link speed is unknown. * @see #LINK_SPEED_UNITS @@ -679,6 +740,7 @@ public class WifiInfo implements Parcelable { .append(", MAC: ").append(mMacAddress == null ? none : mMacAddress) .append(", Supplicant state: ") .append(mSupplicantState == null ? none : mSupplicantState) + .append(", Wi-Fi technology: ").append(mWifiTechnology) .append(", RSSI: ").append(mRssi) .append(", Link speed: ").append(mLinkSpeed).append(LINK_SPEED_UNITS) .append(", Tx Link speed: ").append(mTxLinkSpeed).append(LINK_SPEED_UNITS) @@ -734,6 +796,7 @@ public class WifiInfo implements Parcelable { dest.writeString(mNetworkSuggestionOrSpecifierPackageName); dest.writeString(mFqdn); dest.writeString(mProviderFriendlyName); + dest.writeInt(mWifiTechnology); } /** Implement the Parcelable interface {@hide} */ @@ -775,6 +838,7 @@ public class WifiInfo implements Parcelable { info.mNetworkSuggestionOrSpecifierPackageName = in.readString(); info.mFqdn = in.readString(); info.mProviderFriendlyName = in.readString(); + info.mWifiTechnology = in.readInt(); return info; } diff --git a/wifi/tests/src/android/net/wifi/WifiInfoTest.java b/wifi/tests/src/android/net/wifi/WifiInfoTest.java index 0ce5d66cf4f7..ea08ea8e8333 100644 --- a/wifi/tests/src/android/net/wifi/WifiInfoTest.java +++ b/wifi/tests/src/android/net/wifi/WifiInfoTest.java @@ -38,6 +38,7 @@ public class WifiInfoTest { private static final String TEST_PACKAGE_NAME = "com.test.example"; private static final String TEST_FQDN = "test.com"; private static final String TEST_PROVIDER_NAME = "test"; + private static final int TEST_WIFI_TECHNOLOGY = WifiInfo.WIFI_TECHNOLOGY_11AC; /** * Verify parcel write/read with WifiInfo. @@ -54,6 +55,7 @@ public class WifiInfoTest { writeWifiInfo.setFQDN(TEST_FQDN); writeWifiInfo.setProviderFriendlyName(TEST_PROVIDER_NAME); writeWifiInfo.setNetworkSuggestionOrSpecifierPackageName(TEST_PACKAGE_NAME); + writeWifiInfo.setWifiTechnology(TEST_WIFI_TECHNOLOGY); Parcel parcel = Parcel.obtain(); writeWifiInfo.writeToParcel(parcel, 0); @@ -72,5 +74,6 @@ public class WifiInfoTest { assertEquals(TEST_PACKAGE_NAME, readWifiInfo.getNetworkSuggestionOrSpecifierPackageName()); assertEquals(TEST_FQDN, readWifiInfo.getPasspointFqdn()); assertEquals(TEST_PROVIDER_NAME, readWifiInfo.getPasspointProviderFriendlyName()); + assertEquals(TEST_WIFI_TECHNOLOGY, readWifiInfo.getWifiTechnology()); } } |