diff options
271 files changed, 7696 insertions, 1445 deletions
diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java index 9b1f2d07d58a..ddcc74696d94 100644 --- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java +++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java @@ -216,4 +216,11 @@ public interface AppStandbyInternal { void dumpState(String[] args, PrintWriter pw); boolean isAppIdleEnabled(); + + /** + * Returns the duration (in millis) for the window where events occurring will be + * considered as broadcast response, starting from the point when an app receives + * a broadcast. + */ + long getBroadcastResponseWindowDurationMs(); } diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index 8b2639781181..050e3df5b5e1 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -347,6 +347,14 @@ public class AppStandbyController */ boolean mLinkCrossProfileApps = ConstantsObserver.DEFAULT_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS; + + /** + * Duration (in millis) for the window where events occurring will be considered as + * broadcast response, starting from the point when an app receives a broadcast. + */ + volatile long mBroadcastResponseWindowDurationMillis = + ConstantsObserver.DEFAULT_BROADCAST_RESPONSE_WINDOW_DURATION_MS; + /** * Whether we should allow apps into the * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket or not. @@ -1774,6 +1782,10 @@ public class AppStandbyController } } + @Override + public long getBroadcastResponseWindowDurationMs() { + return mBroadcastResponseWindowDurationMillis; + } @Override public void flushToDisk() { @@ -2042,6 +2054,10 @@ public class AppStandbyController TimeUtils.formatDuration(mSystemUpdateUsageTimeoutMillis, pw); pw.println(); + pw.print(" mBroadcastResponseWindowDurationMillis="); + TimeUtils.formatDuration(mBroadcastResponseWindowDurationMillis, pw); + pw.println(); + pw.println(); pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled); pw.print(" mAllowRestrictedBucket="); @@ -2473,6 +2489,8 @@ public class AppStandbyController KEY_PREFIX_ELAPSED_TIME_THRESHOLD + "rare", KEY_PREFIX_ELAPSED_TIME_THRESHOLD + "restricted" }; + private static final String KEY_BROADCAST_RESPONSE_WINDOW_DURATION_MS = + "broadcast_response_window_timeout_ms"; public static final long DEFAULT_CHECK_IDLE_INTERVAL_MS = COMPRESS_TIME ? ONE_MINUTE : 4 * ONE_HOUR; public static final long DEFAULT_STRONG_USAGE_TIMEOUT = @@ -2502,6 +2520,8 @@ public class AppStandbyController public static final long DEFAULT_AUTO_RESTRICTED_BUCKET_DELAY_MS = COMPRESS_TIME ? ONE_MINUTE : ONE_DAY; public static final boolean DEFAULT_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS = true; + public static final long DEFAULT_BROADCAST_RESPONSE_WINDOW_DURATION_MS = + 2 * ONE_MINUTE; ConstantsObserver(Handler handler) { super(handler); @@ -2619,6 +2639,11 @@ public class AppStandbyController KEY_UNEXEMPTED_SYNC_SCHEDULED_HOLD_DURATION, DEFAULT_UNEXEMPTED_SYNC_SCHEDULED_TIMEOUT); break; + case KEY_BROADCAST_RESPONSE_WINDOW_DURATION_MS: + mBroadcastResponseWindowDurationMillis = properties.getLong( + KEY_BROADCAST_RESPONSE_WINDOW_DURATION_MS, + DEFAULT_BROADCAST_RESPONSE_WINDOW_DURATION_MS); + break; default: if (!timeThresholdsUpdated && (name.startsWith(KEY_PREFIX_SCREEN_TIME_THRESHOLD) diff --git a/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING index c5dc51cc9c24..e407e3126058 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING +++ b/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING @@ -5,7 +5,9 @@ "options": [ {"include-filter": "android.app.usage.cts.UsageStatsTest"}, {"exclude-annotation": "android.platform.test.annotations.FlakyTest"}, - {"exclude-annotation": "androidx.test.filters.FlakyTest"} + {"exclude-annotation": "androidx.test.filters.FlakyTest"}, + {"exclude-annotation": "androidx.test.filters.MediumTest"}, + {"exclude-annotation": "androidx.test.filters.LargeTest"} ] }, { diff --git a/api/api.go b/api/api.go index aa9e399e7272..17649e80e81a 100644 --- a/api/api.go +++ b/api/api.go @@ -27,7 +27,6 @@ import ( const art = "art.module.public.api" const conscrypt = "conscrypt.module.public.api" const i18n = "i18n.module.public.api" -var modules_with_only_public_scope = []string{i18n, conscrypt} // The intention behind this soong plugin is to generate a number of "merged" // API-related modules that would otherwise require a large amount of very @@ -149,8 +148,6 @@ func createMergedStubsSrcjar(ctx android.LoadHookContext, modules []string) { // This produces the same annotations.zip as framework-doc-stubs, but by using // outputs from individual modules instead of all the source code. func createMergedAnnotations(ctx android.LoadHookContext, modules []string) { - // Conscrypt and i18n currently do not enable annotations - modules = removeAll(modules, []string{conscrypt, i18n}) props := genruleProps{} props.Name = proptools.StringPtr("sdk-annotations.zip") props.Tools = []string{"merge_annotation_zips", "soong_zip"} @@ -195,11 +192,8 @@ func createMergedPublicStubs(ctx android.LoadHookContext, modules []string) { func createMergedSystemStubs(ctx android.LoadHookContext, modules []string) { props := libraryProps{} - modules_with_system_stubs := removeAll(modules, modules_with_only_public_scope) props.Name = proptools.StringPtr("all-modules-system-stubs") - props.Static_libs = append( - transformArray(modules_with_only_public_scope, "", ".stubs"), - transformArray(modules_with_system_stubs, "", ".stubs.system")...) + props.Static_libs = transformArray(modules, "", ".stubs.system") props.Sdk_version = proptools.StringPtr("module_current") props.Visibility = []string{"//frameworks/base"} ctx.CreateModule(java.LibraryFactory, &props) @@ -226,8 +220,6 @@ func createPublicStubsSourceFilegroup(ctx android.LoadHookContext, modules []str func createMergedTxts(ctx android.LoadHookContext, bootclasspath, system_server_classpath []string) { var textFiles []MergedTxtDefinition - // Two module libraries currently do not support @SystemApi so only have the public scope. - bcpWithSystemApi := removeAll(bootclasspath, modules_with_only_public_scope) tagSuffix := []string{".api.txt}", ".removed-api.txt}"} for i, f := range []string{"current.txt", "removed.txt"} { @@ -241,14 +233,14 @@ func createMergedTxts(ctx android.LoadHookContext, bootclasspath, system_server_ textFiles = append(textFiles, MergedTxtDefinition{ TxtFilename: f, BaseTxt: ":non-updatable-system-" + f, - Modules: bcpWithSystemApi, + Modules: bootclasspath, ModuleTag: "{.system" + tagSuffix[i], Scope: "system", }) textFiles = append(textFiles, MergedTxtDefinition{ TxtFilename: f, BaseTxt: ":non-updatable-module-lib-" + f, - Modules: bcpWithSystemApi, + Modules: bootclasspath, ModuleTag: "{.module-lib" + tagSuffix[i], Scope: "module-lib", }) diff --git a/cmds/incidentd/src/WorkDirectory.cpp b/cmds/incidentd/src/WorkDirectory.cpp index dd33fdfc28c3..c8d2e0ec4aca 100644 --- a/cmds/incidentd/src/WorkDirectory.cpp +++ b/cmds/incidentd/src/WorkDirectory.cpp @@ -280,7 +280,7 @@ void ReportFile::addReport(const IncidentReportArgs& args) { // Lower privacy policy (less restrictive) wins. report->set_privacy_policy(args.getPrivacyPolicy()); } - report->set_all_sections(report->all_sections() | args.all()); + report->set_all_sections(report->all_sections() || args.all()); for (int section: args.sections()) { if (!has_section(*report, section)) { report->add_section(section); diff --git a/core/api/current.txt b/core/api/current.txt index 922ad16c2a6e..fe938a9a99ec 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -37882,6 +37882,7 @@ package android.service.autofill { method public abstract void onFillRequest(@NonNull android.service.autofill.FillRequest, @NonNull android.os.CancellationSignal, @NonNull android.service.autofill.FillCallback); method public abstract void onSaveRequest(@NonNull android.service.autofill.SaveRequest, @NonNull android.service.autofill.SaveCallback); method public void onSavedDatasetsInfoRequest(@NonNull android.service.autofill.SavedDatasetsInfoCallback); + field public static final String EXTRA_FILL_RESPONSE = "android.service.autofill.extra.FILL_RESPONSE"; field public static final String SERVICE_INTERFACE = "android.service.autofill.AutofillService"; field public static final String SERVICE_META_DATA = "android.autofill"; } @@ -38037,6 +38038,7 @@ package android.service.autofill { public final class FillRequest implements android.os.Parcelable { method public int describeContents(); method @Nullable public android.os.Bundle getClientState(); + method @Nullable public android.content.IntentSender getDelayedFillIntentSender(); method @NonNull public java.util.List<android.service.autofill.FillContext> getFillContexts(); method public int getFlags(); method public int getId(); @@ -38051,6 +38053,7 @@ package android.service.autofill { method public int describeContents(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.service.autofill.FillResponse> CREATOR; + field public static final int FLAG_DELAY_FILL = 4; // 0x4 field public static final int FLAG_DISABLE_ACTIVITY_ONLY = 2; // 0x2 field public static final int FLAG_TRACK_CONTEXT_COMMITED = 1; // 0x1 } diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index 8ea1abca7a3f..df61a968ffc0 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -446,6 +446,17 @@ package android.net { } +package android.net.netstats { + + public class NetworkStatsDataMigrationUtils { + method @NonNull public static android.net.NetworkStatsCollection readPlatformCollection(@NonNull String, long) throws java.io.IOException; + field public static final String PREFIX_UID = "uid"; + field public static final String PREFIX_UID_TAG = "uid_tag"; + field public static final String PREFIX_XT = "xt"; + } + +} + package android.os { public final class BatteryStatsManager { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index ba5873766d2f..9da2abbf20cd 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -2610,6 +2610,8 @@ package android.companion.virtual { public final class VirtualDeviceParams implements android.os.Parcelable { method public int describeContents(); + method @Nullable public java.util.Set<android.content.ComponentName> getAllowedActivities(); + method @Nullable public java.util.Set<android.content.ComponentName> getBlockedActivities(); method public int getLockState(); method @NonNull public java.util.Set<android.os.UserHandle> getUsersWithMatchingAccounts(); method public void writeToParcel(@NonNull android.os.Parcel, int); @@ -2621,6 +2623,8 @@ package android.companion.virtual { public static final class VirtualDeviceParams.Builder { ctor public VirtualDeviceParams.Builder(); method @NonNull public android.companion.virtual.VirtualDeviceParams build(); + method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setAllowedActivities(@Nullable java.util.Set<android.content.ComponentName>); + method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedActivities(@Nullable java.util.Set<android.content.ComponentName>); method @NonNull @RequiresPermission(value=android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY, conditional=true) public android.companion.virtual.VirtualDeviceParams.Builder setLockState(int); method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setUsersWithMatchingAccounts(@NonNull java.util.Set<android.os.UserHandle>); } @@ -2688,7 +2692,7 @@ package android.content { field public static final String BATTERY_STATS_SERVICE = "batterystats"; field @Deprecated public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 1048576; // 0x100000 field public static final int BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND = 262144; // 0x40000 - field public static final String CLOUDSEARCH_SERVICE = "cloudsearch_service"; + field public static final String CLOUDSEARCH_SERVICE = "cloudsearch"; field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions"; field public static final String CONTEXTHUB_SERVICE = "contexthub"; field public static final String ETHERNET_SERVICE = "ethernet"; diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt index dbb427467a68..e17a9bb1512c 100644 --- a/core/api/system-lint-baseline.txt +++ b/core/api/system-lint-baseline.txt @@ -294,7 +294,6 @@ SamShouldBeLast: android.webkit.WebChromeClient#onShowFileChooser(android.webkit ServiceName: android.content.Context#CLOUDSEARCH_SERVICE: - Inconsistent service value; expected `cloudsearch`, was `cloudsearch_service` (Note: Do not change the name of already released services, which will break tools using `adb shell dumpsys`. Instead add `@SuppressLint("ServiceName"))` UserHandleName: android.app.search.SearchAction.Builder#setUserHandle(android.os.UserHandle): diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 39e12f40a79d..c39394bb1e4f 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -2,6 +2,7 @@ package android { public static final class Manifest.permission { + field public static final String ACCESS_KEYGUARD_SECURE_STORAGE = "android.permission.ACCESS_KEYGUARD_SECURE_STORAGE"; field public static final String ACCESS_NOTIFICATIONS = "android.permission.ACCESS_NOTIFICATIONS"; field public static final String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING"; field public static final String APPROVE_INCIDENT_REPORTS = "android.permission.APPROVE_INCIDENT_REPORTS"; @@ -287,8 +288,8 @@ package android.app { } public class KeyguardManager { - method @RequiresPermission(anyOf={android.Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS, "android.permission.ACCESS_KEYGUARD_SECURE_STORAGE"}) public boolean checkLock(int, @Nullable byte[]); - method @RequiresPermission(anyOf={android.Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS, "android.permission.ACCESS_KEYGUARD_SECURE_STORAGE"}) public boolean setLock(int, @Nullable byte[], int, @Nullable byte[]); + method @RequiresPermission(anyOf={android.Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS, android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE}) public boolean checkLock(int, @Nullable byte[]); + method @RequiresPermission(anyOf={android.Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS, android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE}) public boolean setLock(int, @Nullable byte[], int, @Nullable byte[]); } public class LocaleManager { @@ -462,6 +463,7 @@ package android.app.admin { method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceSecurityLogs(); method public void forceUpdateUserSetupComplete(int); method @NonNull public java.util.Set<java.lang.String> getDefaultCrossProfilePackages(); + method public int getDeviceOwnerType(@NonNull android.content.ComponentName); method @NonNull public java.util.Set<java.lang.String> getDisallowedSystemApps(@NonNull android.content.ComponentName, int, @NonNull String); method public long getLastBugReportRequestTime(); method public long getLastNetworkLogRetrievalTime(); @@ -477,6 +479,7 @@ package android.app.admin { method @RequiresPermission(allOf={android.Manifest.permission.MANAGE_DEVICE_ADMINS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public void setActiveAdmin(@NonNull android.content.ComponentName, boolean, int); method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean setDeviceOwner(@NonNull android.content.ComponentName, @Nullable String, int); method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean setDeviceOwnerOnly(@NonNull android.content.ComponentName, @Nullable String, int); + method public void setDeviceOwnerType(@NonNull android.content.ComponentName, int); method @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public void setNextOperationSafety(int, int); field public static final String ACTION_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED"; field public static final String ACTION_DEVICE_POLICY_CONSTANTS_CHANGED = "android.app.action.DEVICE_POLICY_CONSTANTS_CHANGED"; @@ -582,6 +585,15 @@ package android.app.prediction { } +package android.app.trust { + + public class TrustManager { + method @RequiresPermission(android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE) public void enableTrustAgentForUserForTest(@NonNull android.content.ComponentName, int); + method @RequiresPermission(android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE) public void reportUserRequestedUnlock(int); + } + +} + package android.app.usage { public class NetworkStatsManager { @@ -2362,6 +2374,14 @@ package android.service.quicksettings { } +package android.service.trust { + + public class TrustAgentService extends android.app.Service { + method public void onUserRequestedUnlock(); + } + +} + package android.service.voice { public class AlwaysOnHotwordDetector implements android.service.voice.HotwordDetector { diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 3b2176ee10a2..876e4014ba45 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -200,6 +200,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IVoiceInteractor; import com.android.internal.content.ReferrerIntent; +import com.android.internal.os.BinderCallsStats; import com.android.internal.os.BinderInternal; import com.android.internal.os.RuntimeInit; import com.android.internal.os.SomeArgs; @@ -7812,6 +7813,8 @@ public final class ActivityThread extends ClientTransactionHandler MediaFrameworkPlatformInitializer.setMediaServiceManager(new MediaServiceManager()); MediaFrameworkInitializer.setMediaServiceManager(new MediaServiceManager()); BluetoothFrameworkInitializer.setBluetoothServiceManager(new BluetoothServiceManager()); + BluetoothFrameworkInitializer.setBinderCallsStatsInitializer(context -> { + BinderCallsStats.startForBluetooth(context); }); } private void purgePendingResources() { diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 3960f4e1518e..9ac4030c73a7 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -454,6 +454,52 @@ public class DevicePolicyManager { * <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}, optional</li> * </ul> * + * <p>Once the device admin app is set as the device owner, the following APIs are available for + * managing polices on the device: + * <ul> + * <li>{@link #isDeviceManaged()}</li> + * <li>{@link #isUninstallBlocked(ComponentName, String)}</li> + * <li>{@link #setUninstallBlocked(ComponentName, String, boolean)}</li> + * <li>{@link #setUserControlDisabledPackages(ComponentName, List)}</li> + * <li>{@link #getUserControlDisabledPackages(ComponentName)}</li> + * <li>{@link #setOrganizationName(ComponentName, CharSequence)}</li> + * <li>{@link #setShortSupportMessage(ComponentName, CharSequence)}</li> + * <li>{@link #isBackupServiceEnabled(ComponentName)}</li> + * <li>{@link #setBackupServiceEnabled(ComponentName, boolean)}</li> + * <li>{@link #isLockTaskPermitted(String)}</li> + * <li>{@link #setLockTaskFeatures(ComponentName, int)}, where the following lock task features + * can be set (otherwise a {@link SecurityException} will be thrown):</li> + * <ul> + * <li>{@link #LOCK_TASK_FEATURE_SYSTEM_INFO}</li> + * <li>{@link #LOCK_TASK_FEATURE_KEYGUARD}</li> + * <li>{@link #LOCK_TASK_FEATURE_HOME}</li> + * <li>{@link #LOCK_TASK_FEATURE_GLOBAL_ACTIONS}</li> + * <li>{@link #LOCK_TASK_FEATURE_NOTIFICATIONS}</li> + * </ul> + * <li>{@link #setLockTaskPackages(ComponentName, String[])}</li> + * <li>{@link #addPersistentPreferredActivity(ComponentName, IntentFilter, ComponentName)}</li> + * <li>{@link #clearPackagePersistentPreferredActivities(ComponentName, String)} </li> + * <li>{@link #wipeData(int)}</li> + * <li>{@link #isDeviceOwnerApp(String)}</li> + * <li>{@link #clearDeviceOwnerApp(String)}</li> + * <li>{@link #setPermissionGrantState(ComponentName, String, String, int)}, where + * {@link permission#READ_PHONE_STATE} is the <b>only</b> permission that can be + * {@link #PERMISSION_GRANT_STATE_GRANTED}, {@link #PERMISSION_GRANT_STATE_DENIED}, or + * {@link #PERMISSION_GRANT_STATE_DEFAULT} and can <b>only</b> be applied to the device admin + * app (otherwise a {@link SecurityException} will be thrown)</li> + * <li>{@link #addUserRestriction(ComponentName, String)}, where the following user restrictions + * are permitted (otherwise a {@link SecurityException} will be thrown):</li> + * <ul> + * <li>{@link UserManager#DISALLOW_ADD_USER}</li> + * <li>{@link UserManager#DISALLOW_DEBUGGING_FEATURES}</li> + * <li>{@link UserManager#DISALLOW_INSTALL_UNKNOWN_SOURCES}</li> + * <li>{@link UserManager#DISALLOW_SAFE_BOOT}</li> + * <li>{@link UserManager#DISALLOW_CONFIG_DATE_TIME}</li> + * <li>{@link UserManager#DISALLOW_OUTGOING_CALLS}</li> + * </ul> + * <li>{@link #clearUserRestriction(ComponentName, String)}</li> + * </ul> + * * @hide */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) @@ -14577,6 +14623,7 @@ public class DevicePolicyManager { * * @hide */ + @TestApi public void setDeviceOwnerType(@NonNull ComponentName admin, @DeviceOwnerType int deviceOwnerType) { throwIfParentInstance("setDeviceOwnerType"); @@ -14600,6 +14647,7 @@ public class DevicePolicyManager { * * @hide */ + @TestApi @DeviceOwnerType public int getDeviceOwnerType(@NonNull ComponentName admin) { throwIfParentInstance("getDeviceOwnerType"); diff --git a/core/java/android/app/trust/ITrustManager.aidl b/core/java/android/app/trust/ITrustManager.aidl index edabccf23c2c..7956a35c7b3d 100644 --- a/core/java/android/app/trust/ITrustManager.aidl +++ b/core/java/android/app/trust/ITrustManager.aidl @@ -17,6 +17,7 @@ package android.app.trust; import android.app.trust.ITrustListener; +import android.content.ComponentName; import android.hardware.biometrics.BiometricSourceType; /** @@ -29,6 +30,7 @@ interface ITrustManager { void reportUserRequestedUnlock(int userId); void reportUnlockLockout(int timeoutMs, int userId); void reportEnabledTrustAgentsChanged(int userId); + void enableTrustAgentForUserForTest(in ComponentName componentName, int userId); void registerTrustListener(in ITrustListener trustListener); void unregisterTrustListener(in ITrustListener trustListener); void reportKeyguardShowingChanged(); diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java index 70b7de0767e4..fba2d3e03769 100644 --- a/core/java/android/app/trust/TrustManager.java +++ b/core/java/android/app/trust/TrustManager.java @@ -16,10 +16,14 @@ package android.app.trust; -import android.Manifest; +import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE; + +import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemService; +import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; +import android.content.ComponentName; import android.content.Context; import android.hardware.biometrics.BiometricSourceType; import android.os.Handler; @@ -33,9 +37,17 @@ import java.util.ArrayList; import java.util.List; /** - * See {@link com.android.server.trust.TrustManagerService} + * Interface to the system service managing trust. + * + * <p>This class is for internal use only. This class is marked {@code @TestApi} to + * enable testing the trust system including {@link android.service.trust.TrustAgentService}. + * Methods which are currently not used in tests are marked @hide. + * + * @see com.android.server.trust.TrustManagerService + * * @hide */ +@TestApi @SystemService(Context.TRUST_SERVICE) public class TrustManager { @@ -51,7 +63,8 @@ public class TrustManager { private final ITrustManager mService; private final ArrayMap<TrustListener, ITrustListener> mTrustListeners; - public TrustManager(IBinder b) { + /** @hide */ + public TrustManager(@NonNull IBinder b) { mService = ITrustManager.Stub.asInterface(b); mTrustListeners = new ArrayMap<TrustListener, ITrustListener>(); } @@ -62,8 +75,10 @@ public class TrustManager { * * @param userId The id for the user to be locked/unlocked. * @param locked The value for that user's locked state. + * + * @hide */ - @RequiresPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE) + @RequiresPermission(ACCESS_KEYGUARD_SECURE_STORAGE) public void setDeviceLockedForUser(int userId, boolean locked) { try { mService.setDeviceLockedForUser(userId, locked); @@ -78,8 +93,11 @@ public class TrustManager { * @param successful if true, the unlock attempt was successful. * * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission. + * + * @hide */ @UnsupportedAppUsage + @RequiresPermission(ACCESS_KEYGUARD_SECURE_STORAGE) public void reportUnlockAttempt(boolean successful, int userId) { try { mService.reportUnlockAttempt(successful, userId); @@ -93,6 +111,7 @@ public class TrustManager { * * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission. */ + @RequiresPermission(ACCESS_KEYGUARD_SECURE_STORAGE) public void reportUserRequestedUnlock(int userId) { try { mService.reportUserRequestedUnlock(userId); @@ -112,7 +131,10 @@ public class TrustManager { * attempt to unlock the device again. * * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission. + * + * @hide */ + @RequiresPermission(ACCESS_KEYGUARD_SECURE_STORAGE) public void reportUnlockLockout(int timeoutMs, int userId) { try { mService.reportUnlockLockout(timeoutMs, userId); @@ -125,7 +147,10 @@ public class TrustManager { * Reports that the list of enabled trust agents changed for user {@param userId}. * * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission. + * + * @hide */ + @RequiresPermission(ACCESS_KEYGUARD_SECURE_STORAGE) public void reportEnabledTrustAgentsChanged(int userId) { try { mService.reportEnabledTrustAgentsChanged(userId); @@ -135,10 +160,33 @@ public class TrustManager { } /** + * Enables a trust agent. + * + * <p>The agent is specified by {@code componentName} and must be a subclass of + * {@link android.service.trust.TrustAgentService} and otherwise meet the requirements + * to be a trust agent. + * + * <p>This method can only be used in tests. + * + * @param componentName the trust agent to enable + */ + @RequiresPermission(ACCESS_KEYGUARD_SECURE_STORAGE) + public void enableTrustAgentForUserForTest(@NonNull ComponentName componentName, int userId) { + try { + mService.enableTrustAgentForUserForTest(componentName, userId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Reports that the visibility of the keyguard has changed. * * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission. + * + * @hide */ + @RequiresPermission(ACCESS_KEYGUARD_SECURE_STORAGE) public void reportKeyguardShowingChanged() { try { mService.reportKeyguardShowingChanged(); @@ -151,7 +199,10 @@ public class TrustManager { * Registers a listener for trust events. * * Requires the {@link android.Manifest.permission#TRUST_LISTENER} permission. + * + * @hide */ + @RequiresPermission(android.Manifest.permission.TRUST_LISTENER) public void registerTrustListener(final TrustListener trustListener) { try { ITrustListener.Stub iTrustListener = new ITrustListener.Stub() { @@ -192,7 +243,10 @@ public class TrustManager { * Unregisters a listener for trust events. * * Requires the {@link android.Manifest.permission#TRUST_LISTENER} permission. + * + * @hide */ + @RequiresPermission(android.Manifest.permission.TRUST_LISTENER) public void unregisterTrustListener(final TrustListener trustListener) { ITrustListener iTrustListener = mTrustListeners.remove(trustListener); if (iTrustListener != null) { @@ -207,6 +261,8 @@ public class TrustManager { /** * @return whether {@param userId} has enabled and configured trust agents. Ignores short-term * unavailability of trust due to {@link LockPatternUtils.StrongAuthTracker}. + * + * @hide */ @RequiresPermission(android.Manifest.permission.TRUST_LISTENER) public boolean isTrustUsuallyManaged(int userId) { @@ -223,8 +279,10 @@ public class TrustManager { * can be skipped. * * @param userId + * + * @hide */ - @RequiresPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE) + @RequiresPermission(ACCESS_KEYGUARD_SECURE_STORAGE) public void unlockedByBiometricForUser(int userId, BiometricSourceType source) { try { mService.unlockedByBiometricForUser(userId, source); @@ -235,8 +293,10 @@ public class TrustManager { /** * Clears authentication by the specified biometric type for all users. + * + * @hide */ - @RequiresPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE) + @RequiresPermission(ACCESS_KEYGUARD_SECURE_STORAGE) public void clearAllBiometricRecognized(BiometricSourceType source, int unlockedUser) { try { mService.clearAllBiometricRecognized(source, unlockedUser); @@ -264,6 +324,7 @@ public class TrustManager { } }; + /** @hide */ public interface TrustListener { /** diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java index 2ddfeb4c8ab5..1d0f7c091807 100644 --- a/core/java/android/companion/virtual/VirtualDeviceParams.java +++ b/core/java/android/companion/virtual/VirtualDeviceParams.java @@ -22,6 +22,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.content.ComponentName; import android.os.Parcel; @@ -106,11 +107,13 @@ public final class VirtualDeviceParams implements Parcelable { } /** - * Returns the set of activities allowed to be streamed, or {@code null} if this is not set. + * Returns the set of activities allowed to be streamed, or {@code null} if all activities are + * allowed, except the ones explicitly blocked. * * @see Builder#setAllowedActivities(Set) - * @hide // TODO(b/194949534): Unhide this API */ + // Null and empty have different semantics - Null allows all activities to be streamed + @SuppressLint("NullableCollection") @Nullable public Set<ComponentName> getAllowedActivities() { if (mAllowedActivities == null) { @@ -120,12 +123,13 @@ public final class VirtualDeviceParams implements Parcelable { } /** - * Returns the set of activities that are blocked from streaming, or {@code null} if this is not - * set. + * Returns the set of activities that are blocked from streaming, or {@code null} to indicate + * that all activities in {@link #getAllowedActivities} are allowed. * * @see Builder#setBlockedActivities(Set) - * @hide // TODO(b/194949534): Unhide this API */ + // Allowing null to enforce that at most one of allowed / blocked activities can be non-null + @SuppressLint("NullableCollection") @Nullable public Set<ComponentName> getBlockedActivities() { if (mBlockedActivities == null) { @@ -255,8 +259,10 @@ public final class VirtualDeviceParams implements Parcelable { * * @param allowedActivities A set of activity {@link ComponentName} allowed to be launched * in the virtual device. - * @hide // TODO(b/194949534): Unhide this API */ + // Null and empty have different semantics - Null allows all activities to be streamed + @SuppressLint("NullableCollection") + @NonNull public Builder setAllowedActivities(@Nullable Set<ComponentName> allowedActivities) { if (mBlockedActivities != null && allowedActivities != null) { throw new IllegalArgumentException( @@ -279,8 +285,10 @@ public final class VirtualDeviceParams implements Parcelable { * * @param blockedActivities A set of {@link ComponentName} to be blocked launching from * virtual device. - * @hide // TODO(b/194949534): Unhide this API */ + // Allowing null to enforce that at most one of allowed / blocked activities can be non-null + @SuppressLint("NullableCollection") + @NonNull public Builder setBlockedActivities(@Nullable Set<ComponentName> blockedActivities) { if (mAllowedActivities != null && blockedActivities != null) { throw new IllegalArgumentException( diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 4b4e00855ac1..0b8a8a23aee4 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -4987,10 +4987,8 @@ public abstract class Context { * @hide * @see #getSystemService(String) */ - // TODO(216507592): Change cloudsearch_service to cloudsearch. @SystemApi - @SuppressLint("ServiceName") - public static final String CLOUDSEARCH_SERVICE = "cloudsearch_service"; + public static final String CLOUDSEARCH_SERVICE = "cloudsearch"; /** * Use with {@link #getSystemService(String)} to access the diff --git a/core/java/android/hardware/biometrics/IBiometricContextListener.aidl b/core/java/android/hardware/biometrics/IBiometricContextListener.aidl new file mode 100644 index 000000000000..55cab52fc4f7 --- /dev/null +++ b/core/java/android/hardware/biometrics/IBiometricContextListener.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hardware.biometrics; + +/** + * A secondary communication channel from AuthController back to BiometricService for + * events that are not associated with an autentication session. See + * {@link IBiometricSysuiReceiver} for events associated with a session. + * + * @hide + */ +oneway interface IBiometricContextListener { + void onDozeChanged(boolean isDozing); +} diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl index 0304815ef8fe..27403ec4fe59 100644 --- a/core/java/android/hardware/input/IInputManager.aidl +++ b/core/java/android/hardware/input/IInputManager.aidl @@ -122,9 +122,9 @@ interface IInputManager { void removePortAssociation(in String inputPort); // Add a runtime association between the input device and display. - void addUniqueIdAssociation(in String inputDeviceName, in String displayUniqueId); + void addUniqueIdAssociation(in String inputPort, in String displayUniqueId); // Remove the runtime association between the input device and display. - void removeUniqueIdAssociation(in String inputDeviceName); + void removeUniqueIdAssociation(in String inputPort); InputSensorInfo[] getSensorList(int deviceId); diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index cbc837393b6b..979e9dd6a1f6 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -1359,19 +1359,18 @@ public final class InputManager { } /** - * Add a runtime association between the input device name and display, by unique id. Input - * device names are expected to be unique. - * @param inputDeviceName The name of the input device. + * Add a runtime association between the input port and display, by unique id. Input ports are + * expected to be unique. + * @param inputPort The port of the input device. * @param displayUniqueId The unique id of the associated display. * <p> * Requires {@link android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}. * </p> * @hide */ - public void addUniqueIdAssociation(@NonNull String inputDeviceName, - @NonNull String displayUniqueId) { + public void addUniqueIdAssociation(@NonNull String inputPort, @NonNull String displayUniqueId) { try { - mIm.addUniqueIdAssociation(inputDeviceName, displayUniqueId); + mIm.addUniqueIdAssociation(inputPort, displayUniqueId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1379,15 +1378,15 @@ public final class InputManager { /** * Removes a runtime association between the input device and display. - * @param inputDeviceName The name of the input device. + * @param inputPort The port of the input device. * <p> * Requires {@link android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY}. * </p> * @hide */ - public void removeUniqueIdAssociation(@NonNull String inputDeviceName) { + public void removeUniqueIdAssociation(@NonNull String inputPort) { try { - mIm.removeUniqueIdAssociation(inputDeviceName); + mIm.removeUniqueIdAssociation(inputPort); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java index 41642e7a9fce..af57f793bf73 100644 --- a/core/java/android/inputmethodservice/IInputMethodWrapper.java +++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java @@ -70,6 +70,7 @@ class IInputMethodWrapper extends IInputMethod.Stub private static final int DO_SET_INPUT_CONTEXT = 20; private static final int DO_UNSET_INPUT_CONTEXT = 30; private static final int DO_START_INPUT = 32; + private static final int DO_ON_SHOULD_SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN_CHANGED = 35; private static final int DO_CREATE_SESSION = 40; private static final int DO_SET_SESSION_ENABLED = 45; private static final int DO_SHOW_SOFT_INPUT = 60; @@ -175,7 +176,7 @@ class IInputMethodWrapper extends IInputMethod.Stub try { inputMethod.initializeInternal((IBinder) args.arg1, (IInputMethodPrivilegedOperations) args.arg2, msg.arg1, - (boolean) args.arg3); + (boolean) args.arg3, msg.arg2 != 0); } finally { args.recycle(); } @@ -195,14 +196,22 @@ class IInputMethodWrapper extends IInputMethod.Stub final EditorInfo info = (EditorInfo) args.arg3; final CancellationGroup cancellationGroup = (CancellationGroup) args.arg4; final boolean restarting = args.argi5 == 1; + final boolean shouldShowImeSwitcherWhenImeIsShown = args.argi6 != 0; final InputConnection ic = inputContext != null ? new RemoteInputConnection(mTarget, inputContext, cancellationGroup) : null; info.makeCompatible(mTargetSdkVersion); - inputMethod.dispatchStartInputWithToken(ic, info, restarting, startInputToken); + inputMethod.dispatchStartInputWithToken(ic, info, restarting, startInputToken, + shouldShowImeSwitcherWhenImeIsShown); args.recycle(); return; } + case DO_ON_SHOULD_SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN_CHANGED: { + final boolean shouldShowImeSwitcherWhenImeIsShown = msg.arg1 != 0; + inputMethod.onShouldShowImeSwitcherWhenImeIsShownChanged( + shouldShowImeSwitcherWhenImeIsShown); + return; + } case DO_CREATE_SESSION: { SomeArgs args = (SomeArgs)msg.obj; inputMethod.createSession(new InputMethodSessionCallbackWrapper( @@ -291,10 +300,11 @@ class IInputMethodWrapper extends IInputMethod.Stub @BinderThread @Override public void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privOps, - int configChanges, boolean stylusHwSupported) { - mCaller.executeOrSendMessage( - mCaller.obtainMessageIOOO( - DO_INITIALIZE_INTERNAL, configChanges, token, privOps, stylusHwSupported)); + int configChanges, boolean stylusHwSupported, + boolean shouldShowImeSwitcherWhenImeIsShown) { + mCaller.executeOrSendMessage(mCaller.obtainMessageIIOOO(DO_INITIALIZE_INTERNAL, + configChanges, shouldShowImeSwitcherWhenImeIsShown ? 1 : 0, token, privOps, + stylusHwSupported)); } @BinderThread @@ -334,13 +344,23 @@ class IInputMethodWrapper extends IInputMethod.Stub @BinderThread @Override public void startInput(IBinder startInputToken, IInputContext inputContext, - EditorInfo attribute, boolean restarting) { + EditorInfo attribute, boolean restarting, boolean shouldShowImeSwitcherWhenImeIsShown) { if (mCancellationGroup == null) { Log.e(TAG, "startInput must be called after bindInput."); mCancellationGroup = new CancellationGroup(); } mCaller.executeOrSendMessage(mCaller.obtainMessageOOOOII(DO_START_INPUT, startInputToken, - inputContext, attribute, mCancellationGroup, restarting ? 1 : 0, 0 /* unused */)); + inputContext, attribute, mCancellationGroup, restarting ? 1 : 0, + shouldShowImeSwitcherWhenImeIsShown ? 1 : 0)); + } + + @BinderThread + @Override + public void onShouldShowImeSwitcherWhenImeIsShownChanged( + boolean shouldShowImeSwitcherWhenImeIsShown) { + mCaller.executeOrSendMessage(mCaller.obtainMessageI( + DO_ON_SHOULD_SHOW_IME_SWITCHER_WHEN_IME_IS_SHOWN_CHANGED, + shouldShowImeSwitcherWhenImeIsShown ? 1 : 0)); } @BinderThread diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 14f92fbb4194..f55c41594389 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -658,7 +658,7 @@ public class InputMethodService extends AbstractInputMethodService { @Override public final void initializeInternal(@NonNull IBinder token, IInputMethodPrivilegedOperations privilegedOperations, int configChanges, - boolean stylusHwSupported) { + boolean stylusHwSupported, boolean shouldShowImeSwitcherWhenImeIsShown) { if (mDestroyed) { Log.i(TAG, "The InputMethodService has already onDestroyed()." + "Ignore the initialization."); @@ -671,6 +671,8 @@ public class InputMethodService extends AbstractInputMethodService { if (stylusHwSupported) { mInkWindow = new InkWindow(mWindow.getContext()); } + mNavigationBarController.setShouldShowImeSwitcherWhenImeIsShown( + shouldShowImeSwitcherWhenImeIsShown); attachToken(token); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } @@ -780,9 +782,10 @@ public class InputMethodService extends AbstractInputMethodService { @Override public final void dispatchStartInputWithToken(@Nullable InputConnection inputConnection, @NonNull EditorInfo editorInfo, boolean restarting, - @NonNull IBinder startInputToken) { + @NonNull IBinder startInputToken, boolean shouldShowImeSwitcherWhenImeIsShown) { mPrivOps.reportStartInputAsync(startInputToken); - + mNavigationBarController.setShouldShowImeSwitcherWhenImeIsShown( + shouldShowImeSwitcherWhenImeIsShown); if (restarting) { restartInput(inputConnection, editorInfo); } else { @@ -796,6 +799,18 @@ public class InputMethodService extends AbstractInputMethodService { */ @MainThread @Override + public void onShouldShowImeSwitcherWhenImeIsShownChanged( + boolean shouldShowImeSwitcherWhenImeIsShown) { + mNavigationBarController.setShouldShowImeSwitcherWhenImeIsShown( + shouldShowImeSwitcherWhenImeIsShown); + } + + /** + * {@inheritDoc} + * @hide + */ + @MainThread + @Override public void hideSoftInputWithToken(int flags, ResultReceiver resultReceiver, IBinder hideInputToken) { mSystemCallingHideSoftInput = true; diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java index 2484cf079c56..508172d13aa3 100644 --- a/core/java/android/inputmethodservice/NavigationBarController.java +++ b/core/java/android/inputmethodservice/NavigationBarController.java @@ -19,6 +19,7 @@ package android.inputmethodservice; import static android.content.Intent.ACTION_OVERLAY_CHANGED; import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; +import android.animation.ValueAnimator; import android.annotation.FloatRange; import android.annotation.NonNull; import android.annotation.Nullable; @@ -44,6 +45,8 @@ import android.view.Window; import android.view.WindowInsets; import android.view.WindowInsetsController.Appearance; import android.view.WindowManagerPolicyConstants; +import android.view.animation.Interpolator; +import android.view.animation.PathInterpolator; import android.widget.FrameLayout; import java.util.Objects; @@ -71,6 +74,10 @@ final class NavigationBarController { default void onDestroy() { } + default void setShouldShowImeSwitcherWhenImeIsShown( + boolean shouldShowImeSwitcherWhenImeIsShown) { + } + default void onSystemBarAppearanceChanged(@Appearance int appearance) { } @@ -106,6 +113,10 @@ final class NavigationBarController { mImpl.onDestroy(); } + void setShouldShowImeSwitcherWhenImeIsShown(boolean shouldShowImeSwitcherWhenImeIsShown) { + mImpl.setShouldShowImeSwitcherWhenImeIsShown(shouldShowImeSwitcherWhenImeIsShown); + } + void onSystemBarAppearanceChanged(@Appearance int appearance) { mImpl.onSystemBarAppearanceChanged(appearance); } @@ -115,6 +126,12 @@ final class NavigationBarController { } private static final class Impl implements Callback { + private static final int DEFAULT_COLOR_ADAPT_TRANSITION_TIME = 1700; + + // Copied from com.android.systemui.animation.Interpolators#LEGACY_DECELERATE + private static final Interpolator LEGACY_DECELERATE = + new PathInterpolator(0f, 0f, 0.2f, 1f); + @NonNull private final InputMethodService mService; @@ -130,9 +147,17 @@ final class NavigationBarController { @Nullable private BroadcastReceiver mSystemOverlayChangedReceiver; + private boolean mShouldShowImeSwitcherWhenImeIsShown; + @Appearance private int mAppearance; + @FloatRange(from = 0.0f, to = 1.0f) + private float mDarkIntensity; + + @Nullable + private ValueAnimator mTintAnimator; + Impl(@NonNull InputMethodService inputMethodService) { mService = inputMethodService; } @@ -190,7 +215,9 @@ final class NavigationBarController { // TODO(b/213337792): Support InputMethodService#setBackDisposition(). // TODO(b/213337792): Set NAVIGATION_HINT_IME_SHOWN only when necessary. final int hints = StatusBarManager.NAVIGATION_HINT_BACK_ALT - | StatusBarManager.NAVIGATION_HINT_IME_SHOWN; + | (mShouldShowImeSwitcherWhenImeIsShown + ? StatusBarManager.NAVIGATION_HINT_IME_SHOWN + : 0); navigationBarView.setNavigationIconHints(hints); } } else { @@ -368,6 +395,10 @@ final class NavigationBarController { if (mDestroyed) { return; } + if (mTintAnimator != null) { + mTintAnimator.cancel(); + mTintAnimator = null; + } if (mSystemOverlayChangedReceiver != null) { mService.unregisterReceiver(mSystemOverlayChangedReceiver); mSystemOverlayChangedReceiver = null; @@ -404,6 +435,31 @@ final class NavigationBarController { } @Override + public void setShouldShowImeSwitcherWhenImeIsShown( + boolean shouldShowImeSwitcherWhenImeIsShown) { + if (mDestroyed) { + return; + } + if (mShouldShowImeSwitcherWhenImeIsShown == shouldShowImeSwitcherWhenImeIsShown) { + return; + } + mShouldShowImeSwitcherWhenImeIsShown = shouldShowImeSwitcherWhenImeIsShown; + + if (mNavigationBarFrame == null) { + return; + } + final NavigationBarView navigationBarView = + mNavigationBarFrame.findViewByPredicate(NavigationBarView.class::isInstance); + if (navigationBarView == null) { + return; + } + final int hints = StatusBarManager.NAVIGATION_HINT_BACK_ALT + | (shouldShowImeSwitcherWhenImeIsShown + ? StatusBarManager.NAVIGATION_HINT_IME_SHOWN : 0); + navigationBarView.setNavigationIconHints(hints); + } + + @Override public void onSystemBarAppearanceChanged(@Appearance int appearance) { if (mDestroyed) { return; @@ -416,10 +472,24 @@ final class NavigationBarController { } final float targetDarkIntensity = calculateTargetDarkIntensity(mAppearance); - setIconTintInternal(targetDarkIntensity); + + if (mTintAnimator != null) { + mTintAnimator.cancel(); + } + mTintAnimator = ValueAnimator.ofFloat(mDarkIntensity, targetDarkIntensity); + mTintAnimator.addUpdateListener( + animation -> setIconTintInternal((Float) animation.getAnimatedValue())); + mTintAnimator.setDuration(DEFAULT_COLOR_ADAPT_TRANSITION_TIME); + mTintAnimator.setStartDelay(0); + mTintAnimator.setInterpolator(LEGACY_DECELERATE); + mTintAnimator.start(); } private void setIconTintInternal(float darkIntensity) { + mDarkIntensity = darkIntensity; + if (mNavigationBarFrame == null) { + return; + } final NavigationBarView navigationBarView = mNavigationBarFrame.findViewByPredicate(NavigationBarView.class::isInstance); if (navigationBarView == null) { @@ -438,7 +508,9 @@ final class NavigationBarController { public String toDebugString() { return "{mRenderGesturalNavButtons=" + mRenderGesturalNavButtons + " mNavigationBarFrame=" + mNavigationBarFrame + + " mShouldShowImeSwitcherWhenImeIsShown" + mShouldShowImeSwitcherWhenImeIsShown + " mAppearance=0x" + Integer.toHexString(mAppearance) + + " mDarkIntensity=" + mDarkIntensity + "}"; } } diff --git a/core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java b/core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java index 24c22a99b78d..9772bde94ac9 100644 --- a/core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java +++ b/core/java/android/net/netstats/NetworkStatsDataMigrationUtils.java @@ -16,9 +16,7 @@ package android.net.netstats; -import static android.app.usage.NetworkStatsManager.PREFIX_UID; -import static android.app.usage.NetworkStatsManager.PREFIX_UID_TAG; -import static android.app.usage.NetworkStatsManager.PREFIX_XT; +import static android.annotation.SystemApi.Client.MODULE_LIBRARIES; import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.ConnectivityManager.TYPE_MOBILE_DUN; import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI; @@ -28,6 +26,7 @@ import static android.net.NetworkStats.SET_DEFAULT; import static android.net.NetworkStats.TAG_NONE; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.net.NetworkIdentity; import android.net.NetworkStatsCollection; import android.net.NetworkStatsHistory; @@ -54,12 +53,27 @@ import java.util.HashSet; import java.util.Set; /** - * Helper class to read old version of persistent network statistics, the implementation is - * intended to be modified by OEM partners to accommodate their custom changes. + * Helper class to read old version of persistent network statistics. + * + * The implementation is intended to be modified by OEM partners to + * accommodate their custom changes. + * * @hide */ -// @SystemApi(client = MODULE_LIBRARIES) +@SystemApi(client = MODULE_LIBRARIES) public class NetworkStatsDataMigrationUtils { + /** + * Prefix of the files which are used to store per network interface statistics. + */ + public static final String PREFIX_XT = "xt"; + /** + * Prefix of the files which are used to store per uid statistics. + */ + public static final String PREFIX_UID = "uid"; + /** + * Prefix of the files which are used to store per uid tagged traffic statistics. + */ + public static final String PREFIX_UID_TAG = "uid_tag"; private static final HashMap<String, String> sPrefixLegacyFileNameMap = new HashMap<String, String>() {{ @@ -146,17 +160,51 @@ public class NetworkStatsDataMigrationUtils { } /** - * Read legacy persisted network stats from disk. This function provides a default - * implementation to read persisted network stats from two different locations. - * And this is intended to be modified by OEM to read from custom file format or - * locations if necessary. + * Read legacy persisted network stats from disk. + * + * This function provides the implementation to read legacy network stats + * from disk. It is used for migration of legacy network stats into the + * stats provided by the Connectivity module. + * This function needs to know about the previous format(s) of the network + * stats data that might be stored on this device so it can be read and + * conserved upon upgrade to Android 13 or above. + * + * This function will be called multiple times sequentially, all on the + * same thread, and will not be called multiple times concurrently. This + * function is expected to do a substantial amount of disk access, and + * doesn't need to return particularly fast, but the first boot after + * an upgrade to Android 13+ will be held until migration is done. As + * migration is only necessary once, after the first boot following the + * upgrade, this delay is not incurred. + * + * If this function fails in any way, it should throw an exception. If this + * happens, the system can't know about the data that was stored in the + * legacy files, but it will still count data usage happening on this + * session. On the next boot, the system will try migration again, and + * merge the returned data with the data used with the previous session. + * The system will only try the migration up to three (3) times. The remaining + * count is stored in the netstats_import_legacy_file_needed device config. The + * legacy data is never deleted by the mainline module to avoid any possible + * data loss. + * + * It is possible to set the netstats_import_legacy_file_needed device config + * to any positive integer to force the module to perform the migration. This + * can be achieved by calling the following command before rebooting : + * adb shell device_config put connectivity netstats_import_legacy_file_needed 1 + * + * The AOSP implementation provides code to read persisted network stats as + * they were written by AOSP prior to Android 13. + * OEMs who have used the AOSP implementation of persisting network stats + * to disk don't need to change anything. + * OEM that had modifications to this format should modify this function + * to read from their custom file format or locations if necessary. * * @param prefix Type of data which is being read by the service. * @param bucketDuration Duration of the buckets of the object, in milliseconds. * @return {@link NetworkStatsCollection} instance. */ @NonNull - public static NetworkStatsCollection readPlatformCollectionLocked( + public static NetworkStatsCollection readPlatformCollection( @NonNull String prefix, long bucketDuration) throws IOException { final NetworkStatsCollection.Builder builder = new NetworkStatsCollection.Builder(bucketDuration); @@ -397,7 +445,7 @@ public class NetworkStatsDataMigrationUtils { if (version >= IdentitySetVersion.VERSION_ADD_OEM_MANAGED_NETWORK) { oemNetCapabilities = in.readInt(); } else { - oemNetCapabilities = NetworkIdentity.OEM_NONE; + oemNetCapabilities = NetworkTemplate.OEM_MANAGED_NO; } // Legacy files might contain TYPE_MOBILE_* types which were deprecated in later diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 2d338179186e..07a51324404c 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -34,6 +34,7 @@ import android.server.ServerProtoEnums; import android.service.batterystats.BatteryStatsServiceDumpHistoryProto; import android.service.batterystats.BatteryStatsServiceDumpProto; import android.telephony.CellSignalStrength; +import android.telephony.ServiceState; import android.telephony.TelephonyManager; import android.text.format.DateFormat; import android.util.ArrayMap; @@ -2654,6 +2655,46 @@ public abstract class BatteryStats implements Parcelable { */ public abstract Timer getPhoneDataConnectionTimer(int dataType); + /** @hide */ + public static final int RADIO_ACCESS_TECHNOLOGY_OTHER = 0; + /** @hide */ + public static final int RADIO_ACCESS_TECHNOLOGY_LTE = 1; + /** @hide */ + public static final int RADIO_ACCESS_TECHNOLOGY_NR = 2; + /** @hide */ + public static final int RADIO_ACCESS_TECHNOLOGY_COUNT = 3; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "RADIO_ACCESS_TECHNOLOGY_", + value = {RADIO_ACCESS_TECHNOLOGY_OTHER, RADIO_ACCESS_TECHNOLOGY_LTE, + RADIO_ACCESS_TECHNOLOGY_NR}) + public @interface RadioAccessTechnology { + } + + /** @hide */ + public static final String[] RADIO_ACCESS_TECHNOLOGY_NAMES = {"Other", "LTE", "NR"}; + + /** + * Returns the time in microseconds that the mobile radio has been active on a + * given Radio Access Technology (RAT), at a given frequency (NR RAT only), for a given + * transmission power level. + * + * @param rat Radio Access Technology {@see RadioAccessTechnology} + * @param frequencyRange frequency range {@see ServiceState.FrequencyRange}, only needed for + * RADIO_ACCESS_TECHNOLOGY_NR. Use + * {@link ServiceState.FREQUENCY_RANGE_UNKNOWN} for other Radio Access + * Technologies. + * @param signalStrength the cellular signal strength. {@see CellSignalStrength#getLevel()} + * @param elapsedRealtimeMs current elapsed realtime + * @return time (in milliseconds) the mobile radio spent active in the specified state, + * while on battery. + * @hide + */ + public abstract long getActiveRadioDurationMs(@RadioAccessTechnology int rat, + @ServiceState.FrequencyRange int frequencyRange, int signalStrength, + long elapsedRealtimeMs); + static final String[] WIFI_SUPPL_STATE_NAMES = { "invalid", "disconn", "disabled", "inactive", "scanning", "authenticating", "associating", "associated", "4-way-handshake", @@ -3997,6 +4038,89 @@ public abstract class BatteryStats implements Parcelable { } } + private void printCellularPerRatBreakdown(PrintWriter pw, StringBuilder sb, String prefix, + long rawRealtimeMs) { + final String allFrequenciesHeader = + " All frequencies:\n"; + final String[] nrFrequencyRangeDescription = new String[]{ + " Unknown frequency:\n", + " Low frequency (less than 1GHz):\n", + " Middle frequency (1GHz to 3GHz):\n", + " High frequency (3GHz to 6GHz):\n", + " Mmwave frequency (greater than 6GHz):\n"}; + final String signalStrengthHeader = + " Signal Strength Time:\n"; + final String[] signalStrengthDescription = new String[]{ + " unknown: ", + " poor: ", + " moderate: ", + " good: ", + " great: "}; + + final long totalActiveTimesMs = getMobileRadioActiveTime(rawRealtimeMs * 1000, + STATS_SINCE_CHARGED) / 1000; + + sb.setLength(0); + sb.append(prefix); + sb.append("Active Cellular Radio Access Technology Breakdown:"); + pw.println(sb); + + boolean hasData = false; + final int numSignalStrength = CellSignalStrength.getNumSignalStrengthLevels(); + for (int rat = RADIO_ACCESS_TECHNOLOGY_COUNT - 1; rat >= 0; rat--) { + sb.setLength(0); + sb.append(prefix); + sb.append(" "); + sb.append(RADIO_ACCESS_TECHNOLOGY_NAMES[rat]); + sb.append(":\n"); + sb.append(prefix); + + final int numFreqLvl = + rat == RADIO_ACCESS_TECHNOLOGY_NR ? nrFrequencyRangeDescription.length : 1; + for (int freqLvl = numFreqLvl - 1; freqLvl >= 0; freqLvl--) { + final int freqDescriptionStart = sb.length(); + boolean hasFreqData = false; + if (rat == RADIO_ACCESS_TECHNOLOGY_NR) { + sb.append(nrFrequencyRangeDescription[freqLvl]); + } else { + sb.append(allFrequenciesHeader); + } + + sb.append(prefix); + sb.append(signalStrengthHeader); + for (int strength = 0; strength < numSignalStrength; strength++) { + final long timeMs = getActiveRadioDurationMs(rat, freqLvl, strength, + rawRealtimeMs); + if (timeMs <= 0) continue; + hasFreqData = true; + sb.append(prefix); + sb.append(signalStrengthDescription[strength]); + formatTimeMs(sb, timeMs); + sb.append("("); + sb.append(formatRatioLocked(timeMs, totalActiveTimesMs)); + sb.append(")\n"); + } + + if (hasFreqData) { + hasData = true; + pw.print(sb); + sb.setLength(0); + sb.append(prefix); + } else { + // No useful data was printed, rewind sb to before the start of this frequency. + sb.setLength(freqDescriptionStart); + } + } + } + + if (!hasData) { + sb.setLength(0); + sb.append(prefix); + sb.append(" (no activity)"); + pw.println(sb); + } + } + /** * Temporary for settings. */ @@ -5269,6 +5393,8 @@ public abstract class BatteryStats implements Parcelable { printControllerActivity(pw, sb, prefix, CELLULAR_CONTROLLER_NAME, getModemControllerActivity(), which); + printCellularPerRatBreakdown(pw, sb, prefix + " ", rawRealtimeMs); + pw.print(" Cellular data received: "); pw.println(formatBytesLocked(mobileRxTotalBytes)); pw.print(" Cellular data sent: "); pw.println(formatBytesLocked(mobileTxTotalBytes)); pw.print(" Cellular packets received: "); pw.println(mobileRxTotalPackets); diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java index 29c7796d8660..cb1b5d3d20b8 100644 --- a/core/java/android/service/autofill/AutofillService.java +++ b/core/java/android/service/autofill/AutofillService.java @@ -578,6 +578,14 @@ public abstract class AutofillService extends Service { public static final String SERVICE_META_DATA = "android.autofill"; /** + * Name of the {@link FillResponse} extra used to return a delayed fill response. + * + * <p>Please see {@link FillRequest#getDelayedFillIntentSender()} on how to send a delayed + * fill response to framework.</p> + */ + public static final String EXTRA_FILL_RESPONSE = "android.service.autofill.extra.FILL_RESPONSE"; + + /** * Name of the {@link IResultReceiver} extra used to return the primary result of a request. * * @hide diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java index f820f0389f0d..e4d3732361ed 100644 --- a/core/java/android/service/autofill/FillRequest.java +++ b/core/java/android/service/autofill/FillRequest.java @@ -19,6 +19,7 @@ package android.service.autofill; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.IntentSender; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -160,6 +161,19 @@ public final class FillRequest implements Parcelable { */ private final @Nullable InlineSuggestionsRequest mInlineSuggestionsRequest; + /** + * Gets the {@link IntentSender} to send a delayed fill response. + * + * <p>The autofill service must first indicate that it wants to return a delayed + * {@link FillResponse} by setting {@link FillResponse#FLAG_DELAY_FILL} in a successful + * fill response. Then it can use this IntentSender to send an Intent with extra + * {@link AutofillService#EXTRA_FILL_RESPONSE} with the delayed response.</p> + * + * <p>Note that this may be null if a delayed fill response is not supported for + * this fill request.</p> + */ + private final @Nullable IntentSender mDelayedFillIntentSender; + private void onConstructed() { Preconditions.checkCollectionElementsNotNull(mFillContexts, "contexts"); } @@ -252,6 +266,16 @@ public final class FillRequest implements Parcelable { * * <p>The Autofill Service must set supportsInlineSuggestions in its XML to enable support * for inline suggestions.</p> + * @param delayedFillIntentSender + * Gets the {@link IntentSender} to send a delayed fill response. + * + * <p>The autofill service must first indicate that it wants to return a delayed + * {@link FillResponse} by setting {@link FillResponse#FLAG_DELAY_FILL} in a successful + * fill response. Then it can use this IntentSender to send an Intent with extra + * {@link AutofillService#EXTRA_FILL_RESPONSE} with the delayed response.</p> + * + * <p>Note that this may be null if a delayed fill response is not supported for + * this fill request.</p> * @hide */ @DataClass.Generated.Member @@ -260,7 +284,8 @@ public final class FillRequest implements Parcelable { @NonNull List<FillContext> fillContexts, @Nullable Bundle clientState, @RequestFlags int flags, - @Nullable InlineSuggestionsRequest inlineSuggestionsRequest) { + @Nullable InlineSuggestionsRequest inlineSuggestionsRequest, + @Nullable IntentSender delayedFillIntentSender) { this.mId = id; this.mFillContexts = fillContexts; com.android.internal.util.AnnotationValidations.validate( @@ -276,6 +301,7 @@ public final class FillRequest implements Parcelable { | FLAG_VIEW_NOT_FOCUSED | FLAG_ACTIVITY_START); this.mInlineSuggestionsRequest = inlineSuggestionsRequest; + this.mDelayedFillIntentSender = delayedFillIntentSender; onConstructed(); } @@ -348,6 +374,22 @@ public final class FillRequest implements Parcelable { return mInlineSuggestionsRequest; } + /** + * Gets the {@link IntentSender} to send a delayed fill response. + * + * <p>The autofill service must first indicate that it wants to return a delayed + * {@link FillResponse} by setting {@link FillResponse#FLAG_DELAY_FILL} in a successful + * fill response. Then it can use this IntentSender to send an Intent with extra + * {@link AutofillService#EXTRA_FILL_RESPONSE} with the delayed response.</p> + * + * <p>Note that this may be null if a delayed fill response is not supported for + * this fill request.</p> + */ + @DataClass.Generated.Member + public @Nullable IntentSender getDelayedFillIntentSender() { + return mDelayedFillIntentSender; + } + @Override @DataClass.Generated.Member public String toString() { @@ -359,7 +401,8 @@ public final class FillRequest implements Parcelable { "fillContexts = " + mFillContexts + ", " + "clientState = " + mClientState + ", " + "flags = " + requestFlagsToString(mFlags) + ", " + - "inlineSuggestionsRequest = " + mInlineSuggestionsRequest + + "inlineSuggestionsRequest = " + mInlineSuggestionsRequest + ", " + + "delayedFillIntentSender = " + mDelayedFillIntentSender + " }"; } @@ -372,12 +415,14 @@ public final class FillRequest implements Parcelable { byte flg = 0; if (mClientState != null) flg |= 0x4; if (mInlineSuggestionsRequest != null) flg |= 0x10; + if (mDelayedFillIntentSender != null) flg |= 0x20; dest.writeByte(flg); dest.writeInt(mId); dest.writeParcelableList(mFillContexts, flags); if (mClientState != null) dest.writeBundle(mClientState); dest.writeInt(mFlags); if (mInlineSuggestionsRequest != null) dest.writeTypedObject(mInlineSuggestionsRequest, flags); + if (mDelayedFillIntentSender != null) dest.writeTypedObject(mDelayedFillIntentSender, flags); } @Override @@ -398,6 +443,7 @@ public final class FillRequest implements Parcelable { Bundle clientState = (flg & 0x4) == 0 ? null : in.readBundle(); int flags = in.readInt(); InlineSuggestionsRequest inlineSuggestionsRequest = (flg & 0x10) == 0 ? null : (InlineSuggestionsRequest) in.readTypedObject(InlineSuggestionsRequest.CREATOR); + IntentSender delayedFillIntentSender = (flg & 0x20) == 0 ? null : (IntentSender) in.readTypedObject(IntentSender.CREATOR); this.mId = id; this.mFillContexts = fillContexts; @@ -414,6 +460,7 @@ public final class FillRequest implements Parcelable { | FLAG_VIEW_NOT_FOCUSED | FLAG_ACTIVITY_START); this.mInlineSuggestionsRequest = inlineSuggestionsRequest; + this.mDelayedFillIntentSender = delayedFillIntentSender; onConstructed(); } @@ -433,10 +480,10 @@ public final class FillRequest implements Parcelable { }; @DataClass.Generated( - time = 1643052544776L, + time = 1643386870464L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/service/autofill/FillRequest.java", - inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PASSWORD_INPUT_TYPE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_VIEW_NOT_FOCUSED\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_ACTIVITY_START\npublic static final int INVALID_REQUEST_ID\nprivate final int mId\nprivate final @android.annotation.NonNull java.util.List<android.service.autofill.FillContext> mFillContexts\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)") + inputSignatures = "public static final @android.service.autofill.FillRequest.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_PASSWORD_INPUT_TYPE\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_VIEW_NOT_FOCUSED\npublic static final @android.service.autofill.FillRequest.RequestFlags int FLAG_ACTIVITY_START\npublic static final int INVALID_REQUEST_ID\nprivate final int mId\nprivate final @android.annotation.NonNull java.util.List<android.service.autofill.FillContext> mFillContexts\nprivate final @android.annotation.Nullable android.os.Bundle mClientState\nprivate final @android.service.autofill.FillRequest.RequestFlags int mFlags\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\nprivate final @android.annotation.Nullable android.content.IntentSender mDelayedFillIntentSender\nprivate void onConstructed()\nclass FillRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genHiddenConstructor=true, genHiddenConstDefs=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java index 903e77fcb3d7..296877a448ab 100644 --- a/core/java/android/service/autofill/FillResponse.java +++ b/core/java/android/service/autofill/FillResponse.java @@ -65,10 +65,22 @@ public final class FillResponse implements Parcelable { */ public static final int FLAG_DISABLE_ACTIVITY_ONLY = 0x2; + /** + * Flag used to request to wait for a delayed fill from the remote Autofill service if it's + * passed to {@link Builder#setFlags(int)}. + * + * <p>Some datasets (i.e. OTP) take time to produce. This flags allows remote service to send + * a {@link FillResponse} to the latest {@link FillRequest} via + * {@link FillRequest#getDelayedFillIntentSender()} even if the original {@link FillCallback} + * has timed out. + */ + public static final int FLAG_DELAY_FILL = 0x4; + /** @hide */ @IntDef(flag = true, prefix = { "FLAG_" }, value = { FLAG_TRACK_CONTEXT_COMMITED, - FLAG_DISABLE_ACTIVITY_ONLY + FLAG_DISABLE_ACTIVITY_ONLY, + FLAG_DELAY_FILL }) @Retention(RetentionPolicy.SOURCE) @interface FillResponseFlags {} @@ -657,7 +669,7 @@ public final class FillResponse implements Parcelable { public Builder setFlags(@FillResponseFlags int flags) { throwIfDestroyed(); mFlags = Preconditions.checkFlagsArgument(flags, - FLAG_TRACK_CONTEXT_COMMITED | FLAG_DISABLE_ACTIVITY_ONLY); + FLAG_TRACK_CONTEXT_COMMITED | FLAG_DISABLE_ACTIVITY_ONLY | FLAG_DELAY_FILL); return this; } diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java index fba61cfd801e..5bd423599f5d 100644 --- a/core/java/android/service/trust/TrustAgentService.java +++ b/core/java/android/service/trust/TrustAgentService.java @@ -21,6 +21,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SdkConstant; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.app.Service; import android.app.admin.DevicePolicyManager; import android.content.ComponentName; @@ -310,9 +311,10 @@ public class TrustAgentService extends Service { * * @see #FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE * - * TODO(b/213631672): Add CTS tests + * TODO(b/213631672): Remove @hide and @TestApi * @hide */ + @TestApi public void onUserRequestedUnlock() { } diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index f2a03558663e..c91851a8896d 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -905,11 +905,12 @@ public abstract class WallpaperService extends Service { if (!ENABLE_WALLPAPER_DIMMING || mBbqSurfaceControl == null) { return; } + + SurfaceControl.Transaction surfaceControlTransaction = new SurfaceControl.Transaction(); // TODO: apply the dimming to preview as well once surface transparency works in // preview mode. if (!isPreview() && mShouldDim) { Log.v(TAG, "Setting wallpaper dimming: " + mWallpaperDimAmount); - SurfaceControl.Transaction surfaceControl = new SurfaceControl.Transaction(); // Animate dimming to gradually change the wallpaper alpha from the previous // dim amount to the new amount only if the dim amount changed. @@ -919,16 +920,15 @@ public abstract class WallpaperService extends Service { ? 0 : DIMMING_ANIMATION_DURATION_MS); animator.addUpdateListener((ValueAnimator va) -> { final float dimValue = (float) va.getAnimatedValue(); - surfaceControl - .setAlpha(mBbqSurfaceControl, 1 - dimValue) - .apply(); + if (mBbqSurfaceControl != null) { + surfaceControlTransaction + .setAlpha(mBbqSurfaceControl, 1 - dimValue).apply(); + } }); animator.start(); } else { Log.v(TAG, "Setting wallpaper dimming: " + 0); - new SurfaceControl.Transaction() - .setAlpha(mBbqSurfaceControl, 1.0f) - .apply(); + surfaceControlTransaction.setAlpha(mBbqSurfaceControl, 1.0f).apply(); } } diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java index ff6903e814b7..08cc31c8534f 100644 --- a/core/java/android/view/inputmethod/InputMethod.java +++ b/core/java/android/view/inputmethod/InputMethod.java @@ -105,12 +105,14 @@ public interface InputMethod { * current IME. * @param configChanges {@link InputMethodInfo#getConfigChanges()} declared by IME. * @param stylusHwSupported {@link InputMethodInfo#supportsStylusHandwriting()} declared by IME. + * @param shouldShowImeSwitcherWhenImeIsShown {@code true} If the IME switcher is expected to be + * shown while the IME is shown. * @hide */ @MainThread default void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privilegedOperations, int configChanges, - boolean stylusHwSupported) { + boolean stylusHwSupported, boolean shouldShowImeSwitcherWhenImeIsShown) { attachToken(token); } @@ -229,6 +231,8 @@ public interface InputMethod { * the next {@link #startInput(InputConnection, EditorInfo, IBinder)} as * long as your implementation of {@link InputMethod} relies on such * IPCs + * @param shouldShowImeSwitcherWhenImeIsShown {@code true} If the IME switcher is expected to be + * shown while the IME is shown. * @see #startInput(InputConnection, EditorInfo) * @see #restartInput(InputConnection, EditorInfo) * @see EditorInfo @@ -237,7 +241,7 @@ public interface InputMethod { @MainThread default void dispatchStartInputWithToken(@Nullable InputConnection inputConnection, @NonNull EditorInfo editorInfo, boolean restarting, - @NonNull IBinder startInputToken) { + @NonNull IBinder startInputToken, boolean shouldShowImeSwitcherWhenImeIsShown) { if (restarting) { restartInput(inputConnection, editorInfo); } else { @@ -246,6 +250,18 @@ public interface InputMethod { } /** + * Notifies that whether the IME should show the IME switcher or not is being changed. + * + * @param shouldShowImeSwitcherWhenImeIsShown {@code true} If the IME switcher is expected to be + * shown while the IME is shown. + * @hide + */ + @MainThread + default void onShouldShowImeSwitcherWhenImeIsShownChanged( + boolean shouldShowImeSwitcherWhenImeIsShown) { + } + + /** * Create a new {@link InputMethodSession} that can be handed to client * applications for interacting with the input method. You can later * use {@link #revokeSession(InputMethodSession)} to destroy the session diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index c6f64f4ad633..b00a3829f468 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -301,6 +301,13 @@ public class RemoteViews implements Parcelable, Filter { public static final int FLAG_USE_LIGHT_BACKGROUND_LAYOUT = 4; /** + * This mask determines which flags are propagated to nested RemoteViews (either added by + * addView, or set as portrait/landscape/sized RemoteViews). + */ + static final int FLAG_MASK_TO_PROPAGATE = + FLAG_WIDGET_IS_COLLECTION_CHILD | FLAG_USE_LIGHT_BACKGROUND_LAYOUT; + + /** * A ReadWriteHelper which has the same behavior as ReadWriteHelper.DEFAULT, but which is * intentionally a different instance in order to trick Bundle reader so that it doesn't allow * lazy initialization. @@ -467,6 +474,18 @@ public class RemoteViews implements Parcelable, Filter { */ public void addFlags(@ApplyFlags int flags) { mApplyFlags = mApplyFlags | flags; + + int flagsToPropagate = flags & FLAG_MASK_TO_PROPAGATE; + if (flagsToPropagate != 0) { + if (hasSizedRemoteViews()) { + for (RemoteViews remoteView : mSizedRemoteViews) { + remoteView.addFlags(flagsToPropagate); + } + } else if (hasLandscapeAndPortraitLayouts()) { + mLandscape.addFlags(flagsToPropagate); + mPortrait.addFlags(flagsToPropagate); + } + } } /** @@ -2407,6 +2426,10 @@ public class RemoteViews implements Parcelable, Filter { // will return -1. final int nextChild = getNextRecyclableChild(target); RemoteViews rvToApply = mNestedViews.getRemoteViewsToApply(context); + + int flagsToPropagate = mApplyFlags & FLAG_MASK_TO_PROPAGATE; + if (flagsToPropagate != 0) rvToApply.addFlags(flagsToPropagate); + if (nextChild >= 0 && mStableId != NO_ID) { // At that point, the views starting at index nextChild are the ones recyclable but // not yet recycled. All views added on that round of application are placed before. @@ -2419,8 +2442,8 @@ public class RemoteViews implements Parcelable, Filter { target.removeViews(nextChild, recycledViewIndex - nextChild); } setNextRecyclableChild(target, nextChild + 1, target.getChildCount()); - rvToApply.reapply(context, child, handler, null /* size */, colorResources, - false /* topLevel */); + rvToApply.reapplyNestedViews(context, child, rootParent, handler, + null /* size */, colorResources); return; } // If we cannot recycle the views, we still remove all views in between to @@ -2431,8 +2454,8 @@ public class RemoteViews implements Parcelable, Filter { // If we cannot recycle, insert the new view before the next recyclable child. // Inflate nested views and add as children - View nestedView = rvToApply.apply(context, target, handler, null /* size */, - colorResources); + View nestedView = rvToApply.applyNestedViews(context, target, rootParent, handler, + null /* size */, colorResources); if (mStableId != NO_ID) { setStableId(nestedView, mStableId); } @@ -3780,7 +3803,7 @@ public class RemoteViews implements Parcelable, Filter { * @param parcel */ public RemoteViews(Parcel parcel) { - this(parcel, /* rootParent= */ null, /* info= */ null, /* depth= */ 0); + this(parcel, /* rootData= */ null, /* info= */ null, /* depth= */ 0); } private RemoteViews(@NonNull Parcel parcel, @Nullable HierarchyRootData rootData, @@ -5580,6 +5603,16 @@ public class RemoteViews implements Parcelable, Filter { return result; } + private View applyNestedViews(Context context, ViewGroup directParent, + ViewGroup rootParent, InteractionHandler handler, SizeF size, + ColorResources colorResources) { + RemoteViews rvToApply = getRemoteViewsToApply(context, size); + + View result = inflateView(context, rvToApply, directParent, 0, colorResources); + rvToApply.performApply(result, rootParent, handler, colorResources); + return result; + } + private View inflateView(Context context, RemoteViews rv, ViewGroup parent) { return inflateView(context, rv, parent, 0, null); } @@ -5895,6 +5928,12 @@ public class RemoteViews implements Parcelable, Filter { } } + private void reapplyNestedViews(Context context, View v, ViewGroup rootParent, + InteractionHandler handler, SizeF size, ColorResources colorResources) { + RemoteViews rvToApply = getRemoteViewsToReapply(context, v, size); + rvToApply.performApply(v, rootParent, handler, colorResources); + } + /** * Applies all the actions to the provided view, moving as much of the task on the background * thread as possible. diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index 07cb48eb1a0a..629a1b36b9e6 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -113,7 +113,7 @@ interface IBatteryStats { void notePhoneOn(); void notePhoneOff(); void notePhoneSignalStrength(in SignalStrength signalStrength); - void notePhoneDataConnectionState(int dataType, boolean hasData, int serviceType); + void notePhoneDataConnectionState(int dataType, boolean hasData, int serviceType, int nrFrequency); void notePhoneState(int phoneState); void noteWifiOn(); void noteWifiOff(); diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 25ee2d0fa019..4fc977f670f0 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -69,10 +69,14 @@ import android.os.connectivity.GpsBatteryStats; import android.os.connectivity.WifiActivityEnergyInfo; import android.os.connectivity.WifiBatteryStats; import android.provider.Settings; +import android.telephony.Annotation.NetworkType; import android.telephony.CellSignalStrength; +import android.telephony.CellSignalStrengthLte; +import android.telephony.CellSignalStrengthNr; import android.telephony.DataConnectionRealTimeInfo; import android.telephony.ModemActivityInfo; import android.telephony.ServiceState; +import android.telephony.ServiceState.RegState; import android.telephony.SignalStrength; import android.telephony.TelephonyManager; import android.text.TextUtils; @@ -935,6 +939,119 @@ public class BatteryStatsImpl extends BatteryStats { final StopwatchTimer[] mPhoneDataConnectionsTimer = new StopwatchTimer[NUM_DATA_CONNECTION_TYPES]; + @RadioAccessTechnology + int mActiveRat = RADIO_ACCESS_TECHNOLOGY_OTHER; + + private static class RadioAccessTechnologyBatteryStats { + /** + * This RAT is currently being used. + */ + private boolean mActive = false; + /** + * Current active frequency range for this RAT. + */ + @ServiceState.FrequencyRange + private int mFrequencyRange = ServiceState.FREQUENCY_RANGE_UNKNOWN; + /** + * Current signal strength for this RAT. + */ + private int mSignalStrength = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN; + /** + * Timers for each combination of frequency range and signal strength. + */ + public final StopwatchTimer[][] perStateTimers; + + RadioAccessTechnologyBatteryStats(int freqCount, Clock clock, TimeBase timeBase) { + perStateTimers = + new StopwatchTimer[freqCount][CellSignalStrength.NUM_SIGNAL_STRENGTH_BINS]; + for (int i = 0; i < freqCount; i++) { + for (int j = 0; j < CellSignalStrength.NUM_SIGNAL_STRENGTH_BINS; j++) { + perStateTimers[i][j] = new StopwatchTimer(clock, null, -1, null, timeBase); + } + } + } + + /** + * Note this RAT is currently being used. + */ + public void noteActive(boolean active, long elapsedRealtimeMs) { + if (mActive == active) return; + mActive = active; + if (mActive) { + perStateTimers[mFrequencyRange][mSignalStrength].startRunningLocked( + elapsedRealtimeMs); + } else { + perStateTimers[mFrequencyRange][mSignalStrength].stopRunningLocked( + elapsedRealtimeMs); + } + } + + /** + * Note current frequency range has changed. + */ + public void noteFrequencyRange(@ServiceState.FrequencyRange int frequencyRange, + long elapsedRealtimeMs) { + if (mFrequencyRange == frequencyRange) return; + + if (!mActive) { + // RAT not in use, note the frequency change and move on. + mFrequencyRange = frequencyRange; + return; + } + perStateTimers[mFrequencyRange][mSignalStrength].stopRunningLocked(elapsedRealtimeMs); + perStateTimers[frequencyRange][mSignalStrength].startRunningLocked(elapsedRealtimeMs); + mFrequencyRange = frequencyRange; + } + + /** + * Note current signal strength has changed. + */ + public void noteSignalStrength(int signalStrength, long elapsedRealtimeMs) { + if (mSignalStrength == signalStrength) return; + + if (!mActive) { + // RAT not in use, note the signal strength change and move on. + mSignalStrength = signalStrength; + return; + } + perStateTimers[mFrequencyRange][mSignalStrength].stopRunningLocked(elapsedRealtimeMs); + perStateTimers[mFrequencyRange][signalStrength].startRunningLocked(elapsedRealtimeMs); + mSignalStrength = signalStrength; + } + + /** + * Reset display timers. + */ + public void reset(long elapsedRealtimeUs) { + final int size = perStateTimers.length; + for (int i = 0; i < size; i++) { + for (int j = 0; j < CellSignalStrength.NUM_SIGNAL_STRENGTH_BINS; j++) { + perStateTimers[i][j].reset(false, elapsedRealtimeUs); + } + } + } + } + + /** + * Number of frequency ranges, keep in sync with {@link ServiceState.FrequencyRange} + */ + private static final int NR_FREQUENCY_COUNT = 5; + + RadioAccessTechnologyBatteryStats[] mPerRatBatteryStats = + new RadioAccessTechnologyBatteryStats[RADIO_ACCESS_TECHNOLOGY_COUNT]; + + @GuardedBy("this") + private RadioAccessTechnologyBatteryStats getRatBatteryStatsLocked( + @RadioAccessTechnology int rat) { + RadioAccessTechnologyBatteryStats stats = mPerRatBatteryStats[rat]; + if (stats == null) { + final int freqCount = rat == RADIO_ACCESS_TECHNOLOGY_NR ? NR_FREQUENCY_COUNT : 1; + stats = new RadioAccessTechnologyBatteryStats(freqCount, mClock, mOnBatteryTimeBase); + mPerRatBatteryStats[rat] = stats; + } + return stats; + } + final LongSamplingCounter[] mNetworkByteActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES]; @@ -5886,6 +6003,10 @@ public class BatteryStatsImpl extends BatteryStats { + Integer.toHexString(mHistoryCur.states)); addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs); mMobileRadioPowerState = powerState; + + // Inform current RatBatteryStats that the modem active state might have changed. + getRatBatteryStatsLocked(mActiveRat).noteActive(active, elapsedRealtimeMs); + if (active) { mMobileRadioActiveTimer.startRunningLocked(elapsedRealtimeMs); mMobileRadioActivePerAppTimer.startRunningLocked(elapsedRealtimeMs); @@ -6307,21 +6428,86 @@ public class BatteryStatsImpl extends BatteryStats { @GuardedBy("this") public void notePhoneSignalStrengthLocked(SignalStrength signalStrength, long elapsedRealtimeMs, long uptimeMs) { - // Bin the strength. - int bin = signalStrength.getLevel(); - updateAllPhoneStateLocked(mPhoneServiceStateRaw, mPhoneSimStateRaw, bin, + final int overallSignalStrength = signalStrength.getLevel(); + final SparseIntArray perRatSignalStrength = new SparseIntArray( + BatteryStats.RADIO_ACCESS_TECHNOLOGY_COUNT); + + // Extract signal strength level for each RAT. + final List<CellSignalStrength> cellSignalStrengths = + signalStrength.getCellSignalStrengths(); + final int size = cellSignalStrengths.size(); + for (int i = 0; i < size; i++) { + CellSignalStrength cellSignalStrength = cellSignalStrengths.get(i); + // Map each CellSignalStrength to a BatteryStats.RadioAccessTechnology + final int ratType; + final int level; + if (cellSignalStrength instanceof CellSignalStrengthNr) { + ratType = RADIO_ACCESS_TECHNOLOGY_NR; + level = cellSignalStrength.getLevel(); + } else if (cellSignalStrength instanceof CellSignalStrengthLte) { + ratType = RADIO_ACCESS_TECHNOLOGY_LTE; + level = cellSignalStrength.getLevel(); + } else { + ratType = RADIO_ACCESS_TECHNOLOGY_OTHER; + level = cellSignalStrength.getLevel(); + } + + // According to SignalStrength#getCellSignalStrengths(), multiple of the same + // cellSignalStrength can be present. Just take the highest level one for each RAT. + if (perRatSignalStrength.get(ratType, -1) < level) { + perRatSignalStrength.put(ratType, level); + } + } + + notePhoneSignalStrengthLocked(overallSignalStrength, perRatSignalStrength, + elapsedRealtimeMs, uptimeMs); + } + + /** + * Note phone signal strength change, including per RAT signal strength. + * + * @param signalStrength overall signal strength {@see SignalStrength#getLevel()} + * @param perRatSignalStrength signal strength of available RATs + */ + @GuardedBy("this") + public void notePhoneSignalStrengthLocked(int signalStrength, + SparseIntArray perRatSignalStrength) { + notePhoneSignalStrengthLocked(signalStrength, perRatSignalStrength, + mClock.elapsedRealtime(), mClock.uptimeMillis()); + } + + /** + * Note phone signal strength change, including per RAT signal strength. + * + * @param signalStrength overall signal strength {@see SignalStrength#getLevel()} + * @param perRatSignalStrength signal strength of available RATs + */ + @GuardedBy("this") + public void notePhoneSignalStrengthLocked(int signalStrength, + SparseIntArray perRatSignalStrength, + long elapsedRealtimeMs, long uptimeMs) { + // Note each RAT's signal strength. + final int size = perRatSignalStrength.size(); + for (int i = 0; i < size; i++) { + final int rat = perRatSignalStrength.keyAt(i); + final int ratSignalStrength = perRatSignalStrength.valueAt(i); + getRatBatteryStatsLocked(rat).noteSignalStrength(ratSignalStrength, elapsedRealtimeMs); + } + updateAllPhoneStateLocked(mPhoneServiceStateRaw, mPhoneSimStateRaw, signalStrength, elapsedRealtimeMs, uptimeMs); } @UnsupportedAppUsage @GuardedBy("this") - public void notePhoneDataConnectionStateLocked(int dataType, boolean hasData, int serviceType) { - notePhoneDataConnectionStateLocked(dataType, hasData, serviceType, + public void notePhoneDataConnectionStateLocked(@NetworkType int dataType, boolean hasData, + @RegState int serviceType, @ServiceState.FrequencyRange int nrFrequency) { + notePhoneDataConnectionStateLocked(dataType, hasData, serviceType, nrFrequency, mClock.elapsedRealtime(), mClock.uptimeMillis()); } @GuardedBy("this") - public void notePhoneDataConnectionStateLocked(int dataType, boolean hasData, int serviceType, + public void notePhoneDataConnectionStateLocked(@NetworkType int dataType, boolean hasData, + @RegState int serviceType, @ServiceState.FrequencyRange int nrFrequency, long elapsedRealtimeMs, long uptimeMs) { // BatteryStats uses 0 to represent no network type. // Telephony does not have a concept of no network type, and uses 0 to represent unknown. @@ -6344,6 +6530,13 @@ public class BatteryStatsImpl extends BatteryStats { } } } + + final int newRat = mapNetworkTypeToRadioAccessTechnology(bin); + if (newRat == RADIO_ACCESS_TECHNOLOGY_NR) { + // Note possible frequency change for the NR RAT. + getRatBatteryStatsLocked(newRat).noteFrequencyRange(nrFrequency, elapsedRealtimeMs); + } + if (DEBUG) Log.i(TAG, "Phone Data Connection -> " + dataType + " = " + hasData); if (mPhoneDataConnectionType != bin) { mHistoryCur.states = (mHistoryCur.states&~HistoryItem.STATE_DATA_CONNECTION_MASK) @@ -6357,6 +6550,45 @@ public class BatteryStatsImpl extends BatteryStats { } mPhoneDataConnectionType = bin; mPhoneDataConnectionsTimer[bin].startRunningLocked(elapsedRealtimeMs); + + if (mActiveRat != newRat) { + getRatBatteryStatsLocked(mActiveRat).noteActive(false, elapsedRealtimeMs); + mActiveRat = newRat; + } + final boolean modemActive = mMobileRadioActiveTimer.isRunningLocked(); + getRatBatteryStatsLocked(newRat).noteActive(modemActive, elapsedRealtimeMs); + } + } + + @RadioAccessTechnology + private static int mapNetworkTypeToRadioAccessTechnology(@NetworkType int dataType) { + switch (dataType) { + case TelephonyManager.NETWORK_TYPE_NR: + return RADIO_ACCESS_TECHNOLOGY_NR; + case TelephonyManager.NETWORK_TYPE_LTE: + return RADIO_ACCESS_TECHNOLOGY_LTE; + case TelephonyManager.NETWORK_TYPE_UNKNOWN: //fallthrough + case TelephonyManager.NETWORK_TYPE_GPRS: //fallthrough + case TelephonyManager.NETWORK_TYPE_EDGE: //fallthrough + case TelephonyManager.NETWORK_TYPE_UMTS: //fallthrough + case TelephonyManager.NETWORK_TYPE_CDMA: //fallthrough + case TelephonyManager.NETWORK_TYPE_EVDO_0: //fallthrough + case TelephonyManager.NETWORK_TYPE_EVDO_A: //fallthrough + case TelephonyManager.NETWORK_TYPE_1xRTT: //fallthrough + case TelephonyManager.NETWORK_TYPE_HSDPA: //fallthrough + case TelephonyManager.NETWORK_TYPE_HSUPA: //fallthrough + case TelephonyManager.NETWORK_TYPE_HSPA: //fallthrough + case TelephonyManager.NETWORK_TYPE_IDEN: //fallthrough + case TelephonyManager.NETWORK_TYPE_EVDO_B: //fallthrough + case TelephonyManager.NETWORK_TYPE_EHRPD: //fallthrough + case TelephonyManager.NETWORK_TYPE_HSPAP: //fallthrough + case TelephonyManager.NETWORK_TYPE_GSM: //fallthrough + case TelephonyManager.NETWORK_TYPE_TD_SCDMA: //fallthrough + case TelephonyManager.NETWORK_TYPE_IWLAN: //fallthrough + return RADIO_ACCESS_TECHNOLOGY_OTHER; + default: + Slog.w(TAG, "Unhandled NetworkType (" + dataType + "), mapping to OTHER"); + return RADIO_ACCESS_TECHNOLOGY_OTHER; } } @@ -7731,6 +7963,23 @@ public class BatteryStatsImpl extends BatteryStats { return mPhoneDataConnectionsTimer[dataType]; } + @Override public long getActiveRadioDurationMs(@RadioAccessTechnology int rat, + @ServiceState.FrequencyRange int frequencyRange, int signalStrength, + long elapsedRealtimeMs) { + final RadioAccessTechnologyBatteryStats stats = mPerRatBatteryStats[rat]; + if (stats == null) return 0L; + + final int freqCount = stats.perStateTimers.length; + if (frequencyRange < 0 || frequencyRange >= freqCount) return 0L; + + final StopwatchTimer[] strengthTimers = stats.perStateTimers[frequencyRange]; + final int strengthCount = strengthTimers.length; + if (signalStrength < 0 || signalStrength >= strengthCount) return 0L; + + return stats.perStateTimers[frequencyRange][signalStrength].getTotalTimeLocked( + elapsedRealtimeMs * 1000, STATS_SINCE_CHARGED) / 1000; + } + @UnsupportedAppUsage @Override public long getMobileRadioActiveTime(long elapsedRealtimeUs, int which) { return mMobileRadioActiveTimer.getTotalTimeLocked(elapsedRealtimeUs, which); @@ -12553,6 +12802,11 @@ public class BatteryStatsImpl extends BatteryStats { mNetworkByteActivityCounters[i].reset(false, elapsedRealtimeUs); mNetworkPacketActivityCounters[i].reset(false, elapsedRealtimeUs); } + for (int i = 0; i < RADIO_ACCESS_TECHNOLOGY_COUNT; i++) { + final RadioAccessTechnologyBatteryStats stats = mPerRatBatteryStats[i]; + if (stats == null) continue; + stats.reset(elapsedRealtimeUs); + } mMobileRadioActiveTimer.reset(false, elapsedRealtimeUs); mMobileRadioActivePerAppTimer.reset(false, elapsedRealtimeUs); mMobileRadioActiveAdjustedTime.reset(false, elapsedRealtimeUs); diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java index be91aaca5d39..0a29fc5285a5 100644 --- a/core/java/com/android/internal/os/BinderCallsStats.java +++ b/core/java/com/android/internal/os/BinderCallsStats.java @@ -1159,6 +1159,17 @@ public class BinderCallsStats implements BinderInternal.Observer { : Integer.compare(a.transactionCode, b.transactionCode); } + /** @hide */ + public static void startForBluetooth(Context context) { + new BinderCallsStats.SettingsObserver( + context, + new BinderCallsStats( + new BinderCallsStats.Injector(), + com.android.internal.os.BinderLatencyProto.Dims.BLUETOOTH)); + + } + + /** * Settings observer for other processes (not system_server). diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index a5cf7ce1d37c..23ebc9f94915 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -20,6 +20,7 @@ import android.app.ITransientNotificationCallback; import android.content.ComponentName; import android.graphics.drawable.Icon; import android.graphics.Rect; +import android.hardware.biometrics.IBiometricContextListener; import android.hardware.biometrics.IBiometricSysuiReceiver; import android.hardware.biometrics.PromptInfo; import android.hardware.fingerprint.IUdfpsHbmListener; @@ -166,6 +167,8 @@ oneway interface IStatusBar * Used to hide the authentication dialog, e.g. when the application cancels authentication. */ void hideAuthenticationDialog(); + /* Used to notify the biometric service of events that occur outside of an operation. */ + void setBiometicContextListener(in IBiometricContextListener listener); /** * Sets an instance of IUdfpsHbmListener for UdfpsController. diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index accb98645599..f28325e3cc51 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -20,6 +20,7 @@ import android.app.Notification; import android.content.ComponentName; import android.graphics.drawable.Icon; import android.graphics.Rect; +import android.hardware.biometrics.IBiometricContextListener; import android.hardware.biometrics.IBiometricSysuiReceiver; import android.hardware.biometrics.PromptInfo; import android.hardware.fingerprint.IUdfpsHbmListener; @@ -125,6 +126,8 @@ interface IStatusBarService void onBiometricError(int modality, int error, int vendorCode); // Used to hide the authentication dialog, e.g. when the application cancels authentication void hideAuthenticationDialog(); + // Used to notify the biometric service of events that occur outside of an operation. + void setBiometicContextListener(in IBiometricContextListener listener); /** * Sets an instance of IUdfpsHbmListener for UdfpsController. diff --git a/core/java/com/android/internal/util/UserIcons.java b/core/java/com/android/internal/util/UserIcons.java index bfe43237da58..17b84ffc2f3f 100644 --- a/core/java/com/android/internal/util/UserIcons.java +++ b/core/java/com/android/internal/util/UserIcons.java @@ -16,6 +16,7 @@ package com.android.internal.util; +import android.annotation.ColorInt; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -72,9 +73,30 @@ public class UserIcons { // Return colored icon instead colorResId = USER_ICON_COLORS[userId % USER_ICON_COLORS.length]; } + return getDefaultUserIconInColor(resources, resources.getColor(colorResId, null)); + } + + /** + * Returns a default user icon in a particular color. + * + * @param resources resources object to fetch the user icon + * @param color the color used for the icon + */ + public static Drawable getDefaultUserIconInColor(Resources resources, @ColorInt int color) { Drawable icon = resources.getDrawable(R.drawable.ic_account_circle, null).mutate(); - icon.setColorFilter(resources.getColor(colorResId, null), Mode.SRC_IN); + icon.setColorFilter(color, Mode.SRC_IN); icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight()); return icon; } + + /** + * Returns an array containing colors to be used for default user icons. + */ + public static int[] getUserIconColors(Resources resources) { + int[] result = new int[USER_ICON_COLORS.length]; + for (int i = 0; i < result.length; i++) { + result[i] = resources.getColor(USER_ICON_COLORS[i], null); + } + return result; + } } diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl index da24832f9d84..d2bc3442ed36 100644 --- a/core/java/com/android/internal/view/IInputMethod.aidl +++ b/core/java/com/android/internal/view/IInputMethod.aidl @@ -37,7 +37,8 @@ import com.android.internal.view.InlineSuggestionsRequestInfo; */ oneway interface IInputMethod { void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privOps, - int configChanges, boolean stylusHwSupported); + int configChanges, boolean stylusHwSupported, + boolean shouldShowImeSwitcherWhenImeIsShown); void onCreateInlineSuggestionsRequest(in InlineSuggestionsRequestInfo requestInfo, in IInlineSuggestionsRequestCallback cb); @@ -47,7 +48,10 @@ oneway interface IInputMethod { void unbindInput(); void startInput(in IBinder startInputToken, in IInputContext inputContext, - in EditorInfo attribute, boolean restarting); + in EditorInfo attribute, boolean restarting, + boolean shouldShowImeSwitcherWhenImeIsShown); + + void onShouldShowImeSwitcherWhenImeIsShownChanged(boolean shouldShowImeSwitcherWhenImeIsShown); void createSession(in InputChannel channel, IInputSessionCallback callback); diff --git a/core/jni/android/opengl/OWNERS b/core/jni/android/opengl/OWNERS new file mode 100644 index 000000000000..ce4b9075d59c --- /dev/null +++ b/core/jni/android/opengl/OWNERS @@ -0,0 +1 @@ +file:/graphics/java/android/graphics/OWNERS diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp index d039bcff3b26..78b403c39a17 100644 --- a/core/jni/android_hardware_SensorManager.cpp +++ b/core/jni/android_hardware_SensorManager.cpp @@ -133,6 +133,18 @@ nativeClassInit (JNIEnv *_env, jclass _this) MakeGlobalRefOrDie(_env, _env->CallObjectMethod(empty.get(), stringOffsets.intern)); } +uint64_t htonll(uint64_t ll) { + constexpr uint32_t kBytesToTest = 0x12345678; + constexpr uint8_t kFirstByte = (const uint8_t &)kBytesToTest; + constexpr bool kIsLittleEndian = kFirstByte == 0x78; + + if constexpr (kIsLittleEndian) { + return static_cast<uint64_t>(htonl(ll & 0xffffffff)) << 32 | htonl(ll >> 32); + } else { + return ll; + } +} + static jstring getJavaInternedString(JNIEnv *env, const String8 &string) { if (string == "") { return gStringOffsets.emptyString; @@ -193,7 +205,8 @@ translateNativeSensorToJavaSensor(JNIEnv *env, jobject sensor, const Sensor& nat int32_t id = nativeSensor.getId(); env->CallVoidMethod(sensor, sensorOffsets.setId, id); Sensor::uuid_t uuid = nativeSensor.getUuid(); - env->CallVoidMethod(sensor, sensorOffsets.setUuid, id, uuid.i64[0], uuid.i64[1]); + env->CallVoidMethod(sensor, sensorOffsets.setUuid, htonll(uuid.i64[0]), + htonll(uuid.i64[1])); } return sensor; } diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index e13b78868a2c..6b82ba80a26a 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -1288,7 +1288,7 @@ static jint convertAudioProfileFromNative(JNIEnv *env, jobject *jAudioProfile, audioFormatFromNative(nAudioProfile->format), jSamplingRates.get(), jChannelMasks.get(), jChannelIndexMasks.get(), encapsulationType); - if (jAudioProfile == nullptr) { + if (*jAudioProfile == nullptr) { return AUDIO_JAVA_ERROR; } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index c9bd22210a77..0c8c7f281fb3 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -719,6 +719,7 @@ <protected-broadcast android:name="android.safetycenter.action.REFRESH_SAFETY_SOURCES" /> <protected-broadcast android:name="android.app.action.DEVICE_POLICY_RESOURCE_UPDATED" /> <protected-broadcast android:name="android.intent.action.SHOW_FOREGROUND_SERVICE_MANAGER" /> + <protected-broadcast android:name="android.service.autofill.action.DELAYED_FILL" /> <!-- ====================================================================== --> <!-- RUNTIME PERMISSIONS --> @@ -5416,7 +5417,7 @@ android:protectionLevel="signature|setup|role" /> <!-- Allows access to keyguard secure storage. Only allowed for system processes. - @hide --> + @hide @TestApi --> <permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" android:protectionLevel="signature|setup" /> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 78841fc914c4..0edc0d6b9ed6 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2707,6 +2707,9 @@ Values are bandwidth_estimator, carrier_config and modem. --> <string name="config_bandwidthEstimateSource">bandwidth_estimator</string> + <!-- Whether force to enable telephony new data stack or not --> + <bool name="config_force_enable_telephony_new_data_stack">false</bool> + <!-- Whether WiFi display is supported by this device. There are many prerequisites for this feature to work correctly. Here are a few of them: diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 20c5c610abe4..602d5f9c691e 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -472,6 +472,7 @@ <java-symbol type="integer" name="config_mobile_mtu" /> <java-symbol type="array" name="config_mobile_tcp_buffers" /> <java-symbol type="string" name="config_tcp_buffers" /> + <java-symbol type="bool" name="config_force_enable_telephony_new_data_stack" /> <java-symbol type="integer" name="config_volte_replacement_rat"/> <java-symbol type="integer" name="config_valid_wappush_index" /> <java-symbol type="integer" name="config_overrideHasPermanentMenuKey" /> diff --git a/core/tests/coretests/res/layout/remote_view_relative_layout.xml b/core/tests/coretests/res/layout/remote_view_relative_layout.xml new file mode 100644 index 000000000000..713a4c89ea4d --- /dev/null +++ b/core/tests/coretests/res/layout/remote_view_relative_layout.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2008, 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. +*/ +--> + +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/container" + android:layout_width="match_parent" + android:layout_height="match_parent" /> diff --git a/core/tests/coretests/res/layout/remote_view_relative_layout_with_theme.xml b/core/tests/coretests/res/layout/remote_view_relative_layout_with_theme.xml new file mode 100644 index 000000000000..74c939b7eaa3 --- /dev/null +++ b/core/tests/coretests/res/layout/remote_view_relative_layout_with_theme.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2008, 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. +*/ +--> + +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/themed_layout" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:theme="@style/RelativeLayoutAlignTop25Alpha"/> diff --git a/core/tests/coretests/res/layout/remote_views_light_background_text.xml b/core/tests/coretests/res/layout/remote_views_light_background_text.xml new file mode 100644 index 000000000000..f300f0991a97 --- /dev/null +++ b/core/tests/coretests/res/layout/remote_views_light_background_text.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2016 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/light_background_text" + android:layout_width="match_parent" + android:layout_height="match_parent" /> diff --git a/core/tests/coretests/res/layout/remote_views_list.xml b/core/tests/coretests/res/layout/remote_views_list.xml new file mode 100644 index 000000000000..ca43bc8986e7 --- /dev/null +++ b/core/tests/coretests/res/layout/remote_views_list.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2016 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<ListView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/list" + android:layout_width="match_parent" + android:layout_height="match_parent"/> + diff --git a/core/tests/coretests/res/values/styles.xml b/core/tests/coretests/res/values/styles.xml index 352b4dceb3cc..32eebb35e0c3 100644 --- a/core/tests/coretests/res/values/styles.xml +++ b/core/tests/coretests/res/values/styles.xml @@ -34,6 +34,16 @@ <style name="LayoutInDisplayCutoutModeAlways"> <item name="android:windowLayoutInDisplayCutoutMode">always</item> </style> + <style name="RelativeLayoutAlignBottom50Alpha"> + <item name="android:layout_alignParentTop">false</item> + <item name="android:layout_alignParentBottom">true</item> + <item name="android:alpha">0.5</item> + </style> + <style name="RelativeLayoutAlignTop25Alpha"> + <item name="android:layout_alignParentTop">true</item> + <item name="android:layout_alignParentBottom">false</item> + <item name="android:alpha">0.25</item> + </style> <style name="WindowBackgroundColorLiteral"> <item name="android:windowBackground">#00FF00</item> </style> diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java index 059c764213bc..00b3693c902b 100644 --- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java +++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java @@ -16,8 +16,12 @@ package android.widget; +import static com.android.internal.R.id.pending_intent_tag; + import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; @@ -31,7 +35,10 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.os.Binder; +import android.os.Looper; import android.os.Parcel; +import android.util.SizeF; +import android.view.ContextThemeWrapper; import android.view.View; import android.view.ViewGroup; @@ -49,6 +56,7 @@ import org.junit.runner.RunWith; import java.util.ArrayList; import java.util.Arrays; +import java.util.Map; import java.util.concurrent.CountDownLatch; /** @@ -261,6 +269,148 @@ public class RemoteViewsTest { verifyViewTree(syncView, asyncView, "row1-c1", "row1-c2", "row1-c3", "row2-c1", "row2-c2"); } + @Test + public void nestedViews_setRemoteAdapter_intent() { + Looper.prepare(); + + AppWidgetHostView widget = new AppWidgetHostView(mContext); + RemoteViews top = new RemoteViews(mPackage, R.layout.remote_view_host); + RemoteViews inner1 = new RemoteViews(mPackage, R.layout.remote_view_host); + RemoteViews inner2 = new RemoteViews(mPackage, R.layout.remote_views_list); + inner2.setRemoteAdapter(R.id.list, new Intent()); + inner1.addView(R.id.container, inner2); + top.addView(R.id.container, inner1); + + View view = top.apply(mContext, widget); + widget.addView(view); + + ListView listView = (ListView) view.findViewById(R.id.list); + listView.onRemoteAdapterConnected(); + assertNotNull(listView.getAdapter()); + + top.reapply(mContext, view); + listView = (ListView) view.findViewById(R.id.list); + assertNotNull(listView.getAdapter()); + } + + @Test + public void nestedViews_setRemoteAdapter_remoteCollectionItems() { + AppWidgetHostView widget = new AppWidgetHostView(mContext); + RemoteViews top = new RemoteViews(mPackage, R.layout.remote_view_host); + RemoteViews inner1 = new RemoteViews(mPackage, R.layout.remote_view_host); + RemoteViews inner2 = new RemoteViews(mPackage, R.layout.remote_views_list); + inner2.setRemoteAdapter( + R.id.list, + new RemoteViews.RemoteCollectionItems.Builder() + .addItem(0, new RemoteViews(mPackage, R.layout.remote_view_host)) + .build()); + inner1.addView(R.id.container, inner2); + top.addView(R.id.container, inner1); + + View view = top.apply(mContext, widget); + widget.addView(view); + + ListView listView = (ListView) view.findViewById(R.id.list); + assertNotNull(listView.getAdapter()); + + top.reapply(mContext, view); + listView = (ListView) view.findViewById(R.id.list); + assertNotNull(listView.getAdapter()); + } + + @Test + public void nestedViews_collectionChildFlag() throws Exception { + RemoteViews nested = new RemoteViews(mPackage, R.layout.remote_views_text); + nested.setOnClickPendingIntent( + R.id.text, + PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.FLAG_MUTABLE) + ); + + RemoteViews listItem = new RemoteViews(mPackage, R.layout.remote_view_host); + listItem.addView(R.id.container, nested); + listItem.addFlags(RemoteViews.FLAG_WIDGET_IS_COLLECTION_CHILD); + + View view = listItem.apply(mContext, mContainer); + TextView text = (TextView) view.findViewById(R.id.text); + assertNull(text.getTag(pending_intent_tag)); + } + + @Test + public void landscapePortraitViews_collectionChildFlag() throws Exception { + RemoteViews inner = new RemoteViews(mPackage, R.layout.remote_views_text); + inner.setOnClickPendingIntent( + R.id.text, + PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.FLAG_MUTABLE) + ); + + RemoteViews listItem = new RemoteViews(inner, inner); + listItem.addFlags(RemoteViews.FLAG_WIDGET_IS_COLLECTION_CHILD); + + View view = listItem.apply(mContext, mContainer); + TextView text = (TextView) view.findViewById(R.id.text); + assertNull(text.getTag(pending_intent_tag)); + } + + @Test + public void sizedViews_collectionChildFlag() throws Exception { + RemoteViews inner = new RemoteViews(mPackage, R.layout.remote_views_text); + inner.setOnClickPendingIntent( + R.id.text, + PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.FLAG_MUTABLE) + ); + + RemoteViews listItem = new RemoteViews( + Map.of(new SizeF(0, 0), inner, new SizeF(100, 100), inner)); + listItem.addFlags(RemoteViews.FLAG_WIDGET_IS_COLLECTION_CHILD); + + View view = listItem.apply(mContext, mContainer); + TextView text = (TextView) view.findViewById(R.id.text); + assertNull(text.getTag(pending_intent_tag)); + } + + @Test + public void nestedViews_lightBackgroundLayoutFlag() { + RemoteViews nested = new RemoteViews(mPackage, R.layout.remote_views_text); + nested.setLightBackgroundLayoutId(R.layout.remote_views_light_background_text); + + RemoteViews parent = new RemoteViews(mPackage, R.layout.remote_view_host); + parent.addView(R.id.container, nested); + parent.setLightBackgroundLayoutId(R.layout.remote_view_host); + parent.addFlags(RemoteViews.FLAG_USE_LIGHT_BACKGROUND_LAYOUT); + + View view = parent.apply(mContext, mContainer); + assertNull(view.findViewById(R.id.text)); + assertNotNull(view.findViewById(R.id.light_background_text)); + } + + + @Test + public void landscapePortraitViews_lightBackgroundLayoutFlag() { + RemoteViews inner = new RemoteViews(mPackage, R.layout.remote_views_text); + inner.setLightBackgroundLayoutId(R.layout.remote_views_light_background_text); + + RemoteViews parent = new RemoteViews(inner, inner); + parent.addFlags(RemoteViews.FLAG_USE_LIGHT_BACKGROUND_LAYOUT); + + View view = parent.apply(mContext, mContainer); + assertNull(view.findViewById(R.id.text)); + assertNotNull(view.findViewById(R.id.light_background_text)); + } + + @Test + public void sizedViews_lightBackgroundLayoutFlag() { + RemoteViews inner = new RemoteViews(mPackage, R.layout.remote_views_text); + inner.setLightBackgroundLayoutId(R.layout.remote_views_light_background_text); + + RemoteViews parent = new RemoteViews( + Map.of(new SizeF(0, 0), inner, new SizeF(100, 100), inner)); + parent.addFlags(RemoteViews.FLAG_USE_LIGHT_BACKGROUND_LAYOUT); + + View view = parent.apply(mContext, mContainer); + assertNull(view.findViewById(R.id.text)); + assertNotNull(view.findViewById(R.id.light_background_text)); + } + private RemoteViews createViewChained(int depth, String... texts) { RemoteViews result = new RemoteViews(mPackage, R.layout.remote_view_host); @@ -483,6 +633,47 @@ public class RemoteViewsTest { index, inflated.getTag(com.android.internal.R.id.notification_action_index_tag)); } + @Test + public void nestedViews_themesPropagateCorrectly() { + Context themedContext = + new ContextThemeWrapper(mContext, R.style.RelativeLayoutAlignBottom50Alpha); + RelativeLayout rootParent = new RelativeLayout(themedContext); + + RemoteViews top = new RemoteViews(mPackage, R.layout.remote_view_relative_layout); + RemoteViews inner1 = + new RemoteViews(mPackage, R.layout.remote_view_relative_layout_with_theme); + RemoteViews inner2 = + new RemoteViews(mPackage, R.layout.remote_view_relative_layout); + + inner1.addView(R.id.themed_layout, inner2); + top.addView(R.id.container, inner1); + + RelativeLayout root = (RelativeLayout) top.apply(themedContext, rootParent); + assertEquals(0.5, root.getAlpha(), 0.); + RelativeLayout.LayoutParams rootParams = + (RelativeLayout.LayoutParams) root.getLayoutParams(); + assertEquals(RelativeLayout.TRUE, + rootParams.getRule(RelativeLayout.ALIGN_PARENT_BOTTOM)); + + // The theme is set on inner1View and its descendants. However, inner1View does + // not get its layout params from its theme (though its descendants do), but other + // attributes such as alpha are set. + RelativeLayout inner1View = (RelativeLayout) root.getChildAt(0); + assertEquals(R.id.themed_layout, inner1View.getId()); + assertEquals(0.25, inner1View.getAlpha(), 0.); + RelativeLayout.LayoutParams inner1Params = + (RelativeLayout.LayoutParams) inner1View.getLayoutParams(); + assertEquals(RelativeLayout.TRUE, + inner1Params.getRule(RelativeLayout.ALIGN_PARENT_BOTTOM)); + + RelativeLayout inner2View = (RelativeLayout) inner1View.getChildAt(0); + assertEquals(0.25, inner2View.getAlpha(), 0.); + RelativeLayout.LayoutParams inner2Params = + (RelativeLayout.LayoutParams) inner2View.getLayoutParams(); + assertEquals(RelativeLayout.TRUE, + inner2Params.getRule(RelativeLayout.ALIGN_PARENT_TOP)); + } + private class WidgetContainer extends AppWidgetHostView { int[] mSharedViewIds; String[] mSharedViewNames; diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java index b655369d7e60..f5cbffb64bb5 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java @@ -17,6 +17,7 @@ package com.android.internal.os; import static android.os.BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; +import static android.os.BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR; import static android.os.BatteryStats.STATS_SINCE_CHARGED; import static android.os.BatteryStats.WAKE_TYPE_PARTIAL; @@ -30,6 +31,12 @@ import android.os.BatteryStats.Uid.Sensor; import android.os.Process; import android.os.UserHandle; import android.os.WorkSource; +import android.telephony.Annotation; +import android.telephony.CellSignalStrength; +import android.telephony.DataConnectionRealTimeInfo; +import android.telephony.ServiceState; +import android.telephony.TelephonyManager; +import android.util.SparseIntArray; import android.util.SparseLongArray; import android.view.Display; @@ -1165,6 +1172,185 @@ public class BatteryStatsNoteTest extends TestCase { "D", totalBlameA, totalBlameB, uid1, blame1A, blame1B, uid2, blame2A, blame2B, bi); } + @SmallTest + public void testGetPerStateActiveRadioDurationMs() { + final MockClock clock = new MockClock(); // holds realtime and uptime in ms + final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clock); + final int ratCount = BatteryStats.RADIO_ACCESS_TECHNOLOGY_COUNT; + final int frequencyCount = ServiceState.FREQUENCY_RANGE_MMWAVE + 1; + final int txLevelCount = CellSignalStrength.getNumSignalStrengthLevels(); + + final long[][][] expectedDurationsMs = new long[ratCount][frequencyCount][txLevelCount]; + for (int rat = 0; rat < ratCount; rat++) { + for (int freq = 0; freq < frequencyCount; freq++) { + for (int txLvl = 0; txLvl < txLevelCount; txLvl++) { + expectedDurationsMs[rat][freq][txLvl] = 0; + } + } + } + + class ModemAndBatteryState { + public long currentTimeMs = 100; + public boolean onBattery = false; + public boolean modemActive = false; + @Annotation.NetworkType + public int currentNetworkDataType = TelephonyManager.NETWORK_TYPE_UNKNOWN; + @BatteryStats.RadioAccessTechnology + public int currentRat = BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER; + @ServiceState.FrequencyRange + public int currentFrequencyRange = ServiceState.FREQUENCY_RANGE_UNKNOWN; + public SparseIntArray currentSignalStrengths = new SparseIntArray(); + + void setOnBattery(boolean onBattery) { + this.onBattery = onBattery; + bi.updateTimeBasesLocked(onBattery, Display.STATE_OFF, currentTimeMs * 1000, + currentTimeMs * 1000); + } + + void setModemActive(boolean active) { + modemActive = active; + final int state = active ? DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH + : DataConnectionRealTimeInfo.DC_POWER_STATE_LOW; + bi.noteMobileRadioPowerStateLocked(state, currentTimeMs * 1000_000L, UID); + } + + void setRatType(@Annotation.NetworkType int dataType, + @BatteryStats.RadioAccessTechnology int rat) { + currentNetworkDataType = dataType; + currentRat = rat; + bi.notePhoneDataConnectionStateLocked(dataType, true, ServiceState.STATE_IN_SERVICE, + currentFrequencyRange); + } + + void setFrequencyRange(@ServiceState.FrequencyRange int frequency) { + currentFrequencyRange = frequency; + bi.notePhoneDataConnectionStateLocked(currentNetworkDataType, true, + ServiceState.STATE_IN_SERVICE, frequency); + } + + void setSignalStrength(@BatteryStats.RadioAccessTechnology int rat, int strength) { + currentSignalStrengths.put(rat, strength); + final int size = currentSignalStrengths.size(); + final int newestGenSignalStrength = currentSignalStrengths.valueAt(size - 1); + bi.notePhoneSignalStrengthLocked(newestGenSignalStrength, currentSignalStrengths); + } + } + final ModemAndBatteryState state = new ModemAndBatteryState(); + + IntConsumer incrementTime = inc -> { + state.currentTimeMs += inc; + clock.realtime = clock.uptime = state.currentTimeMs; + + // If the device is not on battery, no timers should increment. + if (!state.onBattery) return; + // If the modem is not active, no timers should increment. + if (!state.modemActive) return; + + final int currentRat = state.currentRat; + final int currentFrequencyRange = + currentRat == RADIO_ACCESS_TECHNOLOGY_NR ? state.currentFrequencyRange : 0; + int currentSignalStrength = state.currentSignalStrengths.get(currentRat); + expectedDurationsMs[currentRat][currentFrequencyRange][currentSignalStrength] += inc; + }; + + state.setOnBattery(false); + state.setModemActive(false); + state.setRatType(TelephonyManager.NETWORK_TYPE_UNKNOWN, + BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER); + state.setFrequencyRange(ServiceState.FREQUENCY_RANGE_UNKNOWN); + state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER, + CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN); + checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs); + + // While not on battery, the timers should not increase. + state.setModemActive(true); + incrementTime.accept(100); + checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs); + + state.setRatType(TelephonyManager.NETWORK_TYPE_NR, BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR); + incrementTime.accept(200); + checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs); + + state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR, + CellSignalStrength.SIGNAL_STRENGTH_GOOD); + incrementTime.accept(500); + checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs); + + state.setFrequencyRange(ServiceState.FREQUENCY_RANGE_MMWAVE); + incrementTime.accept(300); + checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs); + + state.setRatType(TelephonyManager.NETWORK_TYPE_LTE, + BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE); + incrementTime.accept(400); + checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs); + + state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE, + CellSignalStrength.SIGNAL_STRENGTH_MODERATE); + incrementTime.accept(500); + checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs); + + // When set on battery, currently active state (RAT:LTE, Signal Strength:Moderate) should + // start counting up. + state.setOnBattery(true); + incrementTime.accept(600); + checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs); + + // Changing LTE signal strength should be tracked. + state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE, + CellSignalStrength.SIGNAL_STRENGTH_POOR); + incrementTime.accept(700); + checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs); + + state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE, + CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN); + incrementTime.accept(800); + checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs); + + state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE, + CellSignalStrength.SIGNAL_STRENGTH_GOOD); + incrementTime.accept(900); + checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs); + + state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE, + CellSignalStrength.SIGNAL_STRENGTH_GREAT); + incrementTime.accept(1000); + checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs); + + // Change in the signal strength of nonactive RAT should not affect anything. + state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER, + CellSignalStrength.SIGNAL_STRENGTH_POOR); + incrementTime.accept(1100); + checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs); + + // Changing to OTHER Rat should start tracking the poor signal strength. + state.setRatType(TelephonyManager.NETWORK_TYPE_CDMA, + BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER); + incrementTime.accept(1200); + checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs); + + // Noting frequency change should not affect non NR Rat. + state.setFrequencyRange(ServiceState.FREQUENCY_RANGE_HIGH); + incrementTime.accept(1300); + checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs); + + // Now the NR Rat, HIGH frequency range, good signal strength should start counting. + state.setRatType(TelephonyManager.NETWORK_TYPE_NR, BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR); + incrementTime.accept(1400); + checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs); + + // Noting frequency change should not affect non NR Rat. + state.setFrequencyRange(ServiceState.FREQUENCY_RANGE_LOW); + incrementTime.accept(1500); + checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs); + + // Modem no longer active, should not be tracking any more. + state.setModemActive(false); + incrementTime.accept(1500); + checkPerStateActiveRadioDurations(expectedDurationsMs, bi, state.currentTimeMs); + + } + private void setFgState(int uid, boolean fgOn, MockBatteryStatsImpl bi) { // Note that noteUidProcessStateLocked uses ActivityManager process states. if (fgOn) { @@ -1238,4 +1424,30 @@ public class BatteryStatsNoteTest extends TestCase { bi.getScreenBrightnessTime(bin, currentTimeMs * 1000, STATS_SINCE_CHARGED)); } } + + private void checkPerStateActiveRadioDurations(long[][][] expectedDurationsMs, + BatteryStatsImpl bi, long currentTimeMs) { + for (int rat = 0; rat < expectedDurationsMs.length; rat++) { + final long[][] expectedRatDurationsMs = expectedDurationsMs[rat]; + for (int freq = 0; freq < expectedRatDurationsMs.length; freq++) { + final long[] expectedFreqDurationsMs = expectedRatDurationsMs[freq]; + for (int strength = 0; strength < expectedFreqDurationsMs.length; strength++) { + final long expectedSignalStrengthDurationMs = expectedFreqDurationsMs[strength]; + final long actualDurationMs = bi.getActiveRadioDurationMs(rat, freq, + strength, currentTimeMs); + + // Build a verbose fail message, just in case. + final StringBuilder sb = new StringBuilder(); + sb.append("Wrong time in state for RAT:"); + sb.append(BatteryStats.RADIO_ACCESS_TECHNOLOGY_NAMES[rat]); + sb.append(", frequency:"); + sb.append(ServiceState.frequencyRangeToString(freq)); + sb.append(", strength:"); + sb.append(strength); + + assertEquals(sb.toString(), expectedSignalStrengthDurationMs, actualDurationMs); + } + } + } + } } diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml index 4ae0fc4ae6ed..5a3a033c1cc8 100644 --- a/data/fonts/fonts.xml +++ b/data/fonts/fonts.xml @@ -272,13 +272,13 @@ <!-- fallback fonts --> <family lang="und-Arab" variant="elegant"> - <font weight="400" style="normal" postScriptName="NotoNaskhArabic"> + <font weight="400" style="normal"> NotoNaskhArabic-Regular.ttf </font> <font weight="700" style="normal">NotoNaskhArabic-Bold.ttf</font> </family> <family lang="und-Arab" variant="compact"> - <font weight="400" style="normal" postScriptName="NotoNaskhArabicUI"> + <font weight="400" style="normal"> NotoNaskhArabicUI-Regular.ttf </font> <font weight="700" style="normal">NotoNaskhArabicUI-Bold.ttf</font> @@ -329,7 +329,7 @@ <font weight="400" style="normal" postScriptName="NotoSansThai">NotoSansThai-Regular.ttf </font> <font weight="700" style="normal">NotoSansThai-Bold.ttf</font> - <font weight="400" style="normal" fallbackFor="serif" postScriptName="NotoSerifThai"> + <font weight="400" style="normal" fallbackFor="serif"> NotoSerifThai-Regular.ttf </font> <font weight="700" style="normal" fallbackFor="serif">NotoSerifThai-Bold.ttf</font> @@ -923,16 +923,16 @@ <font weight="700" style="normal">NotoSansKhmerUI-Bold.ttf</font> </family> <family lang="und-Laoo" variant="elegant"> - <font weight="400" style="normal" postScriptName="NotoSansLao">NotoSansLao-Regular.ttf + <font weight="400" style="normal">NotoSansLao-Regular.ttf </font> <font weight="700" style="normal">NotoSansLao-Bold.ttf</font> - <font weight="400" style="normal" fallbackFor="serif" postScriptName="NotoSerifLao"> + <font weight="400" style="normal" fallbackFor="serif"> NotoSerifLao-Regular.ttf </font> <font weight="700" style="normal" fallbackFor="serif">NotoSerifLao-Bold.ttf</font> </family> <family lang="und-Laoo" variant="compact"> - <font weight="400" style="normal" postScriptName="NotoSansLaoUI">NotoSansLaoUI-Regular.ttf + <font weight="400" style="normal">NotoSansLaoUI-Regular.ttf </font> <font weight="700" style="normal">NotoSansLaoUI-Bold.ttf</font> </family> @@ -1013,7 +1013,7 @@ </font> </family> <family lang="und-Cans"> - <font weight="400" style="normal" postScriptName="NotoSansCanadianAboriginal"> + <font weight="400" style="normal"> NotoSansCanadianAboriginal-Regular.ttf </font> </family> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java index eda09e3ce0b0..5ebdceba135b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java @@ -150,43 +150,45 @@ public class DragAndDropPolicy { if (inLandscape) { final Rect leftHitRegion = new Rect(); - final Rect leftDrawRegion = topOrLeftBounds; final Rect rightHitRegion = new Rect(); - final Rect rightDrawRegion = bottomOrRightBounds; // If we have existing split regions use those bounds, otherwise split it 50/50 if (inSplitScreen) { - // Add the divider bounds to each side since that counts for the hit region. - leftHitRegion.set(topOrLeftBounds); - leftHitRegion.right += dividerWidth / 2; - rightHitRegion.set(bottomOrRightBounds); - rightHitRegion.left -= dividerWidth / 2; + // The bounds of the existing split will have a divider bar, the hit region + // should include that space. Find the center of the divider bar: + float centerX = topOrLeftBounds.right + (dividerWidth / 2); + // Now set the hit regions using that center. + leftHitRegion.set(displayRegion); + leftHitRegion.right = (int) centerX; + rightHitRegion.set(displayRegion); + rightHitRegion.left = (int) centerX; } else { displayRegion.splitVertically(leftHitRegion, rightHitRegion); } - mTargets.add(new Target(TYPE_SPLIT_LEFT, leftHitRegion, leftDrawRegion)); - mTargets.add(new Target(TYPE_SPLIT_RIGHT, rightHitRegion, rightDrawRegion)); + mTargets.add(new Target(TYPE_SPLIT_LEFT, leftHitRegion, topOrLeftBounds)); + mTargets.add(new Target(TYPE_SPLIT_RIGHT, rightHitRegion, bottomOrRightBounds)); } else { final Rect topHitRegion = new Rect(); - final Rect topDrawRegion = topOrLeftBounds; final Rect bottomHitRegion = new Rect(); - final Rect bottomDrawRegion = bottomOrRightBounds; // If we have existing split regions use those bounds, otherwise split it 50/50 if (inSplitScreen) { - // Add the divider bounds to each side since that counts for the hit region. - topHitRegion.set(topOrLeftBounds); - topHitRegion.bottom += dividerWidth / 2; - bottomHitRegion.set(bottomOrRightBounds); - bottomHitRegion.top -= dividerWidth / 2; + // The bounds of the existing split will have a divider bar, the hit region + // should include that space. Find the center of the divider bar: + float centerX = topOrLeftBounds.bottom + (dividerWidth / 2); + // Now set the hit regions using that center. + topHitRegion.set(displayRegion); + topHitRegion.bottom = (int) centerX; + bottomHitRegion.set(displayRegion); + bottomHitRegion.top = (int) centerX; } else { displayRegion.splitHorizontally(topHitRegion, bottomHitRegion); } - mTargets.add(new Target(TYPE_SPLIT_TOP, topHitRegion, topDrawRegion)); - mTargets.add(new Target(TYPE_SPLIT_BOTTOM, bottomHitRegion, bottomDrawRegion)); + mTargets.add(new Target(TYPE_SPLIT_TOP, topHitRegion, topOrLeftBounds)); + mTargets.add(new Target(TYPE_SPLIT_BOTTOM, bottomHitRegion, bottomOrRightBounds)); } } else { // Split-screen not allowed, so only show the fullscreen target diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java index fd3be2b11c15..d44db498451e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java @@ -24,7 +24,6 @@ import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSIT import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; import android.annotation.SuppressLint; import android.app.ActivityManager; import android.app.ActivityTaskManager; @@ -156,10 +155,6 @@ public class DragLayout extends LinearLayout { } } - public boolean hasDropTarget() { - return mCurrentTarget != null; - } - public boolean hasDropped() { return mHasDropped; } @@ -271,6 +266,9 @@ public class DragLayout extends LinearLayout { * Updates the visible drop target as the user drags. */ public void update(DragEvent event) { + if (mHasDropped) { + return; + } // Find containing region, if the same as mCurrentRegion, then skip, otherwise, animate the // visibility of the current region DragAndDropPolicy.Target target = mPolicy.getTargetAtLocation( @@ -286,7 +284,8 @@ public class DragLayout extends LinearLayout { animateHighlight(target); } else { // Switching between targets - animateHighlight(target); + mDropZoneView1.animateSwitch(); + mDropZoneView2.animateSwitch(); } mCurrentTarget = target; } @@ -323,7 +322,7 @@ public class DragLayout extends LinearLayout { : DISABLE_NONE); mDropZoneView1.setShowingMargin(visible); mDropZoneView2.setShowingMargin(visible); - ObjectAnimator animator = mDropZoneView1.getAnimator(); + Animator animator = mDropZoneView1.getAnimator(); if (animCompleteCallback != null) { if (animator != null) { animator.addListener(new AnimatorListenerAdapter() { @@ -343,17 +342,11 @@ public class DragLayout extends LinearLayout { if (target.type == DragAndDropPolicy.Target.TYPE_SPLIT_LEFT || target.type == DragAndDropPolicy.Target.TYPE_SPLIT_TOP) { mDropZoneView1.setShowingHighlight(true); - mDropZoneView1.setShowingSplash(false); - mDropZoneView2.setShowingHighlight(false); - mDropZoneView2.setShowingSplash(true); } else if (target.type == DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT || target.type == DragAndDropPolicy.Target.TYPE_SPLIT_BOTTOM) { mDropZoneView1.setShowingHighlight(false); - mDropZoneView1.setShowingSplash(true); - mDropZoneView2.setShowingHighlight(true); - mDropZoneView2.setShowingSplash(false); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java index 2f47af57d496..a3ee8aed204d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DropZoneView.java @@ -18,6 +18,7 @@ package com.android.wm.shell.draganddrop; import static com.android.wm.shell.animation.Interpolators.FAST_OUT_SLOW_IN; +import android.animation.Animator; import android.animation.ObjectAnimator; import android.content.Context; import android.graphics.Canvas; @@ -27,7 +28,6 @@ import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.FloatProperty; -import android.util.IntProperty; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; @@ -43,8 +43,8 @@ import com.android.wm.shell.R; */ public class DropZoneView extends FrameLayout { - private static final int SPLASHSCREEN_ALPHA_INT = (int) (255 * 0.90f); - private static final int HIGHLIGHT_ALPHA_INT = 255; + private static final float SPLASHSCREEN_ALPHA = 0.90f; + private static final float HIGHLIGHT_ALPHA = 1f; private static final int MARGIN_ANIMATION_ENTER_DURATION = 400; private static final int MARGIN_ANIMATION_EXIT_DURATION = 250; @@ -61,54 +61,27 @@ public class DropZoneView extends FrameLayout { } }; - private static final IntProperty<ColorDrawable> SPLASHSCREEN_ALPHA = - new IntProperty<ColorDrawable>("splashscreen") { - @Override - public void setValue(ColorDrawable d, int alpha) { - d.setAlpha(alpha); - } - - @Override - public Integer get(ColorDrawable d) { - return d.getAlpha(); - } - }; - - private static final IntProperty<ColorDrawable> HIGHLIGHT_ALPHA = - new IntProperty<ColorDrawable>("highlight") { - @Override - public void setValue(ColorDrawable d, int alpha) { - d.setAlpha(alpha); - } - - @Override - public Integer get(ColorDrawable d) { - return d.getAlpha(); - } - }; - private final Path mPath = new Path(); private final float[] mContainerMargin = new float[4]; private float mCornerRadius; private float mBottomInset; private int mMarginColor; // i.e. color used for negative space like the container insets - private int mHighlightColor; private boolean mShowingHighlight; private boolean mShowingSplash; private boolean mShowingMargin; - // TODO: might be more seamless to animate between splash/highlight color instead of 2 separate - private ObjectAnimator mSplashAnimator; - private ObjectAnimator mHighlightAnimator; + private int mSplashScreenColor; + private int mHighlightColor; + + private ObjectAnimator mBackgroundAnimator; private ObjectAnimator mMarginAnimator; private float mMarginPercent; // Renders a highlight or neutral transparent color - private ColorDrawable mDropZoneDrawable; + private ColorDrawable mColorDrawable; // Renders the translucent splashscreen with the app icon in the middle private ImageView mSplashScreenView; - private ColorDrawable mSplashBackgroundDrawable; // Renders the margin / insets around the dropzone container private MarginView mMarginView; @@ -130,19 +103,14 @@ public class DropZoneView extends FrameLayout { mCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context); mMarginColor = getResources().getColor(R.color.taskbar_background); - mHighlightColor = getResources().getColor(android.R.color.system_accent1_500); - - mDropZoneDrawable = new ColorDrawable(); - mDropZoneDrawable.setColor(mHighlightColor); - mDropZoneDrawable.setAlpha(0); - setBackgroundDrawable(mDropZoneDrawable); + int c = getResources().getColor(android.R.color.system_accent1_500); + mHighlightColor = Color.argb(HIGHLIGHT_ALPHA, Color.red(c), Color.green(c), Color.blue(c)); + mSplashScreenColor = Color.argb(SPLASHSCREEN_ALPHA, 0, 0, 0); + mColorDrawable = new ColorDrawable(); + setBackgroundDrawable(mColorDrawable); mSplashScreenView = new ImageView(context); mSplashScreenView.setScaleType(ImageView.ScaleType.CENTER); - mSplashBackgroundDrawable = new ColorDrawable(); - mSplashBackgroundDrawable.setColor(Color.WHITE); - mSplashBackgroundDrawable.setAlpha(SPLASHSCREEN_ALPHA_INT); - mSplashScreenView.setBackgroundDrawable(mSplashBackgroundDrawable); addView(mSplashScreenView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); mSplashScreenView.setAlpha(0f); @@ -157,10 +125,6 @@ public class DropZoneView extends FrameLayout { mMarginColor = getResources().getColor(R.color.taskbar_background); mHighlightColor = getResources().getColor(android.R.color.system_accent1_500); - final int alpha = mDropZoneDrawable.getAlpha(); - mDropZoneDrawable.setColor(mHighlightColor); - mDropZoneDrawable.setAlpha(alpha); - if (mMarginPercent > 0) { mMarginView.invalidate(); } @@ -187,38 +151,39 @@ public class DropZoneView extends FrameLayout { } /** Sets the color and icon to use for the splashscreen when shown. */ - public void setAppInfo(int splashScreenColor, Drawable appIcon) { - mSplashBackgroundDrawable.setColor(splashScreenColor); + public void setAppInfo(int color, Drawable appIcon) { + Color c = Color.valueOf(color); + mSplashScreenColor = Color.argb(SPLASHSCREEN_ALPHA, c.red(), c.green(), c.blue()); mSplashScreenView.setImageDrawable(appIcon); } /** @return an active animator for this view if one exists. */ @Nullable - public ObjectAnimator getAnimator() { + public Animator getAnimator() { if (mMarginAnimator != null && mMarginAnimator.isRunning()) { return mMarginAnimator; - } else if (mHighlightAnimator != null && mHighlightAnimator.isRunning()) { - return mHighlightAnimator; - } else if (mSplashAnimator != null && mSplashAnimator.isRunning()) { - return mSplashAnimator; + } else if (mBackgroundAnimator != null && mBackgroundAnimator.isRunning()) { + return mBackgroundAnimator; } return null; } - /** Animates the splashscreen to show or hide. */ - public void setShowingSplash(boolean showingSplash) { - if (mShowingSplash != showingSplash) { - mShowingSplash = showingSplash; - animateSplashToState(); - } + /** Animates between highlight and splashscreen depending on current state. */ + public void animateSwitch() { + mShowingHighlight = !mShowingHighlight; + mShowingSplash = !mShowingHighlight; + final int newColor = mShowingHighlight ? mHighlightColor : mSplashScreenColor; + animateBackground(mColorDrawable.getColor(), newColor); + animateSplashScreenIcon(); } /** Animates the highlight indicating the zone is hovered on or not. */ public void setShowingHighlight(boolean showingHighlight) { - if (mShowingHighlight != showingHighlight) { - mShowingHighlight = showingHighlight; - animateHighlightToState(); - } + mShowingHighlight = showingHighlight; + mShowingSplash = !mShowingHighlight; + final int newColor = mShowingHighlight ? mHighlightColor : mSplashScreenColor; + animateBackground(Color.TRANSPARENT, newColor); + animateSplashScreenIcon(); } /** Animates the margins around the drop zone to show or hide. */ @@ -228,38 +193,29 @@ public class DropZoneView extends FrameLayout { animateMarginToState(); } if (!mShowingMargin) { - setShowingHighlight(false); - setShowingSplash(false); + mShowingHighlight = false; + mShowingSplash = false; + animateBackground(mColorDrawable.getColor(), Color.TRANSPARENT); + animateSplashScreenIcon(); } } - private void animateSplashToState() { - if (mSplashAnimator != null) { - mSplashAnimator.cancel(); + private void animateBackground(int startColor, int endColor) { + if (mBackgroundAnimator != null) { + mBackgroundAnimator.cancel(); } - mSplashAnimator = ObjectAnimator.ofInt(mSplashBackgroundDrawable, - SPLASHSCREEN_ALPHA, - mSplashBackgroundDrawable.getAlpha(), - mShowingSplash ? SPLASHSCREEN_ALPHA_INT : 0); - if (!mShowingSplash) { - mSplashAnimator.setInterpolator(FAST_OUT_SLOW_IN); + mBackgroundAnimator = ObjectAnimator.ofArgb(mColorDrawable, + "color", + startColor, + endColor); + if (!mShowingSplash && !mShowingHighlight) { + mBackgroundAnimator.setInterpolator(FAST_OUT_SLOW_IN); } - mSplashAnimator.start(); - mSplashScreenView.animate().alpha(mShowingSplash ? 1f : 0f).start(); + mBackgroundAnimator.start(); } - private void animateHighlightToState() { - if (mHighlightAnimator != null) { - mHighlightAnimator.cancel(); - } - mHighlightAnimator = ObjectAnimator.ofInt(mDropZoneDrawable, - HIGHLIGHT_ALPHA, - mDropZoneDrawable.getAlpha(), - mShowingHighlight ? HIGHLIGHT_ALPHA_INT : 0); - if (!mShowingHighlight) { - mHighlightAnimator.setInterpolator(FAST_OUT_SLOW_IN); - } - mHighlightAnimator.start(); + private void animateSplashScreenIcon() { + mSplashScreenView.animate().alpha(mShowingSplash ? 1f : 0f).start(); } private void animateMarginToState() { diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h index 8ad8abcff2ed..25ed935b7a76 100644 --- a/libs/hwui/renderthread/DrawFrameTask.h +++ b/libs/hwui/renderthread/DrawFrameTask.h @@ -16,19 +16,18 @@ #ifndef DRAWFRAMETASK_H #define DRAWFRAMETASK_H -#include <optional> -#include <vector> - -#include <performance_hint_private.h> +#include <android/performance_hint.h> #include <utils/Condition.h> #include <utils/Mutex.h> #include <utils/StrongPointer.h> -#include "RenderTask.h" +#include <optional> +#include <vector> #include "../FrameInfo.h" #include "../Rect.h" #include "../TreeInfo.h" +#include "RenderTask.h" namespace android { namespace uirenderer { diff --git a/media/native/midi/amidi.cpp b/media/native/midi/amidi.cpp index aa076e85e30d..fd8a06da5c6d 100644 --- a/media/native/midi/amidi.cpp +++ b/media/native/midi/amidi.cpp @@ -401,10 +401,14 @@ ssize_t AMIDI_API AMidiInputPort_send(const AMidiInputPort *inputPort, const uin ssize_t AMIDI_API AMidiInputPort_sendWithTimestamp(const AMidiInputPort *inputPort, const uint8_t *data, size_t numBytes, int64_t timestamp) { - if (inputPort == nullptr || data == nullptr) { + if (inputPort == nullptr || data == nullptr || numBytes < 0 || timestamp < 0) { return AMEDIA_ERROR_INVALID_PARAMETER; } + if (numBytes == 0) { + return 0; + } + // AMIDI_logBuffer(data, numBytes); uint8_t writeBuffer[AMIDI_BUFFER_SIZE + AMIDI_PACKET_OVERHEAD]; diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt index 35c794e82695..f9a17746892e 100644 --- a/native/android/libandroid.map.txt +++ b/native/android/libandroid.map.txt @@ -315,18 +315,18 @@ LIBANDROID { AThermal_registerThermalStatusListener; # introduced=30 AThermal_unregisterThermalStatusListener; # introduced=30 AThermal_getThermalHeadroom; # introduced=31 + APerformanceHint_getManager; # introduced=Tiramisu + APerformanceHint_createSession; # introduced=Tiramisu + APerformanceHint_getPreferredUpdateRateNanos; # introduced=Tiramisu + APerformanceHint_updateTargetWorkDuration; # introduced=Tiramisu + APerformanceHint_reportActualWorkDuration; # introduced=Tiramisu + APerformanceHint_closeSession; # introduced=Tiramisu local: *; }; LIBANDROID_PLATFORM { global: - APerformanceHint_getManager; - APerformanceHint_createSession; - APerformanceHint_getPreferredUpdateRateNanos; - APerformanceHint_updateTargetWorkDuration; - APerformanceHint_reportActualWorkDuration; - APerformanceHint_closeSession; APerformanceHint_setIHintManagerForTesting; extern "C++" { ASurfaceControl_registerSurfaceStatsListener*; diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp index 51a0c99af66e..0c360519ceb2 100644 --- a/native/android/performance_hint.cpp +++ b/native/android/performance_hint.cpp @@ -16,17 +16,18 @@ #define LOG_TAG "perf_hint" -#include <utility> -#include <vector> - #include <android/os/IHintManager.h> #include <android/os/IHintSession.h> +#include <android/performance_hint.h> #include <binder/Binder.h> #include <binder/IBinder.h> #include <binder/IServiceManager.h> #include <performance_hint_private.h> #include <utils/SystemClock.h> +#include <utility> +#include <vector> + using namespace android; using namespace android::os; diff --git a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp index 284e9ee909ee..b17850e5d1e4 100644 --- a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp +++ b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp @@ -18,10 +18,12 @@ #include <android/os/IHintManager.h> #include <android/os/IHintSession.h> +#include <android/performance_hint.h> #include <binder/IBinder.h> #include <gmock/gmock.h> #include <gtest/gtest.h> #include <performance_hint_private.h> + #include <memory> #include <vector> diff --git a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java index 84adef53e488..5ce7e59b38ff 100644 --- a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java +++ b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java @@ -41,6 +41,7 @@ import android.net.NetworkStateSnapshot; import android.net.NetworkTemplate; import android.net.UnderlyingNetworkInfo; import android.net.netstats.IUsageCallback; +import android.net.netstats.NetworkStatsDataMigrationUtils; import android.net.netstats.provider.INetworkStatsProviderCallback; import android.net.netstats.provider.NetworkStatsProvider; import android.os.Build; @@ -126,17 +127,12 @@ public class NetworkStatsManager { private final INetworkStatsService mService; /** - * Type constants for reading different types of Data Usage. + * @deprecated Use {@link NetworkStatsDataMigrationUtils#PREFIX_XT} + * instead. * @hide */ - // @SystemApi(client = MODULE_LIBRARIES) + @Deprecated public static final String PREFIX_DEV = "dev"; - /** @hide */ - public static final String PREFIX_XT = "xt"; - /** @hide */ - public static final String PREFIX_UID = "uid"; - /** @hide */ - public static final String PREFIX_UID_TAG = "uid_tag"; /** @hide */ public static final int FLAG_POLL_ON_OPEN = 1 << 0; diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java index 748b0ae02088..9f3371b724cf 100644 --- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java +++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java @@ -20,9 +20,6 @@ import static android.Manifest.permission.NETWORK_STATS_PROVIDER; import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY; import static android.Manifest.permission.UPDATE_DEVICE_STATS; import static android.app.usage.NetworkStatsManager.PREFIX_DEV; -import static android.app.usage.NetworkStatsManager.PREFIX_UID; -import static android.app.usage.NetworkStatsManager.PREFIX_UID_TAG; -import static android.app.usage.NetworkStatsManager.PREFIX_XT; import static android.content.Intent.ACTION_SHUTDOWN; import static android.content.Intent.ACTION_UID_REMOVED; import static android.content.Intent.ACTION_USER_REMOVED; @@ -50,6 +47,9 @@ import static android.net.TrafficStats.KB_IN_BYTES; import static android.net.TrafficStats.MB_IN_BYTES; import static android.net.TrafficStats.UID_TETHERING; import static android.net.TrafficStats.UNSUPPORTED; +import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_UID; +import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_UID_TAG; +import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_XT; import static android.os.Trace.TRACE_TAG_NETWORK; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static android.text.format.DateUtils.DAY_IN_MILLIS; diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp index fcf2282160a7..684f4dee9ac2 100644 --- a/packages/SettingsLib/Android.bp +++ b/packages/SettingsLib/Android.bp @@ -50,6 +50,7 @@ android_library { "SettingsLibSettingsTransition", "SettingsLibActivityEmbedding", "SettingsLibButtonPreference", + "setupdesign", ], // ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES diff --git a/packages/SettingsLib/AndroidManifest.xml b/packages/SettingsLib/AndroidManifest.xml index a3473459948d..13f8a372c9b5 100644 --- a/packages/SettingsLib/AndroidManifest.xml +++ b/packages/SettingsLib/AndroidManifest.xml @@ -18,4 +18,10 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.settingslib"> + <application> + <activity + android:name="com.android.settingslib.users.AvatarPickerActivity" + android:theme="@style/SudThemeGlifV2.DayNight"/> + </application> + </manifest> diff --git a/packages/SettingsLib/res/drawable/avatar_choose_photo_circled.xml b/packages/SettingsLib/res/drawable/avatar_choose_photo_circled.xml new file mode 100644 index 000000000000..97aec740ea43 --- /dev/null +++ b/packages/SettingsLib/res/drawable/avatar_choose_photo_circled.xml @@ -0,0 +1,30 @@ +<!-- + ~ Copyright (C) 2022 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item> + <shape android:shape="oval"> + <stroke + android:width="2dp" + android:color="?android:attr/colorPrimary"/> + </shape> + </item> + <item + android:left="@dimen/avatar_picker_icon_inset" + android:right="@dimen/avatar_picker_icon_inset" + android:top="@dimen/avatar_picker_icon_inset" + android:bottom="@dimen/avatar_picker_icon_inset" + android:drawable="@drawable/ic_avatar_choose_photo"/> +</layer-list> diff --git a/packages/SettingsLib/res/drawable/avatar_selector.xml b/packages/SettingsLib/res/drawable/avatar_selector.xml new file mode 100644 index 000000000000..ccde59763a4a --- /dev/null +++ b/packages/SettingsLib/res/drawable/avatar_selector.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 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. + --> +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_selected="true"> + <shape android:shape="oval"> + <stroke + android:color="?android:attr/colorPrimary" + android:width="@dimen/avatar_picker_padding"/> + </shape> + </item> +</selector>
\ No newline at end of file diff --git a/packages/SettingsLib/res/drawable/avatar_take_photo_circled.xml b/packages/SettingsLib/res/drawable/avatar_take_photo_circled.xml new file mode 100644 index 000000000000..7033aaeec911 --- /dev/null +++ b/packages/SettingsLib/res/drawable/avatar_take_photo_circled.xml @@ -0,0 +1,30 @@ +<!-- + ~ Copyright (C) 2022 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item> + <shape android:shape="oval"> + <stroke + android:width="2dp" + android:color="?android:attr/colorPrimary"/> + </shape> + </item> + <item + android:left="@dimen/avatar_picker_icon_inset" + android:right="@dimen/avatar_picker_icon_inset" + android:top="@dimen/avatar_picker_icon_inset" + android:bottom="@dimen/avatar_picker_icon_inset" + android:drawable="@drawable/ic_avatar_take_photo"/> +</layer-list> diff --git a/packages/SettingsLib/res/drawable/ic_account_circle_outline.xml b/packages/SettingsLib/res/drawable/ic_account_circle_outline.xml new file mode 100644 index 000000000000..0cc54b6b4972 --- /dev/null +++ b/packages/SettingsLib/res/drawable/ic_account_circle_outline.xml @@ -0,0 +1,25 @@ +<!-- + Copyright (C) 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="?android:attr/colorPrimary" + android:pathData="M5.85,17.1q1.275,-0.975 2.85,-1.538Q10.275,15 12,15q1.725,0 3.3,0.563 1.575,0.562 2.85,1.537 0.875,-1.025 1.363,-2.325Q20,13.475 20,12q0,-3.325 -2.337,-5.662Q15.325,4 12,4T6.338,6.338Q4,8.675 4,12q0,1.475 0.487,2.775 0.488,1.3 1.363,2.325zM12,13q-1.475,0 -2.488,-1.012Q8.5,10.975 8.5,9.5t1.012,-2.487Q10.525,6 12,6t2.488,1.013Q15.5,8.024 15.5,9.5t-1.012,2.488Q13.475,13 12,13zM12,22q-2.075,0 -3.9,-0.788 -1.825,-0.787 -3.175,-2.137 -1.35,-1.35 -2.137,-3.175Q2,14.075 2,12t0.788,-3.9q0.787,-1.825 2.137,-3.175 1.35,-1.35 3.175,-2.137Q9.925,2 12,2t3.9,0.788q1.825,0.787 3.175,2.137 1.35,1.35 2.137,3.175Q22,9.925 22,12t-0.788,3.9q-0.787,1.825 -2.137,3.175 -1.35,1.35 -3.175,2.137Q14.075,22 12,22zM12,20q1.325,0 2.5,-0.387 1.175,-0.388 2.15,-1.113 -0.975,-0.725 -2.15,-1.113Q13.325,17 12,17t-2.5,0.387q-1.175,0.388 -2.15,1.113 0.975,0.725 2.15,1.113Q10.675,20 12,20zM12,11q0.65,0 1.075,-0.425 0.425,-0.425 0.425,-1.075 0,-0.65 -0.425,-1.075Q12.65,8 12,8q-0.65,0 -1.075,0.425Q10.5,8.85 10.5,9.5q0,0.65 0.425,1.075Q11.35,11 12,11zM12,9.5zM12,18.5z"/> +</vector> + diff --git a/packages/SettingsLib/res/drawable/ic_avatar_choose_photo.xml b/packages/SettingsLib/res/drawable/ic_avatar_choose_photo.xml new file mode 100644 index 000000000000..b85fdc2de597 --- /dev/null +++ b/packages/SettingsLib/res/drawable/ic_avatar_choose_photo.xml @@ -0,0 +1,24 @@ +<!-- + ~ Copyright (C) 2022 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="?android:attr/colorPrimary" + android:pathData="M9,14h10l-3.45,-4.5 -2.3,3 -1.55,-2zM8,18q-0.825,0 -1.412,-0.587Q6,16.825 6,16L6,4q0,-0.825 0.588,-1.413Q7.175,2 8,2h12q0.825,0 1.413,0.587Q22,3.175 22,4v12q0,0.825 -0.587,1.413Q20.825,18 20,18zM8,16h12L20,4L8,4v12zM4,22q-0.825,0 -1.413,-0.587Q2,20.825 2,20L2,6h2v14h14v2zM8,4v12L8,4z"/> +</vector> diff --git a/packages/SettingsLib/res/drawable/ic_avatar_take_photo.xml b/packages/SettingsLib/res/drawable/ic_avatar_take_photo.xml new file mode 100644 index 000000000000..5c56276ec4f3 --- /dev/null +++ b/packages/SettingsLib/res/drawable/ic_avatar_take_photo.xml @@ -0,0 +1,24 @@ +<!-- + ~ Copyright (C) 2022 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:fillColor="?android:attr/colorPrimary" + android:pathData="M12,17.5q1.875,0 3.188,-1.313Q16.5,14.876 16.5,13q0,-1.875 -1.313,-3.188Q13.876,8.5 12,8.5q-1.875,0 -3.188,1.313Q7.5,11.124 7.5,13q0,1.875 1.313,3.188Q10.124,17.5 12,17.5zM4,21q-0.825,0 -1.413,-0.587Q2,19.825 2,19L2,7q0,-0.825 0.587,-1.412Q3.175,5 4,5h3.15L9,3h6l1.85,2L20,5q0.825,0 1.413,0.588Q22,6.175 22,7v12q0,0.825 -0.587,1.413Q20.825,21 20,21zM20,19L20,7L4,7v12zM4,19L4,7v12z"/> +</vector> diff --git a/packages/SettingsLib/res/layout/avatar_item.xml b/packages/SettingsLib/res/layout/avatar_item.xml new file mode 100644 index 000000000000..c52f6648cb7e --- /dev/null +++ b/packages/SettingsLib/res/layout/avatar_item.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 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. + --> +<ImageView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/avatar_image" + android:layout_height="@dimen/avatar_size_in_picker" + android:layout_width="@dimen/avatar_size_in_picker" + android:layout_margin="@dimen/avatar_picker_margin" + android:layout_gravity="center" + android:padding="@dimen/avatar_picker_padding" + android:background="@drawable/avatar_selector"/> diff --git a/packages/SettingsLib/res/layout/avatar_picker.xml b/packages/SettingsLib/res/layout/avatar_picker.xml new file mode 100644 index 000000000000..2d40bd0de7ea --- /dev/null +++ b/packages/SettingsLib/res/layout/avatar_picker.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 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. + --> +<com.google.android.setupdesign.GlifLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/glif_layout" + android:layout_height="match_parent" + android:layout_width="match_parent" + android:icon="@drawable/ic_account_circle_outline" + app:sucUsePartnerResource="true" + app:sucHeaderText="@string/avatar_picker_title"> + + <LinearLayout + android:layout_height="match_parent" + android:layout_width="match_parent" + android:gravity="center_horizontal" + style="@style/SudContentFrame"> + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/avatar_grid" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> + </LinearLayout> + +</com.google.android.setupdesign.GlifLayout>
\ No newline at end of file diff --git a/packages/SettingsLib/res/values-w1280dp-land/dimens.xml b/packages/SettingsLib/res/values-w1280dp-land/dimens.xml new file mode 100644 index 000000000000..4097d0564251 --- /dev/null +++ b/packages/SettingsLib/res/values-w1280dp-land/dimens.xml @@ -0,0 +1,21 @@ +<!-- + Copyright (C) 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<resources> + <integer name="avatar_picker_columns">4</integer> + <dimen name="avatar_size_in_picker">104dp</dimen> + <dimen name="avatar_picker_padding">8dp</dimen> + <dimen name="avatar_picker_margin">3dp</dimen> +</resources>
\ No newline at end of file diff --git a/packages/SettingsLib/res/values-w1440dp-land/dimens.xml b/packages/SettingsLib/res/values-w1440dp-land/dimens.xml new file mode 100644 index 000000000000..764870efe222 --- /dev/null +++ b/packages/SettingsLib/res/values-w1440dp-land/dimens.xml @@ -0,0 +1,21 @@ +<!-- + Copyright (C) 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<resources> + <integer name="avatar_picker_columns">4</integer> + <dimen name="avatar_size_in_picker">128dp</dimen> + <dimen name="avatar_picker_padding">8dp</dimen> + <dimen name="avatar_picker_margin">4dp</dimen> +</resources>
\ No newline at end of file diff --git a/packages/SettingsLib/res/values-w1600dp-land/dimens.xml b/packages/SettingsLib/res/values-w1600dp-land/dimens.xml new file mode 100644 index 000000000000..872b88a3776d --- /dev/null +++ b/packages/SettingsLib/res/values-w1600dp-land/dimens.xml @@ -0,0 +1,21 @@ +<!-- + Copyright (C) 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<resources> + <integer name="avatar_picker_columns">4</integer> + <dimen name="avatar_size_in_picker">148dp</dimen> + <dimen name="avatar_picker_padding">8dp</dimen> + <dimen name="avatar_picker_margin">6dp</dimen> +</resources>
\ No newline at end of file diff --git a/packages/SettingsLib/res/values-w480dp-port/dimens.xml b/packages/SettingsLib/res/values-w480dp-port/dimens.xml new file mode 100644 index 000000000000..cab78d64ce8d --- /dev/null +++ b/packages/SettingsLib/res/values-w480dp-port/dimens.xml @@ -0,0 +1,21 @@ +<!-- + Copyright (C) 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<resources> + <integer name="avatar_picker_columns">3</integer> + <dimen name="avatar_size_in_picker">112dp</dimen> + <dimen name="avatar_picker_padding">8dp</dimen> + <dimen name="avatar_picker_margin">3dp</dimen> +</resources> diff --git a/packages/SettingsLib/res/values-w600dp-port/dimens.xml b/packages/SettingsLib/res/values-w600dp-port/dimens.xml new file mode 100644 index 000000000000..4097d0564251 --- /dev/null +++ b/packages/SettingsLib/res/values-w600dp-port/dimens.xml @@ -0,0 +1,21 @@ +<!-- + Copyright (C) 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<resources> + <integer name="avatar_picker_columns">4</integer> + <dimen name="avatar_size_in_picker">104dp</dimen> + <dimen name="avatar_picker_padding">8dp</dimen> + <dimen name="avatar_picker_margin">3dp</dimen> +</resources>
\ No newline at end of file diff --git a/packages/SettingsLib/res/values-w720dp-port/dimens.xml b/packages/SettingsLib/res/values-w720dp-port/dimens.xml new file mode 100644 index 000000000000..764870efe222 --- /dev/null +++ b/packages/SettingsLib/res/values-w720dp-port/dimens.xml @@ -0,0 +1,21 @@ +<!-- + Copyright (C) 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<resources> + <integer name="avatar_picker_columns">4</integer> + <dimen name="avatar_size_in_picker">128dp</dimen> + <dimen name="avatar_picker_padding">8dp</dimen> + <dimen name="avatar_picker_margin">4dp</dimen> +</resources>
\ No newline at end of file diff --git a/packages/SettingsLib/res/values-w840dp-port/dimens.xml b/packages/SettingsLib/res/values-w840dp-port/dimens.xml new file mode 100644 index 000000000000..872b88a3776d --- /dev/null +++ b/packages/SettingsLib/res/values-w840dp-port/dimens.xml @@ -0,0 +1,21 @@ +<!-- + Copyright (C) 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<resources> + <integer name="avatar_picker_columns">4</integer> + <dimen name="avatar_size_in_picker">148dp</dimen> + <dimen name="avatar_picker_padding">8dp</dimen> + <dimen name="avatar_picker_margin">6dp</dimen> +</resources>
\ No newline at end of file diff --git a/packages/SettingsLib/res/values-w960dp-land/dimens.xml b/packages/SettingsLib/res/values-w960dp-land/dimens.xml new file mode 100644 index 000000000000..8403dba54329 --- /dev/null +++ b/packages/SettingsLib/res/values-w960dp-land/dimens.xml @@ -0,0 +1,21 @@ +<!-- + Copyright (C) 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<resources> + <integer name="avatar_picker_columns">4</integer> + <dimen name="avatar_size_in_picker">96dp</dimen> + <dimen name="avatar_picker_padding">6dp</dimen> + <dimen name="avatar_picker_margin">2dp</dimen> +</resources> diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml index 2b5e9cdc017d..93e3deef7aa5 100644 --- a/packages/SettingsLib/res/values/arrays.xml +++ b/packages/SettingsLib/res/values/arrays.xml @@ -647,4 +647,6 @@ <item>disabled</item> </array> + <array name="avatar_images"/> + </resources> diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml index 9ee42b648d3d..120df76218b3 100644 --- a/packages/SettingsLib/res/values/dimens.xml +++ b/packages/SettingsLib/res/values/dimens.xml @@ -102,4 +102,11 @@ <dimen name="user_photo_size_in_profile_info_dialog">112dp</dimen> <dimen name="add_a_photo_icon_size_in_profile_info_dialog">32dp</dimen> + <integer name="avatar_picker_columns">3</integer> + <dimen name="avatar_size_in_picker">96dp</dimen> + <dimen name="avatar_picker_padding">6dp</dimen> + <dimen name="avatar_picker_margin">2dp</dimen> + + <dimen name="avatar_picker_icon_inset">25dp</dimen> + </resources> diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 5ada02825b31..45f8f1debe34 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -1548,4 +1548,7 @@ <!-- Content description of the no calling for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> <string name="accessibility_no_calling">No calling.</string> + + <!-- Title for a screen allowing the user to choose a profile picture. [CHAR LIMIT=NONE] --> + <string name="avatar_picker_title">Choose a profile picture</string> </resources> diff --git a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java new file mode 100644 index 000000000000..61b8911acea4 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPhotoController.java @@ -0,0 +1,297 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.users; + +import android.app.Activity; +import android.content.ClipData; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.RectF; +import android.media.ExifInterface; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.StrictMode; +import android.provider.ContactsContract; +import android.provider.MediaStore; +import android.util.EventLog; +import android.util.Log; + +import androidx.core.content.FileProvider; + +import libcore.io.Streams; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +class AvatarPhotoController { + private static final String TAG = "AvatarPhotoController"; + + private static final int REQUEST_CODE_CHOOSE_PHOTO = 1001; + private static final int REQUEST_CODE_TAKE_PHOTO = 1002; + private static final int REQUEST_CODE_CROP_PHOTO = 1003; + // in rare cases we get a null Cursor when querying for DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI + // so we need a default photo size + private static final int DEFAULT_PHOTO_SIZE = 500; + + private static final String IMAGES_DIR = "multi_user"; + private static final String CROP_PICTURE_FILE_NAME = "CropEditUserPhoto.jpg"; + private static final String TAKE_PICTURE_FILE_NAME = "TakeEditUserPhoto.jpg"; + + private final int mPhotoSize; + + private final AvatarPickerActivity mActivity; + private final String mFileAuthority; + + private final File mImagesDir; + private final Uri mCropPictureUri; + private final Uri mTakePictureUri; + + AvatarPhotoController(AvatarPickerActivity activity, boolean waiting, String fileAuthority) { + mActivity = activity; + mFileAuthority = fileAuthority; + + mImagesDir = new File(activity.getCacheDir(), IMAGES_DIR); + mImagesDir.mkdir(); + mCropPictureUri = createTempImageUri(activity, CROP_PICTURE_FILE_NAME, !waiting); + mTakePictureUri = createTempImageUri(activity, TAKE_PICTURE_FILE_NAME, !waiting); + mPhotoSize = getPhotoSize(activity); + } + + /** + * Handles activity result from containing activity/fragment after a take/choose/crop photo + * action result is received. + */ + public boolean onActivityResult(int requestCode, int resultCode, Intent data) { + if (resultCode != Activity.RESULT_OK) { + return false; + } + final Uri pictureUri = data != null && data.getData() != null + ? data.getData() : mTakePictureUri; + + // Check if the result is a content uri + if (!ContentResolver.SCHEME_CONTENT.equals(pictureUri.getScheme())) { + Log.e(TAG, "Invalid pictureUri scheme: " + pictureUri.getScheme()); + EventLog.writeEvent(0x534e4554, "172939189", -1, pictureUri.getPath()); + return false; + } + + switch (requestCode) { + case REQUEST_CODE_CROP_PHOTO: + mActivity.returnUriResult(pictureUri); + return true; + case REQUEST_CODE_TAKE_PHOTO: + case REQUEST_CODE_CHOOSE_PHOTO: + if (mTakePictureUri.equals(pictureUri)) { + if (PhotoCapabilityUtils.canCropPhoto(mActivity)) { + cropPhoto(); + } else { + onPhotoNotCropped(pictureUri); + } + } else { + copyAndCropPhoto(pictureUri); + } + return true; + } + return false; + } + + void takePhoto() { + Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE_SECURE); + appendOutputExtra(intent, mTakePictureUri); + mActivity.startActivityForResult(intent, REQUEST_CODE_TAKE_PHOTO); + } + + void choosePhoto() { + Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES, null); + intent.setType("image/*"); + mActivity.startActivityForResult(intent, REQUEST_CODE_CHOOSE_PHOTO); + } + + private void copyAndCropPhoto(final Uri pictureUri) { + // TODO: Replace AsyncTask + new AsyncTask<Void, Void, Void>() { + @Override + protected Void doInBackground(Void... params) { + final ContentResolver cr = mActivity.getContentResolver(); + try (InputStream in = cr.openInputStream(pictureUri); + OutputStream out = cr.openOutputStream(mTakePictureUri)) { + Streams.copy(in, out); + } catch (IOException e) { + Log.w(TAG, "Failed to copy photo", e); + } + return null; + } + + @Override + protected void onPostExecute(Void result) { + if (!mActivity.isFinishing() && !mActivity.isDestroyed()) { + cropPhoto(); + } + } + }.execute(); + } + + private void cropPhoto() { + // TODO: Use a public intent, when there is one. + Intent intent = new Intent("com.android.camera.action.CROP"); + intent.setDataAndType(mTakePictureUri, "image/*"); + appendOutputExtra(intent, mCropPictureUri); + appendCropExtras(intent); + if (intent.resolveActivity(mActivity.getPackageManager()) != null) { + try { + StrictMode.disableDeathOnFileUriExposure(); + mActivity.startActivityForResult(intent, REQUEST_CODE_CROP_PHOTO); + } finally { + StrictMode.enableDeathOnFileUriExposure(); + } + } else { + onPhotoNotCropped(mTakePictureUri); + } + } + + private void appendOutputExtra(Intent intent, Uri pictureUri) { + intent.putExtra(MediaStore.EXTRA_OUTPUT, pictureUri); + intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION + | Intent.FLAG_GRANT_READ_URI_PERMISSION); + intent.setClipData(ClipData.newRawUri(MediaStore.EXTRA_OUTPUT, pictureUri)); + } + + private void appendCropExtras(Intent intent) { + intent.putExtra("crop", "true"); + intent.putExtra("scale", true); + intent.putExtra("scaleUpIfNeeded", true); + intent.putExtra("aspectX", 1); + intent.putExtra("aspectY", 1); + intent.putExtra("outputX", mPhotoSize); + intent.putExtra("outputY", mPhotoSize); + } + + private void onPhotoNotCropped(final Uri data) { + // TODO: Replace AsyncTask to avoid possible memory leaks and handle configuration change + new AsyncTask<Void, Void, Bitmap>() { + @Override + protected Bitmap doInBackground(Void... params) { + // Scale and crop to a square aspect ratio + Bitmap croppedImage = Bitmap.createBitmap(mPhotoSize, mPhotoSize, + Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(croppedImage); + Bitmap fullImage; + try { + InputStream imageStream = mActivity.getContentResolver() + .openInputStream(data); + fullImage = BitmapFactory.decodeStream(imageStream); + } catch (FileNotFoundException fe) { + return null; + } + if (fullImage != null) { + int rotation = getRotation(mActivity, data); + final int squareSize = Math.min(fullImage.getWidth(), + fullImage.getHeight()); + final int left = (fullImage.getWidth() - squareSize) / 2; + final int top = (fullImage.getHeight() - squareSize) / 2; + + Matrix matrix = new Matrix(); + RectF rectSource = new RectF(left, top, + left + squareSize, top + squareSize); + RectF rectDest = new RectF(0, 0, mPhotoSize, mPhotoSize); + matrix.setRectToRect(rectSource, rectDest, Matrix.ScaleToFit.CENTER); + matrix.postRotate(rotation, mPhotoSize / 2f, mPhotoSize / 2f); + canvas.drawBitmap(fullImage, matrix, new Paint()); + return croppedImage; + } else { + // Bah! Got nothin. + return null; + } + } + + @Override + protected void onPostExecute(Bitmap bitmap) { + saveBitmapToFile(bitmap, new File(mImagesDir, CROP_PICTURE_FILE_NAME)); + mActivity.returnUriResult(mCropPictureUri); + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null); + } + + /** + * Reads the image's exif data and determines the rotation degree needed to display the image + * in portrait mode. + */ + private int getRotation(Context context, Uri selectedImage) { + int rotation = -1; + try { + InputStream imageStream = context.getContentResolver().openInputStream(selectedImage); + ExifInterface exif = new ExifInterface(imageStream); + rotation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, -1); + } catch (IOException exception) { + Log.e(TAG, "Error while getting rotation", exception); + } + + switch (rotation) { + case ExifInterface.ORIENTATION_ROTATE_90: + return 90; + case ExifInterface.ORIENTATION_ROTATE_180: + return 180; + case ExifInterface.ORIENTATION_ROTATE_270: + return 270; + default: + return 0; + } + } + + private void saveBitmapToFile(Bitmap bitmap, File file) { + try { + OutputStream os = new FileOutputStream(file); + bitmap.compress(Bitmap.CompressFormat.PNG, 100, os); + os.flush(); + os.close(); + } catch (IOException e) { + Log.e(TAG, "Cannot create temp file", e); + } + } + + private static int getPhotoSize(Context context) { + try (Cursor cursor = context.getContentResolver().query( + ContactsContract.DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI, + new String[]{ContactsContract.DisplayPhoto.DISPLAY_MAX_DIM}, null, null, null)) { + if (cursor != null) { + cursor.moveToFirst(); + return cursor.getInt(0); + } else { + return DEFAULT_PHOTO_SIZE; + } + } + } + + private Uri createTempImageUri(Context context, String fileName, boolean purge) { + final File fullPath = new File(mImagesDir, fileName); + if (purge) { + fullPath.delete(); + } + return FileProvider.getUriForFile(context, mFileAuthority, fullPath); + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java new file mode 100644 index 000000000000..50015e653399 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.users; + +import android.app.Activity; +import android.content.ContentResolver; +import android.content.Intent; +import android.content.res.TypedArray; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; + +import androidx.annotation.NonNull; +import androidx.core.graphics.drawable.RoundedBitmapDrawable; +import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory; +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.android.internal.util.UserIcons; +import com.android.settingslib.R; + +import com.google.android.setupcompat.template.FooterBarMixin; +import com.google.android.setupcompat.template.FooterButton; +import com.google.android.setupdesign.GlifLayout; +import com.google.android.setupdesign.util.ThemeHelper; + +import java.util.ArrayList; +import java.util.List; + +/** + * Activity to allow the user to choose a user profile picture. + * + * <p>Options are provided to take a photo or choose a photo using the photo picker. In addition, + * preselected avatar images may be provided in the resource array {@code avatar_images}. If + * provided, every element of that array must be a bitmap drawable. + * + * <p>If preselected images are not provided, the default avatar will be shown instead, in a range + * of colors. + * + * <p>This activity should be started with startActivityForResult. If a photo or a preselected image + * is selected, a Uri will be returned in the data field of the result intent. If a colored default + * avatar is selected, the chosen color will be returned as {@code EXTRA_DEFAULT_ICON_TINT_COLOR} + * and the data field will be empty. + */ +public class AvatarPickerActivity extends Activity { + + static final String EXTRA_FILE_AUTHORITY = "file_authority"; + static final String EXTRA_DEFAULT_ICON_TINT_COLOR = "default_icon_tint_color"; + + private static final String KEY_AWAITING_RESULT = "awaiting_result"; + private static final String KEY_SELECTED_POSITION = "selected_position"; + + private boolean mWaitingForActivityResult; + + private FooterButton mDoneButton; + private AvatarAdapter mAdapter; + + private AvatarPhotoController mAvatarPhotoController; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + ThemeHelper.trySetDynamicColor(this); + setContentView(R.layout.avatar_picker); + setUpButtons(); + + RecyclerView recyclerView = findViewById(R.id.avatar_grid); + mAdapter = new AvatarAdapter(); + recyclerView.setAdapter(mAdapter); + recyclerView.setLayoutManager(new GridLayoutManager(this, + getResources().getInteger(R.integer.avatar_picker_columns))); + + restoreState(savedInstanceState); + + mAvatarPhotoController = new AvatarPhotoController( + this, mWaitingForActivityResult, getFileAuthority()); + } + + private void setUpButtons() { + GlifLayout glifLayout = findViewById(R.id.glif_layout); + FooterBarMixin mixin = glifLayout.getMixin(FooterBarMixin.class); + + FooterButton secondaryButton = + new FooterButton.Builder(this) + .setText("Cancel") + .setListener(view -> cancel()) + .build(); + + mDoneButton = + new FooterButton.Builder(this) + .setText("Done") + .setListener(view -> mAdapter.returnSelectionResult()) + .build(); + mDoneButton.setEnabled(false); + + mixin.setSecondaryButton(secondaryButton); + mixin.setPrimaryButton(mDoneButton); + } + + private String getFileAuthority() { + String authority = getIntent().getStringExtra(EXTRA_FILE_AUTHORITY); + if (authority == null) { + throw new IllegalStateException("File authority must be provided"); + } + return authority; + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + mWaitingForActivityResult = false; + mAvatarPhotoController.onActivityResult(requestCode, resultCode, data); + } + + @Override + protected void onSaveInstanceState(@NonNull Bundle outState) { + outState.putBoolean(KEY_AWAITING_RESULT, mWaitingForActivityResult); + outState.putInt(KEY_SELECTED_POSITION, mAdapter.mSelectedPosition); + super.onSaveInstanceState(outState); + } + + private void restoreState(Bundle savedInstanceState) { + if (savedInstanceState != null) { + mWaitingForActivityResult = savedInstanceState.getBoolean(KEY_AWAITING_RESULT, false); + mAdapter.mSelectedPosition = + savedInstanceState.getInt(KEY_SELECTED_POSITION, AvatarAdapter.NONE); + } + } + + @Override + public void startActivityForResult(Intent intent, int requestCode) { + mWaitingForActivityResult = true; + super.startActivityForResult(intent, requestCode); + } + + void returnUriResult(Uri uri) { + Intent resultData = new Intent(); + resultData.setData(uri); + setResult(RESULT_OK, resultData); + finish(); + } + + void returnColorResult(int color) { + Intent resultData = new Intent(); + resultData.putExtra(EXTRA_DEFAULT_ICON_TINT_COLOR, color); + setResult(RESULT_OK, resultData); + finish(); + } + + private void cancel() { + setResult(RESULT_CANCELED); + finish(); + } + + private class AvatarAdapter extends RecyclerView.Adapter<AvatarViewHolder> { + + private static final int NONE = -1; + + private final int mTakePhotoPosition; + private final int mChoosePhotoPosition; + private final int mPreselectedImageStartPosition; + + private final List<Drawable> mImageDrawables; + private final TypedArray mPreselectedImages; + private final int[] mUserIconColors; + private int mSelectedPosition = NONE; + + AvatarAdapter() { + final boolean canTakePhoto = + PhotoCapabilityUtils.canTakePhoto(AvatarPickerActivity.this); + final boolean canChoosePhoto = + PhotoCapabilityUtils.canChoosePhoto(AvatarPickerActivity.this); + mTakePhotoPosition = (canTakePhoto ? 0 : NONE); + mChoosePhotoPosition = (canChoosePhoto ? (canTakePhoto ? 1 : 0) : NONE); + mPreselectedImageStartPosition = (canTakePhoto ? 1 : 0) + (canChoosePhoto ? 1 : 0); + + mPreselectedImages = getResources().obtainTypedArray(R.array.avatar_images); + mUserIconColors = UserIcons.getUserIconColors(getResources()); + mImageDrawables = buildDrawableList(); + } + + @NonNull + @Override + public AvatarViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int position) { + LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext()); + View itemView = layoutInflater.inflate(R.layout.avatar_item, parent, false); + return new AvatarViewHolder(itemView); + } + + @Override + public void onBindViewHolder(@NonNull AvatarViewHolder viewHolder, int position) { + if (position == mTakePhotoPosition) { + viewHolder.setDrawable(getDrawable(R.drawable.avatar_take_photo_circled)); + viewHolder.setClickListener(view -> mAvatarPhotoController.takePhoto()); + + } else if (position == mChoosePhotoPosition) { + viewHolder.setDrawable(getDrawable(R.drawable.avatar_choose_photo_circled)); + viewHolder.setClickListener(view -> mAvatarPhotoController.choosePhoto()); + + } else if (position >= mPreselectedImageStartPosition) { + viewHolder.setSelected(position == mSelectedPosition); + viewHolder.setDrawable(mImageDrawables.get(indexFromPosition(position))); + viewHolder.setClickListener(view -> { + if (mSelectedPosition == position) { + deselect(position); + } else { + select(position); + } + }); + } + } + + @Override + public int getItemCount() { + return mPreselectedImageStartPosition + mImageDrawables.size(); + } + + private List<Drawable> buildDrawableList() { + List<Drawable> result = new ArrayList<>(); + + for (int i = 0; i < mPreselectedImages.length(); i++) { + Drawable drawable = mPreselectedImages.getDrawable(i); + if (drawable instanceof BitmapDrawable) { + result.add(circularDrawableFrom((BitmapDrawable) drawable)); + } else { + throw new IllegalStateException("Avatar drawables must be bitmaps"); + } + } + if (!result.isEmpty()) { + return result; + } + + // No preselected images. Use tinted default icon. + for (int i = 0; i < mUserIconColors.length; i++) { + result.add(UserIcons.getDefaultUserIconInColor(getResources(), mUserIconColors[i])); + } + return result; + } + + private Drawable circularDrawableFrom(BitmapDrawable drawable) { + Bitmap bitmap = drawable.getBitmap(); + + RoundedBitmapDrawable roundedBitmapDrawable = + RoundedBitmapDrawableFactory.create(getResources(), bitmap); + roundedBitmapDrawable.setCircular(true); + + return roundedBitmapDrawable; + } + + private int indexFromPosition(int position) { + return position - mPreselectedImageStartPosition; + } + + private void select(int position) { + final int oldSelection = mSelectedPosition; + mSelectedPosition = position; + notifyItemChanged(position); + if (oldSelection != NONE) { + notifyItemChanged(oldSelection); + } else { + mDoneButton.setEnabled(true); + } + } + + private void deselect(int position) { + mSelectedPosition = NONE; + notifyItemChanged(position); + mDoneButton.setEnabled(false); + } + + private void returnSelectionResult() { + int index = indexFromPosition(mSelectedPosition); + if (mPreselectedImages.length() > 0) { + int resourceId = mPreselectedImages.getResourceId(index, -1); + if (resourceId == -1) { + throw new IllegalStateException("Preselected avatar images must be resources."); + } + returnUriResult(uriForResourceId(resourceId)); + } else { + returnColorResult( + mUserIconColors[index]); + } + } + + private Uri uriForResourceId(int resourceId) { + return new Uri.Builder() + .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) + .authority(getResources().getResourcePackageName(resourceId)) + .appendPath(getResources().getResourceTypeName(resourceId)) + .appendPath(getResources().getResourceEntryName(resourceId)) + .build(); + } + } + + private static class AvatarViewHolder extends RecyclerView.ViewHolder { + private final ImageView mImageView; + + AvatarViewHolder(View view) { + super(view); + mImageView = view.findViewById(R.id.avatar_image); + } + + public void setDrawable(Drawable drawable) { + mImageView.setImageDrawable(drawable); + } + + public void setClickListener(View.OnClickListener listener) { + mImageView.setOnClickListener(listener); + } + + public void setSelected(boolean selected) { + mImageView.setSelected(selected); + } + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java b/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java index 62043363d85f..80ee86f5e489 100644 --- a/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java +++ b/packages/SettingsLib/src/com/android/settingslib/users/EditUserInfoController.java @@ -25,6 +25,7 @@ import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.UserHandle; +import android.os.UserManager; import android.view.LayoutInflater; import android.view.View; import android.view.WindowManager; @@ -36,6 +37,8 @@ import androidx.annotation.VisibleForTesting; import com.android.internal.util.UserIcons; import com.android.settingslib.R; +import com.android.settingslib.RestrictedLockUtils; +import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settingslib.drawable.CircleFramedDrawable; import java.io.File; @@ -139,12 +142,20 @@ public class EditUserInfoController { Drawable userIcon = getUserIcon(activity, defaultUserIcon); userPhotoView.setImageDrawable(userIcon); - if (canChangePhoto(activity)) { - mEditUserPhotoController = createEditUserPhotoController(activity, activityStarter, - userPhotoView); - } else { - // some users can't change their photos, so we need to remove the suggestive icon + if (isChangePhotoRestrictedByBase(activity)) { + // some users can't change their photos so we need to remove the suggestive icon content.findViewById(R.id.add_a_photo_icon).setVisibility(View.GONE); + } else { + RestrictedLockUtils.EnforcedAdmin adminRestriction = + getChangePhotoAdminRestriction(activity); + if (adminRestriction != null) { + userPhotoView.setOnClickListener(view -> + RestrictedLockUtils.sendShowAdminSupportDetailsIntent( + activity, adminRestriction)); + } else { + mEditUserPhotoController = createEditUserPhotoController(activity, activityStarter, + userPhotoView); + } } mEditUserInfoDialog = buildDialog(activity, content, userNameView, oldUserIcon, @@ -203,16 +214,21 @@ public class EditUserInfoController { } @VisibleForTesting - boolean canChangePhoto(Context context) { - return (PhotoCapabilityUtils.canCropPhoto(context) - && PhotoCapabilityUtils.canChoosePhoto(context)) - || PhotoCapabilityUtils.canTakePhoto(context); + boolean isChangePhotoRestrictedByBase(Context context) { + return RestrictedLockUtilsInternal.hasBaseUserRestriction( + context, UserManager.DISALLOW_SET_USER_ICON, UserHandle.myUserId()); + } + + @VisibleForTesting + RestrictedLockUtils.EnforcedAdmin getChangePhotoAdminRestriction(Context context) { + return RestrictedLockUtilsInternal.checkIfRestrictionEnforced( + context, UserManager.DISALLOW_SET_USER_ICON, UserHandle.myUserId()); } @VisibleForTesting EditUserPhotoController createEditUserPhotoController(Activity activity, ActivityStarter activityStarter, ImageView userPhotoView) { return new EditUserPhotoController(activity, activityStarter, userPhotoView, - mSavedPhoto, mWaitingForActivityResult, mFileAuthority); + mSavedPhoto, mFileAuthority); } } diff --git a/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java b/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java index f9584a3e15e9..f8bb38b5978e 100644 --- a/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java +++ b/packages/SettingsLib/src/com/android/settingslib/users/EditUserPhotoController.java @@ -16,46 +16,21 @@ package com.android.settingslib.users; +import android.annotation.NonNull; import android.app.Activity; -import android.content.ClipData; -import android.content.ContentResolver; -import android.content.Context; import android.content.Intent; -import android.database.Cursor; import android.graphics.Bitmap; -import android.graphics.Bitmap.Config; import android.graphics.BitmapFactory; import android.graphics.Canvas; -import android.graphics.Matrix; -import android.graphics.Paint; -import android.graphics.RectF; import android.graphics.drawable.Drawable; -import android.media.ExifInterface; import android.net.Uri; -import android.os.AsyncTask; -import android.os.StrictMode; -import android.os.UserHandle; -import android.os.UserManager; -import android.provider.ContactsContract.DisplayPhoto; -import android.provider.MediaStore; -import android.util.EventLog; import android.util.Log; -import android.view.Gravity; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; import android.widget.ImageView; -import android.widget.ListPopupWindow; -import android.widget.TextView; - -import androidx.core.content.FileProvider; +import com.android.internal.util.UserIcons; import com.android.settingslib.R; -import com.android.settingslib.RestrictedLockUtils; -import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settingslib.drawable.CircleFramedDrawable; - -import libcore.io.Streams; +import com.android.settingslib.utils.ThreadUtils; import java.io.File; import java.io.FileNotFoundException; @@ -63,8 +38,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.ArrayList; -import java.util.List; +import java.util.concurrent.ExecutionException; /** * This class contains logic for starting activities to take/choose/crop photo, reads and transforms @@ -75,45 +49,30 @@ public class EditUserPhotoController { // It seems that this class generates custom request codes and they may // collide with ours, these values are very unlikely to have a conflict. - private static final int REQUEST_CODE_CHOOSE_PHOTO = 1001; - private static final int REQUEST_CODE_TAKE_PHOTO = 1002; - private static final int REQUEST_CODE_CROP_PHOTO = 1003; - // in rare cases we get a null Cursor when querying for DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI - // so we need a default photo size - private static final int DEFAULT_PHOTO_SIZE = 500; + private static final int REQUEST_CODE_PICK_AVATAR = 1004; private static final String IMAGES_DIR = "multi_user"; - private static final String CROP_PICTURE_FILE_NAME = "CropEditUserPhoto.jpg"; - private static final String TAKE_PICTURE_FILE_NAME = "TakeEditUserPhoto.jpg"; private static final String NEW_USER_PHOTO_FILE_NAME = "NewUserPhoto.png"; - private final int mPhotoSize; - private final Activity mActivity; private final ActivityStarter mActivityStarter; private final ImageView mImageView; private final String mFileAuthority; private final File mImagesDir; - private final Uri mCropPictureUri; - private final Uri mTakePictureUri; - private Bitmap mNewUserPhotoBitmap; private Drawable mNewUserPhotoDrawable; public EditUserPhotoController(Activity activity, ActivityStarter activityStarter, - ImageView view, Bitmap bitmap, boolean waiting, String fileAuthority) { + ImageView view, Bitmap bitmap, String fileAuthority) { mActivity = activity; mActivityStarter = activityStarter; - mImageView = view; mFileAuthority = fileAuthority; mImagesDir = new File(activity.getCacheDir(), IMAGES_DIR); mImagesDir.mkdir(); - mCropPictureUri = createTempImageUri(activity, CROP_PICTURE_FILE_NAME, !waiting); - mTakePictureUri = createTempImageUri(activity, TAKE_PICTURE_FILE_NAME, !waiting); - mPhotoSize = getPhotoSize(activity); - mImageView.setOnClickListener(v -> showUpdatePhotoPopup()); + mImageView = view; + mImageView.setOnClickListener(v -> showAvatarPicker()); mNewUserPhotoBitmap = bitmap; } @@ -125,32 +84,19 @@ public class EditUserPhotoController { if (resultCode != Activity.RESULT_OK) { return false; } - final Uri pictureUri = data != null && data.getData() != null - ? data.getData() : mTakePictureUri; - // Check if the result is a content uri - if (!ContentResolver.SCHEME_CONTENT.equals(pictureUri.getScheme())) { - Log.e(TAG, "Invalid pictureUri scheme: " + pictureUri.getScheme()); - EventLog.writeEvent(0x534e4554, "172939189", -1, pictureUri.getPath()); - return false; - } - - switch (requestCode) { - case REQUEST_CODE_CROP_PHOTO: - onPhotoCropped(pictureUri); + if (requestCode == REQUEST_CODE_PICK_AVATAR) { + if (data.hasExtra(AvatarPickerActivity.EXTRA_DEFAULT_ICON_TINT_COLOR)) { + int tintColor = + data.getIntExtra(AvatarPickerActivity.EXTRA_DEFAULT_ICON_TINT_COLOR, -1); + onDefaultIconSelected(tintColor); return true; - case REQUEST_CODE_TAKE_PHOTO: - case REQUEST_CODE_CHOOSE_PHOTO: - if (mTakePictureUri.equals(pictureUri)) { - if (PhotoCapabilityUtils.canCropPhoto(mActivity)) { - cropPhoto(); - } else { - onPhotoNotCropped(pictureUri); - } - } else { - copyAndCropPhoto(pictureUri); - } + } + if (data.getData() != null) { + onPhotoCropped(data.getData()); return true; + } + } return false; } @@ -159,224 +105,60 @@ public class EditUserPhotoController { return mNewUserPhotoDrawable; } - private void showUpdatePhotoPopup() { - final Context context = mImageView.getContext(); - final boolean canTakePhoto = PhotoCapabilityUtils.canTakePhoto(context); - final boolean canChoosePhoto = PhotoCapabilityUtils.canChoosePhoto(context); - - if (!canTakePhoto && !canChoosePhoto) { - return; - } - - final List<EditUserPhotoController.RestrictedMenuItem> items = new ArrayList<>(); - - if (canTakePhoto) { - final String title = context.getString(R.string.user_image_take_photo); - items.add(new RestrictedMenuItem(context, title, UserManager.DISALLOW_SET_USER_ICON, - this::takePhoto)); - } - - if (canChoosePhoto) { - final String title = context.getString(R.string.user_image_choose_photo); - items.add(new RestrictedMenuItem(context, title, UserManager.DISALLOW_SET_USER_ICON, - this::choosePhoto)); - } - - final ListPopupWindow listPopupWindow = new ListPopupWindow(context); - - listPopupWindow.setAnchorView(mImageView); - listPopupWindow.setModal(true); - listPopupWindow.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED); - listPopupWindow.setAdapter(new RestrictedPopupMenuAdapter(context, items)); - - final int width = Math.max(mImageView.getWidth(), context.getResources() - .getDimensionPixelSize(R.dimen.update_user_photo_popup_min_width)); - listPopupWindow.setWidth(width); - listPopupWindow.setDropDownGravity(Gravity.START); - - listPopupWindow.setOnItemClickListener((parent, view, position, id) -> { - listPopupWindow.dismiss(); - final RestrictedMenuItem item = - (RestrictedMenuItem) parent.getAdapter().getItem(position); - item.doAction(); - }); - - listPopupWindow.show(); - } - - private void takePhoto() { - Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE_SECURE); - appendOutputExtra(intent, mTakePictureUri); - mActivityStarter.startActivityForResult(intent, REQUEST_CODE_TAKE_PHOTO); - } - - private void choosePhoto() { - Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null); - intent.setType("image/*"); - appendOutputExtra(intent, mTakePictureUri); - mActivityStarter.startActivityForResult(intent, REQUEST_CODE_CHOOSE_PHOTO); + private void showAvatarPicker() { + Intent intent = new Intent(mImageView.getContext(), AvatarPickerActivity.class); + intent.putExtra(AvatarPickerActivity.EXTRA_FILE_AUTHORITY, mFileAuthority); + mActivityStarter.startActivityForResult(intent, REQUEST_CODE_PICK_AVATAR); } - private void copyAndCropPhoto(final Uri pictureUri) { - // TODO: Replace AsyncTask - new AsyncTask<Void, Void, Void>() { - @Override - protected Void doInBackground(Void... params) { - final ContentResolver cr = mActivity.getContentResolver(); - try (InputStream in = cr.openInputStream(pictureUri); - OutputStream out = cr.openOutputStream(mTakePictureUri)) { - Streams.copy(in, out); - } catch (IOException e) { - Log.w(TAG, "Failed to copy photo", e); - } - return null; - } - - @Override - protected void onPostExecute(Void result) { - if (!mActivity.isFinishing() && !mActivity.isDestroyed()) { - cropPhoto(); - } - } - }.execute(); - } + private void onDefaultIconSelected(int tintColor) { + try { + ThreadUtils.postOnBackgroundThread(() -> { + Drawable drawable = + UserIcons.getDefaultUserIconInColor(mActivity.getResources(), tintColor); + Bitmap bitmap = convertToBitmap(drawable, + (int) mActivity.getResources().getDimension(R.dimen.circle_avatar_size)); - private void cropPhoto() { - // TODO: Use a public intent, when there is one. - Intent intent = new Intent("com.android.camera.action.CROP"); - intent.setDataAndType(mTakePictureUri, "image/*"); - appendOutputExtra(intent, mCropPictureUri); - appendCropExtras(intent); - if (intent.resolveActivity(mActivity.getPackageManager()) != null) { - try { - StrictMode.disableDeathOnFileUriExposure(); - mActivityStarter.startActivityForResult(intent, REQUEST_CODE_CROP_PHOTO); - } finally { - StrictMode.enableDeathOnFileUriExposure(); - } - } else { - onPhotoNotCropped(mTakePictureUri); + ThreadUtils.postOnMainThread(() -> onPhotoProcessed(bitmap)); + }).get(); + } catch (InterruptedException | ExecutionException e) { + Log.e(TAG, "Error processing default icon", e); } } - private void appendOutputExtra(Intent intent, Uri pictureUri) { - intent.putExtra(MediaStore.EXTRA_OUTPUT, pictureUri); - intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION - | Intent.FLAG_GRANT_READ_URI_PERMISSION); - intent.setClipData(ClipData.newRawUri(MediaStore.EXTRA_OUTPUT, pictureUri)); - } - - private void appendCropExtras(Intent intent) { - intent.putExtra("crop", "true"); - intent.putExtra("scale", true); - intent.putExtra("scaleUpIfNeeded", true); - intent.putExtra("aspectX", 1); - intent.putExtra("aspectY", 1); - intent.putExtra("outputX", mPhotoSize); - intent.putExtra("outputY", mPhotoSize); + private static Bitmap convertToBitmap(@NonNull Drawable icon, int size) { + Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(bitmap); + icon.setBounds(0, 0, size, size); + icon.draw(canvas); + return bitmap; } private void onPhotoCropped(final Uri data) { - // TODO: Replace AsyncTask to avoid possible memory leaks and handle configuration change - new AsyncTask<Void, Void, Bitmap>() { - @Override - protected Bitmap doInBackground(Void... params) { - InputStream imageStream = null; - try { - imageStream = mActivity.getContentResolver() - .openInputStream(data); - return BitmapFactory.decodeStream(imageStream); - } catch (FileNotFoundException fe) { - Log.w(TAG, "Cannot find image file", fe); - return null; - } finally { - if (imageStream != null) { - try { - imageStream.close(); - } catch (IOException ioe) { - Log.w(TAG, "Cannot close image stream", ioe); - } + ThreadUtils.postOnBackgroundThread(() -> { + InputStream imageStream = null; + Bitmap bitmap = null; + try { + imageStream = mActivity.getContentResolver() + .openInputStream(data); + bitmap = BitmapFactory.decodeStream(imageStream); + } catch (FileNotFoundException fe) { + Log.w(TAG, "Cannot find image file", fe); + } finally { + if (imageStream != null) { + try { + imageStream.close(); + } catch (IOException ioe) { + Log.w(TAG, "Cannot close image stream", ioe); } } } - @Override - protected void onPostExecute(Bitmap bitmap) { - onPhotoProcessed(bitmap); - - } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null); - } - - private void onPhotoNotCropped(final Uri data) { - // TODO: Replace AsyncTask to avoid possible memory leaks and handle configuration change - new AsyncTask<Void, Void, Bitmap>() { - @Override - protected Bitmap doInBackground(Void... params) { - // Scale and crop to a square aspect ratio - Bitmap croppedImage = Bitmap.createBitmap(mPhotoSize, mPhotoSize, - Config.ARGB_8888); - Canvas canvas = new Canvas(croppedImage); - Bitmap fullImage; - try { - InputStream imageStream = mActivity.getContentResolver() - .openInputStream(data); - fullImage = BitmapFactory.decodeStream(imageStream); - } catch (FileNotFoundException fe) { - return null; - } - if (fullImage != null) { - int rotation = getRotation(mActivity, data); - final int squareSize = Math.min(fullImage.getWidth(), - fullImage.getHeight()); - final int left = (fullImage.getWidth() - squareSize) / 2; - final int top = (fullImage.getHeight() - squareSize) / 2; - - Matrix matrix = new Matrix(); - RectF rectSource = new RectF(left, top, - left + squareSize, top + squareSize); - RectF rectDest = new RectF(0, 0, mPhotoSize, mPhotoSize); - matrix.setRectToRect(rectSource, rectDest, Matrix.ScaleToFit.CENTER); - matrix.postRotate(rotation, mPhotoSize / 2f, mPhotoSize / 2f); - canvas.drawBitmap(fullImage, matrix, new Paint()); - return croppedImage; - } else { - // Bah! Got nothin. - return null; - } - } - - @Override - protected void onPostExecute(Bitmap bitmap) { - onPhotoProcessed(bitmap); + if (bitmap != null) { + Bitmap finalBitmap = bitmap; + ThreadUtils.postOnMainThread(() -> onPhotoProcessed(finalBitmap)); } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null); - } - - /** - * Reads the image's exif data and determines the rotation degree needed to display the image - * in portrait mode. - */ - private int getRotation(Context context, Uri selectedImage) { - int rotation = -1; - try { - InputStream imageStream = context.getContentResolver().openInputStream(selectedImage); - ExifInterface exif = new ExifInterface(imageStream); - rotation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, -1); - } catch (IOException exception) { - Log.e(TAG, "Error while getting rotation", exception); - } - - switch (rotation) { - case ExifInterface.ORIENTATION_ROTATE_90: - return 90; - case ExifInterface.ORIENTATION_ROTATE_180: - return 180; - case ExifInterface.ORIENTATION_ROTATE_270: - return 270; - default: - return 0; - } + }); } private void onPhotoProcessed(Bitmap bitmap) { @@ -386,29 +168,6 @@ public class EditUserPhotoController { .getInstance(mImageView.getContext(), mNewUserPhotoBitmap); mImageView.setImageDrawable(mNewUserPhotoDrawable); } - new File(mImagesDir, TAKE_PICTURE_FILE_NAME).delete(); - new File(mImagesDir, CROP_PICTURE_FILE_NAME).delete(); - } - - private static int getPhotoSize(Context context) { - try (Cursor cursor = context.getContentResolver().query( - DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI, - new String[]{DisplayPhoto.DISPLAY_MAX_DIM}, null, null, null)) { - if (cursor != null) { - cursor.moveToFirst(); - return cursor.getInt(0); - } else { - return DEFAULT_PHOTO_SIZE; - } - } - } - - private Uri createTempImageUri(Context context, String fileName, boolean purge) { - final File fullPath = new File(mImagesDir, fileName); - if (purge) { - fullPath.delete(); - } - return FileProvider.getUriForFile(context, mFileAuthority, fullPath); } File saveNewUserPhotoBitmap() { @@ -435,84 +194,4 @@ public class EditUserPhotoController { void removeNewUserPhotoBitmapFile() { new File(mImagesDir, NEW_USER_PHOTO_FILE_NAME).delete(); } - - private static final class RestrictedMenuItem { - private final Context mContext; - private final String mTitle; - private final Runnable mAction; - private final RestrictedLockUtils.EnforcedAdmin mAdmin; - // Restriction may be set by system or something else via UserManager.setUserRestriction(). - private final boolean mIsRestrictedByBase; - - /** - * The menu item, used for popup menu. Any element of such a menu can be disabled by admin. - * - * @param context A context. - * @param title The title of the menu item. - * @param restriction The restriction, that if is set, blocks the menu item. - * @param action The action on menu item click. - */ - RestrictedMenuItem(Context context, String title, String restriction, - Runnable action) { - mContext = context; - mTitle = title; - mAction = action; - - final int myUserId = UserHandle.myUserId(); - mAdmin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced(context, - restriction, myUserId); - mIsRestrictedByBase = RestrictedLockUtilsInternal.hasBaseUserRestriction(mContext, - restriction, myUserId); - } - - @Override - public String toString() { - return mTitle; - } - - void doAction() { - if (isRestrictedByBase()) { - return; - } - - if (isRestrictedByAdmin()) { - RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, mAdmin); - return; - } - - mAction.run(); - } - - boolean isRestrictedByAdmin() { - return mAdmin != null; - } - - boolean isRestrictedByBase() { - return mIsRestrictedByBase; - } - } - - /** - * Provide this adapter to ListPopupWindow.setAdapter() to have a popup window menu, where - * any element can be restricted by admin (profile owner or device owner). - */ - private static final class RestrictedPopupMenuAdapter extends ArrayAdapter<RestrictedMenuItem> { - RestrictedPopupMenuAdapter(Context context, List<RestrictedMenuItem> items) { - super(context, R.layout.restricted_popup_menu_item, R.id.text, items); - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - final View view = super.getView(position, convertView, parent); - final RestrictedMenuItem item = getItem(position); - final TextView text = (TextView) view.findViewById(R.id.text); - final ImageView image = (ImageView) view.findViewById(R.id.restricted_icon); - - text.setEnabled(!item.isRestrictedByAdmin() && !item.isRestrictedByBase()); - image.setVisibility(item.isRestrictedByAdmin() && !item.isRestrictedByBase() - ? ImageView.VISIBLE : ImageView.GONE); - - return view; - } - } } diff --git a/packages/SettingsLib/src/com/android/settingslib/users/PhotoCapabilityUtils.java b/packages/SettingsLib/src/com/android/settingslib/users/PhotoCapabilityUtils.java index 165c2808f16d..b8615a7e5aa9 100644 --- a/packages/SettingsLib/src/com/android/settingslib/users/PhotoCapabilityUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/users/PhotoCapabilityUtils.java @@ -40,12 +40,12 @@ public class PhotoCapabilityUtils { /** * Check if the current user can perform any activity for - * android.intent.action.GET_CONTENT action for images. + * ACTION_PICK_IMAGES action for images. * Returns false if the device is currently locked and * requires a PIN, pattern or password to unlock. */ public static boolean canChoosePhoto(Context context) { - Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); intent.setType("image/*"); boolean canPerformActivityForGetImage = context.getPackageManager().queryIntentActivities(intent, 0).size() > 0; diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java index d6c8816ecc58..a5ee4c35f724 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/EditUserInfoControllerTest.java @@ -62,7 +62,7 @@ public class EditUserInfoControllerTest { @Mock private ActivityStarter mActivityStarter; - private boolean mCanChangePhoto; + private boolean mPhotoRestrictedByBase; private Activity mActivity; private TestEditUserInfoController mController; @@ -85,8 +85,8 @@ public class EditUserInfoControllerTest { } @Override - boolean canChangePhoto(Context context) { - return mCanChangePhoto; + boolean isChangePhotoRestrictedByBase(Context context) { + return mPhotoRestrictedByBase; } } @@ -96,7 +96,7 @@ public class EditUserInfoControllerTest { mActivity = spy(ActivityController.of(new FragmentActivity()).get()); mActivity.setTheme(R.style.Theme_AppCompat_DayNight); mController = new TestEditUserInfoController(); - mCanChangePhoto = true; + mPhotoRestrictedByBase = true; } @Test @@ -260,7 +260,7 @@ public class EditUserInfoControllerTest { @Test public void createDialog_canNotChangePhoto_nullPhotoController() { - mCanChangePhoto = false; + mPhotoRestrictedByBase = false; mController.createDialog(mActivity, mActivityStarter, mCurrentIcon, "test", "title", null, null); diff --git a/packages/SystemUI/res/layout/controls_fullscreen.xml b/packages/SystemUI/res/layout/controls_fullscreen.xml index 7fd029cb2c1e..11a566588738 100644 --- a/packages/SystemUI/res/layout/controls_fullscreen.xml +++ b/packages/SystemUI/res/layout/controls_fullscreen.xml @@ -35,7 +35,8 @@ android:layout_height="wrap_content" android:clipChildren="false" android:orientation="vertical" - android:clipToPadding="false" /> + android:clipToPadding="false" + android:paddingHorizontal="@dimen/controls_padding_horizontal" /> </com.android.systemui.globalactions.MinHeightScrollView> </LinearLayout> diff --git a/packages/SystemUI/res/layout/dream_overlay_complications_layer.xml b/packages/SystemUI/res/layout/dream_overlay_complications_layer.xml index 51359471ff98..aaff3f930d04 100644 --- a/packages/SystemUI/res/layout/dream_overlay_complications_layer.xml +++ b/packages/SystemUI/res/layout/dream_overlay_complications_layer.xml @@ -20,6 +20,30 @@ android:id="@+id/dream_overlay_complications_layer" android:layout_width="match_parent" android:layout_height="match_parent"> + <androidx.constraintlayout.widget.Guideline + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/complication_top_guide" + app:layout_constraintGuide_percent="@dimen/dream_overlay_complication_guide_top_percent" + android:orientation="horizontal"/> + <androidx.constraintlayout.widget.Guideline + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/complication_end_guide" + app:layout_constraintGuide_percent="@dimen/dream_overlay_complication_guide_end_percent" + android:orientation="vertical"/> + <androidx.constraintlayout.widget.Guideline + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/complication_bottom_guide" + app:layout_constraintGuide_percent="@dimen/dream_overlay_complication_guide_bottom_percent" + android:orientation="horizontal"/> + <androidx.constraintlayout.widget.Guideline + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/complication_start_guide" + app:layout_constraintGuide_percent="@dimen/dream_overlay_complication_guide_start_percent" + android:orientation="vertical"/> <TextClock android:id="@+id/time_view" android:layout_width="wrap_content" diff --git a/packages/SystemUI/res/values-land/config.xml b/packages/SystemUI/res/values-land/config.xml index ac4dfd212bb8..8c5006de577e 100644 --- a/packages/SystemUI/res/values-land/config.xml +++ b/packages/SystemUI/res/values-land/config.xml @@ -31,9 +31,6 @@ <!-- orientation of the dead zone when touches have recently occurred elsewhere on screen --> <integer name="navigation_bar_deadzone_orientation">1</integer> - <!-- Max number of columns for quick controls area --> - <integer name="controls_max_columns">4</integer> - <!-- Max number of columns for power menu --> <integer name="power_menu_max_columns">4</integer> </resources> diff --git a/packages/SystemUI/res/values-sw600dp-land/config.xml b/packages/SystemUI/res/values-sw600dp-land/config.xml index dabc3108458f..fe546f65bb13 100644 --- a/packages/SystemUI/res/values-sw600dp-land/config.xml +++ b/packages/SystemUI/res/values-sw600dp-land/config.xml @@ -15,9 +15,6 @@ ~ limitations under the License --> <resources> - <!-- Max number of columns for quick controls area --> - <integer name="controls_max_columns">2</integer> - <!-- The maximum number of rows in the QSPanel --> <integer name="quick_settings_max_rows">3</integer> diff --git a/packages/SystemUI/res/values-sw600dp-port/config.xml b/packages/SystemUI/res/values-sw600dp-port/config.xml index 02fd25bd077c..3c6a81e7c617 100644 --- a/packages/SystemUI/res/values-sw600dp-port/config.xml +++ b/packages/SystemUI/res/values-sw600dp-port/config.xml @@ -15,7 +15,6 @@ ~ limitations under the License --> <resources> - <!-- The maximum number of tiles in the QuickQSPanel --> <integer name="quick_qs_panel_max_tiles">6</integer> diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml index f5dc7e3e16da..1b8453ae824d 100644 --- a/packages/SystemUI/res/values-sw600dp/config.xml +++ b/packages/SystemUI/res/values-sw600dp/config.xml @@ -29,9 +29,6 @@ <!-- orientation of the dead zone when touches have recently occurred elsewhere on screen --> <integer name="navigation_bar_deadzone_orientation">0</integer> - <!-- Max number of columns for quick controls area --> - <integer name="controls_max_columns">4</integer> - <!-- How many lines to show in the security footer --> <integer name="qs_security_footer_maxLines">1</integer> diff --git a/packages/SystemUI/res/values-sw720dp-land/config.xml b/packages/SystemUI/res/values-sw720dp-land/config.xml index ae89ef4ccc86..be34a48d1cd6 100644 --- a/packages/SystemUI/res/values-sw720dp-land/config.xml +++ b/packages/SystemUI/res/values-sw720dp-land/config.xml @@ -15,9 +15,6 @@ ~ limitations under the License --> <resources> - <!-- Max number of columns for quick controls area --> - <integer name="controls_max_columns">2</integer> - <!-- The maximum number of rows in the QSPanel --> <integer name="quick_settings_max_rows">3</integer> diff --git a/packages/SystemUI/res/values-w500dp/config.xml b/packages/SystemUI/res/values-w500dp/config.xml new file mode 100644 index 000000000000..ef499ff5cdb7 --- /dev/null +++ b/packages/SystemUI/res/values-w500dp/config.xml @@ -0,0 +1,20 @@ +<!-- + ~ Copyright (C) 2022 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<resources> + <!-- Max number of columns for quick controls area --> + <integer name="controls_max_columns">3</integer> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/res/values-w500dp/dimens.xml b/packages/SystemUI/res/values-w500dp/dimens.xml new file mode 100644 index 000000000000..5ce5ceee6dc9 --- /dev/null +++ b/packages/SystemUI/res/values-w500dp/dimens.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ Copyright (C) 2022 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<resources> + <dimen name="controls_padding_horizontal">75dp</dimen> +</resources> diff --git a/packages/SystemUI/res/values-w850dp/config.xml b/packages/SystemUI/res/values-w850dp/config.xml new file mode 100644 index 000000000000..337ebe19c748 --- /dev/null +++ b/packages/SystemUI/res/values-w850dp/config.xml @@ -0,0 +1,20 @@ +<!-- + ~ Copyright (C) 2022 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<resources> + <!-- Max number of columns for quick controls area --> + <integer name="controls_max_columns">4</integer> +</resources>
\ No newline at end of file diff --git a/packages/SystemUI/res/values-w850dp/dimens.xml b/packages/SystemUI/res/values-w850dp/dimens.xml new file mode 100644 index 000000000000..bb6ba8fb07b6 --- /dev/null +++ b/packages/SystemUI/res/values-w850dp/dimens.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- + ~ Copyright (C) 2022 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<resources> + <dimen name="controls_padding_horizontal">205dp</dimen> +</resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index b35571c17db7..800dd0a6d7a3 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1035,6 +1035,7 @@ <dimen name="controls_header_bottom_margin">24dp</dimen> <dimen name="controls_header_app_icon_size">24dp</dimen> <dimen name="controls_top_margin">48dp</dimen> + <dimen name="controls_padding_horizontal">0dp</dimen> <dimen name="control_header_text_size">20sp</dimen> <dimen name="control_item_text_size">16sp</dimen> <dimen name="control_menu_item_text_size">16sp</dimen> @@ -1343,4 +1344,37 @@ <!-- Height of the area at the top of the dream overlay to allow dragging down the notifications shade. --> <dimen name="dream_overlay_notifications_drag_area_height">100dp</dimen> + + <!-- The position of the end guide, which dream overlay complications can align their start with + if their end is aligned with the parent end. Represented as the percentage over from the + start of the parent container. --> + <item name="dream_overlay_complication_guide_end_percent" format="float" type="dimen"> + 0.75 + </item> + + <!-- The position of the start guide, which dream overlay complications can align their end to + if their start is aligned with the parent start. Represented as the percentage over from + the start of the parent container. --> + <item name="dream_overlay_complication_guide_start_percent" format="float" type="dimen"> + 0.25 + </item> + + <!-- The position of the bottom guide, which dream overlay complications can align their top to + if their bottom is aligned with the parent bottom. Represented as the percentage over from + the top of the parent container. --> + <item name="dream_overlay_complication_guide_bottom_percent" format="float" type="dimen"> + 0.90 + </item> + + <!-- The position of the top guide, which dream overlay complications can align their bottom to + if their top is aligned with the parent top. Represented as the percentage over from + the top of the parent container. --> + <item name="dream_overlay_complication_guide_top_percent" format="float" type="dimen"> + 0.10 + </item> + + <!-- The percentage of the screen from which a swipe can start to reveal the bouncer. --> + <item name="dream_overlay_bouncer_start_region_screen_percentage" format="float" type="dimen"> + .2 + </item> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index b0f7e55112af..fe5e36ef23e6 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -37,6 +37,7 @@ import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricManager.Authenticators; import android.hardware.biometrics.BiometricManager.BiometricMultiSensorMode; import android.hardware.biometrics.BiometricPrompt; +import android.hardware.biometrics.IBiometricContextListener; import android.hardware.biometrics.IBiometricSysuiReceiver; import android.hardware.biometrics.PromptInfo; import android.hardware.display.DisplayManager; @@ -64,6 +65,7 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.DozeReceiver; import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.util.concurrency.Execution; @@ -96,6 +98,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba private final Handler mHandler; private final Execution mExecution; private final CommandQueue mCommandQueue; + private final StatusBarStateController mStatusBarStateController; private final ActivityTaskManager mActivityTaskManager; @Nullable private final FingerprintManager mFingerprintManager; @@ -118,6 +121,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba @Nullable private UdfpsController mUdfpsController; @Nullable private IUdfpsHbmListener mUdfpsHbmListener; @Nullable private SidefpsController mSidefpsController; + @Nullable private IBiometricContextListener mBiometricContextListener; @VisibleForTesting TaskStackListener mTaskStackListener; @VisibleForTesting @@ -130,7 +134,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba @Nullable private List<FingerprintSensorPropertiesInternal> mSidefpsProps; @NonNull private final SparseBooleanArray mUdfpsEnrolledForUser; - private SensorPrivacyManager mSensorPrivacyManager; + @NonNull private final SensorPrivacyManager mSensorPrivacyManager; private final WakefulnessLifecycle mWakefulnessLifecycle; private class BiometricTaskStackListener extends TaskStackListener { @@ -491,6 +495,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba Provider<SidefpsController> sidefpsControllerFactory, @NonNull DisplayManager displayManager, WakefulnessLifecycle wakefulnessLifecycle, + @NonNull StatusBarStateController statusBarStateController, @Main Handler handler) { super(context); mExecution = execution; @@ -504,6 +509,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba mSidefpsControllerFactory = sidefpsControllerFactory; mWindowManager = windowManager; mUdfpsEnrolledForUser = new SparseBooleanArray(); + mOrientationListener = new BiometricDisplayListener( context, displayManager, @@ -514,6 +520,14 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba return Unit.INSTANCE; }); + mStatusBarStateController = statusBarStateController; + mStatusBarStateController.addCallback(new StatusBarStateController.StateListener() { + @Override + public void onDozingChanged(boolean isDozing) { + notifyDozeChanged(isDozing); + } + }); + mFaceProps = mFaceManager != null ? mFaceManager.getSensorPropertiesInternal() : null; int[] faceAuthLocation = context.getResources().getIntArray( @@ -564,6 +578,22 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba mActivityTaskManager.registerTaskStackListener(mTaskStackListener); } + @Override + public void setBiometicContextListener(IBiometricContextListener listener) { + mBiometricContextListener = listener; + notifyDozeChanged(mStatusBarStateController.isDozing()); + } + + private void notifyDozeChanged(boolean isDozing) { + if (mBiometricContextListener != null) { + try { + mBiometricContextListener.onDozeChanged(isDozing); + } catch (RemoteException e) { + Log.w(TAG, "failed to notify initial doze state"); + } + } + } + /** * Stores the listener received from {@link com.android.server.display.DisplayModeDirector}. * diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java index 3ee0cad32097..2160744c6803 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java @@ -88,16 +88,20 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ } }; + private DreamOverlayStateController mStateController; + @Inject public DreamOverlayService( Context context, @Main Executor executor, DreamOverlayComponent.Factory dreamOverlayComponentFactory, + DreamOverlayStateController stateController, KeyguardUpdateMonitor keyguardUpdateMonitor) { mContext = context; mExecutor = executor; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mKeyguardUpdateMonitor.registerCallback(mKeyguardCallback); + mStateController = stateController; final DreamOverlayComponent component = dreamOverlayComponentFactory.create(mViewModelStore, mHost); @@ -118,6 +122,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ setCurrentState(Lifecycle.State.DESTROYED); final WindowManager windowManager = mContext.getSystemService(WindowManager.class); windowManager.removeView(mWindow.getDecorView()); + mStateController.setOverlayActive(false); super.onDestroy(); } @@ -127,6 +132,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ mExecutor.execute(() -> { addOverlayWindowLocked(layoutParams); setCurrentState(Lifecycle.State.RESUMED); + mStateController.setOverlayActive(true); }); } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java index e83884819f70..3e4ae57000f1 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java @@ -16,6 +16,8 @@ package com.android.systemui.dreams; +import android.util.Log; + import androidx.annotation.NonNull; import com.android.internal.annotations.VisibleForTesting; @@ -30,6 +32,7 @@ import java.util.Collections; import java.util.HashSet; import java.util.Objects; import java.util.concurrent.Executor; +import java.util.function.Consumer; import javax.inject.Inject; @@ -41,6 +44,16 @@ import javax.inject.Inject; @SysUISingleton public class DreamOverlayStateController implements CallbackController<DreamOverlayStateController.Callback> { + private static final String TAG = "DreamOverlayStateCtlr"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + public static final int STATE_DREAM_OVERLAY_ACTIVE = 1 << 0; + + private static final int OP_CLEAR_STATE = 1; + private static final int OP_SET_STATE = 2; + + private int mState; + /** * Callback for dream overlay events. */ @@ -50,6 +63,12 @@ public class DreamOverlayStateController implements */ default void onComplicationsChanged() { } + + /** + * Called when the dream overlay state changes. + */ + default void onStateChanged() { + } } private final Executor mExecutor; @@ -92,6 +111,14 @@ public class DreamOverlayStateController implements return Collections.unmodifiableCollection(mComplications); } + private void notifyCallbacks(Consumer<Callback> callbackConsumer) { + mExecutor.execute(() -> { + for (Callback callback : mCallbacks) { + callbackConsumer.accept(callback); + } + }); + } + @Override public void addCallback(@NonNull Callback callback) { mExecutor.execute(() -> { @@ -117,4 +144,40 @@ public class DreamOverlayStateController implements mCallbacks.remove(callback); }); } + + /** + * Returns whether the overlay is active. + * @return {@code true} if overlay is active, {@code false} otherwise. + */ + public boolean isOverlayActive() { + return containsState(STATE_DREAM_OVERLAY_ACTIVE); + } + + private boolean containsState(int state) { + return (mState & state) != 0; + } + + private void modifyState(int op, int state) { + final int existingState = mState; + switch (op) { + case OP_CLEAR_STATE: + mState &= ~state; + break; + case OP_SET_STATE: + mState |= state; + break; + } + + if (existingState != mState) { + notifyCallbacks(callback -> callback.onStateChanged()); + } + } + + /** + * Sets whether the overlay is active. + * @param active {@code true} if overlay is active, {@code false} otherwise. + */ + public void setOverlayActive(boolean active) { + modifyState(active ? OP_SET_STATE : OP_CLEAR_STATE, STATE_DREAM_OVERLAY_ACTIVE); + } } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/Complication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/Complication.java index 96cf50d58d10..4332f5880e2e 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/Complication.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/Complication.java @@ -154,6 +154,27 @@ public interface Complication { int CATEGORY_SYSTEM = 1 << 1; /** + * The type of dream complications which can be provided by a {@link Complication}. + */ + @IntDef(prefix = {"COMPLICATION_TYPE_"}, flag = true, value = { + COMPLICATION_TYPE_NONE, + COMPLICATION_TYPE_TIME, + COMPLICATION_TYPE_DATE, + COMPLICATION_TYPE_WEATHER, + COMPLICATION_TYPE_AIR_QUALITY, + COMPLICATION_TYPE_CAST_INFO + }) + @Retention(RetentionPolicy.SOURCE) + @interface ComplicationType {} + + int COMPLICATION_TYPE_NONE = 0; + int COMPLICATION_TYPE_TIME = 1; + int COMPLICATION_TYPE_DATE = 1 << 1; + int COMPLICATION_TYPE_WEATHER = 1 << 2; + int COMPLICATION_TYPE_AIR_QUALITY = 1 << 3; + int COMPLICATION_TYPE_CAST_INFO = 1 << 4; + + /** * The {@link Host} interface specifies a way a {@link Complication} to communicate with its * parent entity for information and actions. */ diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java index cb24ae609eeb..5223f379508f 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java @@ -25,10 +25,13 @@ import android.view.ViewGroup; import androidx.constraintlayout.widget.ConstraintLayout; import androidx.constraintlayout.widget.Constraints; +import com.android.systemui.R; + import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; +import java.util.function.Consumer; import javax.inject.Inject; import javax.inject.Named; @@ -102,6 +105,8 @@ public class ComplicationLayoutEngine { final int direction = getLayoutParams().getDirection(); + final boolean snapsToGuide = getLayoutParams().snapsToGuide(); + // If no parent, view is the anchor. In this case, it is given the highest priority for // alignment. All alignment preferences are done in relation to the parent container. final boolean isRoot = head == mView; @@ -125,6 +130,11 @@ public class ComplicationLayoutEngine { } else { params.startToEnd = head.getId(); } + if (snapsToGuide + && (direction == ComplicationLayoutParams.DIRECTION_DOWN + || direction == ComplicationLayoutParams.DIRECTION_UP)) { + params.endToStart = R.id.complication_start_guide; + } break; case ComplicationLayoutParams.POSITION_TOP: if (isRoot || direction != ComplicationLayoutParams.DIRECTION_DOWN) { @@ -132,6 +142,11 @@ public class ComplicationLayoutEngine { } else { params.topToBottom = head.getId(); } + if (snapsToGuide + && (direction == ComplicationLayoutParams.DIRECTION_END + || direction == ComplicationLayoutParams.DIRECTION_START)) { + params.endToStart = R.id.complication_top_guide; + } break; case ComplicationLayoutParams.POSITION_BOTTOM: if (isRoot || direction != ComplicationLayoutParams.DIRECTION_UP) { @@ -139,6 +154,11 @@ public class ComplicationLayoutEngine { } else { params.bottomToTop = head.getId(); } + if (snapsToGuide + && (direction == ComplicationLayoutParams.DIRECTION_END + || direction == ComplicationLayoutParams.DIRECTION_START)) { + params.topToBottom = R.id.complication_bottom_guide; + } break; case ComplicationLayoutParams.POSITION_END: if (isRoot || direction != ComplicationLayoutParams.DIRECTION_START) { @@ -146,6 +166,11 @@ public class ComplicationLayoutEngine { } else { params.endToStart = head.getId(); } + if (snapsToGuide + && (direction == ComplicationLayoutParams.DIRECTION_UP + || direction == ComplicationLayoutParams.DIRECTION_DOWN)) { + params.startToEnd = R.id.complication_end_guide; + } break; } }); @@ -153,6 +178,16 @@ public class ComplicationLayoutEngine { mView.setLayoutParams(params); } + private void setGuide(ConstraintLayout.LayoutParams lp, int validDirections, + Consumer<ConstraintLayout.LayoutParams> consumer) { + final ComplicationLayoutParams layoutParams = getLayoutParams(); + if (!layoutParams.snapsToGuide()) { + return; + } + + consumer.accept(lp); + } + /** * Informs the {@link ViewEntry}'s parent entity to remove the {@link ViewEntry} from * being shown further. diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java index f9a69fadfedc..8e8cb72d6ad0 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutParams.java @@ -40,13 +40,13 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams { @interface Position {} /** Align view with the top of parent or bottom of preceding {@link Complication}. */ - static final int POSITION_TOP = 1 << 0; + public static final int POSITION_TOP = 1 << 0; /** Align view with the bottom of parent or top of preceding {@link Complication}. */ - static final int POSITION_BOTTOM = 1 << 1; + public static final int POSITION_BOTTOM = 1 << 1; /** Align view with the start of parent or end of preceding {@link Complication}. */ - static final int POSITION_START = 1 << 2; + public static final int POSITION_START = 1 << 2; /** Align view with the end of parent or start of preceding {@link Complication}. */ - static final int POSITION_END = 1 << 3; + public static final int POSITION_END = 1 << 3; private static final int FIRST_POSITION = POSITION_TOP; private static final int LAST_POSITION = POSITION_END; @@ -61,13 +61,13 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams { @interface Direction {} /** Position view upward from position. */ - static final int DIRECTION_UP = 1 << 0; + public static final int DIRECTION_UP = 1 << 0; /** Position view downward from position. */ - static final int DIRECTION_DOWN = 1 << 1; + public static final int DIRECTION_DOWN = 1 << 1; /** Position view towards the start of the parent. */ - static final int DIRECTION_START = 1 << 2; + public static final int DIRECTION_START = 1 << 2; /** Position view towards the end of parent. */ - static final int DIRECTION_END = 1 << 3; + public static final int DIRECTION_END = 1 << 3; @Position private final int mPosition; @@ -77,6 +77,8 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams { private final int mWeight; + private final boolean mSnapToGuide; + // Do not allow specifying opposite positions private static final int[] INVALID_POSITIONS = { POSITION_BOTTOM | POSITION_TOP, POSITION_END | POSITION_START }; @@ -104,6 +106,27 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams { */ public ComplicationLayoutParams(int width, int height, @Position int position, @Direction int direction, int weight) { + this(width, height, position, direction, weight, false); + } + + /** + * Constructs a {@link ComplicationLayoutParams}. + * @param width The width {@link android.view.View.MeasureSpec} for the view. + * @param height The height {@link android.view.View.MeasureSpec} for the view. + * @param position The place within the parent container where the view should be positioned. + * @param direction The direction the view should be laid out from either the parent container + * or preceding view. + * @param weight The weight that should be considered for this view when compared to other + * views. This has an impact on the placement of the view but not the rendering of + * the view. + * @param snapToGuide When set to {@code true}, the dimension perpendicular to the direction + * will be automatically set to align with a predetermined guide for that + * side. For example, if the complication is aligned to the top end and + * direction is down, then the width of the complication will be set to span + * from the end of the parent to the guide. + */ + public ComplicationLayoutParams(int width, int height, @Position int position, + @Direction int direction, int weight, boolean snapToGuide) { super(width, height); if (!validatePosition(position)) { @@ -118,6 +141,8 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams { mDirection = direction; mWeight = weight; + + mSnapToGuide = snapToGuide; } /** @@ -128,6 +153,7 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams { mPosition = source.mPosition; mDirection = source.mDirection; mWeight = source.mWeight; + mSnapToGuide = source.mSnapToGuide; } private static boolean validateDirection(@Position int position, @Direction int direction) { @@ -180,7 +206,19 @@ public class ComplicationLayoutParams extends ViewGroup.LayoutParams { return mPosition; } + /** + * Returns the set weight for the complication. The weight determines ordering a complication + * given the same position/direction. + */ public int getWeight() { return mWeight; } + + /** + * Returns whether the complication's dimension perpendicular to direction should be + * automatically set. + */ + public boolean snapsToGuide() { + return mSnapToGuide; + } } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java new file mode 100644 index 000000000000..3a2a6ef60f03 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2022 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.dreams.complication; + +import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_AIR_QUALITY; +import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_CAST_INFO; +import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_DATE; +import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_NONE; +import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_TIME; +import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_WEATHER; + +import com.android.settingslib.dream.DreamBackend; + +/** + * A collection of utility methods for working with {@link Complication}. + */ +public class ComplicationUtils { + /** + * Converts a {@link com.android.settingslib.dream.DreamBackend.ComplicationType} to + * {@link ComplicationType}. + */ + @Complication.ComplicationType + public static int convertComplicationType(@DreamBackend.ComplicationType int type) { + switch (type) { + case DreamBackend.COMPLICATION_TYPE_TIME: + return COMPLICATION_TYPE_TIME; + case DreamBackend.COMPLICATION_TYPE_DATE: + return COMPLICATION_TYPE_DATE; + case DreamBackend.COMPLICATION_TYPE_WEATHER: + return COMPLICATION_TYPE_WEATHER; + case DreamBackend.COMPLICATION_TYPE_AIR_QUALITY: + return COMPLICATION_TYPE_AIR_QUALITY; + case DreamBackend.COMPLICATION_TYPE_CAST_INFO: + return COMPLICATION_TYPE_CAST_INFO; + default: + return COMPLICATION_TYPE_NONE; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java index 503817a23f7f..4eb5cb97607a 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java @@ -34,7 +34,6 @@ import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dreams.DreamOverlayContainerView; import com.android.systemui.dreams.DreamOverlayStatusBarView; -import com.android.systemui.dreams.touch.DreamTouchHandler; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.tuner.TunerService; @@ -44,7 +43,6 @@ import javax.inject.Named; import dagger.Lazy; import dagger.Module; import dagger.Provides; -import dagger.multibindings.IntoSet; /** Dagger module for {@link DreamOverlayComponent}. */ @Module @@ -149,12 +147,4 @@ public abstract class DreamOverlayModule { static Lifecycle providesLifecycle(LifecycleOwner lifecycleOwner) { return lifecycleOwner.getLifecycle(); } - - // TODO: This stub should be removed once there is a {@link DreamTouchHandler} - // implementation present. - @Provides - @IntoSet - static DreamTouchHandler provideDreamTouchHandler() { - return session -> { }; - } } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java new file mode 100644 index 000000000000..d16c8c8c59d6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2022 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.dreams.touch; + +import static com.android.systemui.dreams.touch.dagger.BouncerSwipeModule.SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_CLOSING; +import static com.android.systemui.dreams.touch.dagger.BouncerSwipeModule.SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_OPENING; +import static com.android.systemui.dreams.touch.dagger.BouncerSwipeModule.SWIPE_TO_BOUNCER_START_REGION; + +import android.animation.ValueAnimator; +import android.util.Log; +import android.view.GestureDetector; +import android.view.InputEvent; +import android.view.MotionEvent; +import android.view.VelocityTracker; + +import com.android.systemui.statusbar.NotificationShadeWindowController; +import com.android.systemui.statusbar.phone.KeyguardBouncer; +import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; +import com.android.wm.shell.animation.FlingAnimationUtils; + +import javax.inject.Inject; +import javax.inject.Named; + +/** + * Monitor for tracking touches on the DreamOverlay to bring up the bouncer. + */ +public class BouncerSwipeTouchHandler implements DreamTouchHandler { + /** + * An interface for creating ValueAnimators. + */ + public interface ValueAnimatorCreator { + /** + * Creates {@link ValueAnimator}. + */ + ValueAnimator create(float start, float finish); + } + + /** + * An interface for obtaining VelocityTrackers. + */ + public interface VelocityTrackerFactory { + /** + * Obtains {@link VelocityTracker}. + */ + VelocityTracker obtain(); + } + + public static final float FLING_PERCENTAGE_THRESHOLD = 0.5f; + + private static final String TAG = "BouncerSwipeTouchHandler"; + private final NotificationShadeWindowController mNotificationShadeWindowController; + private final float mBouncerZoneScreenPercentage; + + private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; + private float mCurrentExpansion; + private final StatusBar mStatusBar; + + private VelocityTracker mVelocityTracker; + + private final FlingAnimationUtils mFlingAnimationUtils; + private final FlingAnimationUtils mFlingAnimationUtilsClosing; + + private Boolean mCapture; + + private TouchSession mTouchSession; + + private ValueAnimatorCreator mValueAnimatorCreator; + + private VelocityTrackerFactory mVelocityTrackerFactory; + + private final GestureDetector.OnGestureListener mOnGestureListener = + new GestureDetector.SimpleOnGestureListener() { + boolean mTrack; + boolean mBouncerPresent; + + @Override + public boolean onDown(MotionEvent e) { + // We only consider gestures that originate from the lower portion of the + // screen. + final float displayHeight = mStatusBar.getDisplayHeight(); + + mBouncerPresent = mStatusBar.isBouncerShowing(); + + // The target zone is either at the top or bottom of the screen, dependent on + // whether the bouncer is present. + final float zonePercentage = + Math.abs(e.getY() - (mBouncerPresent ? 0 : displayHeight)) + / displayHeight; + + mTrack = zonePercentage < mBouncerZoneScreenPercentage; + + // Never capture onDown. While this might lead to some false positive touches + // being sent to other windows/layers, this is necessary to make sure the + // proper touch event sequence is received by others in the event we do not + // consume the sequence here. + return false; + } + + @Override + public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, + float distanceY) { + // Do not handle scroll gestures if not tracking touch events. + if (!mTrack) { + return false; + } + + if (mCapture == null) { + // If the user scrolling favors a vertical direction, begin capturing + // scrolls. + mCapture = Math.abs(distanceY) > Math.abs(distanceX); + + if (mCapture) { + // Since the user is dragging the bouncer up, set scrimmed to false. + mStatusBarKeyguardViewManager.showBouncer(false); + } + } + + if (!mCapture) { + return false; + } + + // For consistency, we adopt the expansion definition found in the + // PanelViewController. In this case, expansion refers to the view above the + // bouncer. As that view's expansion shrinks, the bouncer appears. The bouncer + // is fully hidden at full expansion (1) and fully visible when fully collapsed + // (0). + final float screenTravelPercentage = + Math.abs((e1.getY() - e2.getY()) / mStatusBar.getDisplayHeight()); + setPanelExpansion( + mBouncerPresent ? screenTravelPercentage : 1 - screenTravelPercentage); + + return true; + } + }; + + private void setPanelExpansion(float expansion) { + mCurrentExpansion = expansion; + mStatusBarKeyguardViewManager.onPanelExpansionChanged(mCurrentExpansion, false, true); + } + + @Inject + public BouncerSwipeTouchHandler( + StatusBarKeyguardViewManager statusBarKeyguardViewManager, + StatusBar statusBar, + NotificationShadeWindowController notificationShadeWindowController, + ValueAnimatorCreator valueAnimatorCreator, + VelocityTrackerFactory velocityTrackerFactory, + @Named(SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_CLOSING) + FlingAnimationUtils flingAnimationUtils, + @Named(SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_OPENING) + FlingAnimationUtils flingAnimationUtilsClosing, + @Named(SWIPE_TO_BOUNCER_START_REGION) float swipeRegionPercentage) { + mStatusBar = statusBar; + mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; + mNotificationShadeWindowController = notificationShadeWindowController; + mBouncerZoneScreenPercentage = swipeRegionPercentage; + mFlingAnimationUtils = flingAnimationUtils; + mFlingAnimationUtilsClosing = flingAnimationUtilsClosing; + mValueAnimatorCreator = valueAnimatorCreator; + mVelocityTrackerFactory = velocityTrackerFactory; + } + + @Override + public void onSessionStart(TouchSession session) { + mVelocityTracker = mVelocityTrackerFactory.obtain(); + mTouchSession = session; + mVelocityTracker.clear(); + mNotificationShadeWindowController.setForcePluginOpen(true, this); + session.registerGestureListener(mOnGestureListener); + session.registerInputListener(ev -> onMotionEvent(ev)); + + } + + @Override + public void onSessionEnd(TouchSession session) { + mVelocityTracker.recycle(); + mCapture = null; + mNotificationShadeWindowController.setForcePluginOpen(false, this); + } + + private void onMotionEvent(InputEvent event) { + if (!(event instanceof MotionEvent)) { + Log.e(TAG, "non MotionEvent received:" + event); + return; + } + + final MotionEvent motionEvent = (MotionEvent) event; + + switch(motionEvent.getAction()) { + case MotionEvent.ACTION_UP: + // If we are not capturing any input, there is no need to consider animating to + // finish transition. + if (mCapture == null || !mCapture) { + break; + } + + // We must capture the resulting velocities as resetMonitor() will clear these + // values. + mVelocityTracker.computeCurrentVelocity(1000); + final float verticalVelocity = mVelocityTracker.getYVelocity(); + final float horizontalVelocity = mVelocityTracker.getXVelocity(); + + final float velocityVector = + (float) Math.hypot(horizontalVelocity, verticalVelocity); + + + final float expansion = flingRevealsOverlay(verticalVelocity, velocityVector) + ? KeyguardBouncer.EXPANSION_HIDDEN : KeyguardBouncer.EXPANSION_VISIBLE; + flingToExpansion(verticalVelocity, expansion); + + if (expansion == KeyguardBouncer.EXPANSION_HIDDEN) { + mStatusBarKeyguardViewManager.reset(false); + } + mTouchSession.pop(); + break; + default: + mVelocityTracker.addMovement(motionEvent); + break; + } + } + + private ValueAnimator createExpansionAnimator(float targetExpansion) { + final ValueAnimator animator = + mValueAnimatorCreator.create(mCurrentExpansion, targetExpansion); + animator.addUpdateListener( + animation -> { + setPanelExpansion((float) animation.getAnimatedValue()); + }); + return animator; + } + + protected boolean flingRevealsOverlay(float velocity, float velocityVector) { + // Fully expand if the user has expanded the bouncer less than halfway or final velocity was + // positive, indicating an downward direction. + if (Math.abs(velocityVector) < mFlingAnimationUtils.getMinVelocityPxPerSecond()) { + return mCurrentExpansion > FLING_PERCENTAGE_THRESHOLD; + } else { + return velocity > 0; + } + } + + protected void flingToExpansion(float velocity, float expansion) { + final float viewHeight = mStatusBar.getDisplayHeight(); + final float currentHeight = viewHeight * mCurrentExpansion; + final float targetHeight = viewHeight * expansion; + + final ValueAnimator animator = createExpansionAnimator(expansion); + if (expansion == KeyguardBouncer.EXPANSION_HIDDEN) { + // The animation utils deal in pixel units, rather than expansion height. + mFlingAnimationUtils.apply(animator, currentHeight, targetHeight, velocity, viewHeight); + } else { + mFlingAnimationUtilsClosing.apply( + animator, mCurrentExpansion, currentHeight, targetHeight, viewHeight); + } + + animator.start(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/BouncerSwipeModule.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/BouncerSwipeModule.java new file mode 100644 index 000000000000..b9436f96c74f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/BouncerSwipeModule.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2022 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.dreams.touch.dagger; + +import android.animation.ValueAnimator; +import android.content.res.Resources; +import android.util.TypedValue; +import android.view.VelocityTracker; + +import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.dreams.touch.BouncerSwipeTouchHandler; +import com.android.systemui.dreams.touch.DreamTouchHandler; +import com.android.systemui.statusbar.phone.PanelViewController; +import com.android.wm.shell.animation.FlingAnimationUtils; + +import javax.inject.Named; +import javax.inject.Provider; + +import dagger.Module; +import dagger.Provides; +import dagger.multibindings.IntoSet; + +/** + * This module captures the components associated with {@link BouncerSwipeTouchHandler}. + */ +@Module +public class BouncerSwipeModule { + /** + * The region, defined as the percentage of the screen, from which a touch gesture to start + * swiping up to the bouncer can occur. + */ + public static final String SWIPE_TO_BOUNCER_START_REGION = "swipe_to_bouncer_start_region"; + + /** + * The {@link android.view.animation.AnimationUtils} for animating the bouncer closing. + */ + public static final String SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_CLOSING = + "swipe_to_bouncer_fling_animation_utils_closing"; + + /** + * The {@link android.view.animation.AnimationUtils} for animating the bouncer opening. + */ + public static final String SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_OPENING = + "swipe_to_bouncer_fling_animation_utils_opening"; + + /** + * Provides {@link BouncerSwipeTouchHandler} for inclusion in touch handling over the dream. + */ + @Provides + @IntoSet + public static DreamTouchHandler providesBouncerSwipeTouchHandler( + BouncerSwipeTouchHandler touchHandler) { + return touchHandler; + } + + /** + * Provides {@link android.view.animation.AnimationUtils} for closing. + */ + @Provides + @Named(SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_CLOSING) + public static FlingAnimationUtils providesSwipeToBouncerFlingAnimationUtilsClosing( + Provider<FlingAnimationUtils.Builder> flingAnimationUtilsBuilderProvider) { + return flingAnimationUtilsBuilderProvider.get() + .reset() + .setMaxLengthSeconds(PanelViewController.FLING_CLOSING_MAX_LENGTH_SECONDS) + .setSpeedUpFactor(PanelViewController.FLING_SPEED_UP_FACTOR) + .build(); + } + + /** + * Provides {@link android.view.animation.AnimationUtils} for opening. + */ + @Provides + @Named(SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_OPENING) + public static FlingAnimationUtils providesSwipeToBouncerFlingAnimationUtilsOpening( + Provider<FlingAnimationUtils.Builder> flingAnimationUtilsBuilderProvider) { + return flingAnimationUtilsBuilderProvider.get() + .reset() + .setMaxLengthSeconds(PanelViewController.FLING_MAX_LENGTH_SECONDS) + .setSpeedUpFactor(PanelViewController.FLING_SPEED_UP_FACTOR) + .build(); + } + + /** + * Provides the region to start swipe gestures from. + */ + @Provides + @Named(SWIPE_TO_BOUNCER_START_REGION) + public static float providesSwipeToBouncerStartRegion(@Main Resources resources) { + TypedValue typedValue = new TypedValue(); + resources.getValue(R.dimen.dream_overlay_bouncer_start_region_screen_percentage, + typedValue, true); + return typedValue.getFloat(); + } + + /** + * Provides the default {@link BouncerSwipeTouchHandler.ValueAnimatorCreator}, which is simply + * a wrapper around {@link ValueAnimator}. + */ + @Provides + public static BouncerSwipeTouchHandler.ValueAnimatorCreator providesValueAnimatorCreator() { + return (start, finish) -> ValueAnimator.ofFloat(start, finish); + } + + /** + * Provides the default {@link BouncerSwipeTouchHandler.VelocityTrackerFactory}. which is a + * passthrough to {@link android.view.VelocityTracker}. + */ + @Provides + public static BouncerSwipeTouchHandler.VelocityTrackerFactory providesVelocityTrackerFactory() { + return () -> VelocityTracker.obtain(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/DreamTouchModule.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/DreamTouchModule.java index 7b77b593b330..dad0004613f6 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/DreamTouchModule.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/DreamTouchModule.java @@ -21,8 +21,10 @@ import dagger.Module; /** * {@link DreamTouchModule} encapsulates dream touch-related components. */ -@Module(subcomponents = { - InputSessionComponent.class, +@Module(includes = { + BouncerSwipeModule.class, + }, subcomponents = { + InputSessionComponent.class, }) public interface DreamTouchModule { String INPUT_SESSION_NAME = "INPUT_SESSION_NAME"; diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 701d1391d271..fd2c6dd3ca36 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -106,6 +106,7 @@ import com.android.systemui.animation.Interpolators; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.dagger.qualifiers.UiBackground; +import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.dagger.KeyguardModule; import com.android.systemui.navigationbar.NavigationModeController; @@ -138,7 +139,7 @@ import dagger.Lazy; * state of the keyguard, power management events that effect whether the keyguard * should be shown or reset, callbacks to the phone window manager to notify * it of when the keyguard is showing, and events from the keyguard view itself - * stating that the keyguard was succesfully unlocked. + * stating that the keyguard was successfully unlocked. * * Note that the keyguard view is shown when the screen is off (as appropriate) * so that once the screen comes on, it will be ready immediately. @@ -152,15 +153,15 @@ import dagger.Lazy; * - the keyguard is showing * * Example external events that translate to keyguard view changes: - * - screen turned off -> reset the keyguard, and show it so it will be ready + * - screen turned off -> reset the keyguard, and show it, so it will be ready * next time the screen turns on * - keyboard is slid open -> if the keyguard is not secure, hide it * * Events from the keyguard view: - * - user succesfully unlocked keyguard -> hide keyguard view, and no longer + * - user successfully unlocked keyguard -> hide keyguard view, and no longer * restrict input events. * - * Note: in addition to normal power managment events that effect the state of + * Note: in addition to normal power management events that effect the state of * whether the keyguard should be showing, external apps and services may request * that the keyguard be disabled via {@link #setKeyguardEnabled(boolean)}. When * false, this will override all other conditions for turning on the keyguard. @@ -224,7 +225,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, /** * How long we'll wait for the {@link ViewMediatorCallback#keyguardDoneDrawing()} * callback before unblocking a call to {@link #setKeyguardEnabled(boolean)} - * that is reenabling the keyguard. + * that is re-enabling the keyguard. */ private static final int KEYGUARD_DONE_DRAWING_TIMEOUT_MS = 2000; @@ -233,6 +234,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, * keyguard to show even if it is disabled for the current user. */ public static final String OPTION_FORCE_SHOW = "force_show"; + private final DreamOverlayStateController mDreamOverlayStateController; /** The stream type that the lock sounds are tied to. */ private int mUiSoundsStreamType; @@ -273,14 +275,14 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, // these are protected by synchronized (this) /** - * External apps (like the phone app) can tell us to disable the keygaurd. + * External apps (like the phone app) can tell us to disable the keyguard. */ private boolean mExternallyEnabled = true; /** * Remember if an external call to {@link #setKeyguardEnabled} with value * false caused us to hide the keyguard, so that we need to reshow it once - * the keygaurd is reenabled with another call with value true. + * the keyguard is re-enabled with another call with value true. */ private boolean mNeedToReshowWhenReenabled = false; @@ -291,6 +293,9 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, // AOD is enabled and status bar is in AOD state. private boolean mAodShowing; + // Dream overlay is visible. + private boolean mDreamOverlayShowing; + /** Cached value of #isInputRestricted */ private boolean mInputRestricted; @@ -304,7 +309,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, private int mDelayedShowingSequence; /** - * Simiar to {@link #mDelayedProfileShowingSequence}, but it is for profile case. + * Similar to {@link #mDelayedProfileShowingSequence}, but it is for profile case. */ private int mDelayedProfileShowingSequence; @@ -341,7 +346,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, private String mPhoneState = TelephonyManager.EXTRA_STATE_IDLE; /** - * Whether a hide is pending an we are just waiting for #startKeyguardExitAnimation to be + * Whether a hide is pending and we are just waiting for #startKeyguardExitAnimation to be * called. * */ private boolean mHiding; @@ -355,7 +360,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS); /** - * {@link #setKeyguardEnabled} waits on this condition when it reenables + * {@link #setKeyguardEnabled} waits on this condition when it re-enables * the keyguard. */ private boolean mWaitingUntilKeyguardVisible = false; @@ -470,6 +475,14 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, } }; + private final DreamOverlayStateController.Callback mDreamOverlayStateCallback = + new DreamOverlayStateController.Callback() { + @Override + public void onStateChanged() { + mDreamOverlayShowing = mDreamOverlayStateController.isOverlayActive(); + } + }; + KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() { @Override @@ -494,7 +507,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, synchronized (KeyguardViewMediator.this) { resetKeyguardDonePendingLocked(); if (mLockPatternUtils.isLockScreenDisabled(userId)) { - // If we switching to a user that has keyguard disabled, dismiss keyguard. + // If we are switching to a user that has keyguard disabled, dismiss keyguard. dismiss(null /* callback */, null /* message */); } else { resetStateLocked(); @@ -508,7 +521,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, if (DEBUG) Log.d(TAG, String.format("onUserSwitchComplete %d", userId)); if (userId != UserHandle.USER_SYSTEM) { UserInfo info = UserManager.get(mContext).getUserInfo(userId); - // Don't try to dismiss if the user has Pin/Patter/Password set + // Don't try to dismiss if the user has Pin/Pattern/Password set if (info == null || mLockPatternUtils.isSecure(userId)) { return; } else if (info.isGuest() || info.isDemo()) { @@ -836,6 +849,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, Lazy<NotificationShadeDepthController> notificationShadeDepthController, ScreenOnCoordinator screenOnCoordinator, InteractionJankMonitor interactionJankMonitor, + DreamOverlayStateController dreamOverlayStateController, Lazy<NotificationShadeWindowController> notificationShadeWindowControllerLazy) { super(context); mFalsingCollector = falsingCollector; @@ -875,6 +889,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, mKeyguardUnlockAnimationControllerLazy = keyguardUnlockAnimationControllerLazy; mScreenOffAnimationController = screenOffAnimationController; mInteractionJankMonitor = interactionJankMonitor; + mDreamOverlayStateController = dreamOverlayStateController; } public void userActivity() { @@ -980,6 +995,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, mSystemReady = true; doKeyguardLocked(null); mUpdateMonitor.registerCallback(mUpdateCallback); + mDreamOverlayStateController.addCallback(mDreamOverlayStateCallback); } // Most services aren't available until the system reaches the ready state, so we // send it here when the device first boots. @@ -1041,7 +1057,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, mUpdateMonitor.dispatchStartedGoingToSleep(offReason); - // Reset keyguard going away state so we can start listening for fingerprint. We + // Reset keyguard going away state, so we can start listening for fingerprint. We // explicitly DO NOT want to call // mKeyguardViewControllerLazy.get().setKeyguardGoingAwayState(false) // here, since that will mess with the device lock state. @@ -1121,9 +1137,9 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, } private long getLockTimeout(int userId) { - // if the screen turned off because of timeout or the user hit the power button + // if the screen turned off because of timeout or the user hit the power button, // and we don't need to lock immediately, set an alarm - // to enable it a little bit later (i.e, give the user a chance + // to enable it a bit later (i.e, give the user a chance // to turn the screen back on within a certain window without // having to unlock the screen) final ContentResolver cr = mContext.getContentResolver(); @@ -1217,7 +1233,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, } /** - * Let's us know when the device is waking up. + * It will let us know when the device is waking up. */ public void onStartedWakingUp(boolean cameraGestureTriggered) { Trace.beginSection("KeyguardViewMediator#onStartedWakingUp"); @@ -1299,7 +1315,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, if (mExitSecureCallback != null) { if (DEBUG) Log.d(TAG, "in process of verifyUnlock request, ignoring"); // we're in the process of handling a request to verify the user - // can get past the keyguard. ignore extraneous requests to disable / reenable + // can get past the keyguard. ignore extraneous requests to disable / re-enable return; } @@ -1310,7 +1326,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, updateInputRestrictedLocked(); hideLocked(); } else if (enabled && mNeedToReshowWhenReenabled) { - // reenabled after previously hidden, reshow + // re-enabled after previously hidden, reshow if (DEBUG) Log.d(TAG, "previously hidden, reshowing, reenabling " + "status bar expansion"); mNeedToReshowWhenReenabled = false; @@ -1328,8 +1344,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, } else { showLocked(null); - // block until we know the keygaurd is done drawing (and post a message - // to unblock us after a timeout so we don't risk blocking too long + // block until we know the keyguard is done drawing (and post a message + // to unblock us after a timeout, so we don't risk blocking too long // and causing an ANR). mWaitingUntilKeyguardVisible = true; mHandler.sendEmptyMessageDelayed(KEYGUARD_DONE_DRAWING, KEYGUARD_DONE_DRAWING_TIMEOUT_MS); @@ -1917,7 +1933,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, mExitSecureCallback = null; - // after succesfully exiting securely, no need to reshow + // after successfully exiting securely, no need to reshow // the keyguard when they've released the lock mExternallyEnabled = true; mNeedToReshowWhenReenabled = false; @@ -1992,7 +2008,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, if (mAudioManager.isStreamMute(mUiSoundsStreamType)) return; int id = mLockSounds.play(soundId, - mLockSoundVolume, mLockSoundVolume, 1/*priortiy*/, 0/*loop*/, 1.0f/*rate*/); + mLockSoundVolume, mLockSoundVolume, 1/*priority*/, 0/*loop*/, 1.0f/*rate*/); synchronized (this) { mLockSoundStreamId = id; } @@ -2078,7 +2094,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, || mScreenOnCoordinator.getWakeAndUnlocking() && mWallpaperSupportsAmbientMode) { // When the wallpaper supports ambient mode, the scrim isn't fully opaque during - // wake and unlock and we should fade in the app on top of the wallpaper + // wake and unlock, and we should fade in the app on top of the wallpaper flags |= WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE; } if (mKeyguardViewControllerLazy.get().isUnlockWithWallpaper()) { @@ -2123,7 +2139,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, Trace.beginSection("KeyguardViewMediator#handleHide"); // It's possible that the device was unlocked in a dream state. It's time to wake up. - if (mAodShowing) { + if (mAodShowing || mDreamOverlayShowing) { PowerManager pm = mContext.getSystemService(PowerManager.class); pm.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_GESTURE, "com.android.systemui:BOUNCER_DOZING"); @@ -2167,15 +2183,15 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, synchronized (KeyguardViewMediator.this) { // Tell ActivityManager that we canceled the keyguard animation if - // handleStartKeyguardExitAnimation was called but we're not hiding the keyguard, unless - // we're animating the surface behind the keyguard and will be hiding the keyguard - // shortly. + // handleStartKeyguardExitAnimation was called, but we're not hiding the keyguard, + // unless we're animating the surface behind the keyguard and will be hiding the + // keyguard shortly. if (!mHiding && !mSurfaceBehindRemoteAnimationRequested && !mKeyguardStateController.isFlingingToDismissKeyguardDuringSwipeGesture()) { if (finishedCallback != null) { // There will not execute animation, send a finish callback to ensure the remote - // animation won't hanging there. + // animation won't hang there. try { finishedCallback.onAnimationFinished(); } catch (RemoteException e) { @@ -2192,7 +2208,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, if (mScreenOnCoordinator.getWakeAndUnlocking()) { // Hack level over 9000: To speed up wake-and-unlock sequence, force it to report - // the next draw from here so we don't have to wait for window manager to signal + // the next draw from here, so we don't have to wait for window manager to signal // this to our ViewRootImpl. mKeyguardViewControllerLazy.get().getViewRootImpl().setReportNextDraw(); mScreenOnCoordinator.setWakeAndUnlocking(false); @@ -2344,7 +2360,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, } /** - * Called if the keyguard exit animation has been cancelled and we should dismiss to the + * Called if the keyguard exit animation has been cancelled, and we should dismiss to the * keyguard. * * This can happen due to the system cancelling the RemoteAnimation (due to a timeout, a new @@ -2595,7 +2611,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, } /** - * Notifies to System UI that the activity behind has now been drawn and it's safe to remove + * Notifies to System UI that the activity behind has now been drawn, and it's safe to remove * the wallpaper and keyguard flag, and WindowManager has started running keyguard exit * animation. * @@ -2609,7 +2625,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, } /** - * Notifies to System UI that the activity behind has now been drawn and it's safe to remove + * Notifies to System UI that the activity behind has now been drawn, and it's safe to remove * the wallpaper and keyguard flag, and System UI should start running keyguard exit animation. * * @param apps The list of apps to animate. @@ -2625,7 +2641,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, } /** - * Notifies to System UI that the activity behind has now been drawn and it's safe to remove + * Notifies to System UI that the activity behind has now been drawn, and it's safe to remove * the wallpaper and keyguard flag, and start running keyguard exit animation. * * @param startTime the start time of the animation in uptime milliseconds. Deprecated. diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java index f14d13093620..b49b49cbbb6d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java @@ -37,6 +37,7 @@ import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.classifier.FalsingModule; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.UiBackground; +import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; @@ -100,6 +101,7 @@ public class KeyguardModule { Lazy<NotificationShadeDepthController> notificationShadeDepthController, ScreenOnCoordinator screenOnCoordinator, InteractionJankMonitor interactionJankMonitor, + DreamOverlayStateController dreamOverlayStateController, Lazy<NotificationShadeWindowController> notificationShadeWindowController) { return new KeyguardViewMediator( context, @@ -125,6 +127,7 @@ public class KeyguardModule { notificationShadeDepthController, screenOnCoordinator, interactionJankMonitor, + dreamOverlayStateController, notificationShadeWindowController ); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index 597e4242af66..9d43d303b834 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -37,6 +37,7 @@ import android.content.Context; import android.graphics.drawable.Icon; import android.hardware.biometrics.BiometricAuthenticator.Modality; import android.hardware.biometrics.BiometricManager.BiometricMultiSensorMode; +import android.hardware.biometrics.IBiometricContextListener; import android.hardware.biometrics.IBiometricSysuiReceiver; import android.hardware.biometrics.PromptInfo; import android.hardware.display.DisplayManager; @@ -154,6 +155,7 @@ public class CommandQueue extends IStatusBar.Stub implements private static final int MSG_SET_UDFPS_HBM_LISTENER = 60 << MSG_SHIFT; private static final int MSG_TILE_SERVICE_REQUEST_ADD = 61 << MSG_SHIFT; private static final int MSG_TILE_SERVICE_REQUEST_CANCEL = 62 << MSG_SHIFT; + private static final int MSG_SET_BIOMETRICS_LISTENER = 63 << MSG_SHIFT; public static final int FLAG_EXCLUDE_NONE = 0; public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0; @@ -317,6 +319,12 @@ public class CommandQueue extends IStatusBar.Stub implements } /** + * @see IStatusBar#setBiometicContextListener(IBiometricContextListener) + */ + default void setBiometicContextListener(IBiometricContextListener listener) { + } + + /** * @see IStatusBar#setUdfpsHbmListener(IUdfpsHbmListener) */ default void setUdfpsHbmListener(IUdfpsHbmListener listener) { @@ -958,6 +966,13 @@ public class CommandQueue extends IStatusBar.Stub implements } @Override + public void setBiometicContextListener(IBiometricContextListener listener) { + synchronized (mLock) { + mHandler.obtainMessage(MSG_SET_BIOMETRICS_LISTENER, listener).sendToTarget(); + } + } + + @Override public void setUdfpsHbmListener(IUdfpsHbmListener listener) { synchronized (mLock) { mHandler.obtainMessage(MSG_SET_UDFPS_HBM_LISTENER, listener).sendToTarget(); @@ -1411,6 +1426,12 @@ public class CommandQueue extends IStatusBar.Stub implements mCallbacks.get(i).hideAuthenticationDialog(); } break; + case MSG_SET_BIOMETRICS_LISTENER: + for (int i = 0; i < mCallbacks.size(); i++) { + mCallbacks.get(i).setBiometicContextListener( + (IBiometricContextListener) msg.obj); + } + break; case MSG_SET_UDFPS_HBM_LISTENER: for (int i = 0; i < mCallbacks.size(); i++) { mCallbacks.get(i).setUdfpsHbmListener((IUdfpsHbmListener) msg.obj); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt index c136d9cc7272..b312ce20b313 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt @@ -337,11 +337,11 @@ class LockscreenShadeTransitionController @Inject constructor( if (field != value || forceApplyAmount) { field = value if (!nsslController.isInLockedDownShade() || field == 0f || forceApplyAmount) { - nsslController.setTransitionToFullShadeAmount(field) - notificationPanelController.setTransitionToFullShadeAmount(field, - false /* animate */, 0 /* delay */) qSDragProgress = MathUtils.saturate(dragDownAmount / scrimTransitionDistance) + nsslController.setTransitionToFullShadeAmount(field, qSDragProgress) qS.setTransitionToFullShadeAmount(field, qSDragProgress) + notificationPanelController.setTransitionToFullShadeAmount(field, + false /* animate */, 0 /* delay */) // TODO: appear media also in split shade val mediaAmount = if (useSplitShade) 0f else field mediaHierarchyManager.setTransitionToFullShadeAmount(mediaAmount) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index 51a66aad39fb..3411eab23d63 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar; +import static com.android.systemui.statusbar.phone.NotificationIconContainer.MAX_ICONS_ON_LOCKSCREEN; + import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; @@ -32,6 +34,7 @@ import android.view.animation.PathInterpolator; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.policy.SystemBarUtils; import com.android.systemui.R; +import com.android.systemui.animation.Interpolators; import com.android.systemui.animation.ShadeInterpolation; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.notification.NotificationUtils; @@ -45,6 +48,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm; import com.android.systemui.statusbar.notification.stack.ViewState; import com.android.systemui.statusbar.phone.NotificationIconContainer; +import com.android.systemui.util.Utils; /** * A notification shelf view that is placed inside the notification scroller. It manages the @@ -81,6 +85,11 @@ public class NotificationShelf extends ActivatableNotificationView implements private int mIndexOfFirstViewInShelf = -1; private float mCornerAnimationDistance; private NotificationShelfController mController; + private int mActualWidth = -1; + private boolean mUseSplitShade; + + /** Fraction of lockscreen to shade animation (on lockscreen swipe down). */ + private float mFractionToShade; public NotificationShelf(Context context, AttributeSet attrs) { super(context, attrs); @@ -122,13 +131,16 @@ public class NotificationShelf extends ActivatableNotificationView implements layoutParams.height = res.getDimensionPixelOffset(R.dimen.notification_shelf_height); setLayoutParams(layoutParams); - int padding = res.getDimensionPixelOffset(R.dimen.shelf_icon_container_padding); + final int padding = res.getDimensionPixelOffset(R.dimen.shelf_icon_container_padding); mShelfIcons.setPadding(padding, 0, padding, 0); mScrollFastThreshold = res.getDimensionPixelOffset(R.dimen.scroll_fast_threshold); mShowNotificationShelf = res.getBoolean(R.bool.config_showNotificationShelf); mCornerAnimationDistance = res.getDimensionPixelSize( R.dimen.notification_corner_animation_distance); + // TODO(b/213480466) enable short shelf on split shade + mUseSplitShade = Utils.shouldUseSplitNotificationShade(mContext.getResources()); + mShelfIcons.setInNotificationIconShelf(true); if (!mShowNotificationShelf) { setVisibility(GONE); @@ -203,6 +215,10 @@ public class NotificationShelf extends ActivatableNotificationView implements final float stackEnd = ambientState.getStackY() + ambientState.getStackHeight(); viewState.yTranslation = stackEnd - viewState.height; + + final int shortestWidth = mShelfIcons.calculateWidthFor(MAX_ICONS_ON_LOCKSCREEN); + final float fraction = Interpolators.STANDARD.getInterpolation(mFractionToShade); + updateStateWidth(viewState, fraction, shortestWidth); } else { viewState.hidden = true; viewState.location = ExpandableViewState.LOCATION_GONE; @@ -211,6 +227,77 @@ public class NotificationShelf extends ActivatableNotificationView implements } /** + * @param shelfState View state for NotificationShelf + * @param fraction Fraction of lockscreen to shade transition + * @param shortestWidth Shortest width to use for lockscreen shelf + */ + @VisibleForTesting + public void updateStateWidth(ShelfState shelfState, float fraction, int shortestWidth) { + shelfState.actualWidth = !mUseSplitShade && mAmbientState.isOnKeyguard() + ? (int) MathUtils.lerp(shortestWidth, getWidth(), fraction) + : getWidth(); + } + + /** + * @param fractionToShade Fraction of lockscreen to shade transition + */ + public void setFractionToShade(float fractionToShade) { + mFractionToShade = fractionToShade; + } + + /** + * @return Actual width of shelf, accounting for possible ongoing width animation + */ + public int getActualWidth() { + return mActualWidth > -1 ? mActualWidth : getWidth(); + } + + /** + * @param localX Click x from left of screen + * @param slop Margin of error within which we count x for valid click + * @param left Left of shelf, from left of screen + * @param right Right of shelf, from left of screen + * @return Whether click x was in view + */ + @VisibleForTesting + public boolean isXInView(float localX, float slop, float left, float right) { + return (left - slop) <= localX && localX < (right + slop); + } + + /** + * @param localY Click y from top of shelf + * @param slop Margin of error within which we count y for valid click + * @param top Top of shelf + * @param bottom Height of shelf + * @return Whether click y was in view + */ + @VisibleForTesting + public boolean isYInView(float localY, float slop, float top, float bottom) { + return (top - slop) <= localY && localY < (bottom + slop); + } + + /** + * @param localX Click x + * @param localY Click y + * @param slop Margin of error for valid click + * @return Whether this click was on the visible (non-clipped) part of the shelf + */ + @Override + public boolean pointInView(float localX, float localY, float slop) { + final float containerWidth = getWidth(); + final float shelfWidth = getActualWidth(); + + final float left = isLayoutRtl() ? containerWidth - shelfWidth : 0; + final float right = isLayoutRtl() ? containerWidth : shelfWidth; + + final float top = mClipTopAmount; + final float bottom = getActualHeight(); + + return isXInView(localX, slop, left, right) + && isYInView(localY, slop, top, bottom); + } + + /** * Update the shelf appearance based on the other notifications around it. This transforms * the icons from the notification area into the shelf. */ @@ -732,11 +819,15 @@ public class NotificationShelf extends ActivatableNotificationView implements // we always want to clip to our sides, such that nothing can draw outside of these bounds int height = getResources().getDisplayMetrics().heightPixels; mClipRect.set(0, -height, getWidth(), height); - mShelfIcons.setClipBounds(mClipRect); + if (mShelfIcons != null) { + mShelfIcons.setClipBounds(mClipRect); + } } private void updateRelativeOffset() { - mCollapsedIcons.getLocationOnScreen(mTmp); + if (mCollapsedIcons != null) { + mCollapsedIcons.getLocationOnScreen(mTmp); + } getLocationOnScreen(mTmp); } @@ -831,9 +922,20 @@ public class NotificationShelf extends ActivatableNotificationView implements mIndexOfFirstViewInShelf = mHostLayoutController.indexOfChild(firstViewInShelf); } - private class ShelfState extends ExpandableViewState { + public class ShelfState extends ExpandableViewState { private boolean hasItemsInStableShelf; private ExpandableView firstViewInShelf; + public int actualWidth = -1; + + private void updateShelfWidth(View view) { + if (actualWidth < 0) { + return; + } + mActualWidth = actualWidth; + ActivatableNotificationView anv = (ActivatableNotificationView) view; + anv.getBackgroundNormal().setActualWidth(actualWidth); + mShelfIcons.setActualLayoutWidth(actualWidth); + } @Override public void applyToView(View view) { @@ -846,19 +948,21 @@ public class NotificationShelf extends ActivatableNotificationView implements updateAppearance(); setHasItemsInStableShelf(hasItemsInStableShelf); mShelfIcons.setAnimationsEnabled(mAnimationsEnabled); + updateShelfWidth(view); } @Override - public void animateTo(View child, AnimationProperties properties) { + public void animateTo(View view, AnimationProperties properties) { if (!mShowNotificationShelf) { return; } - super.animateTo(child, properties); + super.animateTo(view, properties); setIndexOfFirstViewInShelf(firstViewInShelf); updateAppearance(); setHasItemsInStableShelf(hasItemsInStableShelf); mShelfIcons.setAnimationsEnabled(mAnimationsEnabled); + updateShelfWidth(view); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java index 5d6d0f701f12..fca2aa167e37 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java @@ -162,6 +162,13 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView updateBackgroundTint(); } + /** + * @return The background of this view. + */ + public NotificationBackgroundView getBackgroundNormal() { + return mBackgroundNormal; + } + @Override protected void onFinishInflate() { super.onFinishInflate(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java index 0f615aa9356f..c640ab6c3a90 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java @@ -29,6 +29,7 @@ import android.view.View; import com.android.internal.util.ArrayUtils; import com.android.systemui.R; +import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.notification.ExpandAnimationParameters; /** @@ -39,15 +40,17 @@ public class NotificationBackgroundView extends View { private final boolean mDontModifyCorners; private Drawable mBackground; private int mClipTopAmount; - private int mActualHeight; private int mClipBottomAmount; private int mTintColor; private final float[] mCornerRadii = new float[8]; private boolean mBottomIsRounded; private int mBackgroundTop; private boolean mBottomAmountClips = true; + private int mActualHeight = -1; + private int mActualWidth = -1; private boolean mExpandAnimationRunning; - private float mActualWidth; + private int mExpandAnimationWidth = -1; + private int mExpandAnimationHeight = -1; private int mDrawableAlpha = 255; private boolean mIsPressedAllowed; @@ -59,11 +62,12 @@ public class NotificationBackgroundView extends View { @Override protected void onDraw(Canvas canvas) { - if (mClipTopAmount + mClipBottomAmount < mActualHeight - mBackgroundTop + if (mClipTopAmount + mClipBottomAmount < getActualHeight() - mBackgroundTop || mExpandAnimationRunning) { canvas.save(); if (!mExpandAnimationRunning) { - canvas.clipRect(0, mClipTopAmount, getWidth(), mActualHeight - mClipBottomAmount); + canvas.clipRect(0, mClipTopAmount, getWidth(), + getActualHeight() - mClipBottomAmount); } draw(canvas, mBackground); canvas.restore(); @@ -73,17 +77,23 @@ public class NotificationBackgroundView extends View { private void draw(Canvas canvas, Drawable drawable) { if (drawable != null) { int top = mBackgroundTop; - int bottom = mActualHeight; + int bottom = getActualHeight(); if (mBottomIsRounded && mBottomAmountClips && !mExpandAnimationRunning) { bottom -= mClipBottomAmount; } - int left = 0; - int right = getWidth(); + final boolean isRtl = isLayoutRtl(); + final int width = getWidth(); + final int actualWidth = getActualWidth(); + + int left = isRtl ? width - actualWidth : 0; + int right = isRtl ? width : actualWidth; + if (mExpandAnimationRunning) { - left = (int) ((getWidth() - mActualWidth) / 2.0f); - right = (int) (left + mActualWidth); + // Horizontally center this background view inside of the container + left = (int) ((width - actualWidth) / 2.0f); + right = (int) (left + actualWidth); } drawable.setBounds(left, top, right, bottom); drawable.draw(canvas); @@ -152,8 +162,26 @@ public class NotificationBackgroundView extends View { invalidate(); } - public int getActualHeight() { - return mActualHeight; + private int getActualHeight() { + if (mExpandAnimationRunning && mExpandAnimationHeight > -1) { + return mExpandAnimationHeight; + } else if (mActualHeight > -1) { + return mActualHeight; + } + return getHeight(); + } + + public void setActualWidth(int actualWidth) { + mActualWidth = actualWidth; + } + + private int getActualWidth() { + if (mExpandAnimationRunning && mExpandAnimationWidth > -1) { + return mExpandAnimationWidth; + } else if (mActualWidth > -1) { + return mActualWidth; + } + return getWidth(); } public void setClipTopAmount(int clipTopAmount) { @@ -241,9 +269,9 @@ public class NotificationBackgroundView extends View { } /** Set the current expand animation size. */ - public void setExpandAnimationSize(int actualWidth, int actualHeight) { - mActualHeight = actualHeight; - mActualWidth = actualWidth; + public void setExpandAnimationSize(int width, int height) { + mExpandAnimationHeight = width; + mExpandAnimationWidth = height; invalidate(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 9655a9ce5e45..9f6a81d3a9ee 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -5497,6 +5497,17 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable } /** + * @param fraction Fraction of the lockscreen to shade transition. 0f for all other states. + * Once the lockscreen to shade transition completes and the shade is 100% open + * LockscreenShadeTransitionController resets fraction to 0 + * where it remains until the next lockscreen-to-shade transition. + */ + public void setFractionToShade(float fraction) { + mShelf.setFractionToShade(fraction); + requestChildrenUpdate(); + } + + /** * Set a listener to when scrolling changes. */ public void setOnScrollListener(Consumer<Integer> listener) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index 0d0e5e850523..334128a2b4ca 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -1515,10 +1515,18 @@ public class NotificationStackScrollLayoutController { } /** - * Set the amount of pixels we have currently dragged down if we're transitioning to the full - * shade. 0.0f means we're not transitioning yet. + * @param amount The amount of pixels we have currently dragged down + * for the lockscreen to shade transition. 0f for all other states. + * @param fraction The fraction of lockscreen to shade transition. + * 0f for all other states. + * + * Once the lockscreen to shade transition completes and the shade is 100% open, + * LockscreenShadeTransitionController resets amount and fraction to 0, where they remain + * until the next lockscreen-to-shade transition. */ - public void setTransitionToFullShadeAmount(float amount) { + public void setTransitionToFullShadeAmount(float amount, float fraction) { + mView.setFractionToShade(fraction); + float extraTopInset = 0.0f; if (mStatusBarStateController.getState() == KEYGUARD) { float overallProgress = MathUtils.saturate(amount / mView.getHeight()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java index 0d2bddcc8b77..89b5aef00656 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java @@ -44,6 +44,7 @@ public class StackStateAnimator { public static final int ANIMATION_DURATION_STANDARD = 360; public static final int ANIMATION_DURATION_CORNER_RADIUS = 200; public static final int ANIMATION_DURATION_WAKEUP = 500; + public static final int ANIMATION_DURATION_WAKEUP_SCRIM = 667; public static final int ANIMATION_DURATION_GO_TO_FULL_SHADE = 448; public static final int ANIMATION_DURATION_APPEAR_DISAPPEAR = 464; public static final int ANIMATION_DURATION_SWIPE = 200; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java index c09c48540901..febf2b5b5f24 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java @@ -135,7 +135,8 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { } }.setDuration(CONTENT_FADE_DURATION); - private static final int MAX_VISIBLE_ICONS_ON_LOCK = 5; + private static final int MAX_ICONS_ON_AOD = 5; + public static final int MAX_ICONS_ON_LOCKSCREEN = 3; public static final int MAX_STATIC_ICONS = 4; private static final int MAX_DOTS = 1; @@ -386,6 +387,19 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { } /** + * @return Width of shelf for the given number of icons and overflow dot + */ + public int calculateWidthFor(int numMaxIcons) { + if (getChildCount() == 0) { + return 0; + } + return (int) (getActualPaddingStart() + + numMaxIcons * mIconSize + + mOverflowWidth + + getActualPaddingEnd()); + } + + /** * Calculate the horizontal translations for each notification based on how much the icons * are inserted into the notification container. * If this is not a whole number, the fraction means by how much the icon is appearing. @@ -394,7 +408,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { float translationX = getActualPaddingStart(); int firstOverflowIndex = -1; int childCount = getChildCount(); - int maxVisibleIcons = mOnLockScreen ? MAX_VISIBLE_ICONS_ON_LOCK : + int maxVisibleIcons = mOnLockScreen ? MAX_ICONS_ON_AOD : mIsStaticLayout ? MAX_STATIC_ICONS : childCount; float layoutEnd = getLayoutEnd(); float overflowStart = getMaxOverflowStart(); @@ -414,7 +428,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { } boolean forceOverflow = mSpeedBumpIndex != -1 && i >= mSpeedBumpIndex && iconState.iconAppearAmount > 0.0f || i >= maxVisibleIcons; - boolean noOverflowAfter = i == childCount - 1; + boolean isLastChild = i == childCount - 1; float drawingScale = mOnLockScreen && view instanceof StatusBarIconView ? ((StatusBarIconView) view).getIconScaleIncreased() : 1f; @@ -423,10 +437,10 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { : StatusBarIconView.STATE_ICON; boolean isOverflowing = - (translationX > (noOverflowAfter ? layoutEnd - mIconSize + (translationX > (isLastChild ? layoutEnd - mIconSize : overflowStart - mIconSize)); if (firstOverflowIndex == -1 && (forceOverflow || isOverflowing)) { - firstOverflowIndex = noOverflowAfter && !forceOverflow ? i - 1 : i; + firstOverflowIndex = isLastChild && !forceOverflow ? i - 1 : i; mVisualOverflowStart = layoutEnd - mOverflowWidth; if (forceOverflow || mIsStaticLayout) { mVisualOverflowStart = Math.min(translationX, mVisualOverflowStart); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index 9d3f19ad039a..bb0ed95cc6ca 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -4188,9 +4188,10 @@ public class NotificationPanelViewController extends PanelViewController return false; } - // Do not allow panel expansion if bouncer is scrimmed, otherwise user would be able - // to pull down QS or expand the shade. - if (mStatusBar.isBouncerShowingScrimmed()) { + // Do not allow panel expansion if bouncer is scrimmed or showing over a dream, + // otherwise user would be able to pull down QS or expand the shade. + if (mStatusBar.isBouncerShowingScrimmed() + || mStatusBar.isBouncerShowingOverDream()) { return false; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java index dc6efba97ff5..c466a8ce6d3f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java @@ -73,6 +73,10 @@ import java.io.PrintWriter; public abstract class PanelViewController { public static final boolean DEBUG = PanelView.DEBUG; public static final String TAG = PanelView.class.getSimpleName(); + public static final float FLING_MAX_LENGTH_SECONDS = 0.6f; + public static final float FLING_SPEED_UP_FACTOR = 0.6f; + public static final float FLING_CLOSING_MAX_LENGTH_SECONDS = 0.6f; + public static final float FLING_CLOSING_SPEED_UP_FACTOR = 0.6f; private static final int NO_FIXED_DURATION = -1; private static final long SHADE_OPEN_SPRING_OUT_DURATION = 350L; private static final long SHADE_OPEN_SPRING_BACK_DURATION = 400L; @@ -269,13 +273,13 @@ public abstract class PanelViewController { mNotificationShadeWindowController = notificationShadeWindowController; mFlingAnimationUtils = flingAnimationUtilsBuilder .reset() - .setMaxLengthSeconds(0.6f) - .setSpeedUpFactor(0.6f) + .setMaxLengthSeconds(FLING_MAX_LENGTH_SECONDS) + .setSpeedUpFactor(FLING_SPEED_UP_FACTOR) .build(); mFlingAnimationUtilsClosing = flingAnimationUtilsBuilder .reset() - .setMaxLengthSeconds(0.5f) - .setSpeedUpFactor(0.6f) + .setMaxLengthSeconds(FLING_CLOSING_MAX_LENGTH_SECONDS) + .setSpeedUpFactor(FLING_CLOSING_SPEED_UP_FACTOR) .build(); mFlingAnimationUtilsDismissing = flingAnimationUtilsBuilder .reset() 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 d2e1650056ac..ef5f21658d83 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java @@ -62,7 +62,7 @@ public enum ScrimState { public void prepare(ScrimState previousState) { mBlankScreen = false; if (previousState == ScrimState.AOD) { - mAnimationDuration = StackStateAnimator.ANIMATION_DURATION_WAKEUP; + mAnimationDuration = StackStateAnimator.ANIMATION_DURATION_WAKEUP_SCRIM; if (mDisplayRequiresBlanking) { // DisplayPowerManager will blank the screen, we'll just // set our scrim to black in this frame to avoid flickering and @@ -70,7 +70,7 @@ public enum ScrimState { mBlankScreen = true; } } else if (previousState == ScrimState.KEYGUARD) { - mAnimationDuration = StackStateAnimator.ANIMATION_DURATION_WAKEUP; + mAnimationDuration = StackStateAnimator.ANIMATION_DURATION_WAKEUP_SCRIM; } else { mAnimationDuration = ScrimController.ANIMATION_DURATION; } 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 c09c3ca9dede..a1445337e7d7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -150,6 +150,7 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.demomode.DemoMode; import com.android.systemui.demomode.DemoModeController; +import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.emergency.EmergencyGesture; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; @@ -338,6 +339,7 @@ public class StatusBar extends CoreStartable implements } private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; + private final DreamOverlayStateController mDreamOverlayStateController; private StatusBarCommandQueueCallbacks mCommandQueueCallbacks; void onStatusBarWindowStateChanged(@WindowVisibleState int state) { @@ -781,7 +783,8 @@ public class StatusBar extends CoreStartable implements ActivityLaunchAnimator activityLaunchAnimator, NotifPipelineFlags notifPipelineFlags, InteractionJankMonitor jankMonitor, - DeviceStateManager deviceStateManager) { + DeviceStateManager deviceStateManager, + DreamOverlayStateController dreamOverlayStateController) { super(context); mNotificationsController = notificationsController; mFragmentService = fragmentService; @@ -869,6 +872,7 @@ public class StatusBar extends CoreStartable implements mMessageRouter = messageRouter; mWallpaperManager = wallpaperManager; mJankMonitor = jankMonitor; + mDreamOverlayStateController = dreamOverlayStateController; mLockscreenShadeTransitionController = lockscreenShadeTransitionController; mStartingSurfaceOptional = startingSurfaceOptional; @@ -4144,6 +4148,10 @@ public class StatusBar extends CoreStartable implements return isBouncerShowing() && mStatusBarKeyguardViewManager.bouncerNeedsScrimming(); } + public boolean isBouncerShowingOverDream() { + return isBouncerShowing() && mDreamOverlayStateController.isOverlayActive(); + } + /** * When {@link KeyguardBouncer} starts to be dismissed, playing its animation. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 316e68227e0c..b20349667379 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -51,6 +51,7 @@ import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.DejankUtils; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dock.DockManager; +import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.navigationbar.NavigationBarView; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -111,6 +112,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private final NotificationShadeWindowController mNotificationShadeWindowController; private final KeyguardBouncer.Factory mKeyguardBouncerFactory; private final KeyguardMessageAreaController.Factory mKeyguardMessageAreaFactory; + private final DreamOverlayStateController mDreamOverlayStateController; private KeyguardMessageAreaController mKeyguardMessageAreaController; private final Lazy<ShadeController> mShadeController; private final BouncerExpansionCallback mExpansionCallback = new BouncerExpansionCallback() { @@ -235,6 +237,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb SysuiStatusBarStateController sysuiStatusBarStateController, ConfigurationController configurationController, KeyguardUpdateMonitor keyguardUpdateMonitor, + DreamOverlayStateController dreamOverlayStateController, NavigationModeController navigationModeController, DockManager dockManager, NotificationShadeWindowController notificationShadeWindowController, @@ -249,6 +252,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mConfigurationController = configurationController; mNavigationModeController = navigationModeController; mNotificationShadeWindowController = notificationShadeWindowController; + mDreamOverlayStateController = dreamOverlayStateController; mKeyguardStateController = keyguardStateController; mMediaManager = notificationMediaManager; mKeyguardUpdateManager = keyguardUpdateMonitor; @@ -1174,7 +1178,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } public boolean bouncerNeedsScrimming() { - return mOccluded || mBouncer.willDismissWithAction() + // When a dream overlay is active, scrimming will cause any expansion to immediately expand. + return (mOccluded && !mDreamOverlayStateController.isOverlayActive()) + || mBouncer.willDismissWithAction() || mStatusBar.isFullScreenUserSwitcherState() || (mBouncer.isShowing() && mBouncer.isScrimmed()) || mBouncer.isFullscreenBouncer(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java index f5364b9363b9..d3ff4a78c893 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java @@ -40,6 +40,7 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.demomode.DemoModeController; +import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.fragments.FragmentService; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; @@ -231,7 +232,8 @@ public interface StatusBarPhoneModule { ActivityLaunchAnimator activityLaunchAnimator, NotifPipelineFlags notifPipelineFlags, InteractionJankMonitor jankMonitor, - DeviceStateManager deviceStateManager) { + DeviceStateManager deviceStateManager, + DreamOverlayStateController dreamOverlayStateController) { return new StatusBar( context, notificationsController, @@ -327,7 +329,8 @@ public interface StatusBarPhoneModule { activityLaunchAnimator, notifPipelineFlags, jankMonitor, - deviceStateManager + deviceStateManager, + dreamOverlayStateController ); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java index 5d39eef999d7..c37e966f3540 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -27,10 +27,12 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -46,6 +48,7 @@ import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.BiometricPrompt; import android.hardware.biometrics.ComponentInfoInternal; +import android.hardware.biometrics.IBiometricContextListener; import android.hardware.biometrics.IBiometricSysuiReceiver; import android.hardware.biometrics.PromptInfo; import android.hardware.biometrics.SensorProperties; @@ -70,6 +73,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.util.concurrency.Execution; import com.android.systemui.util.concurrency.FakeExecution; @@ -80,6 +84,7 @@ import org.junit.runner.RunWith; import org.mockito.AdditionalMatchers; import org.mockito.ArgumentCaptor; import org.mockito.Captor; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -99,6 +104,8 @@ public class AuthControllerTest extends SysuiTestCase { @Mock private IBiometricSysuiReceiver mReceiver; @Mock + private IBiometricContextListener mContextListener; + @Mock private AuthDialog mDialog1; @Mock private AuthDialog mDialog2; @@ -120,10 +127,14 @@ public class AuthControllerTest extends SysuiTestCase { private DisplayManager mDisplayManager; @Mock private WakefulnessLifecycle mWakefulnessLifecycle; + @Mock + private StatusBarStateController mStatusBarStateController; @Captor ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> mAuthenticatorsRegisteredCaptor; @Captor ArgumentCaptor<FingerprintStateListener> mFingerprintStateCaptor; + @Captor + ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateListenerCaptor; private TestableContext mContextSpy; private Execution mExecution; @@ -175,12 +186,15 @@ public class AuthControllerTest extends SysuiTestCase { mAuthController = new TestableAuthController(mContextSpy, mExecution, mCommandQueue, mActivityTaskManager, mWindowManager, mFingerprintManager, mFaceManager, - () -> mUdfpsController, () -> mSidefpsController); + () -> mUdfpsController, () -> mSidefpsController, mStatusBarStateController); mAuthController.start(); verify(mFingerprintManager).addAuthenticatorsRegisteredCallback( mAuthenticatorsRegisteredCaptor.capture()); + when(mStatusBarStateController.isDozing()).thenReturn(false); + verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture()); + mAuthenticatorsRegisteredCaptor.getValue().onAllAuthenticatorsRegistered(props); // Ensures that the operations posted on the handler get executed. @@ -198,7 +212,8 @@ public class AuthControllerTest extends SysuiTestCase { // This test requires an uninitialized AuthController. AuthController authController = new TestableAuthController(mContextSpy, mExecution, mCommandQueue, mActivityTaskManager, mWindowManager, mFingerprintManager, - mFaceManager, () -> mUdfpsController, () -> mSidefpsController); + mFaceManager, () -> mUdfpsController, () -> mSidefpsController, + mStatusBarStateController); authController.start(); verify(mFingerprintManager).addAuthenticatorsRegisteredCallback( @@ -221,7 +236,8 @@ public class AuthControllerTest extends SysuiTestCase { // This test requires an uninitialized AuthController. AuthController authController = new TestableAuthController(mContextSpy, mExecution, mCommandQueue, mActivityTaskManager, mWindowManager, mFingerprintManager, - mFaceManager, () -> mUdfpsController, () -> mSidefpsController); + mFaceManager, () -> mUdfpsController, () -> mSidefpsController, + mStatusBarStateController); authController.start(); verify(mFingerprintManager).addAuthenticatorsRegisteredCallback( @@ -656,6 +672,19 @@ public class AuthControllerTest extends SysuiTestCase { verify(callback).onBiometricPromptDismissed(); } + @Test + public void testForwardsDozeEvent() throws RemoteException { + mAuthController.setBiometicContextListener(mContextListener); + + mStatusBarStateListenerCaptor.getValue().onDozingChanged(false); + mStatusBarStateListenerCaptor.getValue().onDozingChanged(true); + + InOrder order = inOrder(mContextListener); + // invoked twice since the initial state is false + order.verify(mContextListener, times(2)).onDozeChanged(eq(false)); + order.verify(mContextListener).onDozeChanged(eq(true)); + } + // Helpers private void showDialog(int[] sensorIds, boolean credentialAllowed) { @@ -705,10 +734,12 @@ public class AuthControllerTest extends SysuiTestCase { FingerprintManager fingerprintManager, FaceManager faceManager, Provider<UdfpsController> udfpsControllerFactory, - Provider<SidefpsController> sidefpsControllerFactory) { + Provider<SidefpsController> sidefpsControllerFactory, + StatusBarStateController statusBarStateController) { super(context, execution, commandQueue, activityTaskManager, windowManager, fingerprintManager, faceManager, udfpsControllerFactory, - sidefpsControllerFactory, mDisplayManager, mWakefulnessLifecycle, mHandler); + sidefpsControllerFactory, mDisplayManager, mWakefulnessLifecycle, + statusBarStateController, mHandler); } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java index b3b5fa509105..d5bd67adcf09 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java @@ -91,6 +91,9 @@ public class DreamOverlayServiceTest extends SysuiTestCase { @Mock DreamOverlayTouchMonitor mDreamOverlayTouchMonitor; + @Mock + DreamOverlayStateController mStateController; + DreamOverlayService mService; @@ -115,6 +118,7 @@ public class DreamOverlayServiceTest extends SysuiTestCase { mService = new DreamOverlayService(mContext, mMainExecutor, mDreamOverlayComponentFactory, + mStateController, mKeyguardUpdateMonitor); final IBinder proxy = mService.onBind(new Intent()); final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java index 7d0833db7ae4..1859569bafe5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java @@ -16,9 +16,12 @@ package com.android.systemui.dreams; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -35,6 +38,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import java.util.Collection; @@ -56,7 +60,29 @@ public class DreamOverlayStateControllerTest extends SysuiTestCase { } @Test - public void testCallback() throws Exception { + public void testStateChange() { + final DreamOverlayStateController stateController = new DreamOverlayStateController( + mExecutor); + stateController.addCallback(mCallback); + stateController.setOverlayActive(true); + mExecutor.runAllReady(); + + verify(mCallback).onStateChanged(); + assertThat(stateController.isOverlayActive()).isTrue(); + + Mockito.clearInvocations(mCallback); + stateController.setOverlayActive(true); + mExecutor.runAllReady(); + verify(mCallback, never()).onStateChanged(); + + stateController.setOverlayActive(false); + mExecutor.runAllReady(); + verify(mCallback).onStateChanged(); + assertThat(stateController.isOverlayActive()).isFalse(); + } + + @Test + public void testCallback() { final DreamOverlayStateController stateController = new DreamOverlayStateController( mExecutor); stateController.addCallback(mCallback); diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java index f227a9b78c39..d5ab708f893b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java @@ -27,6 +27,7 @@ import android.view.View; import androidx.constraintlayout.widget.ConstraintLayout; import androidx.test.filters.SmallTest; +import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import org.junit.Before; @@ -122,6 +123,34 @@ public class ComplicationLayoutEngineTest extends SysuiTestCase { } /** + * Makes sure the engine properly places a view within the {@link ConstraintLayout}. + */ + @Test + public void testSnapToGuide() { + final ViewInfo firstViewInfo = new ViewInfo( + new ComplicationLayoutParams( + 100, + 100, + ComplicationLayoutParams.POSITION_TOP + | ComplicationLayoutParams.POSITION_END, + ComplicationLayoutParams.DIRECTION_DOWN, + 0, + true), + Complication.CATEGORY_STANDARD, + mLayout); + + final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout); + addComplication(engine, firstViewInfo); + + // Ensure the view is added to the top end corner + verifyChange(firstViewInfo, true, lp -> { + assertThat(lp.topToTop == ConstraintLayout.LayoutParams.PARENT_ID).isTrue(); + assertThat(lp.endToEnd == ConstraintLayout.LayoutParams.PARENT_ID).isTrue(); + assertThat(lp.startToEnd == R.id.complication_end_guide).isTrue(); + }); + } + + /** * Ensures layout in a particular direction updates. */ @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java new file mode 100644 index 000000000000..f1978b214594 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2022 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.dreams.complication; + +import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_AIR_QUALITY; +import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_CAST_INFO; +import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_DATE; +import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_TIME; +import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_WEATHER; +import static com.android.systemui.dreams.complication.ComplicationUtils.convertComplicationType; + + +import static com.google.common.truth.Truth.assertThat; + +import android.testing.AndroidTestingRunner; + +import androidx.test.filters.SmallTest; + +import com.android.settingslib.dream.DreamBackend; +import com.android.systemui.SysuiTestCase; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class ComplicationUtilsTest extends SysuiTestCase { + @Test + public void testConvertComplicationType() { + assertThat(convertComplicationType(DreamBackend.COMPLICATION_TYPE_TIME)) + .isEqualTo(COMPLICATION_TYPE_TIME); + assertThat(convertComplicationType(DreamBackend.COMPLICATION_TYPE_DATE)) + .isEqualTo(COMPLICATION_TYPE_DATE); + assertThat(convertComplicationType(DreamBackend.COMPLICATION_TYPE_WEATHER)) + .isEqualTo(COMPLICATION_TYPE_WEATHER); + assertThat(convertComplicationType(DreamBackend.COMPLICATION_TYPE_AIR_QUALITY)) + .isEqualTo(COMPLICATION_TYPE_AIR_QUALITY); + assertThat(convertComplicationType(DreamBackend.COMPLICATION_TYPE_CAST_INFO)) + .isEqualTo(COMPLICATION_TYPE_CAST_INFO); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java new file mode 100644 index 000000000000..1a8326fd5bd1 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2022 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.dreams.touch; + +import static com.google.common.truth.Truth.assertThat; + + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyFloat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.animation.ValueAnimator; +import android.testing.AndroidTestingRunner; +import android.view.GestureDetector; +import android.view.MotionEvent; +import android.view.VelocityTracker; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.shared.system.InputChannelCompat; +import com.android.systemui.statusbar.NotificationShadeWindowController; +import com.android.systemui.statusbar.phone.KeyguardBouncer; +import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; +import com.android.wm.shell.animation.FlingAnimationUtils; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import java.util.Random; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class BouncerSwipeTouchHandlerTest extends SysuiTestCase { + @Mock + StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; + + @Mock + StatusBar mStatusBar; + + @Mock + NotificationShadeWindowController mNotificationShadeWindowController; + + @Mock + FlingAnimationUtils mFlingAnimationUtils; + + + @Mock + FlingAnimationUtils mFlingAnimationUtilsClosing; + + @Mock + DreamTouchHandler.TouchSession mTouchSession; + + BouncerSwipeTouchHandler mTouchHandler; + + @Mock + BouncerSwipeTouchHandler.ValueAnimatorCreator mValueAnimatorCreator; + + @Mock + ValueAnimator mValueAnimator; + + @Mock + BouncerSwipeTouchHandler.VelocityTrackerFactory mVelocityTrackerFactory; + + @Mock + VelocityTracker mVelocityTracker; + + private static final float TOUCH_REGION = .3f; + private static final float SCREEN_HEIGHT_PX = 100; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mTouchHandler = new BouncerSwipeTouchHandler( + mStatusBarKeyguardViewManager, + mStatusBar, + mNotificationShadeWindowController, + mValueAnimatorCreator, + mVelocityTrackerFactory, + mFlingAnimationUtils, + mFlingAnimationUtilsClosing, + TOUCH_REGION); + when(mStatusBar.getDisplayHeight()).thenReturn(SCREEN_HEIGHT_PX); + when(mValueAnimatorCreator.create(anyFloat(), anyFloat())).thenReturn(mValueAnimator); + when(mVelocityTrackerFactory.obtain()).thenReturn(mVelocityTracker); + } + + private static void beginValidSwipe(GestureDetector.OnGestureListener listener) { + listener.onDown(MotionEvent.obtain(0, 0, + MotionEvent.ACTION_DOWN, 0, + SCREEN_HEIGHT_PX - (.5f * TOUCH_REGION * SCREEN_HEIGHT_PX), 0)); + } + + /** + * Ensures expansion only happens when touch down happens in valid part of the screen. + */ + @Test + public void testSessionStart() { + mTouchHandler.onSessionStart(mTouchSession); + verify(mNotificationShadeWindowController).setForcePluginOpen(eq(true), any()); + ArgumentCaptor<InputChannelCompat.InputEventListener> eventListenerCaptor = + ArgumentCaptor.forClass(InputChannelCompat.InputEventListener.class); + ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor = + ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class); + verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture()); + verify(mTouchSession).registerInputListener(eventListenerCaptor.capture()); + + final Random random = new Random(System.currentTimeMillis()); + + // If an initial touch down meeting criteria has been met, scroll behavior should be + // ignored. + assertThat(gestureListenerCaptor.getValue() + .onScroll(Mockito.mock(MotionEvent.class), + Mockito.mock(MotionEvent.class), + random.nextFloat(), + random.nextFloat())).isFalse(); + + // A touch at the top of the screen should also not trigger listening. + final MotionEvent touchDownEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, + 0, 0, 0); + + gestureListenerCaptor.getValue().onDown(touchDownEvent); + assertThat(gestureListenerCaptor.getValue() + .onScroll(Mockito.mock(MotionEvent.class), + Mockito.mock(MotionEvent.class), + random.nextFloat(), + random.nextFloat())).isFalse(); + + // A touch within range at the bottom of the screen should trigger listening + beginValidSwipe(gestureListenerCaptor.getValue()); + assertThat(gestureListenerCaptor.getValue() + .onScroll(Mockito.mock(MotionEvent.class), + Mockito.mock(MotionEvent.class), + random.nextFloat(), + random.nextFloat())).isTrue(); + } + + /** + * Makes sure expansion amount is proportional to scroll. + */ + @Test + public void testExpansionAmount() { + mTouchHandler.onSessionStart(mTouchSession); + ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor = + ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class); + verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture()); + + beginValidSwipe(gestureListenerCaptor.getValue()); + + final float scrollAmount = .3f; + final float distanceY = SCREEN_HEIGHT_PX * scrollAmount; + + final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, + 0, SCREEN_HEIGHT_PX, 0); + final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, + 0, SCREEN_HEIGHT_PX - distanceY, 0); + + assertThat(gestureListenerCaptor.getValue().onScroll(event1, event2, 0 , distanceY)) + .isTrue(); + + // Ensure only called once + verify(mStatusBarKeyguardViewManager) + .onPanelExpansionChanged(anyFloat(), anyBoolean(), anyBoolean()); + + // Ensure correct expansion passed in. + verify(mStatusBarKeyguardViewManager) + .onPanelExpansionChanged(eq(1 - scrollAmount), eq(false), eq(true)); + } + + private void swipeToPosition(float position, float velocityY) { + mTouchHandler.onSessionStart(mTouchSession); + ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor = + ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class); + ArgumentCaptor<InputChannelCompat.InputEventListener> inputEventListenerCaptor = + ArgumentCaptor.forClass(InputChannelCompat.InputEventListener.class); + verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture()); + verify(mTouchSession).registerInputListener(inputEventListenerCaptor.capture()); + + when(mVelocityTracker.getYVelocity()).thenReturn(velocityY); + + beginValidSwipe(gestureListenerCaptor.getValue()); + + final float distanceY = SCREEN_HEIGHT_PX * position; + + final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, + 0, SCREEN_HEIGHT_PX, 0); + final MotionEvent event2 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, + 0, SCREEN_HEIGHT_PX - distanceY, 0); + + assertThat(gestureListenerCaptor.getValue().onScroll(event1, event2, 0 , distanceY)) + .isTrue(); + + final MotionEvent upEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, + 0, 0, 0); + + inputEventListenerCaptor.getValue().onInputEvent(upEvent); + } + + /** + * Tests that ending a swipe before the set expansion threshold leads to bouncer collapsing + * down. + */ + @Test + public void testCollapseOnThreshold() { + final float swipeUpPercentage = .3f; + swipeToPosition(swipeUpPercentage, -1); + + verify(mValueAnimatorCreator).create(eq(1 - swipeUpPercentage), + eq(KeyguardBouncer.EXPANSION_VISIBLE)); + verify(mFlingAnimationUtilsClosing).apply(eq(mValueAnimator), anyFloat(), anyFloat(), + anyFloat(), anyFloat()); + verify(mValueAnimator).start(); + } + + /** + * Tests that ending a swipe above the set expansion threshold will continue the expansion. + */ + @Test + public void testExpandOnThreshold() { + final float swipeUpPercentage = .7f; + swipeToPosition(swipeUpPercentage, 1); + + verify(mValueAnimatorCreator).create(eq(1 - swipeUpPercentage), + eq(KeyguardBouncer.EXPANSION_HIDDEN)); + verify(mFlingAnimationUtils).apply(eq(mValueAnimator), anyFloat(), anyFloat(), + anyFloat(), anyFloat()); + verify(mValueAnimator).start(); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java index da8ab27d7e3d..d94e2eee9ffa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -48,6 +48,7 @@ import com.android.keyguard.mediator.ScreenOnCoordinator; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.classifier.FalsingCollectorFake; +import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.dump.DumpManager; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.statusbar.NotificationShadeDepthController; @@ -98,6 +99,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { private @Mock InteractionJankMonitor mInteractionJankMonitor; private @Mock ScreenOnCoordinator mScreenOnCoordinator; private @Mock Lazy<NotificationShadeWindowController> mNotificationShadeWindowControllerLazy; + private @Mock DreamOverlayStateController mDreamOverlayStateController; private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake(); private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); @@ -202,6 +204,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { () -> mNotificationShadeDepthController, mScreenOnCoordinator, mInteractionJankMonitor, + mDreamOverlayStateController, mNotificationShadeWindowControllerLazy); mViewMediator.start(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt index 42647f7b026a..d51d370eecb9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt @@ -227,7 +227,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { fun testDragDownAmountDoesntCallOutInLockedDownShade() { whenever(nsslController.isInLockedDownShade).thenReturn(true) transitionController.dragDownAmount = 10f - verify(nsslController, never()).setTransitionToFullShadeAmount(anyFloat()) + verify(nsslController, never()).setTransitionToFullShadeAmount(anyFloat(), anyFloat()) verify(mediaHierarchyManager, never()).setTransitionToFullShadeAmount(anyFloat()) verify(scrimController, never()).setTransitionToFullShadeProgress(anyFloat()) verify(notificationPanelController, never()).setTransitionToFullShadeAmount(anyFloat(), @@ -238,7 +238,7 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { @Test fun testDragDownAmountCallsOut() { transitionController.dragDownAmount = 10f - verify(nsslController).setTransitionToFullShadeAmount(anyFloat()) + verify(nsslController).setTransitionToFullShadeAmount(anyFloat(), anyFloat()) verify(mediaHierarchyManager).setTransitionToFullShadeAmount(anyFloat()) verify(scrimController).setTransitionToFullShadeProgress(anyFloat()) verify(notificationPanelController).setTransitionToFullShadeAmount(anyFloat(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt new file mode 100644 index 000000000000..d280f54e32f2 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt @@ -0,0 +1,153 @@ +package com.android.systemui.statusbar.notification.stack + +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper.RunWithLooper +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.NotificationShelf +import junit.framework.Assert.assertFalse +import junit.framework.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.* +import org.mockito.Mockito.`when` as whenever + +/** + * Tests for {@link NotificationShelf}. + */ +@SmallTest +@RunWith(AndroidTestingRunner::class) +@RunWithLooper +class NotificationShelfTest : SysuiTestCase() { + + private val shelf = NotificationShelf(context, /* attrs */ null) + private val shelfState = shelf.viewState as NotificationShelf.ShelfState + private val ambientState = mock(AmbientState::class.java) + + @Before + fun setUp() { + shelf.bind(ambientState, /* hostLayoutController */ null) + shelf.layout(/* left */ 0, /* top */ 0, /* right */ 30, /* bottom */5) + } + + @Test + fun testShadeWidth_BasedOnFractionToShade() { + setFractionToShade(0f) + setOnLockscreen(true) + + shelf.updateStateWidth(shelfState, /* fraction */ 0f, /* shortestWidth */ 10) + assertTrue(shelfState.actualWidth == 10) + + shelf.updateStateWidth(shelfState, /* fraction */ 0.5f, /* shortestWidth */ 10) + assertTrue(shelfState.actualWidth == 20) + + shelf.updateStateWidth(shelfState, /* fraction */ 1f, /* shortestWidth */ 10) + assertTrue(shelfState.actualWidth == 30) + } + + @Test + fun testShelfIsLong_WhenNotOnLockscreen() { + setFractionToShade(0f) + setOnLockscreen(false) + + shelf.updateStateWidth(shelfState, /* fraction */ 0f, /* shortestWidth */ 10) + assertTrue(shelfState.actualWidth == 30) + } + + @Test + fun testX_inViewForClick() { + val isXInView = shelf.isXInView( + /* localX */ 5f, + /* slop */ 5f, + /* left */ 0f, + /* right */ 10f) + assertTrue(isXInView) + } + + @Test + fun testXSlop_inViewForClick() { + val isLeftXSlopInView = shelf.isXInView( + /* localX */ -3f, + /* slop */ 5f, + /* left */ 0f, + /* right */ 10f) + assertTrue(isLeftXSlopInView) + + val isRightXSlopInView = shelf.isXInView( + /* localX */ 13f, + /* slop */ 5f, + /* left */ 0f, + /* right */ 10f) + assertTrue(isRightXSlopInView) + } + + @Test + fun testX_notInViewForClick() { + val isXLeftOfShelfInView = shelf.isXInView( + /* localX */ -10f, + /* slop */ 5f, + /* left */ 0f, + /* right */ 10f) + assertFalse(isXLeftOfShelfInView) + + val isXRightOfShelfInView = shelf.isXInView( + /* localX */ 20f, + /* slop */ 5f, + /* left */ 0f, + /* right */ 10f) + assertFalse(isXRightOfShelfInView) + } + + @Test + fun testY_inViewForClick() { + val isYInView = shelf.isYInView( + /* localY */ 5f, + /* slop */ 5f, + /* top */ 0f, + /* bottom */ 10f) + assertTrue(isYInView) + } + + @Test + fun testYSlop_inViewForClick() { + val isTopYSlopInView = shelf.isYInView( + /* localY */ -3f, + /* slop */ 5f, + /* top */ 0f, + /* bottom */ 10f) + assertTrue(isTopYSlopInView) + + val isBottomYSlopInView = shelf.isYInView( + /* localY */ 13f, + /* slop */ 5f, + /* top */ 0f, + /* bottom */ 10f) + assertTrue(isBottomYSlopInView) + } + + @Test + fun testY_notInViewForClick() { + val isYAboveShelfInView = shelf.isYInView( + /* localY */ -10f, + /* slop */ 5f, + /* top */ 0f, + /* bottom */ 5f) + assertFalse(isYAboveShelfInView) + + val isYBelowShelfInView = shelf.isYInView( + /* localY */ 15f, + /* slop */ 5f, + /* top */ 0f, + /* bottom */ 5f) + assertFalse(isYBelowShelfInView) + } + + private fun setFractionToShade(fraction: Float) { + shelf.setFractionToShade(fraction) + } + + private fun setOnLockscreen(isOnLockscreen: Boolean) { + whenever(ambientState.isOnKeyguard).thenReturn(isOnLockscreen) + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index 5d80bca03e03..bb79941b0e53 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -43,6 +43,7 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.SysuiTestCase; import com.android.systemui.dock.DockManager; +import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; import com.android.systemui.statusbar.NotificationMediaManager; @@ -97,6 +98,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { private KeyguardMessageArea mKeyguardMessageArea; @Mock private ShadeController mShadeController; + @Mock + private DreamOverlayStateController mDreamOverlayStateController; private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; @@ -116,6 +119,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { mStatusBarStateController, mock(ConfigurationController.class), mKeyguardUpdateMonitor, + mDreamOverlayStateController, mock(NavigationModeController.class), mock(DockManager.class), mock(NotificationShadeWindowController.class), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index b7c00fe5e3a1..1564dfe8cd06 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -87,6 +87,7 @@ import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.demomode.DemoModeController; +import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.fragments.FragmentService; @@ -285,6 +286,7 @@ public class StatusBarTest extends SysuiTestCase { @Mock private NotifLiveDataStore mNotifLiveDataStore; @Mock private InteractionJankMonitor mJankMonitor; @Mock private DeviceStateManager mDeviceStateManager; + @Mock private DreamOverlayStateController mDreamOverlayStateController; private ShadeController mShadeController; private final FakeSystemClock mFakeSystemClock = new FakeSystemClock(); private FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock); @@ -474,7 +476,8 @@ public class StatusBarTest extends SysuiTestCase { mActivityLaunchAnimator, mNotifPipelineFlags, mJankMonitor, - mDeviceStateManager); + mDeviceStateManager, + mDreamOverlayStateController); when(mKeyguardViewMediator.registerStatusBar( any(StatusBar.class), any(NotificationPanelViewController.class), diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index 2168fb18888f..a65d5b3b94f7 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java @@ -3346,8 +3346,10 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku // Isolate the changes relating to RROs. The app info must be copied to prevent // affecting other parts of system server that may have cached this app info. oldAppInfo = new ApplicationInfo(oldAppInfo); - oldAppInfo.overlayPaths = newAppInfo.overlayPaths.clone(); - oldAppInfo.resourceDirs = newAppInfo.resourceDirs.clone(); + oldAppInfo.overlayPaths = newAppInfo.overlayPaths == null + ? null : newAppInfo.overlayPaths.clone(); + oldAppInfo.resourceDirs = newAppInfo.resourceDirs == null + ? null : newAppInfo.resourceDirs.clone(); provider.info.providerInfo.applicationInfo = oldAppInfo; for (int j = 0, M = provider.widgets.size(); j < M; j++) { diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 095c1fc80d3e..76ee728fdb07 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -17,6 +17,7 @@ package com.android.server.autofill; import static android.service.autofill.AutofillFieldClassificationService.EXTRA_SCORES; +import static android.service.autofill.AutofillService.EXTRA_FILL_RESPONSE; import static android.service.autofill.FillRequest.FLAG_ACTIVITY_START; import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST; import static android.service.autofill.FillRequest.FLAG_PASSWORD_INPUT_TYPE; @@ -47,13 +48,16 @@ import android.annotation.Nullable; import android.app.Activity; import android.app.ActivityTaskManager; import android.app.IAssistDataReceiver; +import android.app.PendingIntent; import android.app.assist.AssistStructure; import android.app.assist.AssistStructure.AutofillOverlay; import android.app.assist.AssistStructure.ViewNode; +import android.content.BroadcastReceiver; import android.content.ClipData; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.IntentSender; import android.content.pm.ApplicationInfo; import android.graphics.Bitmap; @@ -148,6 +152,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState AutoFillUI.AutoFillUiCallback, ValueFinder { private static final String TAG = "AutofillSession"; + private static final String ACTION_DELAYED_FILL = + "android.service.autofill.action.DELAYED_FILL"; private static final String EXTRA_REQUEST_ID = "android.service.autofill.extra.REQUEST_ID"; final Object mLock; @@ -155,6 +161,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState private final AutofillManagerServiceImpl mService; private final Handler mHandler; private final AutoFillUI mUi; + @NonNull private final Context mContext; private final MetricsLogger mMetricsLogger = new MetricsLogger(); @@ -269,6 +276,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState */ private boolean mHasCallback; + @GuardedBy("mLock") + private boolean mDelayedFillBroadcastReceiverRegistered; + + @GuardedBy("mLock") + private PendingIntent mDelayedFillPendingIntent; + /** * Extras sent by service on {@code onFillRequest()} calls; the most recent non-null extra is * saved and used on subsequent {@code onFillRequest()} and {@code onSaveRequest()} calls. @@ -356,6 +369,32 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState private final AccessibilityManager mAccessibilityManager; + // TODO(b/216576510): Share one BroadcastReceiver between all Sessions instead of creating a + // new one per Session. + private final BroadcastReceiver mDelayedFillBroadcastReceiver = + new BroadcastReceiver() { + // ErrorProne says mAssistReceiver#processDelayedFillLocked needs to be guarded by + // 'Session.this.mLock', which is the same as mLock. + @SuppressWarnings("GuardedBy") + @Override + public void onReceive(final Context context, final Intent intent) { + if (!intent.getAction().equals(ACTION_DELAYED_FILL)) { + Slog.wtf(TAG, "Unexpected action is received."); + return; + } + if (!intent.hasExtra(EXTRA_REQUEST_ID)) { + Slog.e(TAG, "Delay fill action is missing request id extra."); + return; + } + Slog.v(TAG, "mDelayedFillBroadcastReceiver delayed fill action received"); + synchronized (mLock) { + int requestId = intent.getIntExtra(EXTRA_REQUEST_ID, 0); + FillResponse response = intent.getParcelableExtra(EXTRA_FILL_RESPONSE); + mAssistReceiver.processDelayedFillLocked(requestId, response); + } + } + }; + void onSwitchInputMethodLocked() { // One caveat is that for the case where the focus is on a field for which regular autofill // returns null, and augmented autofill is triggered, and then the user switches the input @@ -408,31 +447,24 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState */ private final class SessionFlags { /** Whether autofill is disabled by the service */ - @GuardedBy("mLock") private boolean mAutofillDisabled; /** Whether the autofill service supports inline suggestions */ - @GuardedBy("mLock") private boolean mInlineSupportedByService; /** True if session is for augmented only */ - @GuardedBy("mLock") private boolean mAugmentedAutofillOnly; /** Whether the session is currently showing the SaveUi. */ - @GuardedBy("mLock") private boolean mShowingSaveUi; /** Whether the current {@link FillResponse} is expired. */ - @GuardedBy("mLock") private boolean mExpiredResponse; /** Whether the client is using {@link android.view.autofill.AutofillRequestCallback}. */ - @GuardedBy("mLock") private boolean mClientSuggestionsEnabled; /** Whether the fill dialog UI is disabled. */ - @GuardedBy("mLock") private boolean mFillDialogDisabled; } @@ -447,6 +479,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState private InlineSuggestionsRequest mPendingInlineSuggestionsRequest; @GuardedBy("mLock") private FillRequest mPendingFillRequest; + @GuardedBy("mLock") + private FillRequest mLastFillRequest; @Nullable Consumer<InlineSuggestionsRequest> newAutofillRequestLocked(ViewState viewState, boolean isInlineRequest) { @@ -473,6 +507,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mPendingInlineSuggestionsRequest = inlineRequest; } + @GuardedBy("mLock") void maybeRequestFillFromServiceLocked() { if (mPendingFillRequest == null) { return; @@ -490,9 +525,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(), mPendingFillRequest.getFillContexts(), mPendingFillRequest.getClientState(), - mPendingFillRequest.getFlags(), mPendingInlineSuggestionsRequest); + mPendingFillRequest.getFlags(), + mPendingInlineSuggestionsRequest, + mPendingFillRequest.getDelayedFillIntentSender()); } } + mLastFillRequest = mPendingFillRequest; mRemoteFillService.onFillRequest(mPendingFillRequest); mPendingInlineSuggestionsRequest = null; @@ -594,8 +632,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final ArrayList<FillContext> contexts = mergePreviousSessionLocked(/* forSave= */ false); + mDelayedFillPendingIntent = createPendingIntent(requestId); request = new FillRequest(requestId, contexts, mClientState, flags, - /*inlineSuggestionsRequest=*/null); + /*inlineSuggestionsRequest=*/ null, + /*delayedFillIntentSender=*/ mDelayedFillPendingIntent == null + ? null + : mDelayedFillPendingIntent.getIntentSender()); mPendingFillRequest = request; maybeRequestFillFromServiceLocked(); @@ -610,7 +652,70 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState public void onHandleAssistScreenshot(Bitmap screenshot) { // Do nothing } - }; + + @GuardedBy("mLock") + void processDelayedFillLocked(int requestId, FillResponse response) { + if (mLastFillRequest != null && requestId == mLastFillRequest.getId()) { + Slog.v(TAG, "processDelayedFillLocked: " + + "calling onFillRequestSuccess with new response"); + onFillRequestSuccess(requestId, response, + mService.getServicePackageName(), mLastFillRequest.getFlags()); + } + } + } + + /** Creates {@link PendingIntent} for autofill service to send a delayed fill. */ + private PendingIntent createPendingIntent(int requestId) { + Slog.d(TAG, "createPendingIntent for request " + requestId); + PendingIntent pendingIntent; + final long identity = Binder.clearCallingIdentity(); + try { + Intent intent = new Intent(ACTION_DELAYED_FILL).setPackage("android") + .putExtra(EXTRA_REQUEST_ID, requestId); + pendingIntent = PendingIntent.getBroadcast( + mContext, this.id, intent, + PendingIntent.FLAG_MUTABLE + | PendingIntent.FLAG_ONE_SHOT + | PendingIntent.FLAG_CANCEL_CURRENT); + } finally { + Binder.restoreCallingIdentity(identity); + } + return pendingIntent; + } + + @GuardedBy("mLock") + private void clearPendingIntentLocked() { + Slog.d(TAG, "clearPendingIntentLocked"); + if (mDelayedFillPendingIntent == null) { + return; + } + final long identity = Binder.clearCallingIdentity(); + try { + mDelayedFillPendingIntent.cancel(); + mDelayedFillPendingIntent = null; + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @GuardedBy("mLock") + private void registerDelayedFillBroadcastLocked() { + if (!mDelayedFillBroadcastReceiverRegistered) { + Slog.v(TAG, "registerDelayedFillBroadcastLocked()"); + IntentFilter intentFilter = new IntentFilter(ACTION_DELAYED_FILL); + mContext.registerReceiver(mDelayedFillBroadcastReceiver, intentFilter); + mDelayedFillBroadcastReceiverRegistered = true; + } + } + + @GuardedBy("mLock") + private void unregisterDelayedFillBroadcastLocked() { + if (mDelayedFillBroadcastReceiverRegistered) { + Slog.v(TAG, "unregisterDelayedFillBroadcastLocked()"); + mContext.unregisterReceiver(mDelayedFillBroadcastReceiver); + mDelayedFillBroadcastReceiverRegistered = false; + } + } /** * Returns the ids of all entries in {@link #mViewStates} in the same order. @@ -964,6 +1069,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mHasCallback = hasCallback; mUiLatencyHistory = uiLatencyHistory; mWtfHistory = wtfHistory; + mContext = context; mComponentName = componentName; mCompatMode = compatMode; mSessionState = STATE_ACTIVE; @@ -1096,6 +1202,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState processNullResponseLocked(requestId, requestFlags); return; } + + final int flags = response.getFlags(); + if ((flags & FillResponse.FLAG_DELAY_FILL) != 0) { + Slog.v(TAG, "Service requested to wait for delayed fill response."); + registerDelayedFillBroadcastLocked(); + } } mService.setLastResponse(id, response); @@ -1206,6 +1318,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @Nullable CharSequence message) { boolean showMessage = !TextUtils.isEmpty(message); synchronized (mLock) { + unregisterDelayedFillBroadcastLocked(); if (mDestroyed) { Slog.w(TAG, "Call to Session#onFillRequestFailureOrTimeout(req=" + requestId + ") rejected - session: " + id + " destroyed"); @@ -1522,7 +1635,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState Slog.e(TAG, "Error sending input show up notification", e); } } - synchronized (Session.this.mLock) { + synchronized (mLock) { // stop to show fill dialog mSessionFlags.mFillDialogDisabled = true; } @@ -3259,7 +3372,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState private boolean isFillDialogUiEnabled() { // TODO read from Settings or somewhere final boolean isSettingsEnabledFillDialog = true; - synchronized (Session.this.mLock) { + synchronized (mLock) { return isSettingsEnabledFillDialog && !mSessionFlags.mFillDialogDisabled; } } @@ -3530,6 +3643,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @GuardedBy("mLock") private void processNullResponseLocked(int requestId, int flags) { + unregisterDelayedFillBroadcastLocked(); if ((flags & FLAG_MANUAL_REQUEST) != 0) { getUiForShowing().showError(R.string.autofill_error_cannot_autofill, this); } @@ -3744,6 +3858,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // only if handling the current response requires it. mUi.hideAll(this); + if ((newResponse.getFlags() & FillResponse.FLAG_DELAY_FILL) == 0) { + Slog.d(TAG, "Service did not request to wait for delayed fill response."); + unregisterDelayedFillBroadcastLocked(); + } + final int requestId = newResponse.getRequestId(); if (sVerbose) { Slog.v(TAG, "processResponseLocked(): mCurrentViewId=" + mCurrentViewId @@ -4296,6 +4415,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return null; } + clearPendingIntentLocked(); + unregisterDelayedFillBroadcastLocked(); + unlinkClientVultureLocked(); mUi.destroyAll(mPendingSaveUi, this, true); mUi.clearCallback(this); diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index cfd37988d234..c3ab2a79e288 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -1255,7 +1255,7 @@ public class CompanionDeviceManagerService extends SystemService } @Override - public void onDeviceDisconnected(BluetoothDevice device, @DisconnectReason int reason) { + public void onDeviceDisconnected(BluetoothDevice device, int reason) { Slog.d(LOG_TAG, device.getAddress() + " disconnected w/ reason: (" + reason + ") " + BluetoothAdapter.BluetoothConnectionCallback.disconnectReasonText(reason)); CompanionDeviceManagerService.this.onDeviceDisconnected(device.getAddress()); diff --git a/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java index dbe866b374f1..93cbe973b00e 100644 --- a/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java +++ b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java @@ -91,7 +91,7 @@ class BluetoothCompanionDeviceConnectionListener */ @Override public void onDeviceDisconnected(@NonNull BluetoothDevice device, - @DisconnectReason int reason) { + int reason) { if (DEBUG) { Log.i(TAG, "onDevice_Disconnected() " + btDeviceToString(device)); Log.d(TAG, " reason=" + disconnectReasonText(reason)); diff --git a/services/companion/java/com/android/server/companion/virtual/InputController.java b/services/companion/java/com/android/server/companion/virtual/InputController.java index 6c56e2f777f3..e6bfd1ff7f1a 100644 --- a/services/companion/java/com/android/server/companion/virtual/InputController.java +++ b/services/companion/java/com/android/server/companion/virtual/InputController.java @@ -18,8 +18,11 @@ package com.android.server.companion.virtual; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.StringDef; import android.graphics.Point; import android.graphics.PointF; +import android.hardware.display.DisplayManagerInternal; +import android.hardware.input.InputManager; import android.hardware.input.InputManagerInternal; import android.hardware.input.VirtualKeyEvent; import android.hardware.input.VirtualMouseButtonEvent; @@ -48,6 +51,20 @@ class InputController { private static final String TAG = "VirtualInputController"; + private static final AtomicLong sNextPhysId = new AtomicLong(1); + + static final String PHYS_TYPE_KEYBOARD = "Keyboard"; + static final String PHYS_TYPE_MOUSE = "Mouse"; + static final String PHYS_TYPE_TOUCHSCREEN = "Touchscreen"; + @StringDef(prefix = { "PHYS_TYPE_" }, value = { + PHYS_TYPE_KEYBOARD, + PHYS_TYPE_MOUSE, + PHYS_TYPE_TOUCHSCREEN, + }) + @Retention(RetentionPolicy.SOURCE) + @interface PhysType { + } + private final Object mLock; /* Token -> file descriptor associations. */ @@ -56,6 +73,8 @@ class InputController { final Map<IBinder, InputDeviceDescriptor> mInputDeviceDescriptors = new ArrayMap<>(); private final NativeWrapper mNativeWrapper; + private final DisplayManagerInternal mDisplayManagerInternal; + private final InputManagerInternal mInputManagerInternal; /** * Because the pointer is a singleton, it can only be targeted at one display at a time. Because @@ -73,6 +92,8 @@ class InputController { mLock = lock; mNativeWrapper = nativeWrapper; mActivePointerDisplayId = Display.INVALID_DISPLAY; + mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); + mInputManagerInternal = LocalServices.getService(InputManagerInternal.class); } void close() { @@ -90,7 +111,9 @@ class InputController { int productId, @NonNull IBinder deviceToken, int displayId) { - final int fd = mNativeWrapper.openUinputKeyboard(deviceName, vendorId, productId); + final String phys = createPhys(PHYS_TYPE_KEYBOARD); + setUniqueIdAssociation(displayId, phys); + final int fd = mNativeWrapper.openUinputKeyboard(deviceName, vendorId, productId, phys); if (fd < 0) { throw new RuntimeException( "A native error occurred when creating keyboard: " + -fd); @@ -99,7 +122,7 @@ class InputController { synchronized (mLock) { mInputDeviceDescriptors.put(deviceToken, new InputDeviceDescriptor(fd, binderDeathRecipient, - InputDeviceDescriptor.TYPE_KEYBOARD, displayId)); + InputDeviceDescriptor.TYPE_KEYBOARD, displayId, phys)); } try { deviceToken.linkToDeath(binderDeathRecipient, /* flags= */ 0); @@ -114,7 +137,9 @@ class InputController { int productId, @NonNull IBinder deviceToken, int displayId) { - final int fd = mNativeWrapper.openUinputMouse(deviceName, vendorId, productId); + final String phys = createPhys(PHYS_TYPE_MOUSE); + setUniqueIdAssociation(displayId, phys); + final int fd = mNativeWrapper.openUinputMouse(deviceName, vendorId, productId, phys); if (fd < 0) { throw new RuntimeException( "A native error occurred when creating mouse: " + -fd); @@ -123,11 +148,9 @@ class InputController { synchronized (mLock) { mInputDeviceDescriptors.put(deviceToken, new InputDeviceDescriptor(fd, binderDeathRecipient, - InputDeviceDescriptor.TYPE_MOUSE, displayId)); - final InputManagerInternal inputManagerInternal = - LocalServices.getService(InputManagerInternal.class); - inputManagerInternal.setVirtualMousePointerDisplayId(displayId); - inputManagerInternal.setPointerAcceleration(1); + InputDeviceDescriptor.TYPE_MOUSE, displayId, phys)); + mInputManagerInternal.setVirtualMousePointerDisplayId(displayId); + mInputManagerInternal.setPointerAcceleration(1); mActivePointerDisplayId = displayId; } try { @@ -144,7 +167,9 @@ class InputController { @NonNull IBinder deviceToken, int displayId, @NonNull Point screenSize) { - final int fd = mNativeWrapper.openUinputTouchscreen(deviceName, vendorId, productId, + final String phys = createPhys(PHYS_TYPE_TOUCHSCREEN); + setUniqueIdAssociation(displayId, phys); + final int fd = mNativeWrapper.openUinputTouchscreen(deviceName, vendorId, productId, phys, screenSize.y, screenSize.x); if (fd < 0) { throw new RuntimeException( @@ -154,7 +179,7 @@ class InputController { synchronized (mLock) { mInputDeviceDescriptors.put(deviceToken, new InputDeviceDescriptor(fd, binderDeathRecipient, - InputDeviceDescriptor.TYPE_TOUCHSCREEN, displayId)); + InputDeviceDescriptor.TYPE_TOUCHSCREEN, displayId, phys)); } try { deviceToken.linkToDeath(binderDeathRecipient, /* flags= */ 0); @@ -174,6 +199,7 @@ class InputController { } token.unlinkToDeath(inputDeviceDescriptor.getDeathRecipient(), /* flags= */ 0); mNativeWrapper.closeUinput(inputDeviceDescriptor.getFileDescriptor()); + InputManager.getInstance().removeUniqueIdAssociation(inputDeviceDescriptor.getPhys()); // Reset values to the default if all virtual mice are unregistered, or set display // id if there's another mouse (choose the most recent). @@ -197,9 +223,7 @@ class InputController { } } if (mostRecentlyCreatedMouse != null) { - final InputManagerInternal inputManagerInternal = - LocalServices.getService(InputManagerInternal.class); - inputManagerInternal.setVirtualMousePointerDisplayId( + mInputManagerInternal.setVirtualMousePointerDisplayId( mostRecentlyCreatedMouse.getDisplayId()); mActivePointerDisplayId = mostRecentlyCreatedMouse.getDisplayId(); } else { @@ -209,14 +233,21 @@ class InputController { } private void resetMouseValuesLocked() { - final InputManagerInternal inputManagerInternal = - LocalServices.getService(InputManagerInternal.class); - inputManagerInternal.setVirtualMousePointerDisplayId(Display.INVALID_DISPLAY); - inputManagerInternal.setPointerAcceleration( + mInputManagerInternal.setVirtualMousePointerDisplayId(Display.INVALID_DISPLAY); + mInputManagerInternal.setPointerAcceleration( IInputConstants.DEFAULT_POINTER_ACCELERATION); mActivePointerDisplayId = Display.INVALID_DISPLAY; } + private static String createPhys(@PhysType String type) { + return String.format("virtual%s:%d", type, sNextPhysId.getAndIncrement()); + } + + private void setUniqueIdAssociation(int displayId, String phys) { + final String displayUniqueId = mDisplayManagerInternal.getDisplayInfo(displayId).uniqueId; + InputManager.getInstance().addUniqueIdAssociation(phys, displayUniqueId); + } + boolean sendKeyEvent(@NonNull IBinder token, @NonNull VirtualKeyEvent event) { synchronized (mLock) { final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get( @@ -321,17 +352,18 @@ class InputController { fout.println(" creationOrder: " + inputDeviceDescriptor.getCreationOrderNumber()); fout.println(" type: " + inputDeviceDescriptor.getType()); + fout.println(" phys: " + inputDeviceDescriptor.getPhys()); } fout.println(" Active mouse display id: " + mActivePointerDisplayId); } } private static native int nativeOpenUinputKeyboard(String deviceName, int vendorId, - int productId); - private static native int nativeOpenUinputMouse(String deviceName, int vendorId, - int productId); + int productId, String phys); + private static native int nativeOpenUinputMouse(String deviceName, int vendorId, int productId, + String phys); private static native int nativeOpenUinputTouchscreen(String deviceName, int vendorId, - int productId, int height, int width); + int productId, String phys, int height, int width); private static native boolean nativeCloseUinput(int fd); private static native boolean nativeWriteKeyEvent(int fd, int androidKeyCode, int action); private static native boolean nativeWriteButtonEvent(int fd, int buttonCode, int action); @@ -345,20 +377,18 @@ class InputController { /** Wrapper around the static native methods for tests. */ @VisibleForTesting protected static class NativeWrapper { - public int openUinputKeyboard(String deviceName, int vendorId, int productId) { - return nativeOpenUinputKeyboard(deviceName, vendorId, - productId); + public int openUinputKeyboard(String deviceName, int vendorId, int productId, String phys) { + return nativeOpenUinputKeyboard(deviceName, vendorId, productId, phys); } - public int openUinputMouse(String deviceName, int vendorId, int productId) { - return nativeOpenUinputMouse(deviceName, vendorId, - productId); + public int openUinputMouse(String deviceName, int vendorId, int productId, String phys) { + return nativeOpenUinputMouse(deviceName, vendorId, productId, phys); } - public int openUinputTouchscreen(String deviceName, int vendorId, int productId, int height, - int width) { - return nativeOpenUinputTouchscreen(deviceName, vendorId, - productId, height, width); + public int openUinputTouchscreen(String deviceName, int vendorId, + int productId, String phys, int height, int width) { + return nativeOpenUinputTouchscreen(deviceName, vendorId, productId, phys, height, + width); } public boolean closeUinput(int fd) { @@ -410,15 +440,17 @@ class InputController { private final IBinder.DeathRecipient mDeathRecipient; private final @Type int mType; private final int mDisplayId; + private final String mPhys; // Monotonically increasing number; devices with lower numbers were created earlier. private final long mCreationOrderNumber; - InputDeviceDescriptor(int fd, IBinder.DeathRecipient deathRecipient, - @Type int type, int displayId) { + InputDeviceDescriptor(int fd, IBinder.DeathRecipient deathRecipient, @Type int type, + int displayId, String phys) { mFd = fd; mDeathRecipient = deathRecipient; mType = type; mDisplayId = displayId; + mPhys = phys; mCreationOrderNumber = sNextCreationOrderNumber.getAndIncrement(); } @@ -445,6 +477,10 @@ class InputController { public long getCreationOrderNumber() { return mCreationOrderNumber; } + + public String getPhys() { + return mPhys; + } } private final class BinderDeathRecipient implements IBinder.DeathRecipient { diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 8dfb7dd21b4e..5da461d8e392 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -1365,7 +1365,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub } public void notePhoneDataConnectionState(final int dataType, final boolean hasData, - final int serviceType) { + final int serviceType, final int nrFrequency) { enforceCallingPermission(); synchronized (mLock) { final long elapsedRealtime = SystemClock.elapsedRealtime(); @@ -1373,7 +1373,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub mHandler.post(() -> { synchronized (mStats) { mStats.notePhoneDataConnectionStateLocked(dataType, hasData, serviceType, - elapsedRealtime, uptime); + nrFrequency, elapsedRealtime, uptime); } }); } diff --git a/services/core/java/com/android/server/am/DataConnectionStats.java b/services/core/java/com/android/server/am/DataConnectionStats.java index 6e39a4c802d9..f0910dcb0da2 100644 --- a/services/core/java/com/android/server/am/DataConnectionStats.java +++ b/services/core/java/com/android/server/am/DataConnectionStats.java @@ -109,7 +109,7 @@ public class DataConnectionStats extends BroadcastReceiver { } try { mBatteryStats.notePhoneDataConnectionState(networkType, visible, - mServiceState.getState()); + mServiceState.getState(), mServiceState.getNrFrequencyRange()); } catch (RemoteException e) { Log.w(TAG, "Error noting data connection state", e); } diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index 42fca9b840df..47f31d505867 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -486,9 +486,7 @@ public class BtHelper { return; } final BluetoothDevice btDevice = deviceList.get(0); - final @BluetoothProfile.BtProfileState int state = - proxy.getConnectionState(btDevice); - if (state == BluetoothProfile.STATE_CONNECTED) { + if (proxy.getConnectionState(btDevice) == BluetoothProfile.STATE_CONNECTED) { mDeviceBroker.queueOnBluetoothActiveDeviceChanged( new AudioDeviceBroker.BtDeviceChangedData(btDevice, null, new BtProfileConnectionInfo(profile), "mBluetoothProfileServiceListener")); diff --git a/services/core/java/com/android/server/biometrics/log/BiometricContext.java b/services/core/java/com/android/server/biometrics/log/BiometricContext.java new file mode 100644 index 000000000000..8d28298542dc --- /dev/null +++ b/services/core/java/com/android/server/biometrics/log/BiometricContext.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2022 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.biometrics.log; + +import android.annotation.NonNull; +import android.hardware.biometrics.common.OperationContext; + +import java.util.function.Consumer; + +/** + * Cache for system state not directly related to biometric operations that is used for + * logging or optimizations. + */ +public interface BiometricContext { + /** Gets the context source. */ + static BiometricContext getInstance() { + return BiometricContextProvider.sInstance.get(); + } + + /** If the display is in AOD. */ + boolean isAoD(); + + /** + * Subscribe to context changes. + * + * @param context context that will be modified when changed + * @param consumer callback when the context is modified + */ + void subscribe(@NonNull OperationContext context, @NonNull Consumer<OperationContext> consumer); + + /** Unsubscribe from context changes. */ + void unsubscribe(@NonNull OperationContext context); +} diff --git a/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java b/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java new file mode 100644 index 000000000000..65e9e3dec60f --- /dev/null +++ b/services/core/java/com/android/server/biometrics/log/BiometricContextProvider.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2022 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.biometrics.log; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.hardware.biometrics.IBiometricContextListener; +import android.hardware.biometrics.common.OperationContext; +import android.os.Handler; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Singleton; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.statusbar.IStatusBarService; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Consumer; + +/** + * A default provider for {@link BiometricContext}. + */ +class BiometricContextProvider implements BiometricContext { + + private static final String TAG = "BiometricContextProvider"; + + static final Singleton<BiometricContextProvider> sInstance = + new Singleton<BiometricContextProvider>() { + @Override + protected BiometricContextProvider create() { + return new BiometricContextProvider(IStatusBarService.Stub.asInterface( + ServiceManager.getService( + Context.STATUS_BAR_SERVICE)), null /* handler */); + } + }; + + @NonNull + private final Map<OperationContext, Consumer<OperationContext>> mSubscribers = + new ConcurrentHashMap<>(); + + @VisibleForTesting + BiometricContextProvider(@NonNull IStatusBarService service, @Nullable Handler handler) { + try { + service.setBiometicContextListener(new IBiometricContextListener.Stub() { + @Override + public void onDozeChanged(boolean isDozing) { + mIsDozing = isDozing; + notifyChanged(); + } + + private void notifyChanged() { + if (handler != null) { + handler.post(() -> notifySubscribers()); + } else { + notifySubscribers(); + } + } + }); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to register biometric context listener", e); + } + } + + private boolean mIsDozing = false; + + @Override + public boolean isAoD() { + return mIsDozing; + } + + @Override + public void subscribe(@NonNull OperationContext context, + @NonNull Consumer<OperationContext> consumer) { + mSubscribers.put(context, consumer); + } + + @Override + public void unsubscribe(@NonNull OperationContext context) { + mSubscribers.remove(context); + } + + private void notifySubscribers() { + mSubscribers.forEach((context, consumer) -> { + context.isAoD = mIsDozing; + consumer.accept(context); + }); + } +} diff --git a/services/core/java/com/android/server/biometrics/log/BiometricLogger.java b/services/core/java/com/android/server/biometrics/log/BiometricLogger.java index d029af38c683..018839079e22 100644 --- a/services/core/java/com/android/server/biometrics/log/BiometricLogger.java +++ b/services/core/java/com/android/server/biometrics/log/BiometricLogger.java @@ -79,6 +79,12 @@ public class BiometricLogger { } }; + /** Get a new logger with all unknown fields (for operations that do not require logs). */ + public static BiometricLogger ofUnknown(@NonNull Context context) { + return new BiometricLogger(context, BiometricsProtoEnums.MODALITY_UNKNOWN, + BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN); + } + /** * @param context system_server context * @param statsModality One of {@link BiometricsProtoEnums} MODALITY_* constants. @@ -103,6 +109,11 @@ public class BiometricLogger { mSensorManager = sensorManager; } + /** Creates a new logger with the action replaced with the new action. */ + public BiometricLogger swapAction(@NonNull Context context, int statsAction) { + return new BiometricLogger(context, mStatsModality, statsAction, mStatsClient); + } + /** Disable logging metrics and only log critical events, such as system health issues. */ public void disableMetrics() { mShouldLogMetrics = false; diff --git a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java index 8b8103e6e2c9..e07a68c6d94d 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java @@ -29,6 +29,9 @@ import android.os.VibrationEffect; import android.os.Vibrator; import android.util.Slog; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; + import java.util.function.Supplier; /** @@ -57,9 +60,9 @@ public abstract class AcquisitionClient<T> extends HalClientMonitor<T> implement public AcquisitionClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner, int cookie, int sensorId, boolean shouldVibrate, - int statsModality, int statsAction, int statsClient) { - super(context, lazyDaemon, token, listener, userId, owner, cookie, sensorId, statsModality, - statsAction, statsClient); + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) { + super(context, lazyDaemon, token, listener, userId, owner, cookie, sensorId, + logger, biometricContext); mPowerManager = context.getSystemService(PowerManager.class); mShouldVibrate = shouldVibrate; } diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java index b715faf3ca8f..949edd0e79ed 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java @@ -29,7 +29,6 @@ import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.BiometricOverlayConstants; -import android.hardware.biometrics.BiometricsProtoEnums; import android.os.IBinder; import android.os.RemoteException; import android.os.SystemClock; @@ -39,6 +38,8 @@ import android.util.Slog; import com.android.server.biometrics.BiometricsProto; import com.android.server.biometrics.Utils; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import java.util.ArrayList; import java.util.List; @@ -93,13 +94,13 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> public AuthenticationClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId, boolean restricted, @NonNull String owner, - int cookie, boolean requireConfirmation, int sensorId, boolean isStrongBiometric, - int statsModality, int statsClient, @Nullable TaskStackListener taskStackListener, + int cookie, boolean requireConfirmation, int sensorId, + @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext, + boolean isStrongBiometric, @Nullable TaskStackListener taskStackListener, @NonNull LockoutTracker lockoutTracker, boolean allowBackgroundAuthentication, boolean shouldVibrate, boolean isKeyguardBypassEnabled) { super(context, lazyDaemon, token, listener, targetUserId, owner, cookie, sensorId, - shouldVibrate, statsModality, BiometricsProtoEnums.ACTION_AUTHENTICATE, - statsClient); + shouldVibrate, biometricLogger, biometricContext); mIsStrongBiometric = isStrongBiometric; mOperationId = operationId; mRequireConfirmation = requireConfirmation; diff --git a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java index e1f7e2ab5461..1b2e606117e7 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java +++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java @@ -21,12 +21,12 @@ import static com.android.internal.annotations.VisibleForTesting.Visibility; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; -import android.hardware.biometrics.BiometricsProtoEnums; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricLogger; import java.util.NoSuchElementException; @@ -50,6 +50,7 @@ public abstract class BaseClientMonitor implements IBinder.DeathRecipient { @NonNull private final String mOwner; private final int mSensorId; // sensorId as configured by the framework @NonNull private final BiometricLogger mLogger; + @NonNull private final BiometricContext mBiometricContext; @Nullable private IBinder mToken; private long mRequestId; @@ -82,22 +83,13 @@ public abstract class BaseClientMonitor implements IBinder.DeathRecipient { * @param owner name of the client that owns this * @param cookie BiometricPrompt authentication cookie (to be moved into a subclass soon) * @param sensorId ID of the sensor that the operation should be requested of - * @param statsModality One of {@link BiometricsProtoEnums} MODALITY_* constants - * @param statsAction One of {@link BiometricsProtoEnums} ACTION_* constants - * @param statsClient One of {@link BiometricsProtoEnums} CLIENT_* constants + * @param logger framework stats logger + * @param biometricContext system context metadata */ public BaseClientMonitor(@NonNull Context context, @Nullable IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId, - @NonNull String owner, int cookie, int sensorId, int statsModality, int statsAction, - int statsClient) { - this(context, token, listener, userId, owner, cookie, sensorId, - new BiometricLogger(context, statsModality, statsAction, statsClient)); - } - - @VisibleForTesting - BaseClientMonitor(@NonNull Context context, - @Nullable IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId, - @NonNull String owner, int cookie, int sensorId, @NonNull BiometricLogger logger) { + @NonNull String owner, int cookie, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) { mSequentialId = sCount++; mContext = context; mToken = token; @@ -108,6 +100,7 @@ public abstract class BaseClientMonitor implements IBinder.DeathRecipient { mCookie = cookie; mSensorId = sensorId; mLogger = logger; + mBiometricContext = biometricContext; try { if (token != null) { @@ -207,20 +200,29 @@ public abstract class BaseClientMonitor implements IBinder.DeathRecipient { return false; } + /** System context that may change during operations. */ + @NonNull + protected BiometricContext getBiometricContext() { + return mBiometricContext; + } + /** Logger for this client */ @NonNull public BiometricLogger getLogger() { return mLogger; } + @NonNull public final Context getContext() { return mContext; } + @NonNull public final String getOwnerString() { return mOwner; } + @Nullable public final ClientMonitorCallbackConverter getListener() { return mListener; } @@ -229,6 +231,7 @@ public abstract class BaseClientMonitor implements IBinder.DeathRecipient { return mTargetUserId; } + @Nullable public final IBinder getToken() { return mToken; } diff --git a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java index 74f4931cf2aa..483ce75eae98 100644 --- a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java @@ -20,13 +20,14 @@ import android.annotation.NonNull; import android.content.Context; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricOverlayConstants; -import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.fingerprint.FingerprintManager; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; import com.android.server.biometrics.BiometricsProto; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import java.util.Arrays; import java.util.function.Supplier; @@ -53,10 +54,10 @@ public abstract class EnrollClient<T> extends AcquisitionClient<T> implements En public EnrollClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils utils, - int timeoutSec, int statsModality, int sensorId, boolean shouldVibrate) { + int timeoutSec, int sensorId, boolean shouldVibrate, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) { super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId, - shouldVibrate, statsModality, BiometricsProtoEnums.ACTION_ENROLL, - BiometricsProtoEnums.CLIENT_UNKNOWN); + shouldVibrate, logger, biometricContext); mBiometricUtils = utils; mHardwareAuthToken = Arrays.copyOf(hardwareAuthToken, hardwareAuthToken.length); mTimeoutSec = timeoutSec; diff --git a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java index 9689418b1f1a..2adf0cb3bab4 100644 --- a/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/GenerateChallengeClient.java @@ -18,12 +18,13 @@ package com.android.server.biometrics.sensors; import android.annotation.NonNull; import android.content.Context; -import android.hardware.biometrics.BiometricsProtoEnums; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; import com.android.server.biometrics.BiometricsProto; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import java.util.function.Supplier; @@ -33,10 +34,10 @@ public abstract class GenerateChallengeClient<T> extends HalClientMonitor<T> { public GenerateChallengeClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, - int userId, @NonNull String owner, int sensorId) { + int userId, @NonNull String owner, int sensorId, + @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext) { super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId, - BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN, - BiometricsProtoEnums.CLIENT_UNKNOWN); + biometricLogger, biometricContext); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java index 66a1c6e876ab..eabafceab590 100644 --- a/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java +++ b/services/core/java/com/android/server/biometrics/sensors/HalClientMonitor.java @@ -19,9 +19,12 @@ package com.android.server.biometrics.sensors; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; -import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.biometrics.common.OperationContext; import android.os.IBinder; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; + import java.util.function.Supplier; /** @@ -33,6 +36,9 @@ public abstract class HalClientMonitor<T> extends BaseClientMonitor { @NonNull protected final Supplier<T> mLazyDaemon; + @NonNull + protected final OperationContext mOperationContext = new OperationContext(); + /** * @param context system_server context * @param lazyDaemon pointer for lazy retrieval of the HAL @@ -42,16 +48,15 @@ public abstract class HalClientMonitor<T> extends BaseClientMonitor { * @param owner name of the client that owns this * @param cookie BiometricPrompt authentication cookie (to be moved into a subclass soon) * @param sensorId ID of the sensor that the operation should be requested of - * @param statsModality One of {@link BiometricsProtoEnums} MODALITY_* constants - * @param statsAction One of {@link BiometricsProtoEnums} ACTION_* constants - * @param statsClient One of {@link BiometricsProtoEnums} CLIENT_* constants + * @param biometricLogger framework stats logger + * @param biometricContext system context metadata */ public HalClientMonitor(@NonNull Context context, @NonNull Supplier<T> lazyDaemon, @Nullable IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId, - @NonNull String owner, int cookie, int sensorId, int statsModality, int statsAction, - int statsClient) { - super(context, token, listener, userId, owner, cookie, sensorId, statsModality, - statsAction, statsClient); + @NonNull String owner, int cookie, int sensorId, + @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext) { + super(context, token, listener, userId, owner, cookie, sensorId, + biometricLogger, biometricContext); mLazyDaemon = lazyDaemon; } @@ -71,4 +76,12 @@ public abstract class HalClientMonitor<T> extends BaseClientMonitor { * {@link #start(ClientMonitorCallback)}. */ public abstract void unableToStart(); + + @Override + public void destroy() { + super.destroy(); + + // subclasses should do this earlier in most cases, but ensure it happens now + getBiometricContext().unsubscribe(mOperationContext); + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java index 0e6d11ec5182..57ea812dbb3a 100644 --- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java @@ -19,11 +19,12 @@ package com.android.server.biometrics.sensors; import android.annotation.NonNull; import android.content.Context; import android.hardware.biometrics.BiometricAuthenticator; -import android.hardware.biometrics.BiometricsProtoEnums; import android.os.IBinder; import android.util.Slog; import com.android.server.biometrics.BiometricsProto; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import java.util.ArrayList; import java.util.List; @@ -101,19 +102,22 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide protected abstract InternalEnumerateClient<T> getEnumerateClient(Context context, Supplier<T> lazyDaemon, IBinder token, int userId, String owner, - List<S> enrolledList, BiometricUtils<S> utils, int sensorId); + List<S> enrolledList, BiometricUtils<S> utils, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext); protected abstract RemovalClient<S, T> getRemovalClient(Context context, Supplier<T> lazyDaemon, IBinder token, int biometricId, int userId, String owner, - BiometricUtils<S> utils, int sensorId, Map<Integer, Long> authenticatorIds); + BiometricUtils<S> utils, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, + Map<Integer, Long> authenticatorIds); protected InternalCleanupClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon, - int userId, @NonNull String owner, int sensorId, int statsModality, + int userId, @NonNull String owner, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, @NonNull List<S> enrolledList, @NonNull BiometricUtils<S> utils, @NonNull Map<Integer, Long> authenticatorIds) { super(context, lazyDaemon, null /* token */, null /* ClientMonitorCallbackConverter */, - userId, owner, 0 /* cookie */, sensorId, statsModality, - BiometricsProtoEnums.ACTION_ENUMERATE, BiometricsProtoEnums.CLIENT_UNKNOWN); + userId, owner, 0 /* cookie */, sensorId, logger, biometricContext); mBiometricUtils = utils; mAuthenticatorIds = authenticatorIds; mEnrolledList = enrolledList; @@ -127,7 +131,8 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide mUnknownHALTemplates.remove(template); mCurrentTask = getRemovalClient(getContext(), mLazyDaemon, getToken(), template.mIdentifier.getBiometricId(), template.mUserId, - getContext().getPackageName(), mBiometricUtils, getSensorId(), mAuthenticatorIds); + getContext().getPackageName(), mBiometricUtils, getSensorId(), + getLogger(), getBiometricContext(), mAuthenticatorIds); getLogger().logUnknownEnrollmentInHal(); @@ -145,7 +150,8 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide // Start enumeration. Removal will start if necessary, when enumeration is completed. mCurrentTask = getEnumerateClient(getContext(), mLazyDaemon, getToken(), getTargetUserId(), - getOwnerString(), mEnrolledList, mBiometricUtils, getSensorId()); + getOwnerString(), mEnrolledList, mBiometricUtils, getSensorId(), getLogger(), + getBiometricContext()); Slog.d(TAG, "Starting enumerate: " + mCurrentTask); mCurrentTask.start(mEnumerateCallback); diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java index 5f97f3711a60..7f8f38f3e9d2 100644 --- a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java @@ -19,11 +19,12 @@ package com.android.server.biometrics.sensors; import android.annotation.NonNull; import android.content.Context; import android.hardware.biometrics.BiometricAuthenticator; -import android.hardware.biometrics.BiometricsProtoEnums; import android.os.IBinder; import android.util.Slog; import com.android.server.biometrics.BiometricsProto; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import java.util.ArrayList; import java.util.List; @@ -47,12 +48,14 @@ public abstract class InternalEnumerateClient<T> extends HalClientMonitor<T> protected InternalEnumerateClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon, @NonNull IBinder token, int userId, @NonNull String owner, @NonNull List<? extends BiometricAuthenticator.Identifier> enrolledList, - @NonNull BiometricUtils utils, int sensorId, int statsModality) { + @NonNull BiometricUtils utils, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) { // Internal enumerate does not need to send results to anyone. Cleanup (enumerate + remove) // is all done internally. super(context, lazyDaemon, token, null /* ClientMonitorCallbackConverter */, userId, owner, - 0 /* cookie */, sensorId, statsModality, BiometricsProtoEnums.ACTION_ENUMERATE, - BiometricsProtoEnums.CLIENT_UNKNOWN); + 0 /* cookie */, sensorId, logger, biometricContext); + //, BiometricsProtoEnums.ACTION_ENUMERATE, + // BiometricsProtoEnums.CLIENT_UNKNOWN); mEnrolledList = enrolledList; mUtils = utils; } diff --git a/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java b/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java index 697d77cfbee6..d5aa5e2c9db6 100644 --- a/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/InvalidationClient.java @@ -19,12 +19,13 @@ package com.android.server.biometrics.sensors; import android.annotation.NonNull; import android.content.Context; import android.hardware.biometrics.BiometricAuthenticator; -import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.IInvalidationCallback; import android.os.RemoteException; import android.util.Slog; import com.android.server.biometrics.BiometricsProto; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import java.util.Map; import java.util.function.Supplier; @@ -42,12 +43,13 @@ public abstract class InvalidationClient<S extends BiometricAuthenticator.Identi @NonNull private final IInvalidationCallback mInvalidationCallback; public InvalidationClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon, - int userId, int sensorId, @NonNull Map<Integer, Long> authenticatorIds, + int userId, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, + @NonNull Map<Integer, Long> authenticatorIds, @NonNull IInvalidationCallback callback) { super(context, lazyDaemon, null /* token */, null /* listener */, userId, context.getOpPackageName(), 0 /* cookie */, sensorId, - BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN, - BiometricsProtoEnums.CLIENT_UNKNOWN); + logger, biometricContext); mAuthenticatorIds = authenticatorIds; mInvalidationCallback = callback; } diff --git a/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java b/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java index b2661a28012d..1097bb7da684 100644 --- a/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/InvalidationRequesterClient.java @@ -20,10 +20,11 @@ import android.annotation.NonNull; import android.content.Context; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricManager; -import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.IInvalidationCallback; import com.android.server.biometrics.BiometricsProto; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; /** * ClientMonitor subclass responsible for coordination of authenticatorId invalidation of other @@ -74,11 +75,10 @@ public class InvalidationRequesterClient<S extends BiometricAuthenticator.Identi }; public InvalidationRequesterClient(@NonNull Context context, int userId, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, @NonNull BiometricUtils<S> utils) { super(context, null /* token */, null /* listener */, userId, - context.getOpPackageName(), 0 /* cookie */, sensorId, - BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN, - BiometricsProtoEnums.CLIENT_UNKNOWN); + context.getOpPackageName(), 0 /* cookie */, sensorId, logger, biometricContext); mBiometricManager = context.getSystemService(BiometricManager.class); mUtils = utils; } diff --git a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java index a0cef94fcf48..07ce841a7cac 100644 --- a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java @@ -19,12 +19,13 @@ package com.android.server.biometrics.sensors; import android.annotation.NonNull; import android.content.Context; import android.hardware.biometrics.BiometricAuthenticator; -import android.hardware.biometrics.BiometricsProtoEnums; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; import com.android.server.biometrics.BiometricsProto; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import java.util.Map; import java.util.function.Supplier; @@ -44,10 +45,12 @@ public abstract class RemovalClient<S extends BiometricAuthenticator.Identifier, public RemovalClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner, @NonNull BiometricUtils<S> utils, int sensorId, - @NonNull Map<Integer, Long> authenticatorIds, int statsModality) { + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, + @NonNull Map<Integer, Long> authenticatorIds) { super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId, - statsModality, BiometricsProtoEnums.ACTION_REMOVE, - BiometricsProtoEnums.CLIENT_UNKNOWN); + logger, biometricContext); + //, BiometricsProtoEnums.ACTION_REMOVE, + // BiometricsProtoEnums.CLIENT_UNKNOWN); mBiometricUtils = utils; mAuthenticatorIds = authenticatorIds; mHasEnrollmentsBeforeStarting = !utils.getBiometricsForUser(context, userId).isEmpty(); diff --git a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java index 7d8386337ece..88f4da261d62 100644 --- a/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/RevokeChallengeClient.java @@ -18,20 +18,21 @@ package com.android.server.biometrics.sensors; import android.annotation.NonNull; import android.content.Context; -import android.hardware.biometrics.BiometricsProtoEnums; import android.os.IBinder; import com.android.server.biometrics.BiometricsProto; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import java.util.function.Supplier; public abstract class RevokeChallengeClient<T> extends HalClientMonitor<T> { public RevokeChallengeClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon, - @NonNull IBinder token, int userId, @NonNull String owner, int sensorId) { + @NonNull IBinder token, int userId, @NonNull String owner, int sensorId, + @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext) { super(context, lazyDaemon, token, null /* listener */, userId, owner, - 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN, - BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN); + 0 /* cookie */, sensorId, biometricLogger, biometricContext); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/StartUserClient.java b/services/core/java/com/android/server/biometrics/sensors/StartUserClient.java index 1bc3248cd0e7..21c9f64eea5b 100644 --- a/services/core/java/com/android/server/biometrics/sensors/StartUserClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/StartUserClient.java @@ -19,11 +19,12 @@ package com.android.server.biometrics.sensors; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; -import android.hardware.biometrics.BiometricsProtoEnums; import android.os.IBinder; import com.android.internal.annotations.VisibleForTesting; import com.android.server.biometrics.BiometricsProto; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import java.util.function.Supplier; @@ -47,10 +48,10 @@ public abstract class StartUserClient<T, U> extends HalClientMonitor<T> { public StartUserClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon, @Nullable IBinder token, int userId, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, @NonNull UserStartedCallback<U> callback) { super(context, lazyDaemon, token, null /* listener */, userId, context.getOpPackageName(), - 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN, - BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN); + 0 /* cookie */, sensorId, logger, biometricContext); mUserStartedCallback = callback; } diff --git a/services/core/java/com/android/server/biometrics/sensors/StopUserClient.java b/services/core/java/com/android/server/biometrics/sensors/StopUserClient.java index 3eafbb8ea9d7..e8654dc059a4 100644 --- a/services/core/java/com/android/server/biometrics/sensors/StopUserClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/StopUserClient.java @@ -19,11 +19,12 @@ package com.android.server.biometrics.sensors; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; -import android.hardware.biometrics.BiometricsProtoEnums; import android.os.IBinder; import com.android.internal.annotations.VisibleForTesting; import com.android.server.biometrics.BiometricsProto; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import java.util.function.Supplier; @@ -47,10 +48,10 @@ public abstract class StopUserClient<T> extends HalClientMonitor<T> { public StopUserClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon, @Nullable IBinder token, int userId, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, @NonNull UserStoppedCallback callback) { super(context, lazyDaemon, token, null /* listener */, userId, context.getOpPackageName(), - 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN, - BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN); + 0 /* cookie */, sensorId, logger, biometricContext); mUserStoppedCallback = callback; } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java index 006667ac659f..29eee6b5bb06 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java @@ -16,11 +16,11 @@ package com.android.server.biometrics.sensors.face.aidl; +import static com.android.server.biometrics.sensors.face.aidl.Sensor.HalSessionCallback; + import android.annotation.NonNull; import android.hardware.biometrics.face.ISession; -import static com.android.server.biometrics.sensors.face.aidl.Sensor.HalSessionCallback; - /** * A holder for an AIDL {@link ISession} with additional metadata about the current user * and the backend. diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java index c4e050215134..75a1e0cf21a8 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java @@ -25,7 +25,6 @@ import android.hardware.SensorPrivacyManager; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricFaceConstants; -import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.common.ICancellationSignal; import android.hardware.biometrics.common.OperationContext; import android.hardware.biometrics.common.OperationReason; @@ -37,7 +36,10 @@ import android.os.RemoteException; import android.util.Slog; import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.biometrics.Utils; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.AuthenticationClient; import com.android.server.biometrics.sensors.BiometricNotificationUtils; import com.android.server.biometrics.sensors.ClientMonitorCallback; @@ -76,19 +78,36 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession> @NonNull IBinder token, long requestId, @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId, boolean restricted, String owner, int cookie, boolean requireConfirmation, int sensorId, - boolean isStrongBiometric, int statsClient, @NonNull UsageStats usageStats, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, + boolean isStrongBiometric, @NonNull UsageStats usageStats, @NonNull LockoutCache lockoutCache, boolean allowBackgroundAuthentication, boolean isKeyguardBypassEnabled) { + this(context, lazyDaemon, token, requestId, listener, targetUserId, operationId, + restricted, owner, cookie, requireConfirmation, sensorId, logger, biometricContext, + isStrongBiometric, usageStats, lockoutCache, allowBackgroundAuthentication, + isKeyguardBypassEnabled, context.getSystemService(SensorPrivacyManager.class)); + } + + @VisibleForTesting + FaceAuthenticationClient(@NonNull Context context, + @NonNull Supplier<AidlSession> lazyDaemon, + @NonNull IBinder token, long requestId, + @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId, + boolean restricted, String owner, int cookie, boolean requireConfirmation, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, + boolean isStrongBiometric, @NonNull UsageStats usageStats, + @NonNull LockoutCache lockoutCache, boolean allowBackgroundAuthentication, + boolean isKeyguardBypassEnabled, SensorPrivacyManager sensorPrivacyManager) { super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted, - owner, cookie, requireConfirmation, sensorId, isStrongBiometric, - BiometricsProtoEnums.MODALITY_FACE, statsClient, null /* taskStackListener */, - lockoutCache, allowBackgroundAuthentication, true /* shouldVibrate */, + owner, cookie, requireConfirmation, sensorId, logger, biometricContext, + isStrongBiometric, null /* taskStackListener */, lockoutCache, + allowBackgroundAuthentication, true /* shouldVibrate */, isKeyguardBypassEnabled); setRequestId(requestId); mUsageStats = usageStats; mLockoutCache = lockoutCache; mNotificationManager = context.getSystemService(NotificationManager.class); - mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class); + mSensorPrivacyManager = sensorPrivacyManager; final Resources resources = getContext().getResources(); mBiometricPromptIgnoreList = resources.getIntArray( @@ -139,10 +158,10 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession> if (session.hasContextMethods()) { final OperationContext context = new OperationContext(); - // TODO: add reason, id, and isAoD + // TODO: add reason, id context.id = 0; context.reason = OperationReason.UNKNOWN; - context.isAoD = false; + context.isAoD = getBiometricContext().isAoD(); context.isCrypto = isCryptoOperation(); return session.getSession().authenticateWithContext(mOperationId, context); } else { diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java index 3f3db4342a7a..c79e60124c45 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java @@ -21,7 +21,6 @@ import android.annotation.Nullable; import android.content.Context; import android.hardware.SensorPrivacyManager; import android.hardware.biometrics.BiometricConstants; -import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.common.ICancellationSignal; import android.hardware.biometrics.common.OperationContext; import android.hardware.biometrics.common.OperationReason; @@ -29,7 +28,10 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.biometrics.BiometricsProto; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.AcquisitionClient; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; @@ -52,13 +54,26 @@ public class FaceDetectClient extends AcquisitionClient<AidlSession> implements FaceDetectClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon, @NonNull IBinder token, long requestId, @NonNull ClientMonitorCallbackConverter listener, int userId, - @NonNull String owner, int sensorId, boolean isStrongBiometric, int statsClient) { + @NonNull String owner, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, + boolean isStrongBiometric) { + this(context, lazyDaemon, token, requestId, listener, userId, owner, sensorId, + logger, biometricContext, isStrongBiometric, + context.getSystemService(SensorPrivacyManager.class)); + } + + @VisibleForTesting + FaceDetectClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon, + @NonNull IBinder token, long requestId, + @NonNull ClientMonitorCallbackConverter listener, int userId, + @NonNull String owner, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, + boolean isStrongBiometric, SensorPrivacyManager sensorPrivacyManager) { super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId, - true /* shouldVibrate */, BiometricsProtoEnums.MODALITY_FACE, - BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient); + true /* shouldVibrate */, logger, biometricContext); setRequestId(requestId); mIsStrongBiometric = isStrongBiometric; - mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class); + mSensorPrivacyManager = sensorPrivacyManager; } @Override @@ -102,10 +117,10 @@ public class FaceDetectClient extends AcquisitionClient<AidlSession> implements if (session.hasContextMethods()) { final OperationContext context = new OperationContext(); - // TODO: add reason, id, and isAoD + // TODO: add reason, id context.id = 0; context.reason = OperationReason.UNKNOWN; - context.isAoD = false; + context.isAoD = getBiometricContext().isAoD(); context.isCrypto = isCryptoOperation(); return session.getSession().detectInteractionWithContext(context); } else { diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java index 8dc53b6346a4..6f6dadc8a041 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java @@ -20,7 +20,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.hardware.biometrics.BiometricFaceConstants; -import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.common.ICancellationSignal; import android.hardware.biometrics.common.OperationContext; import android.hardware.biometrics.common.OperationReason; @@ -40,6 +39,8 @@ import android.view.Surface; import com.android.internal.R; import com.android.server.biometrics.HardwareAuthTokenUtils; import com.android.server.biometrics.Utils; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.BaseClientMonitor; import com.android.server.biometrics.sensors.BiometricNotificationUtils; import com.android.server.biometrics.sensors.BiometricUtils; @@ -89,11 +90,11 @@ public class FaceEnrollClient extends EnrollClient<AidlSession> { @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull byte[] hardwareAuthToken, @NonNull String opPackageName, long requestId, @NonNull BiometricUtils<Face> utils, @NonNull int[] disabledFeatures, int timeoutSec, - @Nullable Surface previewSurface, int sensorId, int maxTemplatesPerUser, - boolean debugConsent) { + @Nullable Surface previewSurface, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, + int maxTemplatesPerUser, boolean debugConsent) { super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, opPackageName, utils, - timeoutSec, BiometricsProtoEnums.MODALITY_FACE, sensorId, - false /* shouldVibrate */); + timeoutSec, sensorId, false /* shouldVibrate */, logger, biometricContext); setRequestId(requestId); mEnrollIgnoreList = getContext().getResources() .getIntArray(R.array.config_face_acquire_enroll_ignorelist); @@ -200,10 +201,10 @@ public class FaceEnrollClient extends EnrollClient<AidlSession> { if (session.hasContextMethods()) { final OperationContext context = new OperationContext(); - // TODO: add reason, id, and isAoD + // TODO: add reason, id context.id = 0; context.reason = OperationReason.UNKNOWN; - context.isAoD = false; + context.isAoD = getBiometricContext().isAoD(); context.isCrypto = isCryptoOperation(); return session.getSession().enrollWithContext( hat, EnrollmentType.DEFAULT, features, mHwPreviewHandle, context); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java index bdad268b9422..165c3a241043 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGenerateChallengeClient.java @@ -23,6 +23,8 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.GenerateChallengeClient; @@ -37,8 +39,10 @@ public class FaceGenerateChallengeClient extends GenerateChallengeClient<AidlSes FaceGenerateChallengeClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner, - int sensorId) { - super(context, lazyDaemon, token, listener, userId, owner, sensorId); + int sensorId, @NonNull BiometricLogger logger, + @NonNull BiometricContext biometricContext) { + super(context, lazyDaemon, token, listener, userId, owner, sensorId, logger, + biometricContext); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java index 2f3187bb4fa0..1f4f6127dafd 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetAuthenticatorIdClient.java @@ -18,11 +18,12 @@ package com.android.server.biometrics.sensors.face.aidl; import android.annotation.NonNull; import android.content.Context; -import android.hardware.biometrics.BiometricsProtoEnums; import android.os.RemoteException; import android.util.Slog; import com.android.server.biometrics.BiometricsProto; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.HalClientMonitor; @@ -38,10 +39,10 @@ class FaceGetAuthenticatorIdClient extends HalClientMonitor<AidlSession> { FaceGetAuthenticatorIdClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon, int userId, @NonNull String opPackageName, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, Map<Integer, Long> authenticatorIds) { super(context, lazyDaemon, null /* token */, null /* listener */, userId, opPackageName, - 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_FACE, - BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN); + 0 /* cookie */, sensorId, logger, biometricContext); mAuthenticatorIds = authenticatorIds; } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java index 79479bebc759..ef3b345402bf 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java @@ -20,7 +20,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.hardware.biometrics.BiometricFaceConstants; -import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.face.IFace; import android.os.IBinder; import android.os.RemoteException; @@ -28,6 +27,8 @@ import android.provider.Settings; import android.util.Slog; import com.android.server.biometrics.BiometricsProto; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.ErrorConsumer; @@ -48,10 +49,10 @@ public class FaceGetFeatureClient extends HalClientMonitor<AidlSession> implemen FaceGetFeatureClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon, @NonNull IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId, - @NonNull String owner, int sensorId) { + @NonNull String owner, int sensorId, @NonNull BiometricLogger logger, + @NonNull BiometricContext biometricContext) { super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId, - BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN, - BiometricsProtoEnums.CLIENT_UNKNOWN); + logger, biometricContext); mUserId = userId; } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java index a2b0339b282f..54f2033b363a 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java @@ -18,11 +18,12 @@ package com.android.server.biometrics.sensors.face.aidl; import android.annotation.NonNull; import android.content.Context; -import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.face.IFace; import android.hardware.face.Face; import android.os.IBinder; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.BiometricUtils; import com.android.server.biometrics.sensors.InternalCleanupClient; import com.android.server.biometrics.sensors.InternalEnumerateClient; @@ -39,29 +40,32 @@ class FaceInternalCleanupClient extends InternalCleanupClient<Face, AidlSession> FaceInternalCleanupClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon, int userId, @NonNull String owner, - int sensorId, @NonNull List<Face> enrolledList, @NonNull BiometricUtils<Face> utils, - @NonNull Map<Integer, Long> authenticatorIds) { - super(context, lazyDaemon, userId, owner, sensorId, BiometricsProtoEnums.MODALITY_FACE, + int sensorId, @NonNull BiometricLogger logger, + @NonNull BiometricContext biometricContext, @NonNull List<Face> enrolledList, + @NonNull BiometricUtils<Face> utils, @NonNull Map<Integer, Long> authenticatorIds) { + super(context, lazyDaemon, userId, owner, sensorId, logger, biometricContext, enrolledList, utils, authenticatorIds); } @Override protected InternalEnumerateClient<AidlSession> getEnumerateClient(Context context, Supplier<AidlSession> lazyDaemon, IBinder token, int userId, String owner, - List<Face> enrolledList, BiometricUtils<Face> utils, int sensorId) { + List<Face> enrolledList, BiometricUtils<Face> utils, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) { return new FaceInternalEnumerateClient(context, lazyDaemon, token, userId, owner, - enrolledList, utils, sensorId); + enrolledList, utils, sensorId, logger, biometricContext); } @Override protected RemovalClient<Face, AidlSession> getRemovalClient(Context context, Supplier<AidlSession> lazyDaemon, IBinder token, int biometricId, int userId, String owner, BiometricUtils<Face> utils, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, Map<Integer, Long> authenticatorIds) { // Internal remove does not need to send results to anyone. Cleanup (enumerate + remove) // is all done internally. return new FaceRemovalClient(context, lazyDaemon, token, null /* ClientMonitorCallbackConverter */, new int[] {biometricId}, userId, owner, - utils, sensorId, authenticatorIds); + utils, sensorId, logger, biometricContext, authenticatorIds); } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClient.java index 88c9d3bd1035..d85455e0e76b 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalEnumerateClient.java @@ -18,13 +18,14 @@ package com.android.server.biometrics.sensors.face.aidl; import android.annotation.NonNull; import android.content.Context; -import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.face.IFace; import android.hardware.face.Face; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.BiometricUtils; import com.android.server.biometrics.sensors.InternalEnumerateClient; @@ -40,9 +41,10 @@ class FaceInternalEnumerateClient extends InternalEnumerateClient<AidlSession> { FaceInternalEnumerateClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon, @NonNull IBinder token, int userId, @NonNull String owner, @NonNull List<Face> enrolledList, - @NonNull BiometricUtils<Face> utils, int sensorId) { + @NonNull BiometricUtils<Face> utils, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) { super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId, - BiometricsProtoEnums.MODALITY_FACE); + logger, biometricContext); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClient.java index 04ea2cfc6eff..39d8de07f652 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInvalidationClient.java @@ -23,6 +23,8 @@ import android.hardware.face.Face; import android.os.RemoteException; import android.util.Slog; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.InvalidationClient; import java.util.Map; @@ -33,8 +35,10 @@ public class FaceInvalidationClient extends InvalidationClient<Face, AidlSession public FaceInvalidationClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon, int userId, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, @NonNull Map<Integer, Long> authenticatorIds, @NonNull IInvalidationCallback callback) { - super(context, lazyDaemon, userId, sensorId, authenticatorIds, callback); + super(context, lazyDaemon, userId, sensorId, logger, biometricContext, + authenticatorIds, callback); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java index 9d7a5529f473..64b0892e92bb 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java @@ -23,6 +23,7 @@ import android.app.ActivityTaskManager; import android.app.TaskStackListener; import android.content.Context; import android.content.pm.UserInfo; +import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.ComponentInfoInternal; import android.hardware.biometrics.IInvalidationCallback; import android.hardware.biometrics.ITestSession; @@ -47,6 +48,8 @@ import android.view.Surface; import com.android.internal.annotations.VisibleForTesting; import com.android.server.biometrics.Utils; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.AuthenticationClient; import com.android.server.biometrics.sensors.BaseClientMonitor; import com.android.server.biometrics.sensors.ClientMonitorCallback; @@ -237,6 +240,9 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { final FaceGetAuthenticatorIdClient client = new FaceGetAuthenticatorIdClient( mContext, mSensors.get(sensorId).getLazySession(), userId, mContext.getOpPackageName(), sensorId, + createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, + BiometricsProtoEnums.CLIENT_UNKNOWN), + BiometricContext.getInstance(), mSensors.get(sensorId).getAuthenticatorIds()); scheduleForSensor(sensorId, client); @@ -247,6 +253,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { mHandler.post(() -> { final InvalidationRequesterClient<Face> client = new InvalidationRequesterClient<>(mContext, userId, sensorId, + BiometricLogger.ofUnknown(mContext), BiometricContext.getInstance(), FaceUtils.getInstance(sensorId)); scheduleForSensor(sensorId, client); }); @@ -285,6 +292,9 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { mHandler.post(() -> { final FaceInvalidationClient client = new FaceInvalidationClient(mContext, mSensors.get(sensorId).getLazySession(), userId, sensorId, + createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, + BiometricsProtoEnums.CLIENT_UNKNOWN), + BiometricContext.getInstance(), mSensors.get(sensorId).getAuthenticatorIds(), callback); scheduleForSensor(sensorId, client); }); @@ -311,7 +321,10 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { mHandler.post(() -> { final FaceGenerateChallengeClient client = new FaceGenerateChallengeClient(mContext, mSensors.get(sensorId).getLazySession(), token, - new ClientMonitorCallbackConverter(receiver), userId, opPackageName, sensorId); + new ClientMonitorCallbackConverter(receiver), userId, opPackageName, sensorId, + createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, + BiometricsProtoEnums.CLIENT_UNKNOWN), + BiometricContext.getInstance()); scheduleForSensor(sensorId, client); }); } @@ -322,7 +335,9 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { mHandler.post(() -> { final FaceRevokeChallengeClient client = new FaceRevokeChallengeClient(mContext, mSensors.get(sensorId).getLazySession(), token, userId, opPackageName, sensorId, - challenge); + createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, + BiometricsProtoEnums.CLIENT_UNKNOWN), + BiometricContext.getInstance(), challenge); scheduleForSensor(sensorId, client); }); } @@ -340,8 +355,10 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { mSensors.get(sensorId).getLazySession(), token, new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken, opPackageName, id, FaceUtils.getInstance(sensorId), disabledFeatures, - ENROLL_TIMEOUT_SEC, previewSurface, sensorId, maxTemplatesPerUser, - debugConsent); + ENROLL_TIMEOUT_SEC, previewSurface, sensorId, + createLogger(BiometricsProtoEnums.ACTION_ENROLL, + BiometricsProtoEnums.CLIENT_UNKNOWN), + BiometricContext.getInstance(), maxTemplatesPerUser, debugConsent); scheduleForSensor(sensorId, client, new ClientMonitorCallback() { @Override public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, @@ -372,8 +389,9 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId); final FaceDetectClient client = new FaceDetectClient(mContext, mSensors.get(sensorId).getLazySession(), - token, id, callback, userId, opPackageName, - sensorId, isStrongBiometric, statsClient); + token, id, callback, userId, opPackageName, sensorId, + createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient), + BiometricContext.getInstance(), isStrongBiometric); scheduleForSensor(sensorId, client); }); @@ -396,7 +414,9 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { final FaceAuthenticationClient client = new FaceAuthenticationClient( mContext, mSensors.get(sensorId).getLazySession(), token, requestId, callback, userId, operationId, restricted, opPackageName, cookie, - false /* requireConfirmation */, sensorId, isStrongBiometric, statsClient, + false /* requireConfirmation */, sensorId, + createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient), + BiometricContext.getInstance(), isStrongBiometric, mUsageStats, mSensors.get(sensorId).getLockoutCache(), allowBackgroundAuthentication, isKeyguardBypassEnabled); scheduleForSensor(sensorId, client); @@ -450,6 +470,9 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { mSensors.get(sensorId).getLazySession(), token, new ClientMonitorCallbackConverter(receiver), faceIds, userId, opPackageName, FaceUtils.getInstance(sensorId), sensorId, + createLogger(BiometricsProtoEnums.ACTION_REMOVE, + BiometricsProtoEnums.CLIENT_UNKNOWN), + BiometricContext.getInstance(), mSensors.get(sensorId).getAuthenticatorIds()); scheduleForSensor(sensorId, client); }); @@ -460,7 +483,10 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { mHandler.post(() -> { final FaceResetLockoutClient client = new FaceResetLockoutClient( mContext, mSensors.get(sensorId).getLazySession(), userId, - mContext.getOpPackageName(), sensorId, hardwareAuthToken, + mContext.getOpPackageName(), sensorId, + createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, + BiometricsProtoEnums.CLIENT_UNKNOWN), + BiometricContext.getInstance(), hardwareAuthToken, mSensors.get(sensorId).getLockoutCache(), mLockoutResetDispatcher); scheduleForSensor(sensorId, client); @@ -481,7 +507,9 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { final FaceSetFeatureClient client = new FaceSetFeatureClient(mContext, mSensors.get(sensorId).getLazySession(), token, new ClientMonitorCallbackConverter(receiver), userId, - mContext.getOpPackageName(), sensorId, feature, enabled, hardwareAuthToken); + mContext.getOpPackageName(), sensorId, + BiometricLogger.ofUnknown(mContext), BiometricContext.getInstance(), + feature, enabled, hardwareAuthToken); scheduleForSensor(sensorId, client); }); } @@ -498,7 +526,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { } final FaceGetFeatureClient client = new FaceGetFeatureClient(mContext, mSensors.get(sensorId).getLazySession(), token, callback, userId, - mContext.getOpPackageName(), sensorId); + mContext.getOpPackageName(), sensorId, BiometricLogger.ofUnknown(mContext), + BiometricContext.getInstance()); scheduleForSensor(sensorId, client); }); } @@ -518,13 +547,21 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { final FaceInternalCleanupClient client = new FaceInternalCleanupClient(mContext, mSensors.get(sensorId).getLazySession(), userId, - mContext.getOpPackageName(), sensorId, enrolledList, + mContext.getOpPackageName(), sensorId, + createLogger(BiometricsProtoEnums.ACTION_ENUMERATE, + BiometricsProtoEnums.CLIENT_UNKNOWN), + BiometricContext.getInstance(), enrolledList, FaceUtils.getInstance(sensorId), mSensors.get(sensorId).getAuthenticatorIds()); scheduleForSensor(sensorId, client, callback); }); } + private BiometricLogger createLogger(int statsAction, int statsClient) { + return new BiometricLogger(mContext, BiometricsProtoEnums.MODALITY_FACE, + statsAction, statsClient); + } + @Override public void dumpProtoState(int sensorId, @NonNull ProtoOutputStream proto, boolean clearSchedulerBuffer) { diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java index 130a05a861d9..0512017394af 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRemovalClient.java @@ -18,13 +18,14 @@ package com.android.server.biometrics.sensors.face.aidl; import android.annotation.NonNull; import android.content.Context; -import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.face.IFace; import android.hardware.face.Face; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.BiometricUtils; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.RemovalClient; @@ -44,9 +45,10 @@ class FaceRemovalClient extends RemovalClient<Face, AidlSession> { @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int[] biometricIds, int userId, @NonNull String owner, @NonNull BiometricUtils<Face> utils, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, @NonNull Map<Integer, Long> authenticatorIds) { super(context, lazyDaemon, token, listener, userId, owner, utils, sensorId, - authenticatorIds, BiometricsProtoEnums.MODALITY_FACE); + logger, biometricContext, authenticatorIds); mBiometricIds = biometricIds; } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java index 67bf3f5b2e4f..de0a36a32b66 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java @@ -18,7 +18,6 @@ package com.android.server.biometrics.sensors.face.aidl; import android.annotation.NonNull; import android.content.Context; -import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.face.IFace; import android.hardware.keymaster.HardwareAuthToken; import android.os.RemoteException; @@ -26,6 +25,8 @@ import android.util.Slog; import com.android.server.biometrics.BiometricsProto; import com.android.server.biometrics.HardwareAuthTokenUtils; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.ErrorConsumer; import com.android.server.biometrics.sensors.HalClientMonitor; @@ -50,11 +51,11 @@ public class FaceResetLockoutClient extends HalClientMonitor<AidlSession> implem FaceResetLockoutClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon, int userId, String owner, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, @NonNull byte[] hardwareAuthToken, @NonNull LockoutCache lockoutTracker, @NonNull LockoutResetDispatcher lockoutResetDispatcher) { super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner, - 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN, - BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN); + 0 /* cookie */, sensorId, logger, biometricContext); mHardwareAuthToken = HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken); mLockoutCache = lockoutTracker; mLockoutResetDispatcher = lockoutResetDispatcher; diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java index acd2e0589ccc..8838345de4d6 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceRevokeChallengeClient.java @@ -23,6 +23,8 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.RevokeChallengeClient; import java.util.function.Supplier; @@ -38,8 +40,10 @@ public class FaceRevokeChallengeClient extends RevokeChallengeClient<AidlSession FaceRevokeChallengeClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon, @NonNull IBinder token, - int userId, @NonNull String owner, int sensorId, long challenge) { - super(context, lazyDaemon, token, userId, owner, sensorId); + int userId, @NonNull String owner, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, + long challenge) { + super(context, lazyDaemon, token, userId, owner, sensorId, logger, biometricContext); mChallenge = challenge; } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java index 9d535a26e12d..6c143872ff8c 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceSetFeatureClient.java @@ -18,7 +18,6 @@ package com.android.server.biometrics.sensors.face.aidl; import android.annotation.NonNull; import android.content.Context; -import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.face.IFace; import android.hardware.keymaster.HardwareAuthToken; import android.os.IBinder; @@ -27,6 +26,8 @@ import android.util.Slog; import com.android.server.biometrics.BiometricsProto; import com.android.server.biometrics.HardwareAuthTokenUtils; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.ErrorConsumer; @@ -47,11 +48,11 @@ public class FaceSetFeatureClient extends HalClientMonitor<AidlSession> implemen FaceSetFeatureClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId, - @NonNull String owner, int sensorId, int feature, boolean enabled, - byte[] hardwareAuthToken) { + @NonNull String owner, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, + int feature, boolean enabled, byte[] hardwareAuthToken) { super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId, - BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN, - BiometricsProtoEnums.CLIENT_UNKNOWN); + logger, biometricContext); mFeature = feature; mEnabled = enabled; mHardwareAuthToken = HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStartUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStartUserClient.java index f5a98ff5881b..61e7ab781e66 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStartUserClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStartUserClient.java @@ -27,6 +27,8 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.StartUserClient; @@ -40,9 +42,10 @@ public class FaceStartUserClient extends StartUserClient<IFace, ISession> { public FaceStartUserClient(@NonNull Context context, @NonNull Supplier<IFace> lazyDaemon, @Nullable IBinder token, int userId, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, @NonNull ISessionCallback sessionCallback, @NonNull UserStartedCallback<ISession> callback) { - super(context, lazyDaemon, token, userId, sensorId, callback); + super(context, lazyDaemon, token, userId, sensorId, logger, biometricContext, callback); mSessionCallback = sessionCallback; } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java index 48b4856fa4b6..0110ae991ae4 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceStopUserClient.java @@ -23,6 +23,8 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.StopUserClient; @@ -33,8 +35,9 @@ public class FaceStopUserClient extends StopUserClient<AidlSession> { public FaceStopUserClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon, @Nullable IBinder token, int userId, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, @NonNull UserStoppedCallback callback) { - super(context, lazyDaemon, token, userId, sensorId, callback); + super(context, lazyDaemon, token, userId, sensorId, logger, biometricContext, callback); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java index 33e6fa4ebf93..fa07d120eb71 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java @@ -42,12 +42,15 @@ import android.os.UserManager; import android.util.Slog; import android.util.proto.ProtoOutputStream; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FrameworkStatsLog; import com.android.server.biometrics.HardwareAuthTokenUtils; import com.android.server.biometrics.SensorServiceStateProto; import com.android.server.biometrics.SensorStateProto; import com.android.server.biometrics.UserStateProto; import com.android.server.biometrics.Utils; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.AuthenticationConsumer; import com.android.server.biometrics.sensors.BaseClientMonitor; import com.android.server.biometrics.sensors.BiometricScheduler; @@ -88,7 +91,8 @@ public class Sensor { @NonNull private final Supplier<AidlSession> mLazySession; @Nullable private AidlSession mCurrentSession; - static class HalSessionCallback extends ISessionCallback.Stub { + @VisibleForTesting + public static class HalSessionCallback extends ISessionCallback.Stub { /** * Interface to sends results to the HalSessionCallback's owner. */ @@ -487,7 +491,9 @@ public class Sensor { @Override public StopUserClient<?> getStopUserClient(int userId) { return new FaceStopUserClient(mContext, mLazySession, mToken, userId, - mSensorProperties.sensorId, () -> mCurrentSession = null); + mSensorProperties.sensorId, + BiometricLogger.ofUnknown(mContext), BiometricContext.getInstance(), + () -> mCurrentSession = null); } @NonNull @@ -523,6 +529,7 @@ public class Sensor { return new FaceStartUserClient(mContext, provider::getHalInstance, mToken, newUserId, mSensorProperties.sensorId, + BiometricLogger.ofUnknown(mContext), BiometricContext.getInstance(), resultController, userStartedCallback); } }); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java index 586abe2d6298..be1ed7d92aeb 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java @@ -55,6 +55,8 @@ import com.android.server.biometrics.SensorServiceStateProto; import com.android.server.biometrics.SensorStateProto; import com.android.server.biometrics.UserStateProto; import com.android.server.biometrics.Utils; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.AcquisitionClient; import com.android.server.biometrics.sensors.AuthenticationConsumer; import com.android.server.biometrics.sensors.BaseClientMonitor; @@ -533,7 +535,10 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { final FaceGenerateChallengeClient client = new FaceGenerateChallengeClient(mContext, mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, - opPackageName, mSensorId, sSystemClock.millis()); + opPackageName, mSensorId, + createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, + BiometricsProtoEnums.CLIENT_UNKNOWN), + BiometricContext.getInstance(), sSystemClock.millis()); mGeneratedChallengeCache = client; mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() { @Override @@ -562,7 +567,10 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { mGeneratedChallengeCache = null; final FaceRevokeChallengeClient client = new FaceRevokeChallengeClient(mContext, - mLazyDaemon, token, userId, opPackageName, mSensorId); + mLazyDaemon, token, userId, opPackageName, mSensorId, + createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, + BiometricsProtoEnums.CLIENT_UNKNOWN), + BiometricContext.getInstance()); mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() { @Override public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, @@ -590,7 +598,10 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { final FaceEnrollClient client = new FaceEnrollClient(mContext, mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken, opPackageName, id, FaceUtils.getLegacyInstance(mSensorId), disabledFeatures, - ENROLL_TIMEOUT_SEC, previewSurface, mSensorId); + ENROLL_TIMEOUT_SEC, previewSurface, mSensorId, + createLogger(BiometricsProtoEnums.ACTION_ENROLL, + BiometricsProtoEnums.CLIENT_UNKNOWN), + BiometricContext.getInstance()); mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() { @Override @@ -637,7 +648,8 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { final FaceAuthenticationClient client = new FaceAuthenticationClient(mContext, mLazyDaemon, token, requestId, receiver, userId, operationId, restricted, opPackageName, cookie, false /* requireConfirmation */, mSensorId, - isStrongBiometric, statsClient, mLockoutTracker, mUsageStats, + createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient), + BiometricContext.getInstance(), isStrongBiometric, mLockoutTracker, mUsageStats, allowBackgroundAuthentication, isKeyguardBypassEnabled); mScheduler.scheduleClientMonitor(client); }); @@ -670,7 +682,10 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { final FaceRemovalClient client = new FaceRemovalClient(mContext, mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), faceId, userId, opPackageName, - FaceUtils.getLegacyInstance(mSensorId), mSensorId, mAuthenticatorIds); + FaceUtils.getLegacyInstance(mSensorId), mSensorId, + createLogger(BiometricsProtoEnums.ACTION_REMOVE, + BiometricsProtoEnums.CLIENT_UNKNOWN), + BiometricContext.getInstance(), mAuthenticatorIds); mScheduler.scheduleClientMonitor(client); }); } @@ -685,7 +700,10 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { final FaceRemovalClient client = new FaceRemovalClient(mContext, mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), 0 /* faceId */, userId, opPackageName, - FaceUtils.getLegacyInstance(mSensorId), mSensorId, mAuthenticatorIds); + FaceUtils.getLegacyInstance(mSensorId), mSensorId, + createLogger(BiometricsProtoEnums.ACTION_REMOVE, + BiometricsProtoEnums.CLIENT_UNKNOWN), + BiometricContext.getInstance(), mAuthenticatorIds); mScheduler.scheduleClientMonitor(client); }); } @@ -702,7 +720,9 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { final FaceResetLockoutClient client = new FaceResetLockoutClient(mContext, mLazyDaemon, userId, mContext.getOpPackageName(), mSensorId, - hardwareAuthToken); + createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, + BiometricsProtoEnums.CLIENT_UNKNOWN), + BiometricContext.getInstance(), hardwareAuthToken); mScheduler.scheduleClientMonitor(client); }); } @@ -723,7 +743,8 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { final int faceId = faces.get(0).getBiometricId(); final FaceSetFeatureClient client = new FaceSetFeatureClient(mContext, mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, - opPackageName, mSensorId, feature, enabled, hardwareAuthToken, faceId); + opPackageName, mSensorId, BiometricLogger.ofUnknown(mContext), + BiometricContext.getInstance(), feature, enabled, hardwareAuthToken, faceId); mScheduler.scheduleClientMonitor(client); }); } @@ -742,7 +763,9 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { final int faceId = faces.get(0).getBiometricId(); final FaceGetFeatureClient client = new FaceGetFeatureClient(mContext, mLazyDaemon, - token, listener, userId, opPackageName, mSensorId, feature, faceId); + token, listener, userId, opPackageName, mSensorId, + BiometricLogger.ofUnknown(mContext), BiometricContext.getInstance(), + feature, faceId); mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() { @Override public void onClientFinished( @@ -767,7 +790,10 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { final List<Face> enrolledList = getEnrolledFaces(mSensorId, userId); final FaceInternalCleanupClient client = new FaceInternalCleanupClient(mContext, - mLazyDaemon, userId, mContext.getOpPackageName(), mSensorId, enrolledList, + mLazyDaemon, userId, mContext.getOpPackageName(), mSensorId, + createLogger(BiometricsProtoEnums.ACTION_ENUMERATE, + BiometricsProtoEnums.CLIENT_UNKNOWN), + BiometricContext.getInstance(), enrolledList, FaceUtils.getLegacyInstance(mSensorId), mAuthenticatorIds); mScheduler.scheduleClientMonitor(client, callback); }); @@ -890,7 +916,9 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { final boolean hasEnrolled = !getEnrolledFaces(mSensorId, targetUserId).isEmpty(); final FaceUpdateActiveUserClient client = new FaceUpdateActiveUserClient(mContext, mLazyDaemon, targetUserId, mContext.getOpPackageName(), mSensorId, - hasEnrolled, mAuthenticatorIds); + createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, + BiometricsProtoEnums.CLIENT_UNKNOWN), + BiometricContext.getInstance(), hasEnrolled, mAuthenticatorIds); mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() { @Override public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, @@ -904,6 +932,11 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { }); } + private BiometricLogger createLogger(int statsAction, int statsClient) { + return new BiometricLogger(mContext, BiometricsProtoEnums.MODALITY_FACE, + statsAction, statsClient); + } + /** * Sends a debug message to the HAL with the provided FileDescriptor and arguments. */ diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java index 9038435c1021..8d76e9f031f7 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java @@ -23,7 +23,6 @@ import android.hardware.SensorPrivacyManager; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricFaceConstants; -import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.face.V1_0.IBiometricsFace; import android.hardware.face.FaceManager; import android.os.IBinder; @@ -32,6 +31,8 @@ import android.util.Slog; import com.android.internal.R; import com.android.server.biometrics.Utils; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.AuthenticationClient; import com.android.server.biometrics.sensors.BiometricNotificationUtils; import com.android.server.biometrics.sensors.ClientMonitorCallback; @@ -66,12 +67,13 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> { @NonNull IBinder token, long requestId, @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId, boolean restricted, String owner, int cookie, boolean requireConfirmation, int sensorId, - boolean isStrongBiometric, int statsClient, @NonNull LockoutTracker lockoutTracker, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, + boolean isStrongBiometric, @NonNull LockoutTracker lockoutTracker, @NonNull UsageStats usageStats, boolean allowBackgroundAuthentication, boolean isKeyguardBypassEnabled) { super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted, - owner, cookie, requireConfirmation, sensorId, isStrongBiometric, - BiometricsProtoEnums.MODALITY_FACE, statsClient, null /* taskStackListener */, + owner, cookie, requireConfirmation, sensorId, logger, biometricContext, + isStrongBiometric, null /* taskStackListener */, lockoutTracker, allowBackgroundAuthentication, true /* shouldVibrate */, isKeyguardBypassEnabled); setRequestId(requestId); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java index 92f7253779ed..226e458ad07b 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceEnrollClient.java @@ -20,7 +20,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.hardware.biometrics.BiometricFaceConstants; -import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.face.V1_0.IBiometricsFace; import android.hardware.biometrics.face.V1_0.Status; import android.hardware.face.Face; @@ -32,6 +31,8 @@ import android.view.Surface; import com.android.internal.R; import com.android.server.biometrics.Utils; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.BiometricUtils; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; @@ -58,10 +59,10 @@ public class FaceEnrollClient extends EnrollClient<IBiometricsFace> { @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull byte[] hardwareAuthToken, @NonNull String owner, long requestId, @NonNull BiometricUtils<Face> utils, @NonNull int[] disabledFeatures, int timeoutSec, - @Nullable Surface previewSurface, int sensorId) { + @Nullable Surface previewSurface, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) { super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils, - timeoutSec, BiometricsProtoEnums.MODALITY_FACE, sensorId, - false /* shouldVibrate */); + timeoutSec, sensorId, false /* shouldVibrate */, logger, biometricContext); setRequestId(requestId); mDisabledFeatures = Arrays.copyOf(disabledFeatures, disabledFeatures.length); mEnrollIgnoreList = getContext().getResources() diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java index b66ad608b4ca..97838a70cbd1 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClient.java @@ -25,6 +25,8 @@ import android.os.RemoteException; import android.util.Slog; import com.android.internal.util.Preconditions; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.GenerateChallengeClient; @@ -51,8 +53,10 @@ public class FaceGenerateChallengeClient extends GenerateChallengeClient<IBiomet FaceGenerateChallengeClient(@NonNull Context context, @NonNull Supplier<IBiometricsFace> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner, - int sensorId, long now) { - super(context, lazyDaemon, token, listener, userId, owner, sensorId); + int sensorId, @NonNull BiometricLogger logger, + @NonNull BiometricContext biometricContext, long now) { + super(context, lazyDaemon, token, listener, userId, owner, sensorId, logger, + biometricContext); mCreatedAt = now; mWaiting = new ArrayList<>(); } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java index 1b387bf7879a..981253699322 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceGetFeatureClient.java @@ -19,7 +19,6 @@ package com.android.server.biometrics.sensors.face.hidl; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; -import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.face.V1_0.IBiometricsFace; import android.hardware.biometrics.face.V1_0.OptionalBool; import android.hardware.biometrics.face.V1_0.Status; @@ -28,6 +27,8 @@ import android.os.RemoteException; import android.util.Slog; import com.android.server.biometrics.BiometricsProto; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.HalClientMonitor; @@ -48,13 +49,13 @@ public class FaceGetFeatureClient extends HalClientMonitor<IBiometricsFace> { FaceGetFeatureClient(@NonNull Context context, @NonNull Supplier<IBiometricsFace> lazyDaemon, @NonNull IBinder token, @Nullable ClientMonitorCallbackConverter listener, int userId, - @NonNull String owner, int sensorId, int feature, int faceId) { + @NonNull String owner, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, + int feature, int faceId) { super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId, - BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN, - BiometricsProtoEnums.CLIENT_UNKNOWN); + logger, biometricContext); mFeature = feature; mFaceId = faceId; - } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalCleanupClient.java index 93a2913fbfa1..d21a7501e516 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalCleanupClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalCleanupClient.java @@ -18,11 +18,12 @@ package com.android.server.biometrics.sensors.face.hidl; import android.annotation.NonNull; import android.content.Context; -import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.face.V1_0.IBiometricsFace; import android.hardware.face.Face; import android.os.IBinder; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.BiometricUtils; import com.android.server.biometrics.sensors.InternalCleanupClient; import com.android.server.biometrics.sensors.InternalEnumerateClient; @@ -40,29 +41,32 @@ class FaceInternalCleanupClient extends InternalCleanupClient<Face, IBiometricsF FaceInternalCleanupClient(@NonNull Context context, @NonNull Supplier<IBiometricsFace> lazyDaemon, int userId, @NonNull String owner, - int sensorId, @NonNull List<Face> enrolledList, @NonNull BiometricUtils<Face> utils, - @NonNull Map<Integer, Long> authenticatorIds) { - super(context, lazyDaemon, userId, owner, sensorId, BiometricsProtoEnums.MODALITY_FACE, + int sensorId, @NonNull BiometricLogger logger, + @NonNull BiometricContext biometricContext, @NonNull List<Face> enrolledList, + @NonNull BiometricUtils<Face> utils, @NonNull Map<Integer, Long> authenticatorIds) { + super(context, lazyDaemon, userId, owner, sensorId, logger, biometricContext, enrolledList, utils, authenticatorIds); } @Override protected InternalEnumerateClient<IBiometricsFace> getEnumerateClient(Context context, Supplier<IBiometricsFace> lazyDaemon, IBinder token, int userId, String owner, - List<Face> enrolledList, BiometricUtils<Face> utils, int sensorId) { + List<Face> enrolledList, BiometricUtils<Face> utils, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) { return new FaceInternalEnumerateClient(context, lazyDaemon, token, userId, owner, - enrolledList, utils, sensorId); + enrolledList, utils, sensorId, logger, biometricContext); } @Override protected RemovalClient<Face, IBiometricsFace> getRemovalClient(Context context, Supplier<IBiometricsFace> lazyDaemon, IBinder token, int biometricId, int userId, String owner, BiometricUtils<Face> utils, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, Map<Integer, Long> authenticatorIds) { // Internal remove does not need to send results to anyone. Cleanup (enumerate + remove) // is all done internally. return new FaceRemovalClient(context, lazyDaemon, token, null /* ClientMonitorCallbackConverter */, biometricId, userId, owner, utils, - sensorId, authenticatorIds); + sensorId, logger, biometricContext, authenticatorIds); } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalEnumerateClient.java index f1788de38565..250dd7e0cdef 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalEnumerateClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalEnumerateClient.java @@ -18,13 +18,14 @@ package com.android.server.biometrics.sensors.face.hidl; import android.annotation.NonNull; import android.content.Context; -import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.face.V1_0.IBiometricsFace; import android.hardware.face.Face; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.BiometricUtils; import com.android.server.biometrics.sensors.InternalEnumerateClient; @@ -41,9 +42,10 @@ class FaceInternalEnumerateClient extends InternalEnumerateClient<IBiometricsFac FaceInternalEnumerateClient(@NonNull Context context, @NonNull Supplier<IBiometricsFace> lazyDaemon, @NonNull IBinder token, int userId, @NonNull String owner, @NonNull List<Face> enrolledList, - @NonNull BiometricUtils<Face> utils, int sensorId) { + @NonNull BiometricUtils<Face> utils, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) { super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId, - BiometricsProtoEnums.MODALITY_FACE); + logger, biometricContext); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRemovalClient.java index cbc23e49f4d8..0ee7a354d5a4 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRemovalClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRemovalClient.java @@ -18,13 +18,14 @@ package com.android.server.biometrics.sensors.face.hidl; import android.annotation.NonNull; import android.content.Context; -import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.face.V1_0.IBiometricsFace; import android.hardware.face.Face; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.BiometricUtils; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.RemovalClient; @@ -44,9 +45,11 @@ class FaceRemovalClient extends RemovalClient<Face, IBiometricsFace> { FaceRemovalClient(@NonNull Context context, @NonNull Supplier<IBiometricsFace> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int biometricId, int userId, @NonNull String owner, @NonNull BiometricUtils<Face> utils, - int sensorId, @NonNull Map<Integer, Long> authenticatorIds) { - super(context, lazyDaemon, token, listener, userId, owner, utils, sensorId, - authenticatorIds, BiometricsProtoEnums.MODALITY_FACE); + int sensorId, @NonNull BiometricLogger logger, + @NonNull BiometricContext biometricContext, + @NonNull Map<Integer, Long> authenticatorIds) { + super(context, lazyDaemon, token, listener, userId, owner, utils, sensorId, logger, + biometricContext, authenticatorIds); mBiometricId = biometricId; } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java index 88e2318b0570..6e74d3622c1a 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceResetLockoutClient.java @@ -18,12 +18,13 @@ package com.android.server.biometrics.sensors.face.hidl; import android.annotation.NonNull; import android.content.Context; -import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.face.V1_0.IBiometricsFace; import android.os.RemoteException; import android.util.Slog; import com.android.server.biometrics.BiometricsProto; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.HalClientMonitor; @@ -42,10 +43,10 @@ public class FaceResetLockoutClient extends HalClientMonitor<IBiometricsFace> { FaceResetLockoutClient(@NonNull Context context, @NonNull Supplier<IBiometricsFace> lazyDaemon, int userId, String owner, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, @NonNull byte[] hardwareAuthToken) { super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner, - 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN, - BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN); + 0 /* cookie */, sensorId, logger, biometricContext); mHardwareAuthToken = new ArrayList<>(); for (byte b : hardwareAuthToken) { diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRevokeChallengeClient.java index ab8d16145fab..b7b0dc046633 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRevokeChallengeClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceRevokeChallengeClient.java @@ -23,6 +23,8 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.RevokeChallengeClient; import java.util.function.Supplier; @@ -37,8 +39,9 @@ public class FaceRevokeChallengeClient extends RevokeChallengeClient<IBiometrics FaceRevokeChallengeClient(@NonNull Context context, @NonNull Supplier<IBiometricsFace> lazyDaemon, @NonNull IBinder token, - int userId, @NonNull String owner, int sensorId) { - super(context, lazyDaemon, token, userId, owner, sensorId); + int userId, @NonNull String owner, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) { + super(context, lazyDaemon, token, userId, owner, sensorId, logger, biometricContext); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java index b2b52e713ec9..3c82f9c73700 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceSetFeatureClient.java @@ -18,7 +18,6 @@ package com.android.server.biometrics.sensors.face.hidl; import android.annotation.NonNull; import android.content.Context; -import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.face.V1_0.IBiometricsFace; import android.hardware.biometrics.face.V1_0.Status; import android.os.IBinder; @@ -26,6 +25,8 @@ import android.os.RemoteException; import android.util.Slog; import com.android.server.biometrics.BiometricsProto; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.HalClientMonitor; @@ -48,11 +49,11 @@ public class FaceSetFeatureClient extends HalClientMonitor<IBiometricsFace> { FaceSetFeatureClient(@NonNull Context context, @NonNull Supplier<IBiometricsFace> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId, - @NonNull String owner, int sensorId, int feature, boolean enabled, - byte[] hardwareAuthToken, int faceId) { + @NonNull String owner, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, + int feature, boolean enabled, byte[] hardwareAuthToken, int faceId) { super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId, - BiometricsProtoEnums.MODALITY_UNKNOWN, BiometricsProtoEnums.ACTION_UNKNOWN, - BiometricsProtoEnums.CLIENT_UNKNOWN); + logger, biometricContext); mFeature = feature; mEnabled = enabled; mFaceId = faceId; diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java index 04b93274e42b..8385c3fa7103 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java @@ -18,13 +18,14 @@ package com.android.server.biometrics.sensors.face.hidl; import android.annotation.NonNull; import android.content.Context; -import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.face.V1_0.IBiometricsFace; import android.os.Environment; import android.os.RemoteException; import android.util.Slog; import com.android.server.biometrics.BiometricsProto; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.HalClientMonitor; @@ -41,11 +42,11 @@ public class FaceUpdateActiveUserClient extends HalClientMonitor<IBiometricsFace FaceUpdateActiveUserClient(@NonNull Context context, @NonNull Supplier<IBiometricsFace> lazyDaemon, int userId, @NonNull String owner, - int sensorId, boolean hasEnrolledBiometrics, + int sensorId, @NonNull BiometricLogger logger, + @NonNull BiometricContext biometricContext, boolean hasEnrolledBiometrics, @NonNull Map<Integer, Long> authenticatorIds) { super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner, - 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN, - BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN); + 0 /* cookie */, sensorId, logger, biometricContext); mHasEnrolledBiometrics = hasEnrolledBiometrics; mAuthenticatorIds = authenticatorIds; } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlSession.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlSession.java index 727101a69b06..55861bb4cc6f 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlSession.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlSession.java @@ -16,11 +16,11 @@ package com.android.server.biometrics.sensors.fingerprint.aidl; +import static com.android.server.biometrics.sensors.fingerprint.aidl.Sensor.HalSessionCallback; + import android.annotation.NonNull; import android.hardware.biometrics.fingerprint.ISession; -import static com.android.server.biometrics.sensors.fingerprint.aidl.Sensor.HalSessionCallback; - /** * A holder for an AIDL {@link ISession} with additional metadata about the current user * and the backend. diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java index 2c1c80ccabb3..184e1ea7d003 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java @@ -23,9 +23,7 @@ import android.content.Context; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricFingerprintConstants; import android.hardware.biometrics.BiometricFingerprintConstants.FingerprintAcquired; -import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.common.ICancellationSignal; -import android.hardware.biometrics.common.OperationContext; import android.hardware.biometrics.common.OperationReason; import android.hardware.biometrics.fingerprint.PointerContext; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; @@ -35,6 +33,8 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.log.CallbackWithProbe; import com.android.server.biometrics.log.Probe; import com.android.server.biometrics.sensors.AuthenticationClient; @@ -72,15 +72,18 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession> @NonNull IBinder token, long requestId, @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId, boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation, - int sensorId, boolean isStrongBiometric, int statsClient, + int sensorId, + @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext, + boolean isStrongBiometric, @Nullable TaskStackListener taskStackListener, @NonNull LockoutCache lockoutCache, @Nullable IUdfpsOverlayController udfpsOverlayController, @Nullable ISidefpsController sidefpsController, boolean allowBackgroundAuthentication, @NonNull FingerprintSensorPropertiesInternal sensorProps) { super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted, owner, - cookie, requireConfirmation, sensorId, isStrongBiometric, - BiometricsProtoEnums.MODALITY_FINGERPRINT, statsClient, taskStackListener, + cookie, requireConfirmation, sensorId, + biometricLogger, biometricContext, + isStrongBiometric, taskStackListener, lockoutCache, allowBackgroundAuthentication, true /* shouldVibrate */, false /* isKeyguardBypassEnabled */); setRequestId(requestId); @@ -175,13 +178,12 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession> final AidlSession session = getFreshDaemon(); if (session.hasContextMethods()) { - final OperationContext context = new OperationContext(); - // TODO: add reason, id, and isAoD - context.id = 0; - context.reason = OperationReason.UNKNOWN; - context.isAoD = false; - context.isCrypto = isCryptoOperation(); - return session.getSession().authenticateWithContext(mOperationId, context); + // TODO: add reason, id + mOperationContext.id = 0; + mOperationContext.reason = OperationReason.UNKNOWN; + mOperationContext.isAoD = getBiometricContext().isAoD(); + mOperationContext.isCrypto = isCryptoOperation(); + return session.getSession().authenticateWithContext(mOperationId, mOperationContext); } else { return session.getSession().authenticate(mOperationId); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java index 6645332c1fac..9d348e118b46 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java @@ -20,7 +20,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.hardware.biometrics.BiometricOverlayConstants; -import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.common.ICancellationSignal; import android.hardware.biometrics.common.OperationContext; import android.hardware.biometrics.common.OperationReason; @@ -30,6 +29,8 @@ import android.os.RemoteException; import android.util.Slog; import com.android.server.biometrics.BiometricsProto; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.AcquisitionClient; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; @@ -54,11 +55,10 @@ class FingerprintDetectClient extends AcquisitionClient<AidlSession> implements @NonNull IBinder token, long requestId, @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner, int sensorId, - @Nullable IUdfpsOverlayController udfpsOverlayController, boolean isStrongBiometric, - int statsClient) { + @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext, + @Nullable IUdfpsOverlayController udfpsOverlayController, boolean isStrongBiometric) { super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId, - true /* shouldVibrate */, BiometricsProtoEnums.MODALITY_FINGERPRINT, - BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient); + true /* shouldVibrate */, biometricLogger, biometricContext); setRequestId(requestId); mIsStrongBiometric = isStrongBiometric; mSensorOverlays = new SensorOverlays(udfpsOverlayController, null /* sideFpsController*/); @@ -100,10 +100,10 @@ class FingerprintDetectClient extends AcquisitionClient<AidlSession> implements if (session.hasContextMethods()) { final OperationContext context = new OperationContext(); - // TODO: add reason, id, and isAoD + // TODO: add reason, id context.id = 0; context.reason = OperationReason.UNKNOWN; - context.isAoD = false; + context.isAoD = getBiometricContext().isAoD(); context.isCrypto = isCryptoOperation(); return session.getSession().detectInteractionWithContext(context); } else { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java index d0c5bb8851e6..ed16a6dd3b5c 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java @@ -22,7 +22,6 @@ import android.content.Context; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricFingerprintConstants; import android.hardware.biometrics.BiometricFingerprintConstants.FingerprintAcquired; -import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.common.ICancellationSignal; import android.hardware.biometrics.common.OperationContext; import android.hardware.biometrics.common.OperationReason; @@ -38,6 +37,8 @@ import android.os.RemoteException; import android.util.Slog; import com.android.server.biometrics.HardwareAuthTokenUtils; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.BiometricNotificationUtils; import com.android.server.biometrics.sensors.BiometricUtils; import com.android.server.biometrics.sensors.ClientMonitorCallback; @@ -68,14 +69,15 @@ class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, @NonNull FingerprintSensorPropertiesInternal sensorProps, @Nullable IUdfpsOverlayController udfpsOverlayController, @Nullable ISidefpsController sidefpsController, int maxTemplatesPerUser, @FingerprintManager.EnrollReason int enrollReason) { // UDFPS haptics occur when an image is acquired (instead of when the result is known) super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils, - 0 /* timeoutSec */, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId, - !sensorProps.isAnyUdfpsType() /* shouldVibrate */); + 0 /* timeoutSec */, sensorId, + !sensorProps.isAnyUdfpsType() /* shouldVibrate */, logger, biometricContext); setRequestId(requestId); mSensorProps = sensorProps; mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController); @@ -177,10 +179,10 @@ class FingerprintEnrollClient extends EnrollClient<AidlSession> implements Udfps if (session.hasContextMethods()) { final OperationContext context = new OperationContext(); - // TODO: add reason, id, and isAoD + // TODO: add reason, id context.id = 0; context.reason = OperationReason.UNKNOWN; - context.isAoD = false; + context.isAoD = getBiometricContext().isAoD(); context.isCrypto = isCryptoOperation(); return session.getSession().enrollWithContext(hat, context); } else { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java index 04a7ca086ced..ddae8bedf7c1 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java @@ -23,6 +23,8 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.GenerateChallengeClient; @@ -38,8 +40,10 @@ class FingerprintGenerateChallengeClient extends GenerateChallengeClient<AidlSes @NonNull Supplier<AidlSession> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, - int userId, @NonNull String owner, int sensorId) { - super(context, lazyDaemon, token, listener, userId, owner, sensorId); + int userId, @NonNull String owner, int sensorId, + @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext) { + super(context, lazyDaemon, token, listener, userId, owner, sensorId, + biometricLogger, biometricContext); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java index 3a487fc98ca4..ea1a622c36ab 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java @@ -18,11 +18,12 @@ package com.android.server.biometrics.sensors.fingerprint.aidl; import android.annotation.NonNull; import android.content.Context; -import android.hardware.biometrics.BiometricsProtoEnums; import android.os.RemoteException; import android.util.Slog; import com.android.server.biometrics.BiometricsProto; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.HalClientMonitor; @@ -36,11 +37,12 @@ class FingerprintGetAuthenticatorIdClient extends HalClientMonitor<AidlSession> private final Map<Integer, Long> mAuthenticatorIds; FingerprintGetAuthenticatorIdClient(@NonNull Context context, - @NonNull Supplier<AidlSession> lazyDaemon, int userId, @NonNull String owner, - int sensorId, Map<Integer, Long> authenticatorIds) { + @NonNull Supplier<AidlSession> lazyDaemon, + int userId, @NonNull String owner, int sensorId, + @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext, + Map<Integer, Long> authenticatorIds) { super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner, - 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_FINGERPRINT, - BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN); + 0 /* cookie */, sensorId, biometricLogger, biometricContext); mAuthenticatorIds = authenticatorIds; } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java index 0ecad725cbeb..09bdd6de49f0 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java @@ -22,6 +22,8 @@ import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.fingerprint.Fingerprint; import android.os.IBinder; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.BiometricUtils; import com.android.server.biometrics.sensors.InternalCleanupClient; import com.android.server.biometrics.sensors.InternalEnumerateClient; @@ -39,28 +41,35 @@ import java.util.function.Supplier; class FingerprintInternalCleanupClient extends InternalCleanupClient<Fingerprint, AidlSession> { FingerprintInternalCleanupClient(@NonNull Context context, - @NonNull Supplier<AidlSession> lazyDaemon, int userId, @NonNull String owner, - int sensorId, @NonNull List<Fingerprint> enrolledList, + @NonNull Supplier<AidlSession> lazyDaemon, + int userId, @NonNull String owner, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, + @NonNull List<Fingerprint> enrolledList, @NonNull FingerprintUtils utils, @NonNull Map<Integer, Long> authenticatorIds) { - super(context, lazyDaemon, userId, owner, sensorId, - BiometricsProtoEnums.MODALITY_FINGERPRINT, enrolledList, utils, authenticatorIds); + super(context, lazyDaemon, userId, owner, sensorId, logger, biometricContext, + enrolledList, utils, authenticatorIds); } @Override protected InternalEnumerateClient<AidlSession> getEnumerateClient(Context context, Supplier<AidlSession> lazyDaemon, IBinder token, int userId, String owner, - List<Fingerprint> enrolledList, BiometricUtils<Fingerprint> utils, int sensorId) { + List<Fingerprint> enrolledList, BiometricUtils<Fingerprint> utils, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) { return new FingerprintInternalEnumerateClient(context, lazyDaemon, token, userId, owner, - enrolledList, utils, sensorId); + enrolledList, utils, sensorId, + logger.swapAction(context, BiometricsProtoEnums.ACTION_ENUMERATE), + biometricContext); } @Override protected RemovalClient<Fingerprint, AidlSession> getRemovalClient(Context context, Supplier<AidlSession> lazyDaemon, IBinder token, int biometricId, int userId, String owner, BiometricUtils<Fingerprint> utils, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, Map<Integer, Long> authenticatorIds) { return new FingerprintRemovalClient(context, lazyDaemon, token, null /* ClientMonitorCallbackConverter */, new int[] {biometricId}, userId, owner, - utils, sensorId, authenticatorIds); + utils, sensorId, logger.swapAction(context, BiometricsProtoEnums.ACTION_REMOVE), + biometricContext, authenticatorIds); } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClient.java index 06ba6d45b407..a5a832aaaf59 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClient.java @@ -18,12 +18,13 @@ package com.android.server.biometrics.sensors.fingerprint.aidl; import android.annotation.NonNull; import android.content.Context; -import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.fingerprint.Fingerprint; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.BiometricUtils; import com.android.server.biometrics.sensors.InternalEnumerateClient; @@ -40,9 +41,10 @@ class FingerprintInternalEnumerateClient extends InternalEnumerateClient<AidlSes protected FingerprintInternalEnumerateClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon, @NonNull IBinder token, int userId, @NonNull String owner, @NonNull List<Fingerprint> enrolledList, - @NonNull BiometricUtils<Fingerprint> utils, int sensorId) { + @NonNull BiometricUtils<Fingerprint> utils, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) { super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId, - BiometricsProtoEnums.MODALITY_FINGERPRINT); + logger, biometricContext); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClient.java index 1ee32e986ca6..bc02897ef5c4 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInvalidationClient.java @@ -23,6 +23,8 @@ import android.hardware.fingerprint.Fingerprint; import android.os.RemoteException; import android.util.Slog; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.InvalidationClient; import java.util.Map; @@ -33,8 +35,10 @@ public class FingerprintInvalidationClient extends InvalidationClient<Fingerprin public FingerprintInvalidationClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon, int userId, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, @NonNull Map<Integer, Long> authenticatorIds, @NonNull IInvalidationCallback callback) { - super(context, lazyDaemon, userId, sensorId, authenticatorIds, callback); + super(context, lazyDaemon, userId, sensorId, logger, biometricContext, + authenticatorIds, callback); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index efc93045f957..221ff94141e2 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -26,6 +26,7 @@ import android.app.TaskStackListener; import android.content.Context; import android.content.pm.UserInfo; import android.content.res.TypedArray; +import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.ComponentInfoInternal; import android.hardware.biometrics.IInvalidationCallback; import android.hardware.biometrics.ITestSession; @@ -53,6 +54,8 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; import com.android.server.biometrics.Utils; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.AuthenticationClient; import com.android.server.biometrics.sensors.BaseClientMonitor; import com.android.server.biometrics.sensors.ClientMonitorCallback; @@ -298,6 +301,9 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi new FingerprintGetAuthenticatorIdClient(mContext, mSensors.get(sensorId).getLazySession(), userId, mContext.getOpPackageName(), sensorId, + createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, + BiometricsProtoEnums.CLIENT_UNKNOWN), + BiometricContext.getInstance(), mSensors.get(sensorId).getAuthenticatorIds()); scheduleForSensor(sensorId, client); }); @@ -307,6 +313,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mHandler.post(() -> { final InvalidationRequesterClient<Fingerprint> client = new InvalidationRequesterClient<>(mContext, userId, sensorId, + BiometricLogger.ofUnknown(mContext), BiometricContext.getInstance(), FingerprintUtils.getInstance(sensorId)); scheduleForSensor(sensorId, client); }); @@ -317,7 +324,10 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mHandler.post(() -> { final FingerprintResetLockoutClient client = new FingerprintResetLockoutClient( mContext, mSensors.get(sensorId).getLazySession(), userId, - mContext.getOpPackageName(), sensorId, hardwareAuthToken, + mContext.getOpPackageName(), sensorId, + createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, + BiometricsProtoEnums.CLIENT_UNKNOWN), + BiometricContext.getInstance(), hardwareAuthToken, mSensors.get(sensorId).getLockoutCache(), mLockoutResetDispatcher); scheduleForSensor(sensorId, client); }); @@ -331,7 +341,9 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi new FingerprintGenerateChallengeClient(mContext, mSensors.get(sensorId).getLazySession(), token, new ClientMonitorCallbackConverter(receiver), userId, opPackageName, - sensorId); + sensorId, createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, + BiometricsProtoEnums.CLIENT_UNKNOWN), + BiometricContext.getInstance()); scheduleForSensor(sensorId, client); }); } @@ -343,7 +355,10 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi final FingerprintRevokeChallengeClient client = new FingerprintRevokeChallengeClient(mContext, mSensors.get(sensorId).getLazySession(), token, - userId, opPackageName, sensorId, challenge); + userId, opPackageName, sensorId, + createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, + BiometricsProtoEnums.CLIENT_UNKNOWN), + BiometricContext.getInstance(), challenge); scheduleForSensor(sensorId, client); }); } @@ -361,6 +376,9 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mSensors.get(sensorId).getLazySession(), token, id, new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken, opPackageName, FingerprintUtils.getInstance(sensorId), sensorId, + createLogger(BiometricsProtoEnums.ACTION_ENROLL, + BiometricsProtoEnums.CLIENT_UNKNOWN), + BiometricContext.getInstance(), mSensors.get(sensorId).getSensorProperties(), mUdfpsOverlayController, mSidefpsController, maxTemplatesPerUser, enrollReason); scheduleForSensor(sensorId, client, new ClientMonitorCallback() { @@ -399,8 +417,10 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId); final FingerprintDetectClient client = new FingerprintDetectClient(mContext, mSensors.get(sensorId).getLazySession(), token, id, callback, userId, - opPackageName, sensorId, mUdfpsOverlayController, isStrongBiometric, - statsClient); + opPackageName, sensorId, + createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient), + BiometricContext.getInstance(), + mUdfpsOverlayController, isStrongBiometric); scheduleForSensor(sensorId, client, mFingerprintStateCallback); }); @@ -417,7 +437,9 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient( mContext, mSensors.get(sensorId).getLazySession(), token, requestId, callback, userId, operationId, restricted, opPackageName, cookie, - false /* requireConfirmation */, sensorId, isStrongBiometric, statsClient, + false /* requireConfirmation */, sensorId, + createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient), + BiometricContext.getInstance(), isStrongBiometric, mTaskStackListener, mSensors.get(sensorId).getLockoutCache(), mUdfpsOverlayController, mSidefpsController, allowBackgroundAuthentication, mSensors.get(sensorId).getSensorProperties()); @@ -479,6 +501,9 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mSensors.get(sensorId).getLazySession(), token, new ClientMonitorCallbackConverter(receiver), fingerprintIds, userId, opPackageName, FingerprintUtils.getInstance(sensorId), sensorId, + createLogger(BiometricsProtoEnums.ACTION_REMOVE, + BiometricsProtoEnums.CLIENT_UNKNOWN), + BiometricContext.getInstance(), mSensors.get(sensorId).getAuthenticatorIds()); scheduleForSensor(sensorId, client, mFingerprintStateCallback); }); @@ -492,14 +517,22 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi final FingerprintInternalCleanupClient client = new FingerprintInternalCleanupClient(mContext, mSensors.get(sensorId).getLazySession(), userId, - mContext.getOpPackageName(), sensorId, enrolledList, - FingerprintUtils.getInstance(sensorId), + mContext.getOpPackageName(), sensorId, + createLogger(BiometricsProtoEnums.ACTION_ENUMERATE, + BiometricsProtoEnums.CLIENT_UNKNOWN), + BiometricContext.getInstance(), + enrolledList, FingerprintUtils.getInstance(sensorId), mSensors.get(sensorId).getAuthenticatorIds()); scheduleForSensor(sensorId, client, new ClientMonitorCompositeCallback(callback, mFingerprintStateCallback)); }); } + private BiometricLogger createLogger(int statsAction, int statsClient) { + return new BiometricLogger(mContext, BiometricsProtoEnums.MODALITY_FINGERPRINT, + statsAction, statsClient); + } + @Override public boolean isHardwareDetected(int sensorId) { return hasHalInstance(); @@ -524,6 +557,9 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi final FingerprintInvalidationClient client = new FingerprintInvalidationClient(mContext, mSensors.get(sensorId).getLazySession(), userId, sensorId, + createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, + BiometricsProtoEnums.CLIENT_UNKNOWN), + BiometricContext.getInstance(), mSensors.get(sensorId).getAuthenticatorIds(), callback); scheduleForSensor(sensorId, client); }); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java index fbc1dc08b726..d559bb1d72f1 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java @@ -19,12 +19,13 @@ package com.android.server.biometrics.sensors.fingerprint.aidl; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; -import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.fingerprint.Fingerprint; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.BiometricUtils; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.RemovalClient; @@ -45,9 +46,10 @@ class FingerprintRemovalClient extends RemovalClient<Fingerprint, AidlSession> { @NonNull Supplier<AidlSession> lazyDaemon, @NonNull IBinder token, @Nullable ClientMonitorCallbackConverter listener, int[] biometricIds, int userId, @NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, @NonNull Map<Integer, Long> authenticatorIds) { super(context, lazyDaemon, token, listener, userId, owner, utils, sensorId, - authenticatorIds, BiometricsProtoEnums.MODALITY_FINGERPRINT); + logger, biometricContext, authenticatorIds); mBiometricIds = biometricIds; } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java index 0e64dab5d325..f90cba79dac2 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java @@ -18,7 +18,6 @@ package com.android.server.biometrics.sensors.fingerprint.aidl; import android.annotation.NonNull; import android.content.Context; -import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.fingerprint.IFingerprint; import android.hardware.keymaster.HardwareAuthToken; import android.os.RemoteException; @@ -26,6 +25,8 @@ import android.util.Slog; import com.android.server.biometrics.BiometricsProto; import com.android.server.biometrics.HardwareAuthTokenUtils; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.ErrorConsumer; import com.android.server.biometrics.sensors.HalClientMonitor; @@ -50,11 +51,11 @@ class FingerprintResetLockoutClient extends HalClientMonitor<AidlSession> implem FingerprintResetLockoutClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon, int userId, String owner, int sensorId, + @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext, @NonNull byte[] hardwareAuthToken, @NonNull LockoutCache lockoutTracker, @NonNull LockoutResetDispatcher lockoutResetDispatcher) { super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner, - 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN, - BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN); + 0 /* cookie */, sensorId, biometricLogger, biometricContext); mHardwareAuthToken = HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken); mLockoutCache = lockoutTracker; mLockoutResetDispatcher = lockoutResetDispatcher; diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java index fd938677105c..afa62e2e3173 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java @@ -23,6 +23,8 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.RevokeChallengeClient; import java.util.function.Supplier; @@ -38,8 +40,10 @@ class FingerprintRevokeChallengeClient extends RevokeChallengeClient<AidlSession FingerprintRevokeChallengeClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon, @NonNull IBinder token, - int userId, @NonNull String owner, int sensorId, long challenge) { - super(context, lazyDaemon, token, userId, owner, sensorId); + int userId, @NonNull String owner, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, + long challenge) { + super(context, lazyDaemon, token, userId, owner, sensorId, logger, biometricContext); mChallenge = challenge; } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStartUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStartUserClient.java index 9dc06e1e0665..52305a31a644 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStartUserClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStartUserClient.java @@ -27,6 +27,8 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.StartUserClient; @@ -40,9 +42,10 @@ public class FingerprintStartUserClient extends StartUserClient<IFingerprint, IS public FingerprintStartUserClient(@NonNull Context context, @NonNull Supplier<IFingerprint> lazyDaemon, @Nullable IBinder token, int userId, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, @NonNull ISessionCallback sessionCallback, @NonNull UserStartedCallback<ISession> callback) { - super(context, lazyDaemon, token, userId, sensorId, callback); + super(context, lazyDaemon, token, userId, sensorId, logger, biometricContext, callback); mSessionCallback = sessionCallback; } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java index fac17f2cc16a..2cc1879c0851 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintStopUserClient.java @@ -23,6 +23,8 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.StopUserClient; @@ -33,8 +35,10 @@ public class FingerprintStopUserClient extends StopUserClient<AidlSession> { public FingerprintStopUserClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon, @Nullable IBinder token, int userId, - int sensorId, @NonNull UserStoppedCallback callback) { - super(context, lazyDaemon, token, userId, sensorId, callback); + int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, + @NonNull UserStoppedCallback callback) { + super(context, lazyDaemon, token, userId, sensorId, logger, biometricContext, callback); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java index 22762329926b..27226b30f548 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java @@ -39,12 +39,15 @@ import android.os.UserManager; import android.util.Slog; import android.util.proto.ProtoOutputStream; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FrameworkStatsLog; import com.android.server.biometrics.HardwareAuthTokenUtils; import com.android.server.biometrics.SensorServiceStateProto; import com.android.server.biometrics.SensorStateProto; import com.android.server.biometrics.UserStateProto; import com.android.server.biometrics.Utils; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.AcquisitionClient; import com.android.server.biometrics.sensors.AuthenticationConsumer; import com.android.server.biometrics.sensors.BaseClientMonitor; @@ -72,7 +75,7 @@ import java.util.function.Supplier; * {@link android.hardware.biometrics.fingerprint.IFingerprint} HAL. */ @SuppressWarnings("deprecation") -class Sensor { +public class Sensor { private boolean mTestHalEnabled; @@ -89,7 +92,8 @@ class Sensor { @Nullable private AidlSession mCurrentSession; @NonNull private final Supplier<AidlSession> mLazySession; - static class HalSessionCallback extends ISessionCallback.Stub { + @VisibleForTesting + public static class HalSessionCallback extends ISessionCallback.Stub { /** * Interface to sends results to the HalSessionCallback's owner. @@ -442,7 +446,9 @@ class Sensor { @Override public StopUserClient<?> getStopUserClient(int userId) { return new FingerprintStopUserClient(mContext, mLazySession, mToken, - userId, mSensorProperties.sensorId, () -> mCurrentSession = null); + userId, mSensorProperties.sensorId, + BiometricLogger.ofUnknown(mContext), BiometricContext.getInstance(), + () -> mCurrentSession = null); } @NonNull @@ -478,6 +484,7 @@ class Sensor { return new FingerprintStartUserClient(mContext, provider::getHalInstance, mToken, newUserId, mSensorProperties.sensorId, + BiometricLogger.ofUnknown(mContext), BiometricContext.getInstance(), resultController, userStartedCallback); } }); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java index 29d460fc1204..6c35c8c91448 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java @@ -57,6 +57,8 @@ import com.android.server.biometrics.Utils; import com.android.server.biometrics.fingerprint.FingerprintServiceDumpProto; import com.android.server.biometrics.fingerprint.FingerprintUserStatsProto; import com.android.server.biometrics.fingerprint.PerformanceStatsProto; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.AcquisitionClient; import com.android.server.biometrics.sensors.AuthenticationClient; import com.android.server.biometrics.sensors.AuthenticationConsumer; @@ -493,6 +495,9 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider final FingerprintUpdateActiveUserClient client = new FingerprintUpdateActiveUserClient(mContext, mLazyDaemon, targetUserId, mContext.getOpPackageName(), mSensorProperties.sensorId, + createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, + BiometricsProtoEnums.CLIENT_UNKNOWN), + BiometricContext.getInstance(), this::getCurrentUser, hasEnrolled, mAuthenticatorIds, force); mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() { @Override @@ -536,7 +541,10 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider // thread. mHandler.post(() -> { final FingerprintResetLockoutClient client = new FingerprintResetLockoutClient(mContext, - userId, mContext.getOpPackageName(), sensorId, mLockoutTracker); + userId, mContext.getOpPackageName(), sensorId, + createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, + BiometricsProtoEnums.CLIENT_UNKNOWN), + BiometricContext.getInstance(), mLockoutTracker); mScheduler.scheduleClientMonitor(client); }); } @@ -548,7 +556,10 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider final FingerprintGenerateChallengeClient client = new FingerprintGenerateChallengeClient(mContext, mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, opPackageName, - mSensorProperties.sensorId); + mSensorProperties.sensorId, + createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, + BiometricsProtoEnums.CLIENT_UNKNOWN), + BiometricContext.getInstance()); mScheduler.scheduleClientMonitor(client); }); } @@ -559,7 +570,10 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider mHandler.post(() -> { final FingerprintRevokeChallengeClient client = new FingerprintRevokeChallengeClient( mContext, mLazyDaemon, token, userId, opPackageName, - mSensorProperties.sensorId); + mSensorProperties.sensorId, + createLogger(BiometricsProtoEnums.ACTION_UNKNOWN, + BiometricsProtoEnums.CLIENT_UNKNOWN), + BiometricContext.getInstance()); mScheduler.scheduleClientMonitor(client); }); } @@ -577,7 +591,11 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider mLazyDaemon, token, id, new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId), ENROLL_TIMEOUT_SEC, - mSensorProperties.sensorId, mUdfpsOverlayController, mSidefpsController, + mSensorProperties.sensorId, + createLogger(BiometricsProtoEnums.ACTION_ENROLL, + BiometricsProtoEnums.CLIENT_UNKNOWN), + BiometricContext.getInstance(), + mUdfpsOverlayController, mSidefpsController, enrollReason); mScheduler.scheduleClientMonitor(client, new ClientMonitorCallback() { @Override @@ -616,8 +634,9 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorProperties.sensorId); final FingerprintDetectClient client = new FingerprintDetectClient(mContext, mLazyDaemon, token, id, listener, userId, opPackageName, - mSensorProperties.sensorId, mUdfpsOverlayController, isStrongBiometric, - statsClient); + mSensorProperties.sensorId, + createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient), + BiometricContext.getInstance(), mUdfpsOverlayController, isStrongBiometric); mScheduler.scheduleClientMonitor(client, mFingerprintStateCallback); }); @@ -636,7 +655,9 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient( mContext, mLazyDaemon, token, requestId, listener, userId, operationId, restricted, opPackageName, cookie, false /* requireConfirmation */, - mSensorProperties.sensorId, isStrongBiometric, statsClient, + mSensorProperties.sensorId, + createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient), + BiometricContext.getInstance(), isStrongBiometric, mTaskStackListener, mLockoutTracker, mUdfpsOverlayController, mSidefpsController, allowBackgroundAuthentication, mSensorProperties); @@ -678,7 +699,10 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider final FingerprintRemovalClient client = new FingerprintRemovalClient(mContext, mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), fingerId, userId, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId), - mSensorProperties.sensorId, mAuthenticatorIds); + mSensorProperties.sensorId, + createLogger(BiometricsProtoEnums.ACTION_REMOVE, + BiometricsProtoEnums.CLIENT_UNKNOWN), + BiometricContext.getInstance(), mAuthenticatorIds); mScheduler.scheduleClientMonitor(client, mFingerprintStateCallback); }); } @@ -695,7 +719,10 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), 0 /* fingerprintId */, userId, opPackageName, FingerprintUtils.getLegacyInstance(mSensorId), - mSensorProperties.sensorId, mAuthenticatorIds); + mSensorProperties.sensorId, + createLogger(BiometricsProtoEnums.ACTION_REMOVE, + BiometricsProtoEnums.CLIENT_UNKNOWN), + BiometricContext.getInstance(), mAuthenticatorIds); mScheduler.scheduleClientMonitor(client, mFingerprintStateCallback); }); } @@ -709,7 +736,10 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider mSensorProperties.sensorId, userId); final FingerprintInternalCleanupClient client = new FingerprintInternalCleanupClient( mContext, mLazyDaemon, userId, mContext.getOpPackageName(), - mSensorProperties.sensorId, enrolledList, + mSensorProperties.sensorId, + createLogger(BiometricsProtoEnums.ACTION_ENUMERATE, + BiometricsProtoEnums.CLIENT_UNKNOWN), + BiometricContext.getInstance(), enrolledList, FingerprintUtils.getLegacyInstance(mSensorId), mAuthenticatorIds); mScheduler.scheduleClientMonitor(client, callback); }); @@ -722,6 +752,11 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider mFingerprintStateCallback)); } + private BiometricLogger createLogger(int statsAction, int statsClient) { + return new BiometricLogger(mContext, BiometricsProtoEnums.MODALITY_FINGERPRINT, + statsAction, statsClient); + } + @Override public boolean isHardwareDetected(int sensorId) { return getDaemon() != null; diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java index 589bfcfba992..97fbb5f6e4fe 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java @@ -23,7 +23,6 @@ import android.content.Context; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; import android.hardware.biometrics.BiometricFingerprintConstants; -import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.ISidefpsController; @@ -32,6 +31,8 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.log.CallbackWithProbe; import com.android.server.biometrics.log.Probe; import com.android.server.biometrics.sensors.AuthenticationClient; @@ -69,7 +70,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi @NonNull IBinder token, long requestId, @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId, boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation, - int sensorId, boolean isStrongBiometric, int statsClient, + int sensorId, @NonNull BiometricLogger logger, + @NonNull BiometricContext biometricContext, boolean isStrongBiometric, @NonNull TaskStackListener taskStackListener, @NonNull LockoutFrameworkImpl lockoutTracker, @Nullable IUdfpsOverlayController udfpsOverlayController, @@ -77,10 +79,9 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi boolean allowBackgroundAuthentication, @NonNull FingerprintSensorPropertiesInternal sensorProps) { super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted, - owner, cookie, requireConfirmation, sensorId, isStrongBiometric, - BiometricsProtoEnums.MODALITY_FINGERPRINT, statsClient, taskStackListener, - lockoutTracker, allowBackgroundAuthentication, true /* shouldVibrate */, - false /* isKeyguardBypassEnabled */); + owner, cookie, requireConfirmation, sensorId, logger, biometricContext, + isStrongBiometric, taskStackListener, lockoutTracker, allowBackgroundAuthentication, + true /* shouldVibrate */, false /* isKeyguardBypassEnabled */); setRequestId(requestId); mLockoutFrameworkImpl = lockoutTracker; mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java index 88487462e51a..f10d4e409b0e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintDetectClient.java @@ -22,7 +22,6 @@ import android.content.Context; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricFingerprintConstants; import android.hardware.biometrics.BiometricOverlayConstants; -import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; import android.hardware.fingerprint.IUdfpsOverlayController; import android.os.IBinder; @@ -30,6 +29,8 @@ import android.os.RemoteException; import android.util.Slog; import com.android.server.biometrics.BiometricsProto; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.AcquisitionClient; import com.android.server.biometrics.sensors.AuthenticationConsumer; import com.android.server.biometrics.sensors.ClientMonitorCallback; @@ -59,11 +60,11 @@ class FingerprintDetectClient extends AcquisitionClient<IBiometricsFingerprint> @NonNull Supplier<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token, long requestId, @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner, - int sensorId, @Nullable IUdfpsOverlayController udfpsOverlayController, - boolean isStrongBiometric, int statsClient) { + int sensorId, + @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext, + @Nullable IUdfpsOverlayController udfpsOverlayController, boolean isStrongBiometric) { super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId, - true /* shouldVibrate */, BiometricsProtoEnums.MODALITY_FINGERPRINT, - BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient); + true /* shouldVibrate */, biometricLogger, biometricContext); setRequestId(requestId); mSensorOverlays = new SensorOverlays(udfpsOverlayController, null /* sideFpsController */); mIsStrongBiometric = isStrongBiometric; diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java index c69deac371d9..1d478e53fd38 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java @@ -21,7 +21,6 @@ import android.annotation.Nullable; import android.content.Context; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricFingerprintConstants; -import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; import android.hardware.fingerprint.Fingerprint; import android.hardware.fingerprint.FingerprintManager; @@ -31,6 +30,8 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.BiometricNotificationUtils; import com.android.server.biometrics.sensors.BiometricUtils; import com.android.server.biometrics.sensors.ClientMonitorCallback; @@ -62,12 +63,13 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint long requestId, @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int timeoutSec, int sensorId, + @NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext, @Nullable IUdfpsOverlayController udfpsOverlayController, @Nullable ISidefpsController sidefpsController, @FingerprintManager.EnrollReason int enrollReason) { super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils, - timeoutSec, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId, - true /* shouldVibrate */); + timeoutSec, sensorId, true /* shouldVibrate */, biometricLogger, + biometricContext); setRequestId(requestId); mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java index 591f542396ef..3bb7135a3e06 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintGenerateChallengeClient.java @@ -23,6 +23,8 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.GenerateChallengeClient; @@ -41,8 +43,10 @@ public class FingerprintGenerateChallengeClient FingerprintGenerateChallengeClient(@NonNull Context context, @NonNull Supplier<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId, @NonNull String owner, - int sensorId) { - super(context, lazyDaemon, token, listener, userId, owner, sensorId); + int sensorId, @NonNull BiometricLogger logger, + @NonNull BiometricContext biometricContext) { + super(context, lazyDaemon, token, listener, userId, owner, sensorId, logger, + biometricContext); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java index 403602b8839d..5e7cf3578411 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java @@ -18,11 +18,12 @@ package com.android.server.biometrics.sensors.fingerprint.hidl; import android.annotation.NonNull; import android.content.Context; -import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; import android.hardware.fingerprint.Fingerprint; import android.os.IBinder; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.BiometricUtils; import com.android.server.biometrics.sensors.InternalCleanupClient; import com.android.server.biometrics.sensors.InternalEnumerateClient; @@ -42,31 +43,35 @@ class FingerprintInternalCleanupClient FingerprintInternalCleanupClient(@NonNull Context context, @NonNull Supplier<IBiometricsFingerprint> lazyDaemon, int userId, - @NonNull String owner, int sensorId, @NonNull List<Fingerprint> enrolledList, + @NonNull String owner, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, + @NonNull List<Fingerprint> enrolledList, @NonNull BiometricUtils<Fingerprint> utils, @NonNull Map<Integer, Long> authenticatorIds) { - super(context, lazyDaemon, userId, owner, sensorId, - BiometricsProtoEnums.MODALITY_FINGERPRINT, enrolledList, utils, authenticatorIds); + super(context, lazyDaemon, userId, owner, sensorId, logger, biometricContext, + enrolledList, utils, authenticatorIds); } @Override protected InternalEnumerateClient<IBiometricsFingerprint> getEnumerateClient( Context context, Supplier<IBiometricsFingerprint> lazyDaemon, IBinder token, int userId, String owner, List<Fingerprint> enrolledList, - BiometricUtils<Fingerprint> utils, int sensorId) { + BiometricUtils<Fingerprint> utils, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) { return new FingerprintInternalEnumerateClient(context, lazyDaemon, token, userId, owner, - enrolledList, utils, sensorId); + enrolledList, utils, sensorId, logger, biometricContext); } @Override protected RemovalClient<Fingerprint, IBiometricsFingerprint> getRemovalClient(Context context, Supplier<IBiometricsFingerprint> lazyDaemon, IBinder token, int biometricId, int userId, String owner, BiometricUtils<Fingerprint> utils, - int sensorId, Map<Integer, Long> authenticatorIds) { + int sensorId, @NonNull BiometricLogger logger, + @NonNull BiometricContext biometricContext, Map<Integer, Long> authenticatorIds) { // Internal remove does not need to send results to anyone. Cleanup (enumerate + remove) // is all done internally. return new FingerprintRemovalClient(context, lazyDaemon, token, null /* ClientMonitorCallbackConverter */, biometricId, userId, owner, utils, - sensorId, authenticatorIds); + sensorId, logger, biometricContext, authenticatorIds); } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalEnumerateClient.java index def8ed0735f5..0840f1b36903 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalEnumerateClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalEnumerateClient.java @@ -18,13 +18,14 @@ package com.android.server.biometrics.sensors.fingerprint.hidl; import android.annotation.NonNull; import android.content.Context; -import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; import android.hardware.fingerprint.Fingerprint; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.BiometricUtils; import com.android.server.biometrics.sensors.InternalEnumerateClient; @@ -42,9 +43,10 @@ class FingerprintInternalEnumerateClient extends InternalEnumerateClient<IBiomet FingerprintInternalEnumerateClient(@NonNull Context context, @NonNull Supplier<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token, int userId, @NonNull String owner, @NonNull List<Fingerprint> enrolledList, - @NonNull BiometricUtils<Fingerprint> utils, int sensorId) { + @NonNull BiometricUtils<Fingerprint> utils, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) { super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId, - BiometricsProtoEnums.MODALITY_FINGERPRINT); + logger, biometricContext); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java index 77c201c5ec97..9ec56c2a7dcd 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java @@ -18,13 +18,14 @@ package com.android.server.biometrics.sensors.fingerprint.hidl; import android.annotation.NonNull; import android.content.Context; -import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; import android.hardware.fingerprint.Fingerprint; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.BiometricUtils; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.RemovalClient; @@ -46,9 +47,10 @@ class FingerprintRemovalClient extends RemovalClient<Fingerprint, IBiometricsFin @NonNull Supplier<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int biometricId, int userId, @NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, @NonNull Map<Integer, Long> authenticatorIds) { super(context, lazyDaemon, token, listener, userId, owner, utils, sensorId, - authenticatorIds, BiometricsProtoEnums.MODALITY_FINGERPRINT); + logger, biometricContext, authenticatorIds); mBiometricId = biometricId; } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java index ed28e3ff481e..559ca0633c42 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintResetLockoutClient.java @@ -18,9 +18,10 @@ package com.android.server.biometrics.sensors.fingerprint.hidl; import android.annotation.NonNull; import android.content.Context; -import android.hardware.biometrics.BiometricsProtoEnums; import com.android.server.biometrics.BiometricsProto; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.BaseClientMonitor; import com.android.server.biometrics.sensors.ClientMonitorCallback; @@ -33,10 +34,11 @@ public class FingerprintResetLockoutClient extends BaseClientMonitor { @NonNull final LockoutFrameworkImpl mLockoutTracker; public FingerprintResetLockoutClient(@NonNull Context context, int userId, - @NonNull String owner, int sensorId, @NonNull LockoutFrameworkImpl lockoutTracker) { + @NonNull String owner, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, + @NonNull LockoutFrameworkImpl lockoutTracker) { super(context, null /* token */, null /* listener */, userId, owner, 0 /* cookie */, - sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN, - BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN); + sensorId, logger, biometricContext); mLockoutTracker = lockoutTracker; } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRevokeChallengeClient.java index 0180a466d7d6..6273417eb0db 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRevokeChallengeClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRevokeChallengeClient.java @@ -23,6 +23,8 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.RevokeChallengeClient; import java.util.function.Supplier; @@ -39,8 +41,9 @@ public class FingerprintRevokeChallengeClient FingerprintRevokeChallengeClient(@NonNull Context context, @NonNull Supplier<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token, - int userId, @NonNull String owner, int sensorId) { - super(context, lazyDaemon, token, userId, owner, sensorId); + int userId, @NonNull String owner, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) { + super(context, lazyDaemon, token, userId, owner, sensorId, logger, biometricContext); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java index cb9c33eb9956..a4e602553101 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java @@ -18,7 +18,6 @@ package com.android.server.biometrics.sensors.fingerprint.hidl; import android.annotation.NonNull; import android.content.Context; -import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; import android.os.Build; import android.os.Environment; @@ -27,6 +26,8 @@ import android.os.SELinux; import android.util.Slog; import com.android.server.biometrics.BiometricsProto; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.HalClientMonitor; @@ -50,12 +51,13 @@ public class FingerprintUpdateActiveUserClient extends HalClientMonitor<IBiometr FingerprintUpdateActiveUserClient(@NonNull Context context, @NonNull Supplier<IBiometricsFingerprint> lazyDaemon, int userId, - @NonNull String owner, int sensorId, Supplier<Integer> currentUserId, + @NonNull String owner, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, + Supplier<Integer> currentUserId, boolean hasEnrolledBiometrics, @NonNull Map<Integer, Long> authenticatorIds, boolean forceUpdateAuthenticatorId) { super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner, - 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_UNKNOWN, - BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN); + 0 /* cookie */, sensorId, logger, biometricContext); mCurrentUserId = currentUserId; mForceUpdateAuthenticatorId = forceUpdateAuthenticatorId; mHasEnrolledBiometrics = hasEnrolledBiometrics; diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 7ad497972462..4e88acd11fac 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -141,7 +141,6 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; @@ -1756,10 +1755,6 @@ public final class DisplayManagerService extends SystemService { void setUserPreferredDisplayModeInternal(int displayId, Display.Mode mode) { synchronized (mSyncRoot) { - if (Objects.equals(mUserPreferredMode, mode) && displayId == Display.INVALID_DISPLAY) { - return; - } - if (mode != null && !isResolutionAndRefreshRateValid(mode) && displayId == Display.INVALID_DISPLAY) { throw new IllegalArgumentException("width, height and refresh rate of mode should " @@ -1813,7 +1808,15 @@ public final class DisplayManagerService extends SystemService { Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.USER_PREFERRED_RESOLUTION_WIDTH, resolutionWidth); mDisplayDeviceRepo.forEachLocked((DisplayDevice device) -> { - device.setUserPreferredDisplayModeLocked(mode); + // If there is a display specific mode, don't override that + final Point deviceUserPreferredResolution = + mPersistentDataStore.getUserPreferredResolution(device); + final float deviceRefreshRate = + mPersistentDataStore.getUserPreferredRefreshRate(device); + if (!isValidResolution(deviceUserPreferredResolution) + && !isValidRefreshRate(deviceRefreshRate)) { + device.setUserPreferredDisplayModeLocked(mode); + } }); } @@ -3533,6 +3536,14 @@ public final class DisplayManagerService extends SystemService { && (brightness <= PowerManager.BRIGHTNESS_MAX); } + private static boolean isValidResolution(Point resolution) { + return (resolution != null) && (resolution.x > 0) && (resolution.y > 0); + } + + private static boolean isValidRefreshRate(float refreshRate) { + return !Float.isNaN(refreshRate) && (refreshRate > 0.0f); + } + private final class LocalService extends DisplayManagerInternal { @Override diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java index 534ed5d8491a..23c17f5af10d 100644 --- a/services/core/java/com/android/server/display/HighBrightnessModeController.java +++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java @@ -521,6 +521,10 @@ class HighBrightnessModeController { } else if (mIsBlockedByLowPowerMode) { reason = FrameworkStatsLog .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_BATTERY_SAVE_ON; + } else if (mBrightness <= mHbmData.transitionPoint) { + // This must be after external thermal check. + reason = FrameworkStatsLog + .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_LOW_REQUESTED_BRIGHTNESS; } } diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index de933cc47005..783a88ca29bf 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -2285,14 +2285,8 @@ public class InputManagerService extends IInputManager.Stub nativeNotifyPortAssociationsChanged(mPtr); } - /** - * Add a runtime association between the input device name and the display unique id. - * @param inputDeviceName The name of the input device. - * @param displayUniqueId The unique id of the associated display. - */ @Override // Binder call - public void addUniqueIdAssociation(@NonNull String inputDeviceName, - @NonNull String displayUniqueId) { + public void addUniqueIdAssociation(@NonNull String inputPort, @NonNull String displayUniqueId) { if (!checkCallingPermission( android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY, "addNameAssociation()")) { @@ -2300,20 +2294,16 @@ public class InputManagerService extends IInputManager.Stub "Requires ASSOCIATE_INPUT_DEVICE_TO_DISPLAY permission"); } - Objects.requireNonNull(inputDeviceName); + Objects.requireNonNull(inputPort); Objects.requireNonNull(displayUniqueId); synchronized (mAssociationsLock) { - mUniqueIdAssociations.put(inputDeviceName, displayUniqueId); + mUniqueIdAssociations.put(inputPort, displayUniqueId); } nativeChangeUniqueIdAssociation(mPtr); } - /** - * Remove the runtime association between the input device and the display. - * @param inputDeviceName The port of the input device to be cleared. - */ @Override // Binder call - public void removeUniqueIdAssociation(@NonNull String inputDeviceName) { + public void removeUniqueIdAssociation(@NonNull String inputPort) { if (!checkCallingPermission( android.Manifest.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY, "removeUniqueIdAssociation()")) { @@ -2321,9 +2311,9 @@ public class InputManagerService extends IInputManager.Stub "Requires ASSOCIATE_INPUT_DEVICE_TO_DISPLAY permission"); } - Objects.requireNonNull(inputDeviceName); + Objects.requireNonNull(inputPort); synchronized (mAssociationsLock) { - mUniqueIdAssociations.remove(inputDeviceName); + mUniqueIdAssociations.remove(inputPort); } nativeChangeUniqueIdAssociation(mPtr); } @@ -2594,6 +2584,13 @@ public class InputManagerService extends IInputManager.Stub pw.println(" display: " + v); }); } + if (!mUniqueIdAssociations.isEmpty()) { + pw.println("Unique Id Associations:"); + mUniqueIdAssociations.forEach((k, v) -> { + pw.print(" port: " + k); + pw.println(" uniqueId: " + v); + }); + } } } diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java index c87ca92d632e..6cb3b3b6740d 100644 --- a/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java +++ b/services/core/java/com/android/server/inputmethod/IInputMethodInvoker.java @@ -107,9 +107,11 @@ final class IInputMethodInvoker { @AnyThread void initializeInternal(IBinder token, IInputMethodPrivilegedOperations privOps, - int configChanges, boolean stylusHwSupported) { + int configChanges, boolean stylusHwSupported, + boolean shouldShowImeSwitcherWhenImeIsShown) { try { - mTarget.initializeInternal(token, privOps, configChanges, stylusHwSupported); + mTarget.initializeInternal(token, privOps, configChanges, stylusHwSupported, + shouldShowImeSwitcherWhenImeIsShown); } catch (RemoteException e) { logRemoteException(e); } @@ -145,9 +147,20 @@ final class IInputMethodInvoker { @AnyThread void startInput(IBinder startInputToken, IInputContext inputContext, EditorInfo attribute, - boolean restarting) { + boolean restarting, boolean shouldShowImeSwitcherWhenImeIsShown) { try { - mTarget.startInput(startInputToken, inputContext, attribute, restarting); + mTarget.startInput(startInputToken, inputContext, attribute, restarting, + shouldShowImeSwitcherWhenImeIsShown); + } catch (RemoteException e) { + logRemoteException(e); + } + } + + @AnyThread + void onShouldShowImeSwitcherWhenImeIsShownChanged(boolean shouldShowImeSwitcherWhenImeIsShown) { + try { + mTarget.onShouldShowImeSwitcherWhenImeIsShownChanged( + shouldShowImeSwitcherWhenImeIsShown); } catch (RemoteException e) { logRemoteException(e); } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 936b1a20e326..c207738a4f88 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -2325,10 +2325,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub true /* direct */); } + final boolean shouldShowImeSwitcherWhenImeIsShown = + shouldShowImeSwitcherWhenImeIsShownLocked(); final SessionState session = mCurClient.curSession; setEnabledSessionLocked(session); - session.method.startInput(startInputToken, mCurInputContext, mCurAttribute, restarting); - + session.method.startInput(startInputToken, mCurInputContext, mCurAttribute, restarting, + shouldShowImeSwitcherWhenImeIsShown); if (mShowRequested) { if (DEBUG) Slog.v(TAG, "Attach new input asks to show input"); showCurrentInputLocked(mCurFocusedWindow, getAppShowFlagsLocked(), null, @@ -2528,7 +2530,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub + mCurTokenDisplayId); } inputMethod.initializeInternal(token, new InputMethodPrivilegedOperationsImpl(this, token), - configChanges, supportStylusHw); + configChanges, supportStylusHw, shouldShowImeSwitcherWhenImeIsShownLocked()); } @AnyThread @@ -2731,6 +2733,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } @GuardedBy("ImfLock.class") + boolean shouldShowImeSwitcherWhenImeIsShownLocked() { + return shouldShowImeSwitcherLocked( + InputMethodService.IME_ACTIVE | InputMethodService.IME_VISIBLE); + } + + @GuardedBy("ImfLock.class") private boolean shouldShowImeSwitcherLocked(int visibility) { if (!mShowOngoingImeSwitcherForPhones) return false; if (mMenuController.getSwitchingDialogLocked() != null) return false; @@ -2990,6 +2998,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub // the same enabled IMEs list. mSwitchingController.resetCircularListLocked(mContext); + sendShouldShowImeSwitcherWhenImeIsShownLocked(); } @GuardedBy("ImfLock.class") @@ -4308,6 +4317,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub updateImeWindowStatus(msg.arg1 == 1); return true; } + // --------------------------------------------------------- case MSG_UNBIND_CLIENT: @@ -4368,6 +4378,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub // -------------------------------------------------------------- case MSG_HARD_KEYBOARD_SWITCH_CHANGED: mMenuController.handleHardKeyboardStatusChange(msg.arg1 == 1); + synchronized (ImfLock.class) { + sendShouldShowImeSwitcherWhenImeIsShownLocked(); + } return true; case MSG_SYSTEM_UNLOCK_USER: { final int userId = msg.arg1; @@ -4638,6 +4651,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub // the same enabled IMEs list. mSwitchingController.resetCircularListLocked(mContext); + sendShouldShowImeSwitcherWhenImeIsShownLocked(); + // Notify InputMethodListListeners of the new installed InputMethods. final List<InputMethodInfo> inputMethodList = new ArrayList<>(mMethodList); mHandler.obtainMessage(MSG_DISPATCH_ON_INPUT_METHOD_LIST_UPDATED, @@ -4645,6 +4660,17 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } @GuardedBy("ImfLock.class") + void sendShouldShowImeSwitcherWhenImeIsShownLocked() { + final IInputMethodInvoker curMethod = mBindingController.getCurMethod(); + if (curMethod == null) { + // No need to send the data if the IME is not yet bound. + return; + } + curMethod.onShouldShowImeSwitcherWhenImeIsShownChanged( + shouldShowImeSwitcherWhenImeIsShownLocked()); + } + + @GuardedBy("ImfLock.class") private void updateDefaultVoiceImeIfNeededLocked() { final String systemSpeechRecognizer = mContext.getString(com.android.internal.R.string.config_systemSpeechRecognizer); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java index 348bb2d868a2..98bde11ad517 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuController.java @@ -203,6 +203,7 @@ final class InputMethodMenuController { attrs.setTitle("Select input method"); w.setAttributes(attrs); mService.updateSystemUiLocked(); + mService.sendShouldShowImeSwitcherWhenImeIsShownLocked(); mSwitchingDialog.show(); } } @@ -238,6 +239,7 @@ final class InputMethodMenuController { mSwitchingDialogTitleView = null; mService.updateSystemUiLocked(); + mService.sendShouldShowImeSwitcherWhenImeIsShownLocked(); mDialogBuilder = null; mIms = null; } diff --git a/services/core/java/com/android/server/location/LocationShellCommand.java b/services/core/java/com/android/server/location/LocationShellCommand.java index b65338d9691d..9c85d18515af 100644 --- a/services/core/java/com/android/server/location/LocationShellCommand.java +++ b/services/core/java/com/android/server/location/LocationShellCommand.java @@ -69,6 +69,14 @@ class LocationShellCommand extends BasicShellCommandHandler { handleSetAdasGnssLocationEnabled(); return 0; } + case "set-automotive-gnss-suspended": { + handleSetAutomotiveGnssSuspended(); + return 0; + } + case "is-automotive-gnss-suspended": { + handleIsAutomotiveGnssSuspended(); + return 0; + } case "providers": { String command = getNextArgRequired(); return parseProvidersCommand(command); @@ -189,6 +197,24 @@ class LocationShellCommand extends BasicShellCommandHandler { mService.setAdasGnssLocationEnabledForUser(enabled, userId); } + private void handleSetAutomotiveGnssSuspended() { + if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) { + throw new IllegalStateException("command only recognized on automotive devices"); + } + + boolean suspended = Boolean.parseBoolean(getNextArgRequired()); + + mService.setAutomotiveGnssSuspended(suspended); + } + + private void handleIsAutomotiveGnssSuspended() { + if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) { + throw new IllegalStateException("command only recognized on automotive devices"); + } + + getOutPrintWriter().println(mService.isAutomotiveGnssSuspended()); + } + private void handleAddTestProvider() { String provider = getNextArgRequired(); @@ -359,6 +385,10 @@ class LocationShellCommand extends BasicShellCommandHandler { pw.println(" set-adas-gnss-location-enabled true|false [--user <USER_ID>]"); pw.println(" Sets the ADAS GNSS location enabled state. If no user is specified,"); pw.println(" the current user is assumed."); + pw.println(" is-automotive-gnss-suspended"); + pw.println(" Gets the automotive GNSS suspended state."); + pw.println(" set-automotive-gnss-suspended true|false"); + pw.println(" Sets the automotive GNSS suspended state."); } pw.println(" providers"); pw.println(" The providers command is followed by a subcommand, as listed below:"); diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java index 583cdd599780..647a89efcbbf 100644 --- a/services/core/java/com/android/server/notification/NotificationComparator.java +++ b/services/core/java/com/android/server/notification/NotificationComparator.java @@ -104,6 +104,12 @@ public class NotificationComparator return -1 * Boolean.compare(leftPeople, rightPeople); } + boolean leftSystemMax = isSystemMax(left); + boolean rightSystemMax = isSystemMax(right); + if (leftSystemMax != rightSystemMax) { + return -1 * Boolean.compare(leftSystemMax, rightSystemMax); + } + if (leftImportance != rightImportance) { // by importance, high to low return -1 * Integer.compare(leftImportance, rightImportance); @@ -173,6 +179,20 @@ public class NotificationComparator return mMessagingUtil.isImportantMessaging(record.getSbn(), record.getImportance()); } + protected boolean isSystemMax(NotificationRecord record) { + if (record.getImportance() < NotificationManager.IMPORTANCE_HIGH) { + return false; + } + String packageName = record.getSbn().getPackageName(); + if ("android".equals(packageName)) { + return true; + } + if ("com.android.systemui".equals(packageName)) { + return true; + } + return false; + } + private boolean isOngoing(NotificationRecord record) { final int ongoingFlags = Notification.FLAG_FOREGROUND_SERVICE; return (record.getNotification().flags & ongoingFlags) != 0; diff --git a/services/core/java/com/android/server/notification/PermissionHelper.java b/services/core/java/com/android/server/notification/PermissionHelper.java index 0cbdbc18ad39..5d18069ea205 100644 --- a/services/core/java/com/android/server/notification/PermissionHelper.java +++ b/services/core/java/com/android/server/notification/PermissionHelper.java @@ -19,7 +19,7 @@ package com.android.server.notification; import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET; import static android.content.pm.PackageManager.GET_PERMISSIONS; -import static android.permission.PermissionManager.PERMISSION_GRANTED; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; import android.Manifest; import android.annotation.NonNull; @@ -77,7 +77,8 @@ public final class PermissionHelper { assertFlag(); final long callingId = Binder.clearCallingIdentity(); try { - return mPmi.checkUidPermission(uid, NOTIFICATION_PERMISSION) == PERMISSION_GRANTED; + return mPmi.checkPostNotificationsPermissionGrantedOrLegacyAccess(uid) + == PERMISSION_GRANTED; } finally { Binder.restoreCallingIdentity(callingId); } diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java index 1fa901352c3d..9bcb7242b645 100644 --- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java +++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java @@ -285,6 +285,19 @@ public class UserRestrictionsUtils { ); /** + * User restrictions available to a device owner whose type is + * {@link android.app.admin.DevicePolicyManager#DEVICE_OWNER_TYPE_FINANCED}. + */ + private static final Set<String> FINANCED_DEVICE_OWNER_RESTRICTIONS = Sets.newArraySet( + UserManager.DISALLOW_ADD_USER, + UserManager.DISALLOW_DEBUGGING_FEATURES, + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, + UserManager.DISALLOW_SAFE_BOOT, + UserManager.DISALLOW_CONFIG_DATE_TIME, + UserManager.DISALLOW_OUTGOING_CALLS + ); + + /** * Returns whether the given restriction name is valid (and logs it if it isn't). */ public static boolean isValidRestriction(@NonNull String restriction) { @@ -458,6 +471,15 @@ public class UserRestrictionsUtils { } /** + * @return {@code true} only if the restriction is allowed for financed devices and can be set + * by a device owner. Otherwise, {@code false} would be returned. + */ + public static boolean canFinancedDeviceOwnerChange(String restriction) { + return FINANCED_DEVICE_OWNER_RESTRICTIONS.contains(restriction) + && canDeviceOwnerChange(restriction); + } + + /** * Whether given user restriction should be enforced globally. */ public static boolean isGlobal(@UserManagerInternal.OwnerType int restrictionOwnerType, diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 317730a9f606..79c5ea2efefe 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -17,6 +17,7 @@ package com.android.server.pm.permission; import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD; +import static android.Manifest.permission.POST_NOTIFICATIONS; import static android.Manifest.permission.RECORD_AUDIO; import static android.Manifest.permission.UPDATE_APP_OPS_STATS; import static android.app.AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE; @@ -608,6 +609,21 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @Override + public int checkPostNotificationsPermissionGrantedOrLegacyAccess(int uid) { + int granted = PermissionManagerService.this.checkUidPermission(uid, + POST_NOTIFICATIONS); + AndroidPackage pkg = mPackageManagerInt.getPackage(uid); + if (granted != PermissionManager.PERMISSION_GRANTED) { + int flags = PermissionManagerService.this.getPermissionFlags(pkg.getPackageName(), + POST_NOTIFICATIONS, UserHandle.getUserId(uid)); + if ((flags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) { + return PermissionManager.PERMISSION_GRANTED; + } + } + return granted; + } + + @Override public void startShellPermissionIdentityDelegation(int uid, @NonNull String packageName, @Nullable List<String> permissionNames) { Objects.requireNonNull(packageName, "packageName"); diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java index d2c4ec4cc5a5..812d7a04dc13 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java @@ -63,6 +63,17 @@ public interface PermissionManagerServiceInternal extends PermissionManagerInter int checkUidPermission(int uid, @NonNull String permissionName); /** + * Check whether a particular UID has been granted the POST_NOTIFICATIONS permission, or if + * access should be granted based on legacy access (currently symbolized by the REVIEW_REQUIRED + * permission flag + * + * @param uid the UID + * @return {@code PERMISSION_GRANTED} if the permission is granted, or legacy access is granted, + * {@code PERMISSION_DENIED} otherwise + */ + int checkPostNotificationsPermissionGrantedOrLegacyAccess(int uid); + + /** * Adds a listener for runtime permission state (permissions or flags) changes. * * @param listener The listener. diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index e71ff784dc23..344edbbef9c7 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -47,6 +47,7 @@ import android.content.pm.ResolveInfo; import android.graphics.drawable.Icon; import android.hardware.biometrics.BiometricAuthenticator.Modality; import android.hardware.biometrics.BiometricManager.BiometricMultiSensorMode; +import android.hardware.biometrics.IBiometricContextListener; import android.hardware.biometrics.IBiometricSysuiReceiver; import android.hardware.biometrics.PromptInfo; import android.hardware.display.DisplayManager; @@ -894,6 +895,17 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D } @Override + public void setBiometicContextListener(IBiometricContextListener listener) { + enforceStatusBarService(); + if (mBar != null) { + try { + mBar.setBiometicContextListener(listener); + } catch (RemoteException ex) { + } + } + } + + @Override public void setUdfpsHbmListener(IUdfpsHbmListener listener) { enforceStatusBarService(); if (mBar != null) { diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java index 9bed24d05f3d..52f7d101ce99 100644 --- a/services/core/java/com/android/server/trust/TrustManagerService.java +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -122,7 +122,8 @@ public class TrustManagerService extends SystemService { private static final int MSG_DISPATCH_UNLOCK_LOCKOUT = 13; private static final int MSG_REFRESH_DEVICE_LOCKED_FOR_USER = 14; private static final int MSG_SCHEDULE_TRUST_TIMEOUT = 15; - public static final int MSG_USER_REQUESTED_UNLOCK = 16; + private static final int MSG_USER_REQUESTED_UNLOCK = 16; + private static final int MSG_ENABLE_TRUST_AGENT = 17; private static final String REFRESH_DEVICE_LOCKED_EXCEPT_USER = "except"; @@ -631,6 +632,24 @@ public class TrustManagerService extends SystemService { } } + + /** + * Uses {@link LockPatternUtils} to enable the setting for trust agent in the specified + * component name. This should only be used for testing. + */ + private void enableTrustAgentForUserForTest(@NonNull ComponentName componentName, int userId) { + Log.i(TAG, + "Enabling trust agent " + componentName.flattenToString() + " for user " + userId); + List<ComponentName> agents = + new ArrayList<>(mLockPatternUtils.getEnabledTrustAgents(userId)); + if (!agents.contains(componentName)) { + agents.add(componentName); + } + // Even if the agent was already there, we still call setEnabledTrustAgents to trigger a + // refresh of installed agents. + mLockPatternUtils.setEnabledTrustAgents(agents, userId); + } + boolean isDeviceLockedInner(int userId) { synchronized (mDeviceLockedForUser) { return mDeviceLockedForUser.get(userId, true); @@ -929,6 +948,7 @@ public class TrustManagerService extends SystemService { continue; } allowedAgents.add(resolveInfo); + if (DEBUG) Slog.d(TAG, "Adding agent " + getComponentName(resolveInfo)); } return allowedAgents; } @@ -1158,6 +1178,13 @@ public class TrustManagerService extends SystemService { } @Override + public void enableTrustAgentForUserForTest(ComponentName componentName, int userId) + throws RemoteException { + enforceReportPermission(); + mHandler.obtainMessage(MSG_ENABLE_TRUST_AGENT, userId, 0, componentName).sendToTarget(); + } + + @Override public void reportKeyguardShowingChanged() throws RemoteException { enforceReportPermission(); // coalesce refresh messages. @@ -1433,6 +1460,9 @@ public class TrustManagerService extends SystemService { // This is also called when the security mode of a user changes. refreshDeviceLockedForUser(UserHandle.USER_ALL); break; + case MSG_ENABLE_TRUST_AGENT: + enableTrustAgentForUserForTest((ComponentName) msg.obj, msg.arg1); + break; case MSG_KEYGUARD_SHOWING_CHANGED: refreshDeviceLockedForUser(mCurrentUser); break; diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index ded58f48b207..ad4594873cf0 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -1039,6 +1039,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe " Rejecting as detached: %s", wc); continue; } + // The level of transition target should be at least window token. + if (wc.asWindowState() != null) continue; final ChangeInfo changeInfo = changes.get(wc); diff --git a/services/core/jni/com_android_server_companion_virtual_InputController.cpp b/services/core/jni/com_android_server_companion_virtual_InputController.cpp index 43018a900f4c..adc91fc3f2e8 100644 --- a/services/core/jni/com_android_server_companion_virtual_InputController.cpp +++ b/services/core/jni/com_android_server_companion_virtual_InputController.cpp @@ -186,7 +186,7 @@ static std::map<int, int> KEY_CODE_MAPPING = { }; /** Creates a new uinput device and assigns a file descriptor. */ -static int openUinput(const char* readableName, jint vendorId, jint productId, +static int openUinput(const char* readableName, jint vendorId, jint productId, const char* phys, DeviceType deviceType, jint screenHeight, jint screenWidth) { android::base::unique_fd fd(TEMP_FAILURE_RETRY(::open("/dev/uinput", O_WRONLY | O_NONBLOCK))); if (fd < 0) { @@ -194,6 +194,8 @@ static int openUinput(const char* readableName, jint vendorId, jint productId, return -errno; } + ioctl(fd, UI_SET_PHYS, phys); + ioctl(fd, UI_SET_EVBIT, EV_KEY); ioctl(fd, UI_SET_EVBIT, EV_SYN); switch (deviceType) { @@ -295,28 +297,30 @@ static int openUinput(const char* readableName, jint vendorId, jint productId, return fd.release(); } -static int openUinputJni(JNIEnv* env, jstring name, jint vendorId, jint productId, +static int openUinputJni(JNIEnv* env, jstring name, jint vendorId, jint productId, jstring phys, DeviceType deviceType, int screenHeight, int screenWidth) { ScopedUtfChars readableName(env, name); - return openUinput(readableName.c_str(), vendorId, productId, deviceType, screenHeight, - screenWidth); + ScopedUtfChars readablePhys(env, phys); + return openUinput(readableName.c_str(), vendorId, productId, readablePhys.c_str(), deviceType, + screenHeight, screenWidth); } static int nativeOpenUinputKeyboard(JNIEnv* env, jobject thiz, jstring name, jint vendorId, - jint productId) { - return openUinputJni(env, name, vendorId, productId, DeviceType::KEYBOARD, /* screenHeight */ 0, - /* screenWidth */ 0); + jint productId, jstring phys) { + return openUinputJni(env, name, vendorId, productId, phys, DeviceType::KEYBOARD, + /* screenHeight */ 0, /* screenWidth */ 0); } static int nativeOpenUinputMouse(JNIEnv* env, jobject thiz, jstring name, jint vendorId, - jint productId) { - return openUinputJni(env, name, vendorId, productId, DeviceType::MOUSE, /* screenHeight */ 0, - /* screenWidth */ 0); + jint productId, jstring phys) { + return openUinputJni(env, name, vendorId, productId, phys, DeviceType::MOUSE, + /* screenHeight */ 0, /* screenWidth */ 0); } static int nativeOpenUinputTouchscreen(JNIEnv* env, jobject thiz, jstring name, jint vendorId, - jint productId, jint height, jint width) { - return openUinputJni(env, name, vendorId, productId, DeviceType::TOUCHSCREEN, height, width); + jint productId, jstring phys, jint height, jint width) { + return openUinputJni(env, name, vendorId, productId, phys, DeviceType::TOUCHSCREEN, height, + width); } static bool nativeCloseUinput(JNIEnv* env, jobject thiz, jint fd) { @@ -435,9 +439,11 @@ static bool nativeWriteScrollEvent(JNIEnv* env, jobject thiz, jint fd, jfloat xA } static JNINativeMethod methods[] = { - {"nativeOpenUinputKeyboard", "(Ljava/lang/String;II)I", (void*)nativeOpenUinputKeyboard}, - {"nativeOpenUinputMouse", "(Ljava/lang/String;II)I", (void*)nativeOpenUinputMouse}, - {"nativeOpenUinputTouchscreen", "(Ljava/lang/String;IIII)I", + {"nativeOpenUinputKeyboard", "(Ljava/lang/String;IILjava/lang/String;)I", + (void*)nativeOpenUinputKeyboard}, + {"nativeOpenUinputMouse", "(Ljava/lang/String;IILjava/lang/String;)I", + (void*)nativeOpenUinputMouse}, + {"nativeOpenUinputTouchscreen", "(Ljava/lang/String;IILjava/lang/String;II)I", (void*)nativeOpenUinputTouchscreen}, {"nativeCloseUinput", "(I)Z", (void*)nativeCloseUinput}, {"nativeWriteKeyEvent", "(III)Z", (void*)nativeWriteKeyEvent}, diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp index 0da8f7ef0dea..161d7ce350fc 100644 --- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp +++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp @@ -1504,11 +1504,14 @@ static jboolean android_location_gnss_hal_GnssNative_set_position_mode( JNIEnv* /* env */, jclass, jint mode, jint recurrence, jint min_interval, jint preferred_accuracy, jint preferred_time, jboolean low_power_mode) { if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) { - auto status = gnssHalAidl->setPositionMode(static_cast<IGnssAidl::GnssPositionMode>(mode), - static_cast<IGnssAidl::GnssPositionRecurrence>( - recurrence), - min_interval, preferred_accuracy, preferred_time, - low_power_mode); + IGnssAidl::PositionModeOptions options; + options.mode = static_cast<IGnssAidl::GnssPositionMode>(mode); + options.recurrence = static_cast<IGnssAidl::GnssPositionRecurrence>(recurrence); + options.minIntervalMs = min_interval; + options.preferredAccuracyMeters = preferred_accuracy; + options.preferredTimeMs = preferred_time; + options.lowPowerMode = low_power_mode; + auto status = gnssHalAidl->setPositionMode(options); return checkAidlStatus(status, "IGnssAidl setPositionMode() failed."); } diff --git a/services/core/jni/gnss/AGnssRil.cpp b/services/core/jni/gnss/AGnssRil.cpp index d760b4d2195e..424ffd463713 100644 --- a/services/core/jni/gnss/AGnssRil.cpp +++ b/services/core/jni/gnss/AGnssRil.cpp @@ -41,7 +41,7 @@ jboolean AGnssRil::setCallback(const std::unique_ptr<AGnssRilCallback>& callback jboolean AGnssRil::setSetId(jint type, const jstring& setid_string) { JNIEnv* env = getJniEnv(); ScopedJniString jniSetId{env, setid_string}; - auto status = mIAGnssRil->setSetId((IAGnssRil::SetIDType)type, jniSetId.c_str()); + auto status = mIAGnssRil->setSetId((IAGnssRil::SetIdType)type, jniSetId.c_str()); return checkAidlStatus(status, "IAGnssRilAidl setSetId() failed."); } diff --git a/services/core/jni/gnss/GnssMeasurementCallback.cpp b/services/core/jni/gnss/GnssMeasurementCallback.cpp index fbdeec6b897e..6c0d5d984980 100644 --- a/services/core/jni/gnss/GnssMeasurementCallback.cpp +++ b/services/core/jni/gnss/GnssMeasurementCallback.cpp @@ -359,9 +359,7 @@ void GnssMeasurementCallbackAidl::translateAndSetGnssData(const GnssData& data) jobjectArray measurementArray = translateAllGnssMeasurements(env, data.measurements); jobjectArray gnssAgcArray = nullptr; - if (data.gnssAgcs.has_value()) { - gnssAgcArray = translateAllGnssAgcs(env, data.gnssAgcs.value()); - } + gnssAgcArray = translateAllGnssAgcs(env, data.gnssAgcs); setMeasurementData(env, mCallbacksObj, clock, measurementArray, gnssAgcArray); env->DeleteLocalRef(clock); @@ -508,8 +506,8 @@ jobjectArray GnssMeasurementCallbackAidl::translateAllGnssMeasurements( return gnssMeasurementArray; } -jobjectArray GnssMeasurementCallbackAidl::translateAllGnssAgcs( - JNIEnv* env, const std::vector<std::optional<GnssAgc>>& agcs) { +jobjectArray GnssMeasurementCallbackAidl::translateAllGnssAgcs(JNIEnv* env, + const std::vector<GnssAgc>& agcs) { if (agcs.size() == 0) { return nullptr; } @@ -518,10 +516,7 @@ jobjectArray GnssMeasurementCallbackAidl::translateAllGnssAgcs( env->NewObjectArray(agcs.size(), class_gnssAgc, nullptr /* initialElement */); for (uint16_t i = 0; i < agcs.size(); ++i) { - if (!agcs[i].has_value()) { - continue; - } - const GnssAgc& gnssAgc = agcs[i].value(); + const GnssAgc& gnssAgc = agcs[i]; jobject agcBuilderObject = env->NewObject(class_gnssAgcBuilder, method_gnssAgcBuilderCtor); env->CallObjectMethod(agcBuilderObject, method_gnssAgcBuilderSetLevelDb, diff --git a/services/core/jni/gnss/GnssMeasurementCallback.h b/services/core/jni/gnss/GnssMeasurementCallback.h index 9b346312db38..17af94939666 100644 --- a/services/core/jni/gnss/GnssMeasurementCallback.h +++ b/services/core/jni/gnss/GnssMeasurementCallback.h @@ -62,8 +62,8 @@ private: jobjectArray translateAllGnssMeasurements( JNIEnv* env, const std::vector<hardware::gnss::GnssMeasurement>& measurements); - jobjectArray translateAllGnssAgcs( - JNIEnv* env, const std::vector<std::optional<hardware::gnss::GnssData::GnssAgc>>& agcs); + jobjectArray translateAllGnssAgcs(JNIEnv* env, + const std::vector<hardware::gnss::GnssData::GnssAgc>& agcs); void translateAndSetGnssData(const hardware::gnss::GnssData& data); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 733cfcdfaea5..3bda7bf049c4 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -56,6 +56,8 @@ import static android.app.admin.DevicePolicyManager.DELEGATION_NETWORK_LOGGING; import static android.app.admin.DevicePolicyManager.DELEGATION_PACKAGE_ACCESS; import static android.app.admin.DevicePolicyManager.DELEGATION_PERMISSION_GRANT; import static android.app.admin.DevicePolicyManager.DELEGATION_SECURITY_LOGGING; +import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT; +import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED; import static android.app.admin.DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER; import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_ID; import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_TYPE_DRAWABLE; @@ -66,9 +68,12 @@ import static android.app.admin.DevicePolicyManager.ID_TYPE_INDIVIDUAL_ATTESTATI import static android.app.admin.DevicePolicyManager.ID_TYPE_MEID; import static android.app.admin.DevicePolicyManager.ID_TYPE_SERIAL; import static android.app.admin.DevicePolicyManager.LEAVE_ALL_SYSTEM_APPS_ENABLED; +import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS; import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME; +import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD; import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS; import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW; +import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_SYSTEM_INFO; import static android.app.admin.DevicePolicyManager.NEARBY_STREAMING_NOT_CONTROLLED_BY_POLICY; import static android.app.admin.DevicePolicyManager.NON_ORG_OWNED_PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER; import static android.app.admin.DevicePolicyManager.OPERATION_SAFETY_REASON_NONE; @@ -3918,8 +3923,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(who); Preconditions.checkCallAuthorization( - isProfileOwner(caller) || isDeviceOwner(caller) || isSystemUid(caller) - || isPasswordLimitingAdminTargetingP(caller)); + isProfileOwner(caller) || isDefaultDeviceOwner(caller) + || isSystemUid(caller) || isPasswordLimitingAdminTargetingP(caller)); if (parent) { Preconditions.checkCallAuthorization( @@ -4772,7 +4777,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(admin, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(admin); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller) + || isProfileOwner(caller)); Preconditions.checkCallingUser(isManagedProfile(caller.getUserId())); return !isSeparateProfileChallengeEnabled(caller.getUserId()); @@ -4860,12 +4866,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { enforceUserUnlocked(caller.getUserId()); if (parent) { Preconditions.checkCallAuthorization( - isDeviceOwner(caller) || isProfileOwner(caller) || isSystemUid(caller), + isDefaultDeviceOwner(caller) || isProfileOwner(caller) || isSystemUid(caller), "Only profile owner, device owner and system may call this method on parent."); } else { Preconditions.checkCallAuthorization( hasCallingOrSelfPermission(REQUEST_PASSWORD_COMPLEXITY) - || isDeviceOwner(caller) || isProfileOwner(caller), + || isDefaultDeviceOwner(caller) || isProfileOwner(caller), "Must have " + REQUEST_PASSWORD_COMPLEXITY + " permission, or be a profile owner or device owner."); } @@ -4888,7 +4894,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { "Provided complexity is not one of the allowed values."); final CallerIdentity caller = getCallerIdentity(); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || isProfileOwner(caller)); Preconditions.checkArgument(!calledOnParent || isProfileOwner(caller)); synchronized (getLockObject()) { @@ -4968,7 +4975,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(); Preconditions.checkCallAuthorization( - isDeviceOwner(caller) || isProfileOwner(caller)); + isDefaultDeviceOwner(caller) || isProfileOwner(caller)); Preconditions.checkArgument(!calledOnParent || isProfileOwner(caller)); @@ -5160,7 +5167,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } // If caller has PO (or DO) throw or fail silently depending on its target SDK level. - if (isDeviceOwner(caller) || isProfileOwner(caller)) { + if (isDefaultDeviceOwner(caller) || isProfileOwner(caller)) { synchronized (getLockObject()) { ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller); if (getTargetSdk(admin.info.getPackageName(), userHandle) < Build.VERSION_CODES.O) { @@ -5219,7 +5226,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return false; } - boolean callerIsDeviceOwnerAdmin = isDeviceOwner(caller); + boolean callerIsDeviceOwnerAdmin = isDefaultDeviceOwner(caller); boolean doNotAskCredentialsOnBoot = (flags & DevicePolicyManager.RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT) != 0; if (callerIsDeviceOwnerAdmin && doNotAskCredentialsOnBoot) { @@ -5406,7 +5413,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "ComponentName is null"); Preconditions.checkArgument(timeoutMs >= 0, "Timeout must not be a negative number."); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || isProfileOwner(caller)); // timeoutMs with value 0 means that the admin doesn't participate // timeoutMs is clamped to the interval in case the internal constants change in the future final long minimumStrongAuthTimeout = getMinimumStrongAuthTimeoutMs(); @@ -5575,7 +5583,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } private boolean canManageCaCerts(CallerIdentity caller) { - return (caller.hasAdminComponent() && (isDeviceOwner(caller) || isProfileOwner(caller))) + return (caller.hasAdminComponent() && (isDefaultDeviceOwner(caller) + || isProfileOwner(caller))) || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_CERT_INSTALL)) || hasCallingOrSelfPermission(MANAGE_CA_CERTIFICATES); } @@ -5689,7 +5698,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final boolean isCallerDelegate = isCallerDelegate(caller, DELEGATION_CERT_INSTALL); final boolean isCredentialManagementApp = isCredentialManagementApp(caller); Preconditions.checkCallAuthorization((caller.hasAdminComponent() - && (isProfileOwner(caller) || isDeviceOwner(caller))) + && (isProfileOwner(caller) || isDefaultDeviceOwner(caller))) || (caller.hasPackage() && (isCallerDelegate || isCredentialManagementApp))); if (isCredentialManagementApp) { Preconditions.checkCallAuthorization(!isUserSelectable, "The credential " @@ -5754,7 +5763,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final boolean isCallerDelegate = isCallerDelegate(caller, DELEGATION_CERT_INSTALL); final boolean isCredentialManagementApp = isCredentialManagementApp(caller); Preconditions.checkCallAuthorization((caller.hasAdminComponent() - && (isProfileOwner(caller) || isDeviceOwner(caller))) + && (isProfileOwner(caller) || isDefaultDeviceOwner(caller))) || (caller.hasPackage() && (isCallerDelegate || isCredentialManagementApp))); if (isCredentialManagementApp) { Preconditions.checkCallAuthorization( @@ -5818,12 +5827,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } private boolean canInstallCertificates(CallerIdentity caller) { - return isProfileOwner(caller) || isDeviceOwner(caller) + return isProfileOwner(caller) || isDefaultDeviceOwner(caller) || isCallerDelegate(caller, DELEGATION_CERT_INSTALL); } private boolean canChooseCertificates(CallerIdentity caller) { - return isProfileOwner(caller) || isDeviceOwner(caller) + return isProfileOwner(caller) || isDefaultDeviceOwner(caller) || isCallerDelegate(caller, DELEGATION_CERT_SELECTION); } @@ -5871,7 +5880,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(who, callerPackage); Preconditions.checkCallAuthorization((caller.hasAdminComponent() - && (isProfileOwner(caller) || isDeviceOwner(caller))) + && (isProfileOwner(caller) || isDefaultDeviceOwner(caller))) || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_CERT_SELECTION))); final int granteeUid; @@ -5984,7 +5993,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // If not, fall back to the device owner check. Preconditions.checkCallAuthorization( - isDeviceOwner(caller) || isCallerDelegate(caller, DELEGATION_CERT_INSTALL)); + isDefaultDeviceOwner(caller) || isCallerDelegate(caller, DELEGATION_CERT_INSTALL)); } @VisibleForTesting @@ -6047,7 +6056,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { enforceIndividualAttestationSupportedIfRequested(attestationUtilsFlags); } else { Preconditions.checkCallAuthorization((caller.hasAdminComponent() - && (isProfileOwner(caller) || isDeviceOwner(caller))) + && (isProfileOwner(caller) || isDefaultDeviceOwner(caller))) || (caller.hasPackage() && (isCallerDelegate || isCredentialManagementApp))); if (isCredentialManagementApp) { Preconditions.checkCallAuthorization( @@ -6182,7 +6191,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final boolean isCallerDelegate = isCallerDelegate(caller, DELEGATION_CERT_INSTALL); final boolean isCredentialManagementApp = isCredentialManagementApp(caller); Preconditions.checkCallAuthorization((caller.hasAdminComponent() - && (isProfileOwner(caller) || isDeviceOwner(caller))) + && (isProfileOwner(caller) || isDefaultDeviceOwner(caller))) || (caller.hasPackage() && (isCallerDelegate || isCredentialManagementApp))); if (isCredentialManagementApp) { Preconditions.checkCallAuthorization( @@ -6337,14 +6346,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final int userId = caller.getUserId(); // Ensure calling process is device/profile owner. if (!Collections.disjoint(scopes, DEVICE_OWNER_OR_MANAGED_PROFILE_OWNER_DELEGATIONS)) { - Preconditions.checkCallAuthorization(isDeviceOwner(caller) + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller) || (isProfileOwner(caller) && isManagedProfile(caller.getUserId()))); } else if (!Collections.disjoint( scopes, DEVICE_OWNER_OR_ORGANIZATION_OWNED_MANAGED_PROFILE_OWNER_DELEGATIONS)) { - Preconditions.checkCallAuthorization(isDeviceOwner(caller) + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)); } else { - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || isProfileOwner(caller)); } synchronized (getLockObject()) { @@ -6434,7 +6444,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // * Either it's a profile owner / device owner, if componentName is provided // * Or it's an app querying its own delegation scopes if (caller.hasAdminComponent()) { - Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + Preconditions.checkCallAuthorization( + isProfileOwner(caller) || isDefaultDeviceOwner(caller)); } else { Preconditions.checkCallAuthorization(isPackage(caller, delegatePackage), String.format("Caller with uid %d is not %s", caller.getUid(), @@ -6467,7 +6478,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // Retrieve the user ID of the calling process. final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || isProfileOwner(caller)); synchronized (getLockObject()) { return getDelegatePackagesInternalLocked(scope, caller.getUserId()); } @@ -6600,7 +6612,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(who); // Ensure calling process is device/profile owner. - Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + Preconditions.checkCallAuthorization( + isProfileOwner(caller) || isDefaultDeviceOwner(caller)); synchronized (getLockObject()) { final DevicePolicyData policy = getUserData(caller.getUserId()); @@ -6712,7 +6725,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || isProfileOwner(caller)); checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_ALWAYS_ON_VPN_PACKAGE); if (vpnPackage == null) { @@ -6792,7 +6806,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(admin, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(admin); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || isProfileOwner(caller)); return mInjector.binderWithCleanCallingIdentity( () -> mInjector.getVpnManager().getAlwaysOnVpnPackageForUser(caller.getUserId())); @@ -6818,7 +6833,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { caller = getCallerIdentity(); } else { caller = getCallerIdentity(admin); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || isProfileOwner(caller)); } return mInjector.binderWithCleanCallingIdentity( @@ -6841,7 +6857,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(admin, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(admin); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || isProfileOwner(caller)); return mInjector.binderWithCleanCallingIdentity( () -> mInjector.getVpnManager().getVpnLockdownAllowlist(caller.getUserId())); @@ -6946,8 +6963,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { + "organization-owned device."); } if ((flags & WIPE_RESET_PROTECTION_DATA) != 0) { - Preconditions.checkCallAuthorization(isDeviceOwner(caller) - || calledByProfileOwnerOnOrgOwnedDevice, + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller) + || calledByProfileOwnerOnOrgOwnedDevice + || isFinancedDeviceOwner(caller), "Only device owners or profile owners of organization-owned device can set " + "WIPE_RESET_PROTECTION_DATA"); } @@ -7139,8 +7157,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Preconditions.checkNotNull(who, "ComponentName is null"); CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) - || isProfileOwnerOfOrganizationOwnedDevice(caller)); + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)); checkCanExecuteOrThrowUnsafe(DevicePolicyManager .OPERATION_SET_FACTORY_RESET_PROTECTION_POLICY); @@ -7189,7 +7207,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { UserHandle.getUserId(frpManagementAgentUid)); } else { Preconditions.checkCallAuthorization( - isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)); + isDefaultDeviceOwner(caller) + || isProfileOwnerOfOrganizationOwnedDevice(caller)); admin = getProfileOwnerOrDeviceOwnerLocked(caller); } } @@ -7617,7 +7636,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public void setRecommendedGlobalProxy(ComponentName who, ProxyInfo proxyInfo) { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller)); + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)); checkAllUsersAreAffiliatedWithDevice(); mInjector.binderWithCleanCallingIdentity( () -> mInjector.getConnectivityManager().setGlobalProxy(proxyInfo)); @@ -7915,7 +7934,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } final CallerIdentity caller = getCallerIdentity(); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || isProfileOwner(caller)); synchronized (getLockObject()) { final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller); @@ -7934,8 +7954,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(); Preconditions.checkCallAuthorization( - isProfileOwner(caller) - || isDeviceOwner(caller) + isProfileOwner(caller) || isDefaultDeviceOwner(caller) || hasCallingOrSelfPermission(permission.READ_NEARBY_STREAMING_POLICY)); synchronized (getLockObject()) { @@ -7955,7 +7974,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } final CallerIdentity caller = getCallerIdentity(); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || isProfileOwner(caller)); synchronized (getLockObject()) { final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller); @@ -7974,8 +7994,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(); Preconditions.checkCallAuthorization( - isProfileOwner(caller) - || isDeviceOwner(caller) + isProfileOwner(caller) || isDefaultDeviceOwner(caller) || hasCallingOrSelfPermission(permission.READ_NEARBY_STREAMING_POLICY)); synchronized (getLockObject()) { @@ -8067,7 +8086,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(who); Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller) - || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDeviceOwner(caller)); + || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(caller)); mInjector.binderWithCleanCallingIdentity(() -> mInjector.settingsGlobalPutInt(Settings.Global.AUTO_TIME, enabled ? 1 : 0)); @@ -8091,7 +8110,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(who); Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller) - || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDeviceOwner(caller)); + || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(caller)); return mInjector.settingsGlobalGetInt(Global.AUTO_TIME, 0) > 0; } @@ -8108,7 +8127,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(who); Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller) - || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDeviceOwner(caller)); + || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(caller)); mInjector.binderWithCleanCallingIdentity(() -> mInjector.settingsGlobalPutInt(Global.AUTO_TIME_ZONE, enabled ? 1 : 0)); @@ -8132,7 +8151,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(who); Preconditions.checkCallAuthorization(isProfileOwnerOnUser0(caller) - || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDeviceOwner(caller)); + || isProfileOwnerOfOrganizationOwnedDevice(caller) || isDefaultDeviceOwner(caller)); return mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) > 0; } @@ -8161,7 +8180,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // which could still contain data related to that user. Should we disallow that, e.g. until // next boot? Might not be needed given that this still requires user consent. final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller)); + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)); checkAllUsersAreAffiliatedWithDevice(); checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_REQUEST_BUGREPORT); @@ -8472,7 +8491,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Objects.requireNonNull(packageList, "packageList is null"); final CallerIdentity caller = getCallerIdentity(who, callerPackage); - Preconditions.checkCallAuthorization((caller.hasAdminComponent() && isDeviceOwner(caller)) + Preconditions.checkCallAuthorization((caller.hasAdminComponent() + && isDefaultDeviceOwner(caller)) || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_KEEP_UNINSTALLED_PACKAGES))); checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_KEEP_UNINSTALLED_PACKAGES); @@ -8501,7 +8521,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return null; } final CallerIdentity caller = getCallerIdentity(who, callerPackage); - Preconditions.checkCallAuthorization((caller.hasAdminComponent() && isDeviceOwner(caller)) + Preconditions.checkCallAuthorization((caller.hasAdminComponent() + && isDefaultDeviceOwner(caller)) || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_KEEP_UNINSTALLED_PACKAGES))); @@ -8615,8 +8636,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean hasDeviceOwner() { final CallerIdentity caller = getCallerIdentity(); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) - || canManageUsers(caller) + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller) + || canManageUsers(caller) || isFinancedDeviceOwner(caller) || hasCallingOrSelfPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)); return mOwners.hasDeviceOwner(); } @@ -8633,17 +8654,29 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } - private boolean isDeviceOwner(CallerIdentity caller) { + /** + * Returns {@code true} <b>only if</b> the caller is the device owner and the device owner type + * is {@link DevicePolicyManager#DEVICE_OWNER_TYPE_DEFAULT}. {@code false} is returned for the + * case where the caller is not the device owner, there is no device owner, or the device owner + * type is not {@link DevicePolicyManager#DEVICE_OWNER_TYPE_DEFAULT}. + * + */ + private boolean isDefaultDeviceOwner(CallerIdentity caller) { synchronized (getLockObject()) { - if (!mOwners.hasDeviceOwner() || mOwners.getDeviceOwnerUserId() != caller.getUserId()) { - return false; - } + return isDeviceOwnerLocked(caller) && getDeviceOwnerTypeLocked( + mOwners.getDeviceOwnerPackageName()) == DEVICE_OWNER_TYPE_DEFAULT; + } + } - if (caller.hasAdminComponent()) { - return mOwners.getDeviceOwnerComponent().equals(caller.getComponentName()); - } else { - return isUidDeviceOwnerLocked(caller.getUid()); - } + private boolean isDeviceOwnerLocked(CallerIdentity caller) { + if (!mOwners.hasDeviceOwner() || mOwners.getDeviceOwnerUserId() != caller.getUserId()) { + return false; + } + + if (caller.hasAdminComponent()) { + return mOwners.getDeviceOwnerComponent().equals(caller.getComponentName()); + } else { + return isUidDeviceOwnerLocked(caller.getUid()); } } @@ -9073,8 +9106,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) - || isProfileOwnerOfOrganizationOwnedDevice(caller)); + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)); mInjector.binderWithCleanCallingIdentity(() -> mLockPatternUtils.setDeviceOwnerInfo(info != null ? info.toString() : null)); @@ -9258,7 +9291,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(who); final int userId = caller.getUserId(); - Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + Preconditions.checkCallAuthorization( + isProfileOwner(caller) || isDefaultDeviceOwner(caller)); Preconditions.checkCallingUser(isManagedProfile(userId)); synchronized (getLockObject()) { @@ -9289,7 +9323,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || isProfileOwner(caller)); mInjector.binderWithCleanCallingIdentity(() -> { mUserManager.setUserName(caller.getUserId(), profileName); @@ -9725,7 +9760,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } private void enforceCanCallLockTaskLocked(CallerIdentity caller) { - Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + Preconditions.checkCallAuthorization(isProfileOwner(caller) + || isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller)); final int userId = caller.getUserId(); if (!canUserUseLockTaskLocked(userId)) { @@ -9982,7 +10018,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { ComponentName activity) { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + Preconditions.checkCallAuthorization(isProfileOwner(caller) + || isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller)); final int userHandle = caller.getUserId(); synchronized (getLockObject()) { @@ -10009,7 +10046,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public void clearPackagePersistentPreferredActivities(ComponentName who, String packageName) { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + Preconditions.checkCallAuthorization(isProfileOwner(caller) + || isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller)); final int userHandle = caller.getUserId(); synchronized (getLockObject()) { @@ -10030,7 +10068,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(admin, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(admin); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller) || (parent && isProfileOwnerOfOrganizationOwnedDevice(caller))); if (parent) { mInjector.binderWithCleanCallingIdentity(() -> enforcePackageIsSystemPackage( @@ -10070,7 +10108,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { String packageName, Bundle settings) { final CallerIdentity caller = getCallerIdentity(who, callerPackage); Preconditions.checkCallAuthorization((caller.hasAdminComponent() - && (isProfileOwner(caller) || isDeviceOwner(caller))) + && (isProfileOwner(caller) || isDefaultDeviceOwner(caller))) || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_APP_RESTRICTIONS))); checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_APPLICATION_RESTRICTIONS); @@ -10168,7 +10206,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public void setRestrictionsProvider(ComponentName who, ComponentName permissionProvider) { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + Preconditions.checkCallAuthorization( + isProfileOwner(caller) || isDefaultDeviceOwner(caller)); checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_RESTRICTIONS_PROVIDER); synchronized (getLockObject()) { @@ -10193,7 +10232,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public void addCrossProfileIntentFilter(ComponentName who, IntentFilter filter, int flags) { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + Preconditions.checkCallAuthorization( + isProfileOwner(caller) || isDefaultDeviceOwner(caller)); int callingUserId = caller.getUserId(); synchronized (getLockObject()) { long id = mInjector.binderClearCallingIdentity(); @@ -10242,7 +10282,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public void clearCrossProfileIntentFilters(ComponentName who) { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + Preconditions.checkCallAuthorization( + isProfileOwner(caller) || isDefaultDeviceOwner(caller)); int callingUserId = caller.getUserId(); synchronized (getLockObject()) { @@ -10382,7 +10423,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || isProfileOwner(caller)); synchronized (getLockObject()) { ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller); @@ -10495,7 +10537,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { + "system input methods when called on the parent instance of an " + "organization-owned device"); } else { - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || isProfileOwner(caller)); } if (packageList != null) { @@ -10553,7 +10596,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (calledOnParentInstance) { Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller)); } else { - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || isProfileOwner(caller)); } synchronized (getLockObject()) { @@ -10732,7 +10776,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // Only allow the system user to use this method Preconditions.checkCallAuthorization(caller.getUserHandle().isSystem(), "createAndManageUser was called from non-system user"); - Preconditions.checkCallAuthorization(isDeviceOwner(caller)); + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)); checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_CREATE_AND_MANAGE_USER); final boolean ephemeral = (flags & DevicePolicyManager.MAKE_USER_EPHEMERAL) != 0; @@ -10974,7 +11018,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "ComponentName is null"); Objects.requireNonNull(userHandle, "UserHandle is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller)); + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)); checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_REMOVE_USER); return mInjector.binderWithCleanCallingIdentity(() -> { @@ -11008,7 +11052,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public boolean switchUser(ComponentName who, UserHandle userHandle) { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller)); + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)); checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SWITCH_USER); boolean switched = false; @@ -11083,7 +11127,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "ComponentName is null"); Objects.requireNonNull(userHandle, "UserHandle is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller)); + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)); checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_START_USER_IN_BACKGROUND); final int userId = userHandle.getIdentifier(); @@ -11119,7 +11163,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "ComponentName is null"); Objects.requireNonNull(userHandle, "UserHandle is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller)); + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)); checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_STOP_USER); final int userId = userHandle.getIdentifier(); @@ -11135,7 +11179,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public int logoutUser(ComponentName who) { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + Preconditions.checkCallAuthorization( + isProfileOwner(caller) || isDefaultDeviceOwner(caller)); checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_LOGOUT_USER); final int callingUserId = caller.getUserId(); @@ -11224,7 +11269,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public List<UserHandle> getSecondaryUsers(ComponentName who) { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller)); + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)); return mInjector.binderWithCleanCallingIdentity(() -> { final List<UserInfo> userInfos = mInjector.getUserManager().getAliveUsers(); @@ -11244,7 +11289,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || isProfileOwner(caller)); return mInjector.binderWithCleanCallingIdentity( () -> mInjector.getUserManager().isUserEphemeral(caller.getUserId())); @@ -11255,7 +11301,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { String packageName) { final CallerIdentity caller = getCallerIdentity(who, callerPackage); Preconditions.checkCallAuthorization((caller.hasAdminComponent() - && (isProfileOwner(caller) || isDeviceOwner(caller))) + && (isProfileOwner(caller) || isDefaultDeviceOwner(caller))) || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_APP_RESTRICTIONS))); return mInjector.binderWithCleanCallingIdentity(() -> { @@ -11306,7 +11352,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(packageNames, "array of packages cannot be null"); final CallerIdentity caller = getCallerIdentity(who, callerPackage); Preconditions.checkCallAuthorization((caller.hasAdminComponent() - && (isProfileOwner(caller) || isDeviceOwner(caller))) + && (isProfileOwner(caller) || isDefaultDeviceOwner(caller))) || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS))); checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_PACKAGES_SUSPENDED); @@ -11369,7 +11415,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public boolean isPackageSuspended(ComponentName who, String callerPackage, String packageName) { final CallerIdentity caller = getCallerIdentity(who, callerPackage); Preconditions.checkCallAuthorization((caller.hasAdminComponent() - && (isProfileOwner(caller) || isDeviceOwner(caller))) + && (isProfileOwner(caller) || isDefaultDeviceOwner(caller))) || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS))); synchronized (getLockObject()) { @@ -11390,8 +11436,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public List<String> listPolicyExemptApps() { CallerIdentity caller = getCallerIdentity(); Preconditions.checkCallAuthorization( - hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS) || isDeviceOwner(caller) - || isProfileOwner(caller)); + hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS) + || isDefaultDeviceOwner(caller) || isProfileOwner(caller)); return listPolicyExemptAppsUnchecked(); } @@ -11432,12 +11478,19 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final ActiveAdmin activeAdmin = getParentOfAdminIfRequired( getProfileOwnerOrDeviceOwnerLocked(caller), parent); - if (isDeviceOwner(caller)) { + if (isDefaultDeviceOwner(caller)) { if (!UserRestrictionsUtils.canDeviceOwnerChange(key)) { throw new SecurityException("Device owner cannot set user restriction " + key); } Preconditions.checkArgument(!parent, "Cannot use the parent instance in Device Owner mode"); + } else if (isFinancedDeviceOwner(caller)) { + if (!UserRestrictionsUtils.canFinancedDeviceOwnerChange(key)) { + throw new SecurityException("Cannot set user restriction " + key + + " when managing a financed device"); + } + Preconditions.checkArgument(!parent, + "Cannot use the parent instance in Financed Device Owner mode"); } else { boolean profileOwnerCanChangeOnItself = !parent && UserRestrictionsUtils.canProfileOwnerChange(key, userHandle); @@ -11546,7 +11599,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller) + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller) + || isProfileOwner(caller) || (parent && isProfileOwnerOfOrganizationOwnedDevice(caller))); synchronized (getLockObject()) { @@ -11561,7 +11615,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { boolean hidden, boolean parent) { final CallerIdentity caller = getCallerIdentity(who, callerPackage); Preconditions.checkCallAuthorization((caller.hasAdminComponent() - && (isProfileOwner(caller) || isDeviceOwner(caller))) + && (isProfileOwner(caller) || isDefaultDeviceOwner(caller))) || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS))); List<String> exemptApps = listPolicyExemptAppsUnchecked(); @@ -11607,7 +11661,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { String packageName, boolean parent) { final CallerIdentity caller = getCallerIdentity(who, callerPackage); Preconditions.checkCallAuthorization((caller.hasAdminComponent() - && (isProfileOwner(caller) || isDeviceOwner(caller))) + && (isProfileOwner(caller) || isDefaultDeviceOwner(caller))) || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS))); final int userId = parent ? getProfileParentId(caller.getUserId()) : caller.getUserId(); @@ -11643,7 +11697,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public void enableSystemApp(ComponentName who, String callerPackage, String packageName) { final CallerIdentity caller = getCallerIdentity(who, callerPackage); Preconditions.checkCallAuthorization((caller.hasAdminComponent() - && (isProfileOwner(caller) || isDeviceOwner(caller))) + && (isProfileOwner(caller) || isDefaultDeviceOwner(caller))) || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_ENABLE_SYSTEM_APP))); synchronized (getLockObject()) { @@ -11687,7 +11741,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public int enableSystemAppWithIntent(ComponentName who, String callerPackage, Intent intent) { final CallerIdentity caller = getCallerIdentity(who, callerPackage); Preconditions.checkCallAuthorization((caller.hasAdminComponent() - && (isProfileOwner(caller) || isDeviceOwner(caller))) + && (isProfileOwner(caller) || isDefaultDeviceOwner(caller))) || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_ENABLE_SYSTEM_APP))); int numberOfAppsInstalled = 0; @@ -11756,7 +11810,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { String packageName) { final CallerIdentity caller = getCallerIdentity(who, callerPackage); Preconditions.checkCallAuthorization((caller.hasAdminComponent() - && (isProfileOwner(caller) || isDeviceOwner(caller))) + && (isProfileOwner(caller) || isDefaultDeviceOwner(caller))) || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_INSTALL_EXISTING_PACKAGE))); @@ -11872,7 +11926,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { boolean uninstallBlocked) { final CallerIdentity caller = getCallerIdentity(who, callerPackage); Preconditions.checkCallAuthorization((caller.hasAdminComponent() - && (isProfileOwner(caller) || isDeviceOwner(caller))) + && (isProfileOwner(caller) || isDefaultDeviceOwner(caller) + || isFinancedDeviceOwner(caller))) || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_BLOCK_UNINSTALL))); final int userId = caller.getUserId(); @@ -11913,7 +11968,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (who != null) { final CallerIdentity caller = getCallerIdentity(who); Preconditions.checkCallAuthorization( - isProfileOwner(caller) || isDeviceOwner(caller)); + isProfileOwner(caller) || isDefaultDeviceOwner(caller) + || isFinancedDeviceOwner(caller)); } try { return mIPackageManager.getBlockUninstallForUser(packageName, userId); @@ -12087,7 +12143,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || isProfileOwner(caller)); synchronized (getLockObject()) { ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller); @@ -12111,7 +12168,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || isProfileOwner(caller)); synchronized (getLockObject()) { ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller); @@ -12136,7 +12194,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // Check can set secondary lockscreen enabled final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || isProfileOwner(caller)); Preconditions.checkCallAuthorization(!isManagedProfile(caller.getUserId()), "User %d is not allowed to call setSecondaryLockscreenEnabled", caller.getUserId()); @@ -12325,6 +12384,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final int userHandle = caller.getUserId(); synchronized (getLockObject()) { enforceCanCallLockTaskLocked(caller); + enforceCanSetLockTaskFeaturesOnFinancedDevice(caller, flags); checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_LOCK_TASK_FEATURES); setLockTaskFeaturesLocked(userHandle, flags); } @@ -12373,6 +12433,24 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { }); } + private void enforceCanSetLockTaskFeaturesOnFinancedDevice(CallerIdentity caller, int flags) { + int allowedFlags = LOCK_TASK_FEATURE_SYSTEM_INFO | LOCK_TASK_FEATURE_KEYGUARD + | LOCK_TASK_FEATURE_HOME | LOCK_TASK_FEATURE_GLOBAL_ACTIONS + | LOCK_TASK_FEATURE_NOTIFICATIONS; + + if (!isFinancedDeviceOwner(caller)) { + return; + } + + if ((flags == 0) || ((flags & ~(allowedFlags)) != 0)) { + throw new SecurityException( + "Permitted lock task features when managing a financed device: " + + "LOCK_TASK_FEATURE_SYSTEM_INFO, LOCK_TASK_FEATURE_KEYGUARD, " + + "LOCK_TASK_FEATURE_HOME, LOCK_TASK_FEATURE_GLOBAL_ACTIONS, " + + "or LOCK_TASK_FEATURE_NOTIFICATIONS"); + } + } + @Override public void notifyLockTaskModeChanged(boolean isEnabled, String pkg, int userHandle) { Preconditions.checkCallAuthorization(isSystemUid(getCallerIdentity()), @@ -12413,7 +12491,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public void setGlobalSetting(ComponentName who, String setting, String value) { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller)); + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)); DevicePolicyEventLogger .createEvent(DevicePolicyEnums.SET_GLOBAL_SETTING) @@ -12454,7 +12532,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "ComponentName is null"); Preconditions.checkStringNotEmpty(setting, "String setting is null or empty"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + Preconditions.checkCallAuthorization( + isProfileOwner(caller) || isDefaultDeviceOwner(caller)); checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_SYSTEM_SETTING); synchronized (getLockObject()) { @@ -12476,8 +12555,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Preconditions.checkNotNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) - || isProfileOwnerOfOrganizationOwnedDevice(caller)); + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)); mInjector.binderWithCleanCallingIdentity(() -> mInjector.settingsGlobalPutInt(Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, @@ -12498,8 +12577,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Preconditions.checkNotNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) - || isProfileOwnerOfOrganizationOwnedDevice(caller)); + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)); return mInjector.binderWithCleanCallingIdentity(() -> mInjector.settingsGlobalGetInt(Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0) > 0); @@ -12510,7 +12589,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Preconditions.checkNotNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller)); + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)); UserHandle userHandle = caller.getUserHandle(); if (mIsAutomotive && !locationEnabled) { @@ -12592,8 +12671,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) - || isProfileOwnerOfOrganizationOwnedDevice(caller)); + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)); // Don't allow set time when auto time is on. if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME, 0) == 1) { @@ -12612,8 +12691,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) - || isProfileOwnerOfOrganizationOwnedDevice(caller)); + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)); // Don't allow set timezone when auto timezone is on. if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) == 1) { @@ -12633,7 +12712,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public void setSecureSetting(ComponentName who, String setting, String value) { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + Preconditions.checkCallAuthorization( + isProfileOwner(caller) || isDefaultDeviceOwner(caller)); int callingUserId = caller.getUserId(); synchronized (getLockObject()) { @@ -12720,7 +12800,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public void setMasterVolumeMuted(ComponentName who, boolean on) { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + Preconditions.checkCallAuthorization( + isProfileOwner(caller) || isDefaultDeviceOwner(caller)); checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_MASTER_VOLUME_MUTED); synchronized (getLockObject()) { @@ -12737,7 +12818,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public boolean isMasterVolumeMuted(ComponentName who) { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + Preconditions.checkCallAuthorization( + isProfileOwner(caller) || isDefaultDeviceOwner(caller)); synchronized (getLockObject()) { AudioManager audioManager = @@ -12750,7 +12832,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public void setUserIcon(ComponentName who, Bitmap icon) { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + Preconditions.checkCallAuthorization( + isProfileOwner(caller) || isDefaultDeviceOwner(caller)); synchronized (getLockObject()) { mInjector.binderWithCleanCallingIdentity( @@ -12766,7 +12849,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public boolean setKeyguardDisabled(ComponentName who, boolean disabled) { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + Preconditions.checkCallAuthorization( + isProfileOwner(caller) || isDefaultDeviceOwner(caller)); final int userId = caller.getUserId(); synchronized (getLockObject()) { @@ -12808,7 +12892,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean setStatusBarDisabled(ComponentName who, boolean disabled) { final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + Preconditions.checkCallAuthorization( + isProfileOwner(caller) || isDefaultDeviceOwner(caller)); int userId = caller.getUserId(); synchronized (getLockObject()) { @@ -13042,7 +13127,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean isActiveDeviceOwner(int uid) { - return isDeviceOwner(new CallerIdentity(uid, null, null)); + return isDefaultDeviceOwner(new CallerIdentity(uid, null, null)); } @Override @@ -13633,7 +13718,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { synchronized (getLockObject()) { Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller) - || isDeviceOwner(caller)); + || isDefaultDeviceOwner(caller)); checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_SYSTEM_UPDATE_POLICY); if (policy == null) { @@ -13831,7 +13916,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(admin, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(admin); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || isProfileOwner(caller)); return mOwners.getSystemUpdateInfo(); } @@ -13840,7 +13926,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public void setPermissionPolicy(ComponentName admin, String callerPackage, int policy) { final CallerIdentity caller = getCallerIdentity(admin, callerPackage); Preconditions.checkCallAuthorization((caller.hasAdminComponent() - && (isProfileOwner(caller) || isDeviceOwner(caller))) + && (isProfileOwner(caller) || isDefaultDeviceOwner(caller))) || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PERMISSION_GRANT))); checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_PERMISSION_POLICY); @@ -13882,11 +13968,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(admin, callerPackage); Preconditions.checkCallAuthorization((caller.hasAdminComponent() - && (isProfileOwner(caller) || isDeviceOwner(caller))) + && (isProfileOwner(caller) || isDefaultDeviceOwner(caller) + || isFinancedDeviceOwner(caller))) || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PERMISSION_GRANT))); checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_PERMISSION_GRANT_STATE); synchronized (getLockObject()) { + if (isFinancedDeviceOwner(caller)) { + enforceCanSetPermissionGrantOnFinancedDevice(packageName, permission); + } long ident = mInjector.binderClearCallingIdentity(); try { boolean isPostQAdmin = getTargetSdk(caller.getPackageName(), caller.getUserId()) @@ -13946,12 +14036,23 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } + private void enforceCanSetPermissionGrantOnFinancedDevice( + String packageName, String permission) { + if (!Manifest.permission.READ_PHONE_STATE.equals(permission)) { + throw new SecurityException("Cannot grant " + permission + + " when managing a financed device"); + } else if (!mOwners.getDeviceOwnerPackageName().equals(packageName)) { + throw new SecurityException("Cannot grant permission to a package that is not" + + " the device owner"); + } + } + @Override public int getPermissionGrantState(ComponentName admin, String callerPackage, String packageName, String permission) throws RemoteException { final CallerIdentity caller = getCallerIdentity(admin, callerPackage); Preconditions.checkCallAuthorization(isSystemUid(caller) || (caller.hasAdminComponent() - && (isProfileOwner(caller) || isDeviceOwner(caller))) + && (isProfileOwner(caller) || isDefaultDeviceOwner(caller))) || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PERMISSION_GRANT))); synchronized (getLockObject()) { @@ -14231,7 +14332,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } private void checkIsDeviceOwner(CallerIdentity caller) { - Preconditions.checkCallAuthorization(isDeviceOwner(caller), caller.getUid() + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller), caller.getUid() + " is not device owner"); } @@ -14263,8 +14364,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(admin, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(admin); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) - || isProfileOwnerOfOrganizationOwnedDevice(caller)); + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)); return mInjector.binderWithCleanCallingIdentity(() -> { String[] macAddresses = mInjector.getWifiManager().getFactoryMacAddresses(); @@ -14299,7 +14400,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(admin, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(admin); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || isProfileOwner(caller)); return isManagedProfile(caller.getUserId()); } @@ -14308,7 +14410,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public void reboot(ComponentName admin) { Objects.requireNonNull(admin, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(admin); - Preconditions.checkCallAuthorization(isDeviceOwner(caller)); + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)); checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_REBOOT); mInjector.binderWithCleanCallingIdentity(() -> { // Make sure there are no ongoing calls on the device. @@ -14542,7 +14644,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return null; } final CallerIdentity caller = getCallerIdentity(); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || canManageUsers(caller)); + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller) + || canManageUsers(caller) || isFinancedDeviceOwner(caller)); synchronized (getLockObject()) { final ActiveAdmin deviceOwnerAdmin = getDeviceOwnerAdminLocked(); return deviceOwnerAdmin == null ? null : deviceOwnerAdmin.organizationName; @@ -14576,7 +14679,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who); Objects.requireNonNull(packageNames); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller), + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || isProfileOwner(caller), "Admin %s does not own the profile", caller.getComponentName()); if (!mHasFeature) { @@ -14627,7 +14731,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return new ArrayList<>(); } final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller), + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || isProfileOwner(caller), "Admin %s does not own the profile", caller.getComponentName()); synchronized (getLockObject()) { @@ -14766,7 +14871,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final Set<String> affiliationIds = new ArraySet<>(ids); final CallerIdentity caller = getCallerIdentity(admin); - Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + Preconditions.checkCallAuthorization( + isProfileOwner(caller) || isDefaultDeviceOwner(caller)); final int callingUserId = caller.getUserId(); synchronized (getLockObject()) { @@ -14797,7 +14903,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(admin); final CallerIdentity caller = getCallerIdentity(admin); - Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + Preconditions.checkCallAuthorization( + isProfileOwner(caller) || isDefaultDeviceOwner(caller)); synchronized (getLockObject()) { return new ArrayList<String>(getUserData(caller.getUserId()).mAffiliationIds); @@ -14891,7 +14998,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (admin != null) { Preconditions.checkCallAuthorization( isProfileOwnerOfOrganizationOwnedDevice(caller) - || isDeviceOwner(caller)); + || isDefaultDeviceOwner(caller)); } else { // A delegate app passes a null admin component, which is expected Preconditions.checkCallAuthorization( @@ -14928,7 +15035,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (admin != null) { Preconditions.checkCallAuthorization( isProfileOwnerOfOrganizationOwnedDevice(caller) - || isDeviceOwner(caller)); + || isDefaultDeviceOwner(caller)); } else { // A delegate app passes a null admin component, which is expected Preconditions.checkCallAuthorization( @@ -14961,7 +15068,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (admin != null) { Preconditions.checkCallAuthorization( isProfileOwnerOfOrganizationOwnedDevice(caller) - || isDeviceOwner(caller)); + || isDefaultDeviceOwner(caller)); } else { // A delegate app passes a null admin component, which is expected Preconditions.checkCallAuthorization( @@ -15007,7 +15114,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (admin != null) { Preconditions.checkCallAuthorization( isProfileOwnerOfOrganizationOwnedDevice(caller) - || isDeviceOwner(caller)); + || isDefaultDeviceOwner(caller)); } else { // A delegate app passes a null admin component, which is expected Preconditions.checkCallAuthorization( @@ -15246,7 +15353,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(admin, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(admin); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller) + || isProfileOwner(caller) || isFinancedDeviceOwner(caller)); toggleBackupServiceActive(caller.getUserId(), enabled); } @@ -15259,7 +15367,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(admin, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(admin); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller) + || isProfileOwner(caller) || isFinancedDeviceOwner(caller)); return mInjector.binderWithCleanCallingIdentity(() -> { synchronized (getLockObject()) { @@ -15334,7 +15443,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Objects.requireNonNull(admin); final CallerIdentity caller = getCallerIdentity(admin); - Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + Preconditions.checkCallAuthorization( + isProfileOwner(caller) || isDefaultDeviceOwner(caller)); synchronized (getLockObject()) { final int callingUserId = caller.getUserId(); @@ -15462,7 +15572,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final boolean isManagedProfileOwner = isProfileOwner(caller) && isManagedProfile(caller.getUserId()); Preconditions.checkCallAuthorization((caller.hasAdminComponent() - && (isDeviceOwner(caller) || isManagedProfileOwner)) + && (isDefaultDeviceOwner(caller) || isManagedProfileOwner)) || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_NETWORK_LOGGING))); synchronized (getLockObject()) { @@ -15622,7 +15732,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } final CallerIdentity caller = getCallerIdentity(admin, packageName); Preconditions.checkCallAuthorization((caller.hasAdminComponent() - && (isDeviceOwner(caller) + && (isDefaultDeviceOwner(caller) || (isProfileOwner(caller) && isManagedProfile(caller.getUserId())))) || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_NETWORK_LOGGING)) || hasCallingOrSelfPermission(permission.MANAGE_USERS)); @@ -15654,7 +15764,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final boolean isManagedProfileOwner = isProfileOwner(caller) && isManagedProfile(caller.getUserId()); Preconditions.checkCallAuthorization((caller.hasAdminComponent() - && (isDeviceOwner(caller) || isManagedProfileOwner)) + && (isDefaultDeviceOwner(caller) || isManagedProfileOwner)) || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_NETWORK_LOGGING))); if (mOwners.hasDeviceOwner()) { checkAllUsersAreAffiliatedWithDevice(); @@ -15815,14 +15925,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public long getLastSecurityLogRetrievalTime() { final CallerIdentity caller = getCallerIdentity(); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || canManageUsers(caller)); + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || canManageUsers(caller)); return getUserData(UserHandle.USER_SYSTEM).mLastSecurityLogRetrievalTime; } @Override public long getLastBugReportRequestTime() { final CallerIdentity caller = getCallerIdentity(); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || canManageUsers(caller)); + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || canManageUsers(caller)); return getUserData(UserHandle.USER_SYSTEM).mLastBugReportRequestTime; } @@ -15830,7 +15942,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public long getLastNetworkLogRetrievalTime() { final CallerIdentity caller = getCallerIdentity(); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller) || (isProfileOwner(caller) && isManagedProfile(caller.getUserId())) || canManageUsers(caller)); final int affectedUserId = getNetworkLoggingAffectedUser(); @@ -15846,7 +15958,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { throw new IllegalArgumentException("token must be at least 32-byte long"); } final CallerIdentity caller = getCallerIdentity(admin); - Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + Preconditions.checkCallAuthorization( + isProfileOwner(caller) || isDefaultDeviceOwner(caller)); synchronized (getLockObject()) { final int userHandle = caller.getUserId(); @@ -15870,7 +15983,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return false; } final CallerIdentity caller = getCallerIdentity(admin); - Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + Preconditions.checkCallAuthorization( + isProfileOwner(caller) || isDefaultDeviceOwner(caller)); synchronized (getLockObject()) { final int userHandle = caller.getUserId(); @@ -15895,7 +16009,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return false; } final CallerIdentity caller = getCallerIdentity(admin); - Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + Preconditions.checkCallAuthorization( + isProfileOwner(caller) || isDefaultDeviceOwner(caller)); synchronized (getLockObject()) { return isResetPasswordTokenActiveForUserLocked(caller.getUserId()); @@ -15920,7 +16035,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(token); final CallerIdentity caller = getCallerIdentity(admin); - Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + Preconditions.checkCallAuthorization( + isProfileOwner(caller) || isDefaultDeviceOwner(caller)); synchronized (getLockObject()) { DevicePolicyData policy = getUserData(caller.getUserId()); @@ -15945,7 +16061,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean isCurrentInputMethodSetByOwner() { final CallerIdentity caller = getCallerIdentity(); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller) || isProfileOwner(caller) || isSystemUid(caller), "Only profile owner, device owner and system may call this method."); return getUserData(caller.getUserId()).mCurrentInputMethodSet; @@ -15956,7 +16072,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final int userId = user.getIdentifier(); final CallerIdentity caller = getCallerIdentity(); Preconditions.checkCallAuthorization((userId == caller.getUserId()) - || isProfileOwner(caller) || isDeviceOwner(caller) + || isProfileOwner(caller) || isDefaultDeviceOwner(caller) || hasFullCrossUsersPermission(caller, userId)); synchronized (getLockObject()) { @@ -15973,7 +16089,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(callback, "callback is null"); final CallerIdentity caller = getCallerIdentity(admin); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || isProfileOwner(caller)); checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_CLEAR_APPLICATION_USER_DATA); long ident = mInjector.binderClearCallingIdentity(); @@ -16005,7 +16122,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Objects.requireNonNull(admin, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(admin); - Preconditions.checkCallAuthorization(isDeviceOwner(caller)); + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)); checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_LOGOUT_ENABLED); synchronized (getLockObject()) { @@ -16054,7 +16171,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { "Provided administrator and target have the same package name."); final CallerIdentity caller = getCallerIdentity(admin); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || isProfileOwner(caller)); final int callingUserId = caller.getUserId(); final DevicePolicyData policy = getUserData(callingUserId); @@ -16096,7 +16214,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (isUserAffiliatedWithDeviceLocked(callingUserId)) { notifyAffiliatedProfileTransferOwnershipComplete(callingUserId); } - } else if (isDeviceOwner(caller)) { + } else if (isDefaultDeviceOwner(caller)) { ownerType = ADMIN_TYPE_DEVICE_OWNER; prepareTransfer(admin, target, bundle, callingUserId, ADMIN_TYPE_DEVICE_OWNER); @@ -16177,7 +16295,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Objects.requireNonNull(admin, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(admin); - Preconditions.checkCallAuthorization(isDeviceOwner(caller)); + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)); final String startUserSessionMessageString = startUserSessionMessage != null ? startUserSessionMessage.toString() : null; @@ -16202,7 +16320,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Objects.requireNonNull(admin, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(admin); - Preconditions.checkCallAuthorization(isDeviceOwner(caller)); + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)); final String endUserSessionMessageString = endUserSessionMessage != null ? endUserSessionMessage.toString() : null; @@ -16227,7 +16345,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Objects.requireNonNull(admin, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(admin); - Preconditions.checkCallAuthorization(isDeviceOwner(caller)); + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)); synchronized (getLockObject()) { final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked(); @@ -16242,7 +16360,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Objects.requireNonNull(admin, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(admin); - Preconditions.checkCallAuthorization(isDeviceOwner(caller)); + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)); synchronized (getLockObject()) { final ActiveAdmin deviceOwner = getDeviceOwnerAdminLocked(); @@ -16258,7 +16376,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Nullable public PersistableBundle getTransferOwnershipBundle() { final CallerIdentity caller = getCallerIdentity(); - Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + Preconditions.checkCallAuthorization( + isProfileOwner(caller) || isDefaultDeviceOwner(caller)); synchronized (getLockObject()) { final int callingUserId = caller.getUserId(); @@ -16288,7 +16407,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "ComponentName is null"); Objects.requireNonNull(apnSetting, "ApnSetting is null in addOverrideApn"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller)); + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)); TelephonyManager tm = mContext.getSystemService(TelephonyManager.class); if (tm != null) { @@ -16309,7 +16428,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "ComponentName is null"); Objects.requireNonNull(apnSetting, "ApnSetting is null in updateOverrideApn"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller)); + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)); if (apnId < 0) { return false; @@ -16331,7 +16450,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller)); + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)); return removeOverrideApnUnchecked(apnId); } @@ -16352,7 +16471,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller)); + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)); return getOverrideApnsUnchecked(); } @@ -16373,7 +16492,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller)); + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)); checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_OVERRIDE_APNS_ENABLED); setOverrideApnsEnabledUnchecked(enabled); @@ -16393,7 +16512,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller)); + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)); Cursor enforceCursor = mInjector.binderWithCleanCallingIdentity( () -> mContext.getContentResolver().query( @@ -16476,7 +16595,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller)); + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)); checkAllUsersAreAffiliatedWithDevice(); checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_GLOBAL_PRIVATE_DNS); @@ -16515,7 +16634,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller)); + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)); final int currentMode = ConnectivitySettingsManager.getPrivateDnsMode(mContext); switch (currentMode) { @@ -16537,7 +16656,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller)); + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)); return mInjector.settingsGlobalGetString(PRIVATE_DNS_SPECIFIER); } @@ -16547,8 +16666,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(admin, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(admin); - Preconditions.checkCallAuthorization(isDeviceOwner(caller) - || isProfileOwnerOfOrganizationOwnedDevice(caller)); + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)); checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_INSTALL_SYSTEM_UPDATE); DevicePolicyEventLogger @@ -16923,7 +17042,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "ComponentName is null"); Objects.requireNonNull(packages, "packages is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller)); + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller)); checkCanExecuteOrThrowUnsafe( DevicePolicyManager.OPERATION_SET_USER_CONTROL_DISABLED_PACKAGES); @@ -16942,7 +17062,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "ComponentName is null"); final CallerIdentity caller = getCallerIdentity(who); - Preconditions.checkCallAuthorization(isDeviceOwner(caller)); + Preconditions.checkCallAuthorization( + isDefaultDeviceOwner(caller) || isFinancedDeviceOwner(caller)); synchronized (getLockObject()) { return mOwners.getDeviceOwnerProtectedPackages(who.getPackageName()); @@ -16954,7 +17075,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(who, "Admin component name must be provided"); final CallerIdentity caller = getCallerIdentity(who); Preconditions.checkCallAuthorization( - isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller), + isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller), "Common Criteria mode can only be controlled by a device owner or " + "a profile owner on an organization-owned device."); synchronized (getLockObject()) { @@ -16974,7 +17095,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (who != null) { final CallerIdentity caller = getCallerIdentity(who); Preconditions.checkCallAuthorization( - isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller), + isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller), "Common Criteria mode can only be controlled by a device owner or " + "a profile owner on an organization-owned device."); @@ -17446,7 +17567,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(callerPackage); Preconditions.checkCallAuthorization( - isDeviceOwner(caller) || isProfileOwner(caller) + isDefaultDeviceOwner(caller) || isProfileOwner(caller) || isCallerDelegate(caller, DELEGATION_CERT_INSTALL)); synchronized (getLockObject()) { @@ -17467,7 +17588,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(callerPackage); // Only the DPC can set this ID. - Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller), + Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller) || isProfileOwner(caller), "Only a Device Owner or Profile Owner may set the Enterprise ID."); // Empty enterprise ID must not be provided in calls to this method. Preconditions.checkArgument(!TextUtils.isEmpty(organizationId), @@ -18141,27 +18262,51 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @DeviceOwnerType int deviceOwnerType) { Preconditions.checkCallAuthorization(hasCallingOrSelfPermission( permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)); - verifyDeviceOwnerTypePreconditions(admin); - final String packageName = admin.getPackageName(); + synchronized (getLockObject()) { + setDeviceOwnerTypeLocked(admin, deviceOwnerType); + } + } + + private void setDeviceOwnerTypeLocked(ComponentName admin, + @DeviceOwnerType int deviceOwnerType) { + String packageName = admin.getPackageName(); + + verifyDeviceOwnerTypePreconditionsLocked(admin); Preconditions.checkState(!mOwners.isDeviceOwnerTypeSetForDeviceOwner(packageName), "The device owner type has already been set for " + packageName); - synchronized (getLockObject()) { - mOwners.setDeviceOwnerType(packageName, deviceOwnerType); - } + mOwners.setDeviceOwnerType(packageName, deviceOwnerType); } @Override @DeviceOwnerType public int getDeviceOwnerType(@NonNull ComponentName admin) { - verifyDeviceOwnerTypePreconditions(admin); synchronized (getLockObject()) { - return mOwners.getDeviceOwnerType(admin.getPackageName()); + verifyDeviceOwnerTypePreconditionsLocked(admin); + return getDeviceOwnerTypeLocked(admin.getPackageName()); + } + } + + @DeviceOwnerType + private int getDeviceOwnerTypeLocked(String packageName) { + return mOwners.getDeviceOwnerType(packageName); + } + + /** + * {@code true} is returned <b>only if</b> the caller is the device owner and the device owner + * type is {@link DevicePolicyManager#DEVICE_OWNER_TYPE_FINANCED}. {@code false} is returned for + * the case where the caller is not the device owner, there is no device owner, or the device + * owner type is not {@link DevicePolicyManager#DEVICE_OWNER_TYPE_FINANCED}. + */ + private boolean isFinancedDeviceOwner(CallerIdentity caller) { + synchronized (getLockObject()) { + return isDeviceOwnerLocked(caller) && getDeviceOwnerTypeLocked( + mOwners.getDeviceOwnerPackageName()) == DEVICE_OWNER_TYPE_FINANCED; } } - private void verifyDeviceOwnerTypePreconditions(@NonNull ComponentName admin) { + private void verifyDeviceOwnerTypePreconditionsLocked(@NonNull ComponentName admin) { Preconditions.checkState(mOwners.hasDeviceOwner(), "there is no device owner"); Preconditions.checkState(mOwners.getDeviceOwnerComponent().equals(admin), "admin is not the device owner"); @@ -18172,7 +18317,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(packageName, "Admin package name must be provided"); final CallerIdentity caller = getCallerIdentity(packageName); Preconditions.checkCallAuthorization( - isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller), + isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller), "USB data signaling can only be controlled by a device owner or " + "a profile owner on an organization-owned device."); Preconditions.checkState(canUsbDataSignalingBeDisabled(), @@ -18213,7 +18358,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { synchronized (getLockObject()) { // If the caller is an admin, return the policy set by itself. Otherwise // return the device-wide policy. - if (isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)) { + if (isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)) { return getProfileOwnerOrDeviceOwnerLocked(caller).mUsbDataSignalingEnabled; } else { return isUsbDataSignalingEnabledInternalLocked(); @@ -18254,7 +18399,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public void setMinimumRequiredWifiSecurityLevel(int level) { final CallerIdentity caller = getCallerIdentity(); Preconditions.checkCallAuthorization( - isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller), + isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller), "Wi-Fi minimum security level can only be controlled by a device owner or " + "a profile owner on an organization-owned device."); @@ -18284,7 +18429,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public void setSsidAllowlist(List<String> ssids) { final CallerIdentity caller = getCallerIdentity(); Preconditions.checkCallAuthorization( - isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller), + isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller), "SSID allowlist can only be controlled by a device owner or " + "a profile owner on an organization-owned device."); @@ -18306,7 +18451,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public List<String> getSsidAllowlist() { final CallerIdentity caller = getCallerIdentity(); Preconditions.checkCallAuthorization( - isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller) + isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller) || isSystemUid(caller), "SSID allowlist can only be retrieved by a device owner or " + "a profile owner on an organization-owned device or a system app."); @@ -18322,7 +18467,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public void setSsidDenylist(List<String> ssids) { final CallerIdentity caller = getCallerIdentity(); Preconditions.checkCallAuthorization( - isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller), + isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller), "SSID denylist can only be controlled by a device owner or " + "a profile owner on an organization-owned device."); @@ -18344,7 +18489,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public List<String> getSsidDenylist() { final CallerIdentity caller = getCallerIdentity(); Preconditions.checkCallAuthorization( - isDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller) + isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller) || isSystemUid(caller), "SSID denylist can only be retrieved by a device owner or " + "a profile owner on an organization-owned device or a system app."); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java b/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java index 685cf0580a48..598f9e88ac6d 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/OverlayPackagesProvider.java @@ -34,6 +34,7 @@ import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.admin.DeviceAdminReceiver; import android.app.admin.DevicePolicyManager; +import android.app.role.RoleManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -41,6 +42,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.os.Binder; import android.util.ArraySet; import android.util.IndentingPrintWriter; import android.view.inputmethod.InputMethodInfo; @@ -91,6 +93,8 @@ public class OverlayPackagesProvider { List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId); String getActiveApexPackageNameContainingPackage(String packageName); + + String getDeviceManagerRoleHolderPackageName(Context context); } private static final class DefaultInjector implements Injector { @@ -104,6 +108,19 @@ public class OverlayPackagesProvider { public String getActiveApexPackageNameContainingPackage(String packageName) { return ApexManager.getInstance().getActiveApexPackageNameContainingPackage(packageName); } + + @Override + public String getDeviceManagerRoleHolderPackageName(Context context) { + return Binder.withCleanCallingIdentity(() -> { + RoleManager roleManager = context.getSystemService(RoleManager.class); + List<String> roleHolders = + roleManager.getRoleHolders(RoleManager.ROLE_DEVICE_MANAGER); + if (roleHolders.isEmpty()) { + return null; + } + return roleHolders.get(0); + }); + } } @VisibleForTesting @@ -142,9 +159,20 @@ public class OverlayPackagesProvider { nonRequiredApps.addAll(getDisallowedApps(provisioningAction)); nonRequiredApps.removeAll( getRequiredAppsMainlineModules(nonRequiredApps, provisioningAction)); + nonRequiredApps.removeAll(getDeviceManagerRoleHolders()); return nonRequiredApps; } + private Set<String> getDeviceManagerRoleHolders() { + HashSet<String> result = new HashSet<>(); + String deviceManagerRoleHolderPackageName = + mInjector.getDeviceManagerRoleHolderPackageName(mContext); + if (deviceManagerRoleHolderPackageName != null) { + result.add(deviceManagerRoleHolderPackageName); + } + return result; + } + /** * Returns a subset of {@code packageNames} whose packages are mainline modules declared as * required apps via their app metadata. diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java new file mode 100644 index 000000000000..ec884f59014f --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricContextProviderTest.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2022 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.biometrics.log; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.same; +import static org.mockito.Mockito.verify; + +import android.hardware.biometrics.IBiometricContextListener; +import android.hardware.biometrics.common.OperationContext; +import android.os.RemoteException; +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; + +import com.android.internal.statusbar.IStatusBarService; + +import com.google.common.collect.ImmutableList; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; + +@Presubmit +@SmallTest +public class BiometricContextProviderTest { + + @Rule + public final MockitoRule mockito = MockitoJUnit.rule(); + + @Mock + private IStatusBarService mStatusBarService; + + private OperationContext mOpContext = new OperationContext(); + private IBiometricContextListener mListener; + private BiometricContextProvider mProvider; + + @Before + public void setup() throws RemoteException { + mProvider = new BiometricContextProvider(mStatusBarService, null /* handler */); + ArgumentCaptor<IBiometricContextListener> captor = + ArgumentCaptor.forClass(IBiometricContextListener.class); + verify(mStatusBarService).setBiometicContextListener(captor.capture()); + mListener = captor.getValue(); + } + + @Test + public void testIsAoD() throws RemoteException { + mListener.onDozeChanged(true); + assertThat(mProvider.isAoD()).isTrue(); + mListener.onDozeChanged(false); + assertThat(mProvider.isAoD()).isFalse(); + } + + @Test + public void testSubscribesToAoD() throws RemoteException { + final List<Boolean> expected = ImmutableList.of(true, false, true, true, false); + final List<Boolean> actual = new ArrayList<>(); + + mProvider.subscribe(mOpContext, ctx -> { + assertThat(ctx).isSameInstanceAs(mOpContext); + actual.add(ctx.isAoD); + }); + + for (boolean v : expected) { + mListener.onDozeChanged(v); + } + + assertThat(actual).containsExactlyElementsIn(expected).inOrder(); + } + + @Test + public void testUnsubscribes() throws RemoteException { + final Consumer<OperationContext> emptyConsumer = mock(Consumer.class); + mProvider.subscribe(mOpContext, emptyConsumer); + mProvider.unsubscribe(mOpContext); + + mListener.onDozeChanged(true); + + final Consumer<OperationContext> nonEmptyConsumer = mock(Consumer.class); + mProvider.subscribe(mOpContext, nonEmptyConsumer); + mListener.onDozeChanged(false); + mProvider.unsubscribe(mOpContext); + mListener.onDozeChanged(true); + + verify(emptyConsumer, never()).accept(any()); + verify(nonEmptyConsumer).accept(same(mOpContext)); + } +} diff --git a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java index 2b72fabe7cca..b0eb810b5da9 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/log/BiometricLoggerTest.java @@ -44,7 +44,8 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; @Presubmit @SmallTest @@ -55,6 +56,9 @@ public class BiometricLoggerTest { private static final int DEFAULT_CLIENT = BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT; @Rule + public final MockitoRule mockito = MockitoJUnit.rule(); + + @Rule public TestableContext mContext = new TestableContext( InstrumentationRegistry.getInstrumentation().getContext()); @Mock @@ -68,8 +72,6 @@ public class BiometricLoggerTest { @Before public void setUp() { - MockitoAnnotations.initMocks(this); - mContext.addMockSystemService(SensorManager.class, mSensorManager); when(mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT)).thenReturn( new Sensor(new InputSensorInfo("", "", 0, 0, Sensor.TYPE_LIGHT, 0, 0, 0, 0, 0, 0, diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java index 9bb722f8a853..2d9d868f2f74 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/AcquisitionClientTest.java @@ -22,6 +22,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -34,6 +35,9 @@ import android.platform.test.annotations.Presubmit; import androidx.annotation.NonNull; import androidx.test.filters.SmallTest; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; + import org.junit.Before; import org.junit.Test; import org.mockito.Mock; @@ -92,9 +96,8 @@ public class AcquisitionClientTest { @NonNull Supplier<Object> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter callback) { super(context, lazyDaemon, token, callback, 0 /* userId */, "Test", 0 /* cookie */, - TEST_SENSOR_ID /* sensorId */, true /* shouldVibrate */, 0 /* statsModality */, - 0 /* statsAction */, - 0 /* statsClient */); + TEST_SENSOR_ID /* sensorId */, true /* shouldVibrate */, + mock(BiometricLogger.class), mock(BiometricContext.class)); } @Override diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BaseClientMonitorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BaseClientMonitorTest.java index 51d234d5afeb..8e6d90c839d8 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BaseClientMonitorTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BaseClientMonitorTest.java @@ -30,6 +30,7 @@ import android.platform.test.annotations.Presubmit; import androidx.annotation.NonNull; import androidx.test.filters.SmallTest; +import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricLogger; import org.junit.Before; @@ -49,6 +50,8 @@ public class BaseClientMonitorTest { @Mock private BiometricLogger mLogger; @Mock + private BiometricContext mBiometricContext; + @Mock private ClientMonitorCallback mCallback; private TestClientMonitor mClientMonitor; @@ -109,7 +112,7 @@ public class BaseClientMonitorTest { TestClientMonitor() { super(mContext, mToken, mListener, 9 /* userId */, "foo" /* owner */, 2 /* cookie */, - 5 /* sensorId */, mLogger); + 5 /* sensorId */, mLogger, mBiometricContext); } @Override diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java index 8751cf3810bf..64be56906d4b 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java @@ -36,6 +36,9 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -54,7 +57,8 @@ public class BiometricSchedulerOperationTest { public abstract static class InterruptableMonitor<T> extends HalClientMonitor<T> implements Interruptable { public InterruptableMonitor() { - super(null, null, null, null, 0, null, 0, 0, 0, 0, 0); + super(null, null, null, null, 0, null, 0, 0, + mock(BiometricLogger.class), mock(BiometricContext.class)); } } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java index ecd9abcb80e6..0fa2b41e8b32 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java @@ -51,6 +51,8 @@ import androidx.annotation.Nullable; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.nano.BiometricSchedulerProto; import com.android.server.biometrics.nano.BiometricsProto; @@ -526,10 +528,10 @@ public class BiometricSchedulerTest { @NonNull ClientMonitorCallbackConverter listener) { super(context, lazyDaemon, token, listener, 0 /* targetUserId */, 0 /* operationId */, false /* restricted */, TAG, 1 /* cookie */, false /* requireConfirmation */, - TEST_SENSOR_ID, true /* isStrongBiometric */, 0 /* statsModality */, - 0 /* statsClient */, null /* taskStackListener */, mock(LockoutTracker.class), - false /* isKeyguard */, true /* shouldVibrate */, - false /* isKeyguardBypassEnabled */); + TEST_SENSOR_ID, mock(BiometricLogger.class), mock(BiometricContext.class), + true /* isStrongBiometric */, null /* taskStackListener */, + mock(LockoutTracker.class), false /* isKeyguard */, + true /* shouldVibrate */, false /* isKeyguardBypassEnabled */); } @Override @@ -573,8 +575,9 @@ public class BiometricSchedulerTest { @NonNull ClientMonitorCallbackConverter listener) { super(context, lazyDaemon, token, listener, 0 /* userId */, new byte[69], "test" /* owner */, mock(BiometricUtils.class), - 5 /* timeoutSec */, 0 /* statsModality */, TEST_SENSOR_ID, - true /* shouldVibrate */); + 5 /* timeoutSec */, TEST_SENSOR_ID, + true /* shouldVibrate */, + mock(BiometricLogger.class), mock(BiometricContext.class)); } @Override @@ -613,8 +616,8 @@ public class BiometricSchedulerTest { TestHalClientMonitor(@NonNull Context context, @NonNull IBinder token, @NonNull Supplier<Object> lazyDaemon, int cookie, int protoEnum) { super(context, lazyDaemon, token /* token */, null /* listener */, 0 /* userId */, - TAG, cookie, TEST_SENSOR_ID, 0 /* statsModality */, - 0 /* statsAction */, 0 /* statsClient */); + TAG, cookie, TEST_SENSOR_ID, + mock(BiometricLogger.class), mock(BiometricContext.class)); mProtoEnum = protoEnum; } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/CompositeCallbackTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/CompositeCallbackTest.java index 587bb600f409..092ca191acba 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/CompositeCallbackTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/CompositeCallbackTest.java @@ -64,7 +64,7 @@ public class CompositeCallbackTest { ClientMonitorCompositeCallback callback = new ClientMonitorCompositeCallback(callbacks); callback.onClientStarted(mClientMonitor); - final InOrder order = inOrder(expected); + final InOrder order = inOrder((Object[]) expected); for (ClientMonitorCallback cb : expected) { order.verify(cb).onClientStarted(eq(mClientMonitor)); } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java index 30777cd79a0c..52eee9a55cc7 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java @@ -41,6 +41,9 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.test.filters.SmallTest; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -66,6 +69,10 @@ public class UserAwareBiometricSchedulerTest { private Context mContext; @Mock private IBiometricService mBiometricService; + @Mock + private BiometricLogger mBiometricLogger; + @Mock + private BiometricContext mBiometricContext; private TestUserStartedCallback mUserStartedCallback = new TestUserStartedCallback(); private TestUserStoppedCallback mUserStoppedCallback = new TestUserStoppedCallback(); @@ -88,7 +95,8 @@ public class UserAwareBiometricSchedulerTest { @Override public StopUserClient<?> getStopUserClient(int userId) { return new TestStopUserClient(mContext, Object::new, mToken, userId, - TEST_SENSOR_ID, mUserStoppedCallback); + TEST_SENSOR_ID, mBiometricLogger, mBiometricContext, + mUserStoppedCallback); } @NonNull @@ -96,7 +104,8 @@ public class UserAwareBiometricSchedulerTest { public StartUserClient<?, ?> getStartUserClient(int newUserId) { mStartUserClientCount++; return new TestStartUserClient(mContext, Object::new, mToken, newUserId, - TEST_SENSOR_ID, mUserStartedCallback, mStartOperationsFinish); + TEST_SENSOR_ID, mBiometricLogger, mBiometricContext, + mUserStartedCallback, mStartOperationsFinish); } }, CoexCoordinator.getInstance()); @@ -226,8 +235,10 @@ public class UserAwareBiometricSchedulerTest { private static class TestStopUserClient extends StopUserClient<Object> { public TestStopUserClient(@NonNull Context context, @NonNull Supplier<Object> lazyDaemon, @Nullable IBinder token, int userId, - int sensorId, @NonNull UserStoppedCallback callback) { - super(context, lazyDaemon, token, userId, sensorId, callback); + int sensorId, @NonNull BiometricLogger logger, + @NonNull BiometricContext biometricContext, + @NonNull UserStoppedCallback callback) { + super(context, lazyDaemon, token, userId, sensorId, logger, biometricContext, callback); } @Override @@ -254,8 +265,10 @@ public class UserAwareBiometricSchedulerTest { public TestStartUserClient(@NonNull Context context, @NonNull Supplier<Object> lazyDaemon, @Nullable IBinder token, int userId, - int sensorId, @NonNull UserStartedCallback<Object> callback, boolean shouldFinish) { - super(context, lazyDaemon, token, userId, sensorId, callback); + int sensorId, @NonNull BiometricLogger logger, + @NonNull BiometricContext biometricContext, + @NonNull UserStartedCallback<Object> callback, boolean shouldFinish) { + super(context, lazyDaemon, token, userId, sensorId, logger, biometricContext, callback); mShouldFinish = shouldFinish; } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java new file mode 100644 index 000000000000..25585dd0cfc3 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClientTest.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2022 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.biometrics.sensors.face.aidl; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.hardware.biometrics.common.OperationContext; +import android.hardware.biometrics.face.ISession; +import android.os.IBinder; +import android.os.RemoteException; +import android.platform.test.annotations.Presubmit; +import android.testing.TestableContext; + +import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; +import com.android.server.biometrics.sensors.ClientMonitorCallback; +import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; +import com.android.server.biometrics.sensors.LockoutCache; +import com.android.server.biometrics.sensors.face.UsageStats; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +@Presubmit +@SmallTest +public class FaceAuthenticationClientTest { + + private static final int USER_ID = 12; + private static final long OP_ID = 32; + private static final boolean HAS_AOD = true; + + @Rule + public final TestableContext mContext = new TestableContext( + InstrumentationRegistry.getInstrumentation().getTargetContext(), null); + + @Mock + private ISession mHal; + @Mock + private IBinder mToken; + @Mock + private ClientMonitorCallbackConverter mClientMonitorCallbackConverter; + @Mock + private BiometricLogger mBiometricLogger; + @Mock + private BiometricContext mBiometricContext; + @Mock + private LockoutCache mLockoutCache; + @Mock + private UsageStats mUsageStats; + @Mock + private ClientMonitorCallback mCallback; + @Mock + private Sensor.HalSessionCallback mHalSessionCallback; + @Captor + private ArgumentCaptor<OperationContext> mOperationContextCaptor; + + @Rule + public final MockitoRule mockito = MockitoJUnit.rule(); + + @Before + public void setup() { + when(mBiometricContext.isAoD()).thenReturn(HAS_AOD); + } + + @Test + public void authNoContext_v1() throws RemoteException { + final FaceAuthenticationClient client = createClient(1); + client.start(mCallback); + + verify(mHal).authenticate(eq(OP_ID)); + verify(mHal, never()).authenticateWithContext(anyLong(), any()); + } + + @Test + public void authWithContext_v2() throws RemoteException { + final FaceAuthenticationClient client = createClient(2); + client.start(mCallback); + + verify(mHal).authenticateWithContext(eq(OP_ID), mOperationContextCaptor.capture()); + verify(mHal, never()).authenticate(anyLong()); + + final OperationContext opContext = mOperationContextCaptor.getValue(); + assertThat(opContext.isAoD).isEqualTo(HAS_AOD); + } + + private FaceAuthenticationClient createClient(int version) throws RemoteException { + when(mHal.getInterfaceVersion()).thenReturn(version); + + final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback); + return new FaceAuthenticationClient(mContext, () -> aidl, mToken, + 2 /* requestId */, mClientMonitorCallbackConverter, 5 /* targetUserId */, OP_ID, + false /* restricted */, "test-owner", 4 /* cookie */, + false /* requireConfirmation */, 9 /* sensorId */, + mBiometricLogger, mBiometricContext, true /* isStrongBiometric */, + mUsageStats, mLockoutCache, false /* allowBackgroundAuthentication */, + false /* isKeyguardBypassEnabled */, null /* sensorPrivacyManager */); + } +} diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java new file mode 100644 index 000000000000..6c72ebfd3674 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2022 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.biometrics.sensors.face.aidl; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.hardware.biometrics.common.OperationContext; +import android.hardware.biometrics.face.ISession; +import android.os.IBinder; +import android.os.RemoteException; +import android.platform.test.annotations.Presubmit; +import android.testing.TestableContext; + +import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; +import com.android.server.biometrics.sensors.ClientMonitorCallback; +import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +@Presubmit +@SmallTest +public class FaceDetectClientTest { + + private static final int USER_ID = 12; + private static final boolean HAS_AOD = true; + + @Rule + public final TestableContext mContext = new TestableContext( + InstrumentationRegistry.getInstrumentation().getTargetContext(), null); + + @Mock + private ISession mHal; + @Mock + private IBinder mToken; + @Mock + private ClientMonitorCallbackConverter mClientMonitorCallbackConverter; + @Mock + private BiometricLogger mBiometricLogger; + @Mock + private BiometricContext mBiometricContext; + @Mock + private ClientMonitorCallback mCallback; + @Mock + private Sensor.HalSessionCallback mHalSessionCallback; + @Captor + private ArgumentCaptor<OperationContext> mOperationContextCaptor; + + @Rule + public final MockitoRule mockito = MockitoJUnit.rule(); + + @Before + public void setup() { + when(mBiometricContext.isAoD()).thenReturn(HAS_AOD); + } + + @Test + public void detectNoContext_v1() throws RemoteException { + final FaceDetectClient client = createClient(1); + client.start(mCallback); + + verify(mHal).detectInteraction(); + verify(mHal, never()).detectInteractionWithContext(any()); + } + + @Test + public void detectWithContext_v2() throws RemoteException { + final FaceDetectClient client = createClient(2); + client.start(mCallback); + + verify(mHal).detectInteractionWithContext(mOperationContextCaptor.capture()); + verify(mHal, never()).detectInteraction(); + + final OperationContext opContext = mOperationContextCaptor.getValue(); + assertThat(opContext.isAoD).isEqualTo(HAS_AOD); + } + + private FaceDetectClient createClient(int version) throws RemoteException { + when(mHal.getInterfaceVersion()).thenReturn(version); + + final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback); + return new FaceDetectClient(mContext, () -> aidl, mToken, + 99 /* requestId */, mClientMonitorCallbackConverter, USER_ID, + "own-it", 5 /* sensorId */, mBiometricLogger, mBiometricContext, + false /* isStrongBiometric */, null /* sensorPrivacyManager */); + } +} diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java new file mode 100644 index 000000000000..22070e9579ee --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClientTest.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2022 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.biometrics.sensors.face.aidl; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyByte; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.hardware.biometrics.face.ISession; +import android.hardware.face.Face; +import android.os.IBinder; +import android.os.RemoteException; +import android.platform.test.annotations.Presubmit; +import android.testing.TestableContext; + +import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; +import com.android.server.biometrics.sensors.BiometricUtils; +import com.android.server.biometrics.sensors.ClientMonitorCallback; +import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; + +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +@Presubmit +@SmallTest +public class FaceEnrollClientTest { + + private static final byte[] HAT = new byte[69]; + private static final int USER_ID = 12; + + @Rule + public final TestableContext mContext = new TestableContext( + InstrumentationRegistry.getInstrumentation().getTargetContext(), null); + + @Mock + private ISession mHal; + @Mock + private IBinder mToken; + @Mock + private ClientMonitorCallbackConverter mClientMonitorCallbackConverter; + @Mock + private BiometricLogger mBiometricLogger; + @Mock + private BiometricContext mBiometricContext; + @Mock + private BiometricUtils<Face> mUtils; + @Mock + private ClientMonitorCallback mCallback; + @Mock + private Sensor.HalSessionCallback mHalSessionCallback; + + @Rule + public final MockitoRule mockito = MockitoJUnit.rule(); + + @Test + public void enrollNoContext_v1() throws RemoteException { + final FaceEnrollClient client = createClient(1); + client.start(mCallback); + + verify(mHal).enroll(any(), anyByte(), any(), any()); + verify(mHal, never()).enrollWithContext(any(), anyByte(), any(), any(), any()); + } + + @Test + public void enrollWithContext_v2() throws RemoteException { + final FaceEnrollClient client = createClient(2); + client.start(mCallback); + + verify(mHal).enrollWithContext(any(), anyByte(), any(), any(), any()); + verify(mHal, never()).enroll(any(), anyByte(), any(), any()); + } + + private FaceEnrollClient createClient(int version) throws RemoteException { + when(mHal.getInterfaceVersion()).thenReturn(version); + + final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback); + return new FaceEnrollClient(mContext, () -> aidl, mToken, mClientMonitorCallbackConverter, + USER_ID, HAT, "com.foo.bar", 44 /* requestId */, + mUtils, new int[0] /* disabledFeatures */, 6 /* timeoutSec */, + null /* previewSurface */, 8 /* sensorId */, + mBiometricLogger, mBiometricContext, 5 /* maxTemplatesPerUser */, + true /* debugConsent */); + } +} diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java index 61e4776e9c50..b60324e88f15 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java @@ -32,6 +32,8 @@ import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.BiometricScheduler; import com.android.server.biometrics.sensors.CoexCoordinator; import com.android.server.biometrics.sensors.LockoutCache; @@ -66,6 +68,10 @@ public class SensorTest { private Sensor.HalSessionCallback.Callback mHalSessionCallback; @Mock private LockoutResetDispatcher mLockoutResetDispatcher; + @Mock + private BiometricLogger mBiometricLogger; + @Mock + private BiometricContext mBiometricContext; private final TestLooper mLooper = new TestLooper(); private final LockoutCache mLockoutCache = new LockoutCache(); @@ -102,7 +108,8 @@ public class SensorTest { mScheduler.scheduleClientMonitor(new FaceResetLockoutClient(mContext, () -> new AidlSession(1, mSession, USER_ID, mHalCallback), - USER_ID, TAG, SENSOR_ID, HAT, mLockoutCache, mLockoutResetDispatcher)); + USER_ID, TAG, SENSOR_ID, mBiometricLogger, mBiometricContext, + HAT, mLockoutCache, mLockoutResetDispatcher)); mLooper.dispatchAll(); verifyNotLocked(); diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java index 931fad14888e..ec083294c0bd 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/FaceGenerateChallengeClientTest.java @@ -34,6 +34,8 @@ import android.platform.test.annotations.Presubmit; import androidx.test.core.app.ApplicationProvider; import androidx.test.filters.SmallTest; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; @@ -62,6 +64,10 @@ public class FaceGenerateChallengeClientTest { private IFaceServiceReceiver mOtherReceiver; @Mock private ClientMonitorCallback mMonitorCallback; + @Mock + private BiometricLogger mBiometricLogger; + @Mock + private BiometricContext mBiometricContext; private FaceGenerateChallengeClient mClient; @@ -75,7 +81,7 @@ public class FaceGenerateChallengeClientTest { mClient = new FaceGenerateChallengeClient(mContext, () -> mIBiometricsFace, new Binder(), new ClientMonitorCallbackConverter(mClientReceiver), USER_ID, - TAG, SENSOR_ID, START_TIME); + TAG, SENSOR_ID, mBiometricLogger, mBiometricContext , START_TIME); } @Test diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java new file mode 100644 index 000000000000..5c360e9147c1 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java @@ -0,0 +1,265 @@ +/* + * Copyright (C) 2022 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.biometrics.sensors.fingerprint.aidl; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyFloat; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.anyLong; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.hardware.biometrics.common.OperationContext; +import android.hardware.biometrics.fingerprint.ISession; +import android.hardware.biometrics.fingerprint.PointerContext; +import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; +import android.hardware.fingerprint.ISidefpsController; +import android.hardware.fingerprint.IUdfpsOverlayController; +import android.os.IBinder; +import android.os.RemoteException; +import android.platform.test.annotations.Presubmit; +import android.testing.TestableContext; + +import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; +import com.android.server.biometrics.log.CallbackWithProbe; +import com.android.server.biometrics.log.Probe; +import com.android.server.biometrics.sensors.ClientMonitorCallback; +import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; +import com.android.server.biometrics.sensors.LockoutCache; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.function.Consumer; + +@Presubmit +@SmallTest +public class FingerprintAuthenticationClientTest { + + private static final int USER_ID = 8; + private static final long OP_ID = 7; + private static final boolean HAS_AOD = true; + private static final int POINTER_ID = 0; + private static final int TOUCH_X = 8; + private static final int TOUCH_Y = 20; + private static final float TOUCH_MAJOR = 4.4f; + private static final float TOUCH_MINOR = 5.5f; + + @Rule + public final TestableContext mContext = new TestableContext( + InstrumentationRegistry.getInstrumentation().getTargetContext(), null); + + @Mock + private ISession mHal; + @Mock + private IBinder mToken; + @Mock + private ClientMonitorCallbackConverter mClientMonitorCallbackConverter; + @Mock + private BiometricLogger mBiometricLogger; + @Mock + private BiometricContext mBiometricContext; + @Mock + private LockoutCache mLockoutCache; + @Mock + private IUdfpsOverlayController mUdfpsOverlayController; + @Mock + private ISidefpsController mSideFpsController; + @Mock + private FingerprintSensorPropertiesInternal mSensorProps; + @Mock + private ClientMonitorCallback mCallback; + @Mock + private Sensor.HalSessionCallback mHalSessionCallback; + @Mock + private Probe mLuxProbe; + @Captor + private ArgumentCaptor<OperationContext> mOperationContextCaptor; + @Captor + private ArgumentCaptor<PointerContext> mPointerContextCaptor; + + @Rule + public final MockitoRule mockito = MockitoJUnit.rule(); + + @Before + public void setup() { + when(mBiometricLogger.createALSCallback(anyBoolean())).thenAnswer(i -> + new CallbackWithProbe<>(mLuxProbe, i.getArgument(0))); + when(mBiometricContext.isAoD()).thenReturn(HAS_AOD); + } + + @Test + public void authNoContext_v1() throws RemoteException { + final FingerprintAuthenticationClient client = createClient(1); + client.start(mCallback); + + verify(mHal).authenticate(eq(OP_ID)); + verify(mHal, never()).authenticateWithContext(anyLong(), any()); + } + + @Test + public void authWithContext_v2() throws RemoteException { + final FingerprintAuthenticationClient client = createClient(2); + client.start(mCallback); + + verify(mHal).authenticateWithContext(eq(OP_ID), mOperationContextCaptor.capture()); + verify(mHal, never()).authenticate(anyLong()); + + final OperationContext opContext = mOperationContextCaptor.getValue(); + assertThat(opContext.isAoD).isEqualTo(HAS_AOD); + } + + @Test + public void pointerUp_v1() throws RemoteException { + final FingerprintAuthenticationClient client = createClient(1); + client.start(mCallback); + client.onPointerUp(); + + verify(mHal).onPointerUp(eq(POINTER_ID)); + verify(mHal, never()).onPointerUpWithContext(any()); + } + + @Test + public void pointerDown_v1() throws RemoteException { + final FingerprintAuthenticationClient client = createClient(1); + client.start(mCallback); + client.onPointerDown(TOUCH_X, TOUCH_Y, TOUCH_MAJOR, TOUCH_MINOR); + + verify(mHal).onPointerDown(eq(0), + eq(TOUCH_X), eq(TOUCH_Y), eq(TOUCH_MAJOR), eq(TOUCH_MINOR)); + verify(mHal, never()).onPointerDownWithContext(any()); + } + + @Test + public void pointerUpWithContext_v2() throws RemoteException { + final FingerprintAuthenticationClient client = createClient(2); + client.start(mCallback); + client.onPointerUp(); + + verify(mHal).onPointerUpWithContext(mPointerContextCaptor.capture()); + verify(mHal, never()).onPointerUp(eq(POINTER_ID)); + + final PointerContext pContext = mPointerContextCaptor.getValue(); + assertThat(pContext.pointerId).isEqualTo(POINTER_ID); + } + + @Test + public void pointerDownWithContext_v2() throws RemoteException { + final FingerprintAuthenticationClient client = createClient(2); + client.start(mCallback); + client.onPointerDown(TOUCH_X, TOUCH_Y, TOUCH_MAJOR, TOUCH_MINOR); + + verify(mHal).onPointerDownWithContext(mPointerContextCaptor.capture()); + verify(mHal, never()).onPointerDown(anyInt(), anyInt(), anyInt(), anyFloat(), anyFloat()); + + final PointerContext pContext = mPointerContextCaptor.getValue(); + assertThat(pContext.pointerId).isEqualTo(POINTER_ID); + } + + @Test + public void luxProbeWhenFingerDown() throws RemoteException { + final FingerprintAuthenticationClient client = createClient(); + client.start(mCallback); + + client.onPointerDown(TOUCH_X, TOUCH_Y, TOUCH_MAJOR, TOUCH_MINOR); + verify(mLuxProbe).enable(); + + client.onAcquired(2, 0); + verify(mLuxProbe, never()).disable(); + + client.onPointerUp(); + verify(mLuxProbe).disable(); + + client.onPointerDown(TOUCH_X, TOUCH_Y, TOUCH_MAJOR, TOUCH_MINOR); + verify(mLuxProbe, times(2)).enable(); + } + + @Test + public void showHideOverlay_cancel() throws RemoteException { + showHideOverlay(c -> c.cancel()); + } + + @Test + public void showHideOverlay_stop() throws RemoteException { + showHideOverlay(c -> c.stopHalOperation()); + } + + @Test + public void showHideOverlay_error() throws RemoteException { + showHideOverlay(c -> c.onError(0, 0)); + verify(mCallback).onClientFinished(any(), eq(false)); + } + + @Test + public void showHideOverlay_lockout() throws RemoteException { + showHideOverlay(c -> c.onLockoutTimed(5000)); + } + + @Test + public void showHideOverlay_lockoutPerm() throws RemoteException { + showHideOverlay(c -> c.onLockoutPermanent()); + } + + private void showHideOverlay(Consumer<FingerprintAuthenticationClient> block) + throws RemoteException { + final FingerprintAuthenticationClient client = createClient(); + + client.start(mCallback); + + verify(mUdfpsOverlayController).showUdfpsOverlay(anyInt(), anyInt(), any()); + verify(mSideFpsController).show(anyInt(), anyInt()); + + block.accept(client); + + verify(mUdfpsOverlayController).hideUdfpsOverlay(anyInt()); + verify(mSideFpsController).hide(anyInt()); + } + + private FingerprintAuthenticationClient createClient() throws RemoteException { + return createClient(100); + } + + private FingerprintAuthenticationClient createClient(int version) throws RemoteException { + when(mHal.getInterfaceVersion()).thenReturn(version); + + final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback); + return new FingerprintAuthenticationClient(mContext, () -> aidl, mToken, + 2 /* requestId */, mClientMonitorCallbackConverter, 5 /* targetUserId */, OP_ID, + false /* restricted */, "test-owner", 4 /* cookie */, false /* requireConfirmation */, + 9 /* sensorId */, mBiometricLogger, mBiometricContext, + true /* isStrongBiometric */, + null /* taskStackListener */, mLockoutCache, + mUdfpsOverlayController, mSideFpsController, + false /* allowBackgroundAuthentication */, mSensorProps); + } +} diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java new file mode 100644 index 000000000000..295fe4766a85 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClientTest.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2022 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.biometrics.sensors.fingerprint.aidl; + +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.hardware.biometrics.common.OperationContext; +import android.hardware.biometrics.fingerprint.ISession; +import android.hardware.fingerprint.IUdfpsOverlayController; +import android.os.IBinder; +import android.os.RemoteException; +import android.platform.test.annotations.Presubmit; +import android.testing.TestableContext; + +import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; +import com.android.server.biometrics.sensors.ClientMonitorCallback; +import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +@Presubmit +@SmallTest +public class FingerprintDetectClientTest { + + private static final int USER_ID = 8; + private static final boolean HAS_AOD = true; + + @Rule + public final TestableContext mContext = new TestableContext( + InstrumentationRegistry.getInstrumentation().getTargetContext(), null); + + @Mock + private ISession mHal; + @Mock + private IBinder mToken; + @Mock + private ClientMonitorCallbackConverter mClientMonitorCallbackConverter; + @Mock + private BiometricLogger mBiometricLogger; + @Mock + private BiometricContext mBiometricContext; + @Mock + private IUdfpsOverlayController mUdfpsOverlayController; + @Mock + private ClientMonitorCallback mCallback; + @Mock + private Sensor.HalSessionCallback mHalSessionCallback; + @Captor + private ArgumentCaptor<OperationContext> mOperationContextCaptor; + + @Rule + public final MockitoRule mockito = MockitoJUnit.rule(); + + @Before + public void setup() { + when(mBiometricContext.isAoD()).thenReturn(HAS_AOD); + } + + @Test + public void detectNoContext_v1() throws RemoteException { + final FingerprintDetectClient client = createClient(1); + + client.start(mCallback); + + verify(mHal).detectInteraction(); + verify(mHal, never()).detectInteractionWithContext(any()); + } + + @Test + public void detectNoContext_v2() throws RemoteException { + final FingerprintDetectClient client = createClient(2); + + client.start(mCallback); + + verify(mHal).detectInteractionWithContext(mOperationContextCaptor.capture()); + verify(mHal, never()).detectInteraction(); + } + + private FingerprintDetectClient createClient(int version) throws RemoteException { + when(mHal.getInterfaceVersion()).thenReturn(version); + + final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback); + return new FingerprintDetectClient(mContext, () -> aidl, mToken, + 6 /* requestId */, mClientMonitorCallbackConverter, 2 /* userId */, + "a-test", 1 /* sensorId */, mBiometricLogger, mBiometricContext, + mUdfpsOverlayController, true /* isStrongBiometric */); + } +} diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java new file mode 100644 index 000000000000..7e44eab0a556 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2022 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.biometrics.sensors.fingerprint.aidl; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyFloat; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.hardware.biometrics.common.OperationContext; +import android.hardware.biometrics.fingerprint.ISession; +import android.hardware.biometrics.fingerprint.PointerContext; +import android.hardware.fingerprint.Fingerprint; +import android.hardware.fingerprint.FingerprintManager; +import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; +import android.hardware.fingerprint.ISidefpsController; +import android.hardware.fingerprint.IUdfpsOverlayController; +import android.os.IBinder; +import android.os.RemoteException; +import android.platform.test.annotations.Presubmit; +import android.testing.TestableContext; + +import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; +import com.android.server.biometrics.sensors.BiometricUtils; +import com.android.server.biometrics.sensors.ClientMonitorCallback; +import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; + +import org.junit.Rule; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.function.Consumer; + +@Presubmit +@SmallTest +public class FingerprintEnrollClientTest { + + private static final byte[] HAT = new byte[69]; + private static final int USER_ID = 8; + private static final int POINTER_ID = 0; + private static final int TOUCH_X = 8; + private static final int TOUCH_Y = 20; + private static final float TOUCH_MAJOR = 4.4f; + private static final float TOUCH_MINOR = 5.5f; + + @Rule + public final TestableContext mContext = new TestableContext( + InstrumentationRegistry.getInstrumentation().getTargetContext(), null); + + @Mock + private ISession mHal; + @Mock + private IBinder mToken; + @Mock + private ClientMonitorCallbackConverter mClientMonitorCallbackConverter; + @Mock + private BiometricLogger mBiometricLogger; + @Mock + private BiometricContext mBiometricContext; + @Mock + private BiometricUtils<Fingerprint> mBiometricUtils; + @Mock + private IUdfpsOverlayController mUdfpsOverlayController; + @Mock + private ISidefpsController mSideFpsController; + @Mock + private FingerprintSensorPropertiesInternal mSensorProps; + @Mock + private ClientMonitorCallback mCallback; + @Mock + private Sensor.HalSessionCallback mHalSessionCallback; + @Captor + private ArgumentCaptor<OperationContext> mOperationContextCaptor; + @Captor + private ArgumentCaptor<PointerContext> mPointerContextCaptor; + + @Rule + public final MockitoRule mockito = MockitoJUnit.rule(); + + @Test + public void enrollNoContext_v1() throws RemoteException { + final FingerprintEnrollClient client = createClient(1); + + client.start(mCallback); + + verify(mHal).enroll(any()); + verify(mHal, never()).enrollWithContext(any(), any()); + } + + @Test + public void enrollWithContext_v2() throws RemoteException { + final FingerprintEnrollClient client = createClient(2); + + client.start(mCallback); + + verify(mHal).enrollWithContext(any(), mOperationContextCaptor.capture()); + verify(mHal, never()).enroll(any()); + } + + @Test + public void pointerUp_v1() throws RemoteException { + final FingerprintEnrollClient client = createClient(1); + client.start(mCallback); + client.onPointerUp(); + + verify(mHal).onPointerUp(eq(POINTER_ID)); + verify(mHal, never()).onPointerUpWithContext(any()); + } + + @Test + public void pointerDown_v1() throws RemoteException { + final FingerprintEnrollClient client = createClient(1); + client.start(mCallback); + client.onPointerDown(TOUCH_X, TOUCH_Y, TOUCH_MAJOR, TOUCH_MINOR); + + verify(mHal).onPointerDown(eq(0), + eq(TOUCH_X), eq(TOUCH_Y), eq(TOUCH_MAJOR), eq(TOUCH_MINOR)); + verify(mHal, never()).onPointerDownWithContext(any()); + } + + @Test + public void pointerUpWithContext_v2() throws RemoteException { + final FingerprintEnrollClient client = createClient(2); + client.start(mCallback); + client.onPointerUp(); + + verify(mHal).onPointerUpWithContext(mPointerContextCaptor.capture()); + verify(mHal, never()).onPointerUp(eq(POINTER_ID)); + + final PointerContext pContext = mPointerContextCaptor.getValue(); + assertThat(pContext.pointerId).isEqualTo(POINTER_ID); + } + + @Test + public void pointerDownWithContext_v2() throws RemoteException { + final FingerprintEnrollClient client = createClient(2); + client.start(mCallback); + client.onPointerDown(TOUCH_X, TOUCH_Y, TOUCH_MAJOR, TOUCH_MINOR); + + verify(mHal).onPointerDownWithContext(mPointerContextCaptor.capture()); + verify(mHal, never()).onPointerDown(anyInt(), anyInt(), anyInt(), anyFloat(), anyFloat()); + + final PointerContext pContext = mPointerContextCaptor.getValue(); + assertThat(pContext.pointerId).isEqualTo(POINTER_ID); + } + + @Test + public void showHideOverlay_cancel() throws RemoteException { + showHideOverlay(c -> c.cancel()); + } + + @Test + public void showHideOverlay_stop() throws RemoteException { + showHideOverlay(c -> c.stopHalOperation()); + } + + @Test + public void showHideOverlay_error() throws RemoteException { + showHideOverlay(c -> c.onError(0, 0)); + verify(mCallback).onClientFinished(any(), eq(false)); + } + + @Test + public void showHideOverlay_result() throws RemoteException { + showHideOverlay(c -> c.onEnrollResult(new Fingerprint("", 1, 1), 0)); + } + + private void showHideOverlay(Consumer<FingerprintEnrollClient> block) + throws RemoteException { + final FingerprintEnrollClient client = createClient(); + + client.start(mCallback); + + verify(mUdfpsOverlayController).showUdfpsOverlay(anyInt(), anyInt(), any()); + verify(mSideFpsController).show(anyInt(), anyInt()); + + block.accept(client); + + verify(mUdfpsOverlayController).hideUdfpsOverlay(anyInt()); + verify(mSideFpsController).hide(anyInt()); + } + + private FingerprintEnrollClient createClient() throws RemoteException { + return createClient(500); + } + + private FingerprintEnrollClient createClient(int version) throws RemoteException { + when(mHal.getInterfaceVersion()).thenReturn(version); + + final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback); + return new FingerprintEnrollClient(mContext, () -> aidl, mToken, 6 /* requestId */, + mClientMonitorCallbackConverter, 0 /* userId */, + HAT, "owner", mBiometricUtils, 8 /* sensorId */, + mBiometricLogger, mBiometricContext, mSensorProps, mUdfpsOverlayController, + mSideFpsController, 6 /* maxTemplatesPerUser */, FingerprintManager.ENROLL_ENROLL); + } +} diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java index 8b7b484b8462..e1a4a2d9f969 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java @@ -32,6 +32,8 @@ import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.BiometricScheduler; import com.android.server.biometrics.sensors.CoexCoordinator; import com.android.server.biometrics.sensors.LockoutCache; @@ -66,6 +68,10 @@ public class SensorTest { private Sensor.HalSessionCallback.Callback mHalSessionCallback; @Mock private LockoutResetDispatcher mLockoutResetDispatcher; + @Mock + private BiometricLogger mLogger; + @Mock + private BiometricContext mBiometricContext; private final TestLooper mLooper = new TestLooper(); private final LockoutCache mLockoutCache = new LockoutCache(); @@ -102,7 +108,8 @@ public class SensorTest { mScheduler.scheduleClientMonitor(new FingerprintResetLockoutClient(mContext, () -> new AidlSession(1, mSession, USER_ID, mHalCallback), - USER_ID, TAG, SENSOR_ID, HAT, mLockoutCache, mLockoutResetDispatcher)); + USER_ID, TAG, SENSOR_ID, mLogger, mBiometricContext, HAT, mLockoutCache, + mLockoutResetDispatcher)); mLooper.dispatchAll(); verifyNotLocked(); diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java index ff1b6f66ef85..83fa7ac02503 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java @@ -17,17 +17,23 @@ package com.android.server.companion.virtual; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import android.hardware.display.DisplayManagerInternal; +import android.hardware.input.IInputManager; +import android.hardware.input.InputManager; import android.hardware.input.InputManagerInternal; import android.os.Binder; import android.os.IBinder; import android.os.IInputConstants; import android.platform.test.annotations.Presubmit; import android.view.Display; +import android.view.DisplayInfo; import androidx.test.runner.AndroidJUnit4; @@ -46,18 +52,31 @@ public class InputControllerTest { @Mock private InputManagerInternal mInputManagerInternalMock; @Mock + private DisplayManagerInternal mDisplayManagerInternalMock; + @Mock private InputController.NativeWrapper mNativeWrapperMock; + @Mock + private IInputManager mIInputManagerMock; private InputController mInputController; @Before - public void setUp() { + public void setUp() throws Exception { MockitoAnnotations.initMocks(this); doNothing().when(mInputManagerInternalMock).setVirtualMousePointerDisplayId(anyInt()); LocalServices.removeServiceForTest(InputManagerInternal.class); LocalServices.addService(InputManagerInternal.class, mInputManagerInternalMock); + final DisplayInfo displayInfo = new DisplayInfo(); + displayInfo.uniqueId = "uniqueId"; + doReturn(displayInfo).when(mDisplayManagerInternalMock).getDisplayInfo(anyInt()); + LocalServices.removeServiceForTest(DisplayManagerInternal.class); + LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock); + + InputManager.resetInstance(mIInputManagerMock); + doNothing().when(mIInputManagerMock).addUniqueIdAssociation(anyString(), anyString()); + doNothing().when(mIInputManagerMock).removeUniqueIdAssociation(anyString()); mInputController = new InputController(new Object(), mNativeWrapperMock); } diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java index e36263e247bf..ceb723a407f9 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java @@ -25,6 +25,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -57,6 +58,7 @@ import android.os.WorkSource; import android.platform.test.annotations.Presubmit; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import android.view.DisplayInfo; import android.view.KeyEvent; import androidx.test.InstrumentationRegistry; @@ -80,6 +82,8 @@ public class VirtualDeviceManagerServiceTest { private static final int DISPLAY_ID = 2; private static final int PRODUCT_ID = 10; private static final int VENDOR_ID = 5; + private static final String UNIQUE_ID = "uniqueid"; + private static final String PHYS = "phys"; private static final int HEIGHT = 1800; private static final int WIDTH = 900; private static final Binder BINDER = new Binder("binder"); @@ -116,6 +120,12 @@ public class VirtualDeviceManagerServiceTest { LocalServices.removeServiceForTest(InputManagerInternal.class); LocalServices.addService(InputManagerInternal.class, mInputManagerInternalMock); + final DisplayInfo displayInfo = new DisplayInfo(); + displayInfo.uniqueId = UNIQUE_ID; + doReturn(displayInfo).when(mDisplayManagerInternalMock).getDisplayInfo(anyInt()); + LocalServices.removeServiceForTest(DisplayManagerInternal.class); + LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock); + mContext = Mockito.spy(new ContextWrapper(InstrumentationRegistry.getTargetContext())); doNothing().when(mContext).enforceCallingOrSelfPermission( eq(Manifest.permission.CREATE_VIRTUAL_DEVICE), anyString()); @@ -274,7 +284,8 @@ public class VirtualDeviceManagerServiceTest { BINDER); assertWithMessage("Virtual keyboard should register fd when the display matches") .that(mInputController.mInputDeviceDescriptors).isNotEmpty(); - verify(mNativeWrapperMock).openUinputKeyboard(DEVICE_NAME, VENDOR_ID, PRODUCT_ID); + verify(mNativeWrapperMock).openUinputKeyboard(eq(DEVICE_NAME), eq(VENDOR_ID), + eq(PRODUCT_ID), anyString()); } @Test @@ -284,7 +295,8 @@ public class VirtualDeviceManagerServiceTest { BINDER); assertWithMessage("Virtual keyboard should register fd when the display matches") .that(mInputController.mInputDeviceDescriptors).isNotEmpty(); - verify(mNativeWrapperMock).openUinputMouse(DEVICE_NAME, VENDOR_ID, PRODUCT_ID); + verify(mNativeWrapperMock).openUinputMouse(eq(DEVICE_NAME), eq(VENDOR_ID), eq(PRODUCT_ID), + anyString()); } @Test @@ -294,8 +306,8 @@ public class VirtualDeviceManagerServiceTest { BINDER, new Point(WIDTH, HEIGHT)); assertWithMessage("Virtual keyboard should register fd when the display matches") .that(mInputController.mInputDeviceDescriptors).isNotEmpty(); - verify(mNativeWrapperMock).openUinputTouchscreen(DEVICE_NAME, VENDOR_ID, PRODUCT_ID, HEIGHT, - WIDTH); + verify(mNativeWrapperMock).openUinputTouchscreen(eq(DEVICE_NAME), eq(VENDOR_ID), + eq(PRODUCT_ID), anyString(), eq(HEIGHT), eq(WIDTH)); } @Test @@ -315,7 +327,7 @@ public class VirtualDeviceManagerServiceTest { final int action = VirtualKeyEvent.ACTION_UP; mInputController.mInputDeviceDescriptors.put(BINDER, new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 1, - /* displayId= */ 1)); + /* displayId= */ 1, PHYS)); mDeviceImpl.sendKeyEvent(BINDER, new VirtualKeyEvent.Builder().setKeyCode(keyCode) .setAction(action).build()); verify(mNativeWrapperMock).writeKeyEvent(fd, keyCode, action); @@ -340,7 +352,7 @@ public class VirtualDeviceManagerServiceTest { final int action = VirtualMouseButtonEvent.ACTION_BUTTON_PRESS; mInputController.mInputDeviceDescriptors.put(BINDER, new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2, - /* displayId= */ 1)); + /* displayId= */ 1, PHYS)); mInputController.mActivePointerDisplayId = 1; mDeviceImpl.sendButtonEvent(BINDER, new VirtualMouseButtonEvent.Builder() .setButtonCode(buttonCode) @@ -355,7 +367,7 @@ public class VirtualDeviceManagerServiceTest { final int action = VirtualMouseButtonEvent.ACTION_BUTTON_PRESS; mInputController.mInputDeviceDescriptors.put(BINDER, new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2, - /* displayId= */ 1)); + /* displayId= */ 1, PHYS)); assertThrows( IllegalStateException.class, () -> @@ -381,7 +393,7 @@ public class VirtualDeviceManagerServiceTest { final float y = 0.7f; mInputController.mInputDeviceDescriptors.put(BINDER, new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2, - /* displayId= */ 1)); + /* displayId= */ 1, PHYS)); mInputController.mActivePointerDisplayId = 1; mDeviceImpl.sendRelativeEvent(BINDER, new VirtualMouseRelativeEvent.Builder() .setRelativeX(x).setRelativeY(y).build()); @@ -395,7 +407,7 @@ public class VirtualDeviceManagerServiceTest { final float y = 0.7f; mInputController.mInputDeviceDescriptors.put(BINDER, new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2, - /* displayId= */ 1)); + /* displayId= */ 1, PHYS)); assertThrows( IllegalStateException.class, () -> @@ -422,7 +434,7 @@ public class VirtualDeviceManagerServiceTest { final float y = 1f; mInputController.mInputDeviceDescriptors.put(BINDER, new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2, - /* displayId= */ 1)); + /* displayId= */ 1, PHYS)); mInputController.mActivePointerDisplayId = 1; mDeviceImpl.sendScrollEvent(BINDER, new VirtualMouseScrollEvent.Builder() .setXAxisMovement(x) @@ -437,7 +449,7 @@ public class VirtualDeviceManagerServiceTest { final float y = 1f; mInputController.mInputDeviceDescriptors.put(BINDER, new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2, - /* displayId= */ 1)); + /* displayId= */ 1, PHYS)); assertThrows( IllegalStateException.class, () -> @@ -470,7 +482,7 @@ public class VirtualDeviceManagerServiceTest { final int action = VirtualTouchEvent.ACTION_UP; mInputController.mInputDeviceDescriptors.put(BINDER, new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 3, - /* displayId= */ 1)); + /* displayId= */ 1, PHYS)); mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder().setX(x) .setY(y).setAction(action).setPointerId(pointerId).setToolType(toolType).build()); verify(mNativeWrapperMock).writeTouchEvent(fd, pointerId, toolType, action, x, y, Float.NaN, @@ -489,7 +501,7 @@ public class VirtualDeviceManagerServiceTest { final float majorAxisSize = 10.0f; mInputController.mInputDeviceDescriptors.put(BINDER, new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 3, - /* displayId= */ 1)); + /* displayId= */ 1, PHYS)); mDeviceImpl.sendTouchEvent(BINDER, new VirtualTouchEvent.Builder().setX(x) .setY(y).setAction(action).setPointerId(pointerId).setToolType(toolType) .setPressure(pressure).setMajorAxisSize(majorAxisSize).build()); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 564c4e439d86..64ce6b282f17 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -28,6 +28,14 @@ import static android.app.admin.DevicePolicyManager.ID_TYPE_BASE_INFO; import static android.app.admin.DevicePolicyManager.ID_TYPE_IMEI; import static android.app.admin.DevicePolicyManager.ID_TYPE_MEID; import static android.app.admin.DevicePolicyManager.ID_TYPE_SERIAL; +import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK; +import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS; +import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME; +import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD; +import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NONE; +import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS; +import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW; +import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_SYSTEM_INFO; import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH; import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW; import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM; @@ -101,6 +109,7 @@ import android.app.admin.WifiSsidPolicy; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -138,6 +147,9 @@ import com.android.internal.widget.LockscreenCredential; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.devicepolicy.DevicePolicyManagerService.RestrictionsListener; +import com.android.server.pm.RestrictionsSet; +import com.android.server.pm.UserManagerInternal; +import com.android.server.pm.UserRestrictionsUtils; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; @@ -7764,6 +7776,296 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + public void testSetUserRestriction_financeDo_invalidRestrictions_restrictionNotSet() + throws Exception { + setDeviceOwner(); + dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED); + + for (String restriction : UserRestrictionsUtils.USER_RESTRICTIONS) { + if (!UserRestrictionsUtils.canFinancedDeviceOwnerChange(restriction)) { + assertNoDeviceOwnerRestrictions(); + assertExpectException(SecurityException.class, /* messageRegex= */ null, + () -> dpm.addUserRestriction(admin1, restriction)); + + verify(getServices().userManagerInternal, never()) + .setDevicePolicyUserRestrictions(anyInt(), any(), any(), anyBoolean()); + } + } + } + + @Test + public void testSetUserRestriction_financeDo_validRestrictions_setsRestriction() + throws Exception { + setDeviceOwner(); + dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED); + + for (String restriction : UserRestrictionsUtils.USER_RESTRICTIONS) { + if (UserRestrictionsUtils.canFinancedDeviceOwnerChange(restriction)) { + assertNoDeviceOwnerRestrictions(); + dpm.addUserRestriction(admin1, restriction); + + Bundle globalRestrictions = + dpms.getDeviceOwnerAdminLocked().getGlobalUserRestrictions( + UserManagerInternal.OWNER_TYPE_DEVICE_OWNER); + RestrictionsSet localRestrictions = new RestrictionsSet(); + localRestrictions.updateRestrictions( + UserHandle.USER_SYSTEM, + dpms.getDeviceOwnerAdminLocked().getLocalUserRestrictions( + UserManagerInternal.OWNER_TYPE_DEVICE_OWNER)); + verify(getServices().userManagerInternal) + .setDevicePolicyUserRestrictions(eq(UserHandle.USER_SYSTEM), + MockUtils.checkUserRestrictions(globalRestrictions), + MockUtils.checkUserRestrictions( + UserHandle.USER_SYSTEM, localRestrictions), + eq(true)); + reset(getServices().userManagerInternal); + + dpm.clearUserRestriction(admin1, restriction); + reset(getServices().userManagerInternal); + } + } + } + + @Test + public void testSetLockTaskFeatures_financeDo_validLockTaskFeatures_lockTaskFeaturesSet() + throws Exception { + int validLockTaskFeatures = LOCK_TASK_FEATURE_SYSTEM_INFO | LOCK_TASK_FEATURE_KEYGUARD + | LOCK_TASK_FEATURE_HOME | LOCK_TASK_FEATURE_GLOBAL_ACTIONS + | LOCK_TASK_FEATURE_NOTIFICATIONS; + setDeviceOwner(); + dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED); + + dpm.setLockTaskFeatures(admin1, validLockTaskFeatures); + + verify(getServices().iactivityTaskManager) + .updateLockTaskFeatures(eq(UserHandle.USER_SYSTEM), eq(validLockTaskFeatures)); + } + + @Test + public void testSetLockTaskFeatures_financeDo_invalidLockTaskFeatures_throwsException() + throws Exception { + int invalidLockTaskFeatures = LOCK_TASK_FEATURE_NONE | LOCK_TASK_FEATURE_OVERVIEW + | LOCK_TASK_FEATURE_HOME | LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK; + setDeviceOwner(); + dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED); + // Called during setup. + verify(getServices().iactivityTaskManager).updateLockTaskFeatures(anyInt(), anyInt()); + + assertExpectException(SecurityException.class, /* messageRegex= */ null, + () -> dpm.setLockTaskFeatures(admin1, invalidLockTaskFeatures)); + + verifyNoMoreInteractions(getServices().iactivityTaskManager); + } + + @Test + public void testIsUninstallBlocked_financeDo_success() throws Exception { + String packageName = "com.android.foo.package"; + setDeviceOwner(); + dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED); + when(getServices().ipackageManager.getBlockUninstallForUser( + eq(packageName), eq(UserHandle.USER_SYSTEM))) + .thenReturn(true); + + assertThat(dpm.isUninstallBlocked(admin1, packageName)).isTrue(); + } + + @Test + public void testSetUninstallBlocked_financeDo_success() throws Exception { + String packageName = "com.android.foo.package"; + setDeviceOwner(); + dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED); + + dpm.setUninstallBlocked(admin1, packageName, false); + + verify(getServices().ipackageManager) + .setBlockUninstallForUser(eq(packageName), eq(false), + eq(UserHandle.USER_SYSTEM)); + } + + @Test + public void testSetUserControlDisabledPackages_financeDo_success() throws Exception { + List<String> packages = new ArrayList<>(); + packages.add("com.android.foo.package"); + setDeviceOwner(); + dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED); + + dpm.setUserControlDisabledPackages(admin1, packages); + + verify(getServices().packageManagerInternal) + .setDeviceOwnerProtectedPackages(eq(admin1.getPackageName()), eq(packages)); + } + + @Test + public void testGetUserControlDisabledPackages_financeDo_success() throws Exception { + setDeviceOwner(); + dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED); + + assertThat(dpm.getUserControlDisabledPackages(admin1)).isEmpty(); + } + + @Test + public void testSetOrganizationName_financeDo_success() throws Exception { + String organizationName = "Test Organization"; + setDeviceOwner(); + dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED); + + dpm.setOrganizationName(admin1, organizationName); + + assertThat(dpm.getDeviceOwnerOrganizationName()).isEqualTo(organizationName); + } + + @Test + public void testSetShortSupportMessage_financeDo_success() throws Exception { + String supportMessage = "Test short support message"; + setDeviceOwner(); + dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED); + + dpm.setShortSupportMessage(admin1, supportMessage); + + assertThat(dpm.getShortSupportMessage(admin1)).isEqualTo(supportMessage); + } + + @Test + public void testIsBackupServiceEnabled_financeDo_success() throws Exception { + setDeviceOwner(); + dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED); + when(getServices().ibackupManager.isBackupServiceActive(eq(UserHandle.USER_SYSTEM))) + .thenReturn(true); + + assertThat(dpm.isBackupServiceEnabled(admin1)).isTrue(); + } + + @Test + public void testSetBackupServiceEnabled_financeDo_success() throws Exception { + setDeviceOwner(); + dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED); + + dpm.setBackupServiceEnabled(admin1, true); + + verify(getServices().ibackupManager) + .setBackupServiceActive(eq(UserHandle.USER_SYSTEM), eq(true)); + } + + @Test + public void testIsLockTaskPermitted_financeDo_success() throws Exception { + String packageName = "com.android.foo.package"; + mockPolicyExemptApps(packageName); + mockVendorPolicyExemptApps(); + setDeviceOwner(); + dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED); + + assertThat(dpm.isLockTaskPermitted(packageName)).isTrue(); + } + + @Test + public void testSetLockTaskPackages_financeDo_success() throws Exception { + String[] packages = {"com.android.foo.package"}; + mockEmptyPolicyExemptApps(); + setDeviceOwner(); + dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED); + + dpm.setLockTaskPackages(admin1, packages); + + verify(getServices().iactivityManager) + .updateLockTaskPackages(eq(UserHandle.USER_SYSTEM), eq(packages)); + } + + @Test + public void testAddPersistentPreferredActivity_financeDo_success() throws Exception { + IntentFilter filter = new IntentFilter(); + ComponentName target = new ComponentName(admin2.getPackageName(), "test.class"); + setDeviceOwner(); + dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED); + + dpm.addPersistentPreferredActivity(admin1, filter, target); + + verify(getServices().ipackageManager) + .addPersistentPreferredActivity(eq(filter), eq(target), eq(UserHandle.USER_SYSTEM)); + verify(getServices().ipackageManager) + .flushPackageRestrictionsAsUser(eq(UserHandle.USER_SYSTEM)); + } + + @Test + public void testClearPackagePersistentPreferredActvities_financeDo_success() throws Exception { + String packageName = admin2.getPackageName(); + setDeviceOwner(); + dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED); + + dpm.clearPackagePersistentPreferredActivities(admin1, packageName); + + verify(getServices().ipackageManager) + .clearPackagePersistentPreferredActivities( + eq(packageName), eq(UserHandle.USER_SYSTEM)); + verify(getServices().ipackageManager) + .flushPackageRestrictionsAsUser(eq(UserHandle.USER_SYSTEM)); + } + + @Test + public void testWipeData_financeDo_success() throws Exception { + setDeviceOwner(); + dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED); + when(getServices().userManager.getUserRestrictionSource( + UserManager.DISALLOW_FACTORY_RESET, + UserHandle.SYSTEM)) + .thenReturn(UserManager.RESTRICTION_SOURCE_DEVICE_OWNER); + when(mMockContext.getResources() + .getString(R.string.work_profile_deleted_description_dpm_wipe)) + .thenReturn("Test string"); + + dpm.wipeData(0); + + verifyRebootWipeUserData(/* wipeEuicc= */ false); + } + + @Test + public void testIsDeviceOwnerApp_financeDo_success() throws Exception { + setDeviceOwner(); + dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED); + + assertThat(dpm.isDeviceOwnerApp(admin1.getPackageName())).isTrue(); + } + + @Test + public void testClearDeviceOwnerApp_financeDo_success() throws Exception { + setDeviceOwner(); + dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED); + + dpm.clearDeviceOwnerApp(admin1.getPackageName()); + + assertThat(dpm.getDeviceOwnerComponentOnAnyUser()).isNull(); + assertThat(dpm.isAdminActiveAsUser(admin1, UserHandle.USER_SYSTEM)).isFalse(); + verify(mMockContext.spiedContext, times(2)) + .sendBroadcastAsUser( + MockUtils.checkIntentAction( + DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED), + eq(UserHandle.SYSTEM)); + } + + @Test + public void testSetPermissionGrantState_financeDo_notReadPhoneStatePermission_throwsException() + throws Exception { + setDeviceOwner(); + dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED); + + assertExpectException(SecurityException.class, /* messageRegex= */ null, + () -> dpm.setPermissionGrantState(admin1, admin1.getPackageName(), + permission.READ_CALENDAR, + DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED)); + } + + @Test + public void testSetPermissionGrantState_financeDo_grantPermissionToNonDeviceOwnerPackage_throwsException() + throws Exception { + setDeviceOwner(); + dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED); + + assertExpectException(SecurityException.class, /* messageRegex= */ null, + () -> dpm.setPermissionGrantState(admin1, "com.android.foo.package", + permission.READ_PHONE_STATE, + DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED)); + } + + @Test public void testSetUsbDataSignalingEnabled_noDeviceOwnerOrPoOfOrgOwnedDevice() { assertThrows(SecurityException.class, () -> dpm.setUsbDataSignalingEnabled(true)); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java index 15f3ed1be552..4cb46b4047b0 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java @@ -109,6 +109,14 @@ public class MockUtils { public static Bundle checkUserRestrictions(String... keys) { final Bundle expected = DpmTestUtils.newRestrictions( java.util.Objects.requireNonNull(keys)); + return checkUserRestrictions(expected); + } + + public static Bundle checkUserRestrictions(Bundle expected) { + return createUserRestrictionsBundleMatcher(expected); + } + + private static Bundle createUserRestrictionsBundleMatcher(Bundle expected) { final Matcher<Bundle> m = new BaseMatcher<Bundle>() { @Override public boolean matches(Object item) { @@ -129,6 +137,15 @@ public class MockUtils { public static RestrictionsSet checkUserRestrictions(int userId, String... keys) { final RestrictionsSet expected = DpmTestUtils.newRestrictions(userId, java.util.Objects.requireNonNull(keys)); + return checkUserRestrictions(userId, expected); + } + + public static RestrictionsSet checkUserRestrictions(int userId, RestrictionsSet expected) { + return createUserRestrictionsSetMatcher(userId, expected); + } + + private static RestrictionsSet createUserRestrictionsSetMatcher( + int userId, RestrictionsSet expected) { final Matcher<RestrictionsSet> m = new BaseMatcher<RestrictionsSet>() { @Override public boolean matches(Object item) { diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java index a8f24ce5e11d..533fb2d8d214 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/OverlayPackagesProviderTest.java @@ -26,6 +26,7 @@ import static android.app.admin.DevicePolicyManager.REQUIRED_APP_MANAGED_USER; import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.when; @@ -76,6 +77,7 @@ public class OverlayPackagesProviderTest { private static final ComponentName TEST_MDM_COMPONENT_NAME = new ComponentName( TEST_DPC_PACKAGE_NAME, "pc.package.name.DeviceAdmin"); private static final int TEST_USER_ID = 123; + private static final String ROLE_HOLDER_PACKAGE_NAME = "test.role.holder.package.name"; private @Mock Resources mResources; @@ -305,6 +307,26 @@ public class OverlayPackagesProviderTest { ACTION_PROVISION_MANAGED_PROFILE, "package1", "package2", "package3"); } + @Test + public void testGetNonRequiredApps_managedProfile_roleHolder_works() { + when(mInjector.getDeviceManagerRoleHolderPackageName(any())) + .thenReturn(ROLE_HOLDER_PACKAGE_NAME); + setSystemAppsWithLauncher("package1", "package2", ROLE_HOLDER_PACKAGE_NAME); + + verifyAppsAreNonRequired( + ACTION_PROVISION_MANAGED_PROFILE, "package1", "package2"); + } + + @Test + public void testGetNonRequiredApps_managedDevice_roleHolder_works() { + when(mInjector.getDeviceManagerRoleHolderPackageName(any())) + .thenReturn(ROLE_HOLDER_PACKAGE_NAME); + setSystemAppsWithLauncher("package1", "package2", ROLE_HOLDER_PACKAGE_NAME); + + verifyAppsAreNonRequired( + ACTION_PROVISION_MANAGED_DEVICE, "package1", "package2"); + } + private void setupRegularModulesWithManagedUser(String... regularModules) { setupRegularModulesWithMetadata(regularModules, REQUIRED_APP_MANAGED_USER); } diff --git a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java index b7af010103bc..6203c2f54f07 100644 --- a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java @@ -663,6 +663,30 @@ public class HighBrightnessModeControllerTest { eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_HDR_PLAYING)); } + @Test + public void tetHbmStats_LowRequestedBrightness() { + final HighBrightnessModeController hbmc = createDefaultHbm(new OffsettableClock()); + final int displayStatsId = mDisplayUniqueId.hashCode(); + + hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED); + hbmc.onAmbientLuxChange(MINIMUM_LUX + 1); + hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f); + advanceTime(0); + // verify in HBM sunlight mode + assertEquals(HIGH_BRIGHTNESS_MODE_SUNLIGHT, hbmc.getHighBrightnessMode()); + // verify HBM_ON_SUNLIGHT + verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId), + eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT), + eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN)); + + hbmcOnBrightnessChanged(hbmc, DEFAULT_MIN); + // verify HBM_SV_OFF due to LOW_REQUESTED_BRIGHTNESS + verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId), + eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF), + eq(FrameworkStatsLog + .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_LOW_REQUESTED_BRIGHTNESS)); + } + private void assertState(HighBrightnessModeController hbmc, float brightnessMin, float brightnessMax, int hbmMode) { assertEquals(brightnessMin, hbmc.getCurrentBrightnessMin(), EPSILON); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java index 5eed30be9279..91d4f8f63f38 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java @@ -67,6 +67,7 @@ public class NotificationComparatorTest extends UiServiceTestCase { @Mock Vibrator mVibrator; private final String callPkg = "com.android.server.notification"; + private final String sysPkg = "android"; private final int callUid = 10; private String smsPkg; private final int smsUid = 11; @@ -79,6 +80,7 @@ public class NotificationComparatorTest extends UiServiceTestCase { private NotificationRecord mRecordHighCall; private NotificationRecord mRecordHighCallStyle; private NotificationRecord mRecordEmail; + private NotificationRecord mRecordSystemMax; private NotificationRecord mRecordInlineReply; private NotificationRecord mRecordSms; private NotificationRecord mRecordStarredContact; @@ -191,6 +193,12 @@ public class NotificationComparatorTest extends UiServiceTestCase { mRecordContact.setContactAffinity(ValidateNotificationPeople.VALID_CONTACT); mRecordContact.setSystemImportance(NotificationManager.IMPORTANCE_DEFAULT); + Notification nSystemMax = new Notification.Builder(mContext, TEST_CHANNEL_ID).build(); + mRecordSystemMax = new NotificationRecord(mContext, new StatusBarNotification(sysPkg, + sysPkg, 1, "systemmax", uid2, uid2, nSystemMax, new UserHandle(userId), + "", 1244), getDefaultChannel()); + mRecordSystemMax.setSystemImportance(NotificationManager.IMPORTANCE_HIGH); + Notification n8 = new Notification.Builder(mContext, TEST_CHANNEL_ID).build(); mRecordUrgent = new NotificationRecord(mContext, new StatusBarNotification(pkg2, pkg2, 1, "urgent", uid2, uid2, n8, new UserHandle(userId), @@ -267,6 +275,7 @@ public class NotificationComparatorTest extends UiServiceTestCase { } expected.add(mRecordStarredContact); expected.add(mRecordContact); + expected.add(mRecordSystemMax); expected.add(mRecordEmail); expected.add(mRecordUrgent); expected.add(mNoMediaSessionMedia); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java index fa294dd61ea3..3b6718207c83 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java @@ -20,8 +20,8 @@ import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED; import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET; import static android.content.pm.PackageManager.GET_PERMISSIONS; -import static android.permission.PermissionManager.PERMISSION_GRANTED; -import static android.permission.PermissionManager.PERMISSION_SOFT_DENIED; +import static android.content.pm.PackageManager.PERMISSION_DENIED; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static com.google.common.truth.Truth.assertThat; @@ -130,13 +130,13 @@ public class PermissionHelperTest extends UiServiceTestCase { @Test public void testHasPermission() throws Exception { - when(mPmi.checkUidPermission(anyInt(), eq(Manifest.permission.POST_NOTIFICATIONS))) + when(mPmi.checkPostNotificationsPermissionGrantedOrLegacyAccess(anyInt())) .thenReturn(PERMISSION_GRANTED); assertThat(mPermissionHelper.hasPermission(1)).isTrue(); - when(mPmi.checkUidPermission(anyInt(), eq(Manifest.permission.POST_NOTIFICATIONS))) - .thenReturn(PERMISSION_SOFT_DENIED); + when(mPmi.checkPostNotificationsPermissionGrantedOrLegacyAccess(anyInt())) + .thenReturn(PERMISSION_DENIED); assertThat(mPermissionHelper.hasPermission(1)).isFalse(); } diff --git a/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java b/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java index 24fda17dbe79..e65501329cdf 100644 --- a/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java +++ b/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java @@ -69,6 +69,12 @@ class BroadcastResponseStatsTracker { private SparseArray<SparseArray<UserBroadcastResponseStats>> mUserResponseStats = new SparseArray<>(); + private AppStandbyInternal mAppStandby; + + BroadcastResponseStatsTracker(@NonNull AppStandbyInternal appStandby) { + mAppStandby = appStandby; + } + // TODO (206518114): Move all callbacks handling to a handler thread. void reportBroadcastDispatchEvent(int sourceUid, @NonNull String targetPackage, UserHandle targetUser, long idForResponseEvent, @@ -132,8 +138,7 @@ class BroadcastResponseStatsTracker { if (dispatchTimestampMs >= timestampMs) { continue; } - // TODO (206518114): Make the constant configurable. - if (elapsedDurationMs <= 2 * 60 * 1000) { + if (elapsedDurationMs <= mAppStandby.getBroadcastResponseWindowDurationMs()) { final BroadcastEvent broadcastEvent = broadcastEvents.valueAt(i); final BroadcastResponseStats responseStats = getBroadcastResponseStats(broadcastEvent); diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index e90d28a11e1d..6906f20f26c2 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -281,7 +281,7 @@ public class UsageStatsService extends SystemService implements mHandler = new H(BackgroundThread.get().getLooper()); mAppStandby = mInjector.getAppStandbyController(getContext()); - mResponseStatsTracker = new BroadcastResponseStatsTracker(); + mResponseStatsTracker = new BroadcastResponseStatsTracker(mAppStandby); mAppTimeLimit = new AppTimeLimitController(getContext(), new AppTimeLimitController.TimeLimitCallbackListener() { |