summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp1
-rw-r--r--StubLibraries.bp8
-rw-r--r--apex/Android.bp3
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java3
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobStore.java12
-rw-r--r--api/module-lib-current.txt90
-rw-r--r--core/java/android/annotation/SystemApi.java2
-rw-r--r--core/java/android/app/ActivityManagerInternal.java4
-rw-r--r--core/java/android/app/AppOpsManager.java9
-rw-r--r--core/java/android/app/PropertyInvalidatedCache.java120
-rw-r--r--core/java/android/bluetooth/BluetoothMapClient.java69
-rw-r--r--core/java/android/content/pm/PackageManager.java2
-rw-r--r--core/java/android/hardware/face/FaceManager.java2
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintManager.java70
-rw-r--r--core/java/android/hardware/fingerprint/IFingerprintService.aidl4
-rw-r--r--core/java/android/permission/PermissionControllerManager.java9
-rwxr-xr-xcore/java/android/provider/Settings.java7
-rw-r--r--core/java/android/view/InsetsAnimationControlImpl.java2
-rw-r--r--core/java/android/view/InsetsController.java10
-rw-r--r--core/java/android/view/InsetsState.java19
-rw-r--r--core/java/android/view/ViewRootImpl.java3
-rw-r--r--core/java/android/view/WindowInsets.java25
-rw-r--r--core/java/android/view/WindowManagerImpl.java2
-rw-r--r--core/java/com/android/internal/view/IInlineSuggestionsRequestCallback.aidl51
-rw-r--r--core/proto/android/providers/settings/secure.proto9
-rw-r--r--core/res/AndroidManifest.xml2
-rw-r--r--core/res/res/values-television/config.xml9
-rw-r--r--core/tests/bluetoothtests/AndroidManifest.xml6
-rw-r--r--core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java24
-rw-r--r--core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestRunner.java11
-rw-r--r--core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java156
-rw-r--r--core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java2
-rw-r--r--core/tests/coretests/src/android/view/InsetsControllerTest.java2
-rw-r--r--core/tests/coretests/src/android/view/InsetsStateTest.java36
-rw-r--r--data/etc/preinstalled-packages-platform-overlays.xml224
-rw-r--r--data/etc/privapp-permissions-platform.xml2
-rw-r--r--libs/hwui/hwui/MinikinSkia.cpp16
-rw-r--r--libs/hwui/jni/FontFamily.cpp12
-rw-r--r--libs/hwui/jni/fonts/Font.cpp12
-rw-r--r--location/java/android/location/package.html2
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java1
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java1
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java7
-rw-r--r--packages/SystemUI/Android.bp3
-rw-r--r--packages/SystemUI/proguard.flags4
-rw-r--r--packages/SystemUI/res/values/config_tv.xml9
-rw-r--r--packages/SystemUI/res/values/ids.xml2
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java27
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java70
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/LogLevel.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java91
-rw-r--r--packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java67
-rw-r--r--packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java131
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java6
-rw-r--r--packages/Tethering/common/TetheringLib/api/module-lib-current.txt88
-rw-r--r--packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java3
-rw-r--r--packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java1
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java24
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java10
-rw-r--r--services/core/java/com/android/server/am/AppErrors.java37
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java12
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java27
-rw-r--r--services/core/java/com/android/server/biometrics/Utils.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java6
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java35
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java43
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java681
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/EnrollClient.java13
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/LockoutResetDispatcher.java (renamed from services/core/java/com/android/server/biometrics/sensors/LockoutResetTracker.java)4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java28
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/Face10.java649
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticationClient.java8
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java13
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceService.java681
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/FaceUpdateActiveUserClient.java94
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java83
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java13
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java34
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/GestureAvailabilityDispatcher.java (renamed from services/core/java/com/android/server/biometrics/sensors/fingerprint/GestureAvailabilityTracker.java)4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/iris/IrisService.java82
-rw-r--r--services/core/java/com/android/server/clipboard/ClipboardService.java56
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java100
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java12
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java14
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java5
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java7
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java11
-rw-r--r--services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java23
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java6
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotController.java5
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java23
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricServiceBaseTest.java122
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java28
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java18
-rw-r--r--telephony/common/com/android/internal/telephony/TelephonyPermissions.java4
-rw-r--r--telephony/java/android/telephony/SmsManager.java4
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java86
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java34
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/SmsMessage.java86
-rw-r--r--tools/validatekeymaps/Android.bp1
-rw-r--r--wifi/TEST_MAPPING12
118 files changed, 2691 insertions, 2303 deletions
diff --git a/Android.bp b/Android.bp
index aae78a02e32e..b620634b485e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -568,6 +568,7 @@ java_library {
"libcore-platform-compat-config",
"services-platform-compat-config",
"documents-ui-compat-config",
+ "calendar-provider-compat-config",
],
libs: ["framework-updatable-stubs-module_libs_api"],
static_libs: [
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 56c568832ea9..decbb85efb7c 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -155,6 +155,9 @@ priv_apps = " " +
module_libs = " " +
" --show-annotation android.annotation.SystemApi\\(" +
"client=android.annotation.SystemApi.Client.MODULE_LIBRARIES" +
+ "\\)" +
+ " --show-for-stub-purposes-annotation android.annotation.SystemApi\\(" +
+ "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
"\\) "
droidstubs {
@@ -232,9 +235,8 @@ droidstubs {
}
/////////////////////////////////////////////////////////////////////
-// Following droidstubs modules are for extra APIs for modules.
-// The framework currently have two more API surfaces for modules:
-// @SystemApi(client=MODULE_APPS) and @SystemApi(client=MODULE_LIBRARIES)
+// Following droidstubs modules are for extra APIs for modules,
+// namely @SystemApi(client=MODULE_LIBRARIES) APIs.
/////////////////////////////////////////////////////////////////////
// TODO(b/146727827) remove the *-api module when we can teach metalava
diff --git a/apex/Android.bp b/apex/Android.bp
index 6d4dc8517237..410e21141f86 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -53,6 +53,9 @@ priv_apps = " " +
module_libs = " " +
" --show-annotation android.annotation.SystemApi\\(" +
"client=android.annotation.SystemApi.Client.MODULE_LIBRARIES" +
+ "\\)" +
+ " --show-for-stub-purposes-annotation android.annotation.SystemApi\\(" +
+ "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
"\\) "
mainline_service_stubs_args =
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 68b22c002d78..a67e928c8cbe 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -2287,7 +2287,8 @@ public class JobSchedulerService extends com.android.server.SystemService
}
// Everything else checked out so far, so this is the final yes/no check
- final boolean appIsBad = mActivityManagerInternal.isAppBad(service.applicationInfo);
+ final boolean appIsBad = mActivityManagerInternal.isAppBad(
+ service.processName, service.applicationInfo.uid);
if (DEBUG && appIsBad) {
Slog.i(TAG, "App is bad for " + job.toShortString() + " so not runnable");
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
index f2a55805d70a..7bd51b77a119 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
@@ -335,7 +335,7 @@ public final class JobStore {
Slog.v(TAG, "Scheduling persist of jobs to disk.");
}
mIoHandler.postDelayed(mWriteRunnable, JOB_PERSIST_DELAY);
- mWriteScheduled = mWriteInProgress = true;
+ mWriteScheduled = true;
}
}
}
@@ -353,7 +353,7 @@ public final class JobStore {
throw new IllegalStateException("An asynchronous write is already scheduled.");
}
- mWriteScheduled = mWriteInProgress = true;
+ mWriteScheduled = true;
mWriteRunnable.run();
}
}
@@ -369,7 +369,7 @@ public final class JobStore {
final long start = SystemClock.uptimeMillis();
final long end = start + maxWaitMillis;
synchronized (mWriteScheduleLock) {
- while (mWriteInProgress) {
+ while (mWriteScheduled || mWriteInProgress) {
final long now = SystemClock.uptimeMillis();
if (now >= end) {
// still not done and we've hit the end; failure
@@ -404,6 +404,12 @@ public final class JobStore {
// a bit of lock contention.
synchronized (mWriteScheduleLock) {
mWriteScheduled = false;
+ if (mWriteInProgress) {
+ // Another runnable is currently writing. Postpone this new write task.
+ maybeWriteStatusToDiskAsync();
+ return;
+ }
+ mWriteInProgress = true;
}
synchronized (mLock) {
// Clone the jobs so we can release the lock before writing.
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index 1afe4493e33c..1803be384389 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -21,24 +21,6 @@ package android.graphics {
package android.net {
- public final class TetheredClient implements android.os.Parcelable {
- ctor public TetheredClient(@NonNull android.net.MacAddress, @NonNull java.util.Collection<android.net.TetheredClient.AddressInfo>, int);
- method public int describeContents();
- method @NonNull public java.util.List<android.net.TetheredClient.AddressInfo> getAddresses();
- method @NonNull public android.net.MacAddress getMacAddress();
- method public int getTetheringType();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient> CREATOR;
- }
-
- public static final class TetheredClient.AddressInfo implements android.os.Parcelable {
- method public int describeContents();
- method @NonNull public android.net.LinkAddress getAddress();
- method @Nullable public String getHostname();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient.AddressInfo> CREATOR;
- }
-
public final class TetheringConstants {
field public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
field public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
@@ -58,69 +40,15 @@ package android.net {
method @NonNull public String[] getTetheringErroredIfaces();
method public boolean isTetheringSupported();
method public boolean isTetheringSupported(@NonNull String);
- method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback);
- method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener);
method public void requestLatestTetheringEntitlementResult(int, @NonNull android.os.ResultReceiver, boolean);
method @Deprecated public int setUsbTethering(boolean);
- method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
- method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(int, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
- method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering();
- method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int);
+ method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(int, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
method @Deprecated public int tether(@NonNull String);
- method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback);
method @Deprecated public int untether(@NonNull String);
- field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED";
- field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY";
- field public static final String EXTRA_ACTIVE_TETHER = "tetherArray";
- field public static final String EXTRA_AVAILABLE_TETHER = "availableArray";
- field public static final String EXTRA_ERRORED_TETHER = "erroredArray";
- field public static final int TETHERING_BLUETOOTH = 2; // 0x2
- field public static final int TETHERING_ETHERNET = 5; // 0x5
- field public static final int TETHERING_INVALID = -1; // 0xffffffff
- field public static final int TETHERING_NCM = 4; // 0x4
- field public static final int TETHERING_USB = 1; // 0x1
- field public static final int TETHERING_WIFI = 0; // 0x0
- field public static final int TETHERING_WIFI_P2P = 3; // 0x3
- field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc
- field public static final int TETHER_ERROR_DISABLE_FORWARDING_ERROR = 9; // 0x9
- field public static final int TETHER_ERROR_ENABLE_FORWARDING_ERROR = 8; // 0x8
- field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd
- field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa
- field public static final int TETHER_ERROR_INTERNAL_ERROR = 5; // 0x5
- field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf
- field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe
- field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0
- field public static final int TETHER_ERROR_PROVISIONING_FAILED = 11; // 0xb
- field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2
- field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6
- field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4
- field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1
- field public static final int TETHER_ERROR_UNKNOWN_TYPE = 16; // 0x10
- field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3
- field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7
- field public static final int TETHER_HARDWARE_OFFLOAD_FAILED = 2; // 0x2
- field public static final int TETHER_HARDWARE_OFFLOAD_STARTED = 1; // 0x1
- field public static final int TETHER_HARDWARE_OFFLOAD_STOPPED = 0; // 0x0
- }
-
- public static interface TetheringManager.OnTetheringEntitlementResultListener {
- method public void onTetheringEntitlementResult(int);
- }
-
- public static interface TetheringManager.StartTetheringCallback {
- method public default void onTetheringFailed(int);
- method public default void onTetheringStarted();
}
public static interface TetheringManager.TetheringEventCallback {
- method public default void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>);
- method public default void onError(@NonNull String, int);
- method public default void onOffloadStatusChanged(int);
method public default void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps);
- method public default void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>);
- method public default void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>);
- method public default void onTetheringSupported(boolean);
- method public default void onUpstreamChanged(@Nullable android.net.Network);
}
public static class TetheringManager.TetheringInterfaceRegexps {
@@ -129,22 +57,6 @@ package android.net {
method @NonNull public java.util.List<java.lang.String> getTetherableWifiRegexs();
}
- public static class TetheringManager.TetheringRequest {
- method @Nullable public android.net.LinkAddress getClientStaticIpv4Address();
- method @Nullable public android.net.LinkAddress getLocalIpv4Address();
- method public boolean getShouldShowEntitlementUi();
- method public int getTetheringType();
- method public boolean isExemptFromEntitlementCheck();
- }
-
- public static class TetheringManager.TetheringRequest.Builder {
- ctor public TetheringManager.TetheringRequest.Builder(int);
- method @NonNull public android.net.TetheringManager.TetheringRequest build();
- method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean);
- method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setShouldShowEntitlementUi(boolean);
- method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress);
- }
-
}
package android.os {
diff --git a/core/java/android/annotation/SystemApi.java b/core/java/android/annotation/SystemApi.java
index 4ac00983af13..a468439c8e74 100644
--- a/core/java/android/annotation/SystemApi.java
+++ b/core/java/android/annotation/SystemApi.java
@@ -23,7 +23,6 @@ import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PACKAGE;
import static java.lang.annotation.ElementType.TYPE;
-import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@@ -41,7 +40,6 @@ import java.lang.annotation.Target;
*/
@Target({TYPE, FIELD, METHOD, CONSTRUCTOR, ANNOTATION_TYPE, PACKAGE})
@Retention(RetentionPolicy.RUNTIME)
-@Repeatable(SystemApi.Container.class) // TODO(b/146727827): make this non-repeatable
public @interface SystemApi {
enum Client {
/**
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 505cfe7e6d28..7ba50cadd959 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -387,8 +387,8 @@ public abstract class ActivityManagerInternal {
/** Returns true if the given uid is the app in the foreground. */
public abstract boolean isAppForeground(int uid);
- /** Returns true if the given uid is currently marked 'bad' */
- public abstract boolean isAppBad(ApplicationInfo info);
+ /** Returns true if the given process name and uid is currently marked 'bad' */
+ public abstract boolean isAppBad(String processName, int uid);
/** Remove pending backup for the given userId. */
public abstract void clearPendingBackup(@UserIdInt int userId);
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index d642f218cb71..3ca5f0deba1b 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -732,6 +732,10 @@ public class AppOpsManager {
public static final int SAMPLING_STRATEGY_BOOT_TIME_SAMPLING =
FrameworkStatsLog.RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__BOOT_TIME_SAMPLING;
+ /** @hide */
+ public static final int SAMPLING_STRATEGY_UNIFORM_OPS =
+ FrameworkStatsLog.RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__UNIFORM_OPS;
+
/**
* Strategies used for message sampling
* @hide
@@ -741,7 +745,8 @@ public class AppOpsManager {
SAMPLING_STRATEGY_DEFAULT,
SAMPLING_STRATEGY_UNIFORM,
SAMPLING_STRATEGY_RARELY_USED,
- SAMPLING_STRATEGY_BOOT_TIME_SAMPLING
+ SAMPLING_STRATEGY_BOOT_TIME_SAMPLING,
+ SAMPLING_STRATEGY_UNIFORM_OPS
})
public @interface SamplingStrategy {}
@@ -8350,7 +8355,7 @@ public class AppOpsManager {
* @hide
*/
private static boolean isCollectingStackTraces() {
- if (sConfig.getSampledOpCode() == OP_NONE &&
+ if (sConfig.getSampledOpCode() == OP_NONE && sConfig.getAcceptableLeftDistance() == 0 &&
sConfig.getExpirationTimeSinceBootMillis() >= SystemClock.elapsedRealtime()) {
return false;
}
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index 4b0d2f86ad34..bca6f39e1ded 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -175,14 +175,33 @@ import java.util.concurrent.atomic.AtomicLong;
*
* Caching can be disabled completely by initializing {@code sEnabled} to false and rebuilding.
*
+ * To test a binder cache, create one or more tests that exercise the binder method. This
+ * should be done twice: once with production code and once with a special image that sets
+ * {@code DEBUG} and {@code VERIFY} true. In the latter case, verify that no cache
+ * inconsistencies are reported. If a cache inconsistency is reported, however, it might be a
+ * false positive. This happens if the server side data can be read and written non-atomically
+ * with respect to cache invalidation.
+ *
* @param <Query> The class used to index cache entries: must be hashable and comparable
* @param <Result> The class holding cache entries; use a boxed primitive if possible
*
* {@hide}
*/
public abstract class PropertyInvalidatedCache<Query, Result> {
- private static final long NONCE_UNSET = 0;
- private static final long NONCE_DISABLED = -1;
+ /**
+ * Reserved nonce values. The code is written assuming that these
+ * values are contiguous.
+ */
+ private static final int NONCE_UNSET = 0;
+ private static final int NONCE_DISABLED = 1;
+ private static final int NONCE_CORKED = 2;
+ private static final int NONCE_RESERVED = NONCE_CORKED + 1;
+
+ /**
+ * The names of the nonces
+ */
+ private static final String[] sNonceName =
+ new String[]{ "unset", "disabled", "corked" };
private static final String TAG = "PropertyInvalidatedCache";
private static final boolean DEBUG = false;
@@ -195,11 +214,28 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
@GuardedBy("mLock")
private long mMisses = 0;
+ @GuardedBy("mLock")
+ private long mMissDisabled[] = new long[]{ 0, 0, 0 };
+
+ @GuardedBy("mLock")
+ private long mMissOverflow = 0;
+
+ @GuardedBy("mLock")
+ private long mHighWaterMark = 0;
+
// Most invalidation is done in a static context, so the counters need to be accessible.
@GuardedBy("sCorkLock")
private static final HashMap<String, Long> sInvalidates = new HashMap<>();
/**
+ * Record the number of invalidate or cork calls that were nops because
+ * the cache was already corked. This is static because invalidation is
+ * done in a static context.
+ */
+ @GuardedBy("sCorkLock")
+ private static final HashMap<String, Long> sCorkedInvalidates = new HashMap<>();
+
+ /**
* If sEnabled is false then all cache operations are stubbed out. Set
* it to false inside test processes.
*/
@@ -271,7 +307,15 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
true /* LRU access order */) {
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
- return size() > maxEntries;
+ final int size = size();
+ if (size > mHighWaterMark) {
+ mHighWaterMark = size;
+ }
+ if (size > maxEntries) {
+ mMissOverflow++;
+ return true;
+ }
+ return false;
}
};
synchronized (sCorkLock) {
@@ -363,14 +407,21 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
// Let access to mDisabled race: it's atomic anyway.
long currentNonce = (!isDisabledLocal()) ? getCurrentNonce() : NONCE_DISABLED;
for (;;) {
- if (currentNonce == NONCE_DISABLED || currentNonce == NONCE_UNSET) {
+ if (currentNonce == NONCE_DISABLED || currentNonce == NONCE_UNSET ||
+ currentNonce == NONCE_CORKED) {
+ if (!mDisabled) {
+ // Do not bother collecting statistics if the cache is
+ // locally disabled.
+ synchronized (mLock) {
+ mMissDisabled[(int) currentNonce]++;
+ }
+ }
+
if (DEBUG) {
if (!mDisabled) {
Log.d(TAG, String.format(
"cache %s %s for %s",
- cacheName(),
- currentNonce == NONCE_DISABLED ? "disabled" : "unset",
- queryToString(query)));
+ cacheName(), sNonceName[(int) currentNonce], queryToString(query)));
}
}
return recompute(query);
@@ -383,10 +434,10 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
if (cachedResult != null) mHits++;
} else {
if (DEBUG) {
- Log.d(TAG,
- String.format("clearing cache %s because nonce changed [%s] -> [%s]",
- cacheName(),
- mLastSeenNonce, currentNonce));
+ Log.d(TAG, String.format(
+ "clearing cache %s of %d entries because nonce changed [%s] -> [%s]",
+ cacheName(), mCache.size(),
+ mLastSeenNonce, currentNonce));
}
mCache.clear();
mLastSeenNonce = currentNonce;
@@ -517,6 +568,8 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
if (DEBUG) {
Log.d(TAG, "ignoring invalidation due to cork: " + name);
}
+ final long count = sCorkedInvalidates.getOrDefault(name, (long) 0);
+ sCorkedInvalidates.put(name, count + 1);
return;
}
invalidateCacheLocked(name);
@@ -538,7 +591,7 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
long newValue;
do {
newValue = NoPreloadHolder.next();
- } while (newValue == NONCE_UNSET || newValue == NONCE_DISABLED);
+ } while (newValue >= 0 && newValue < NONCE_RESERVED);
final String newValueString = Long.toString(newValue);
if (DEBUG) {
Log.d(TAG,
@@ -567,13 +620,21 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
* @param name Name of the cache-key property to cork
*/
public static void corkInvalidations(@NonNull String name) {
+ if (!sEnabled) {
+ if (DEBUG) {
+ Log.w(TAG, String.format(
+ "cache cork %s suppressed", name));
+ }
+ return;
+ }
+
synchronized (sCorkLock) {
int numberCorks = sCorks.getOrDefault(name, 0);
if (DEBUG) {
Log.d(TAG, String.format("corking %s: numberCorks=%s", name, numberCorks));
}
- // If we're the first ones to cork this cache, set the cache to the unset state so
+ // If we're the first ones to cork this cache, set the cache to the corked state so
// existing caches talk directly to their services while we've corked updates.
// Make sure we don't clobber a disabled cache value.
@@ -584,8 +645,11 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
if (numberCorks == 0) {
final long nonce = SystemProperties.getLong(name, NONCE_UNSET);
if (nonce != NONCE_UNSET && nonce != NONCE_DISABLED) {
- SystemProperties.set(name, Long.toString(NONCE_UNSET));
+ SystemProperties.set(name, Long.toString(NONCE_CORKED));
}
+ } else {
+ final long count = sCorkedInvalidates.getOrDefault(name, (long) 0);
+ sCorkedInvalidates.put(name, count + 1);
}
sCorks.put(name, numberCorks + 1);
if (DEBUG) {
@@ -602,6 +666,14 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
* @param name Name of the cache-key property to uncork
*/
public static void uncorkInvalidations(@NonNull String name) {
+ if (!sEnabled) {
+ if (DEBUG) {
+ Log.w(TAG, String.format(
+ "cache uncork %s suppressed", name));
+ }
+ return;
+ }
+
synchronized (sCorkLock) {
int numberCorks = sCorks.getOrDefault(name, 0);
if (DEBUG) {
@@ -729,8 +801,9 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
boolean nonceChanged = (getCurrentNonce() != mLastSeenNonce);
if (!nonceChanged && !debugCompareQueryResults(proposedResult, resultToCompare)) {
Log.e(TAG, String.format(
- "cache %s inconsistent for %s",
- cacheName(), queryToString(query)));
+ "cache %s inconsistent for %s is %s should be %s",
+ cacheName(), queryToString(query),
+ proposedResult, resultToCompare));
}
// Always return the "true" result in verification mode.
return resultToCompare;
@@ -784,18 +857,23 @@ public abstract class PropertyInvalidatedCache<Query, Result> {
private void dumpContents(PrintWriter pw, String[] args) {
long invalidateCount;
-
+ long corkedInvalidates;
synchronized (sCorkLock) {
invalidateCount = sInvalidates.getOrDefault(mPropertyName, (long) 0);
+ corkedInvalidates = sCorkedInvalidates.getOrDefault(mPropertyName, (long) 0);
}
synchronized (mLock) {
pw.println(String.format(" Cache Property Name: %s", cacheName()));
- pw.println(String.format(" Hits: %d, Misses: %d, Invalidates: %d",
- mHits, mMisses, invalidateCount));
+ pw.println(String.format(" Hits: %d, Misses: %d, Invalidates: %d, Overflows: %d",
+ mHits, mMisses, invalidateCount, mMissOverflow));
+ pw.println(String.format(" Miss-corked: %d, Miss-unset: %d, Miss-other: %d," +
+ " CorkedInvalidates: %d",
+ mMissDisabled[NONCE_CORKED], mMissDisabled[NONCE_UNSET],
+ mMissDisabled[NONCE_DISABLED], corkedInvalidates));
pw.println(String.format(" Last Observed Nonce: %d", mLastSeenNonce));
- pw.println(String.format(" Current Size: %d, Max Size: %d",
- mCache.entrySet().size(), mMaxEntries));
+ pw.println(String.format(" Current Size: %d, Max Size: %d, HW Mark: %d",
+ mCache.size(), mMaxEntries, mHighWaterMark));
pw.println(String.format(" Enabled: %s", mDisabled ? "false" : "true"));
Set<Map.Entry<Query, Result>> cacheEntries = mCache.entrySet();
diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java
index 4f5c4feb3684..df11d3adac01 100644
--- a/core/java/android/bluetooth/BluetoothMapClient.java
+++ b/core/java/android/bluetooth/BluetoothMapClient.java
@@ -52,6 +52,18 @@ public final class BluetoothMapClient implements BluetoothProfile {
public static final String ACTION_MESSAGE_DELIVERED_SUCCESSFULLY =
"android.bluetooth.mapmce.profile.action.MESSAGE_DELIVERED_SUCCESSFULLY";
+ /**
+ * Action to notify read status changed
+ */
+ public static final String ACTION_MESSAGE_READ_STATUS_CHANGED =
+ "android.bluetooth.mapmce.profile.action.MESSAGE_READ_STATUS_CHANGED";
+
+ /**
+ * Action to notify deleted status changed
+ */
+ public static final String ACTION_MESSAGE_DELETED_STATUS_CHANGED =
+ "android.bluetooth.mapmce.profile.action.MESSAGE_DELETED_STATUS_CHANGED";
+
/* Extras used in ACTION_MESSAGE_RECEIVED intent.
* NOTE: HANDLE is only valid for a single session with the device. */
public static final String EXTRA_MESSAGE_HANDLE =
@@ -65,6 +77,25 @@ public final class BluetoothMapClient implements BluetoothProfile {
public static final String EXTRA_SENDER_CONTACT_NAME =
"android.bluetooth.mapmce.profile.extra.SENDER_CONTACT_NAME";
+ /**
+ * Used as a boolean extra in ACTION_MESSAGE_DELETED_STATUS_CHANGED
+ * Contains the MAP message deleted status
+ * Possible values are:
+ * true: deleted
+ * false: undeleted
+ */
+ public static final String EXTRA_MESSAGE_DELETED_STATUS =
+ "android.bluetooth.mapmce.profile.extra.MESSAGE_DELETED_STATUS";
+
+ /**
+ * Extra used in ACTION_MESSAGE_READ_STATUS_CHANGED or ACTION_MESSAGE_DELETED_STATUS_CHANGED
+ * Possible values are:
+ * 0: failure
+ * 1: success
+ */
+ public static final String EXTRA_RESULT_CODE =
+ "android.bluetooth.device.extra.RESULT_CODE";
+
/** There was an error trying to obtain the state */
public static final int STATE_ERROR = -1;
@@ -75,6 +106,12 @@ public final class BluetoothMapClient implements BluetoothProfile {
private static final int UPLOADING_FEATURE_BITMASK = 0x08;
+ /** Parameters in setMessageStatus */
+ public static final int UNREAD = 0;
+ public static final int READ = 1;
+ public static final int UNDELETED = 2;
+ public static final int DELETED = 3;
+
private BluetoothAdapter mAdapter;
private final BluetoothProfileConnector<IBluetoothMapClient> mProfileConnector =
new BluetoothProfileConnector(this, BluetoothProfile.MAP_CLIENT,
@@ -405,6 +442,38 @@ public final class BluetoothMapClient implements BluetoothProfile {
return false;
}
+ /**
+ * Set message status of message on MSE
+ * <p>
+ * When read status changed, the result will be published via
+ * {@link #ACTION_MESSAGE_READ_STATUS_CHANGED}
+ * When deleted status changed, the result will be published via
+ * {@link #ACTION_MESSAGE_DELETED_STATUS_CHANGED}
+ *
+ * @param device Bluetooth device
+ * @param handle message handle
+ * @param status <code>UNREAD</code> for "unread", <code>READ</code> for
+ * "read", <code>UNDELETED</code> for "undeleted", <code>DELETED</code> for
+ * "deleted", otherwise return error
+ * @return <code>true</code> if request has been sent, <code>false</code> on error
+ *
+ */
+ @RequiresPermission(Manifest.permission.READ_SMS)
+ public boolean setMessageStatus(BluetoothDevice device, String handle, int status) {
+ if (DBG) Log.d(TAG, "setMessageStatus(" + device + ", " + handle + ", " + status + ")");
+ final IBluetoothMapClient service = getService();
+ if (service != null && isEnabled() && isValidDevice(device) && handle != null &&
+ (status == READ || status == UNREAD || status == UNDELETED || status == DELETED)) {
+ try {
+ return service.setMessageStatus(device, handle, status);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ return false;
+ }
+ }
+ return false;
+ }
+
private boolean isEnabled() {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index f533760de84d..9c7dc9610522 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -8179,7 +8179,7 @@ public abstract class PackageManager {
private static final PropertyInvalidatedCache<PackageInfoQuery, PackageInfo>
sPackageInfoCache =
new PropertyInvalidatedCache<PackageInfoQuery, PackageInfo>(
- 16, PermissionManager.CACHE_KEY_PACKAGE_INFO) {
+ 32, PermissionManager.CACHE_KEY_PACKAGE_INFO) {
@Override
protected PackageInfo recompute(PackageInfoQuery query) {
return getPackageInfoAsUserUncached(
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index b4546a49a07c..efe71f15007c 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -71,7 +71,7 @@ public class FaceManager implements BiometricAuthenticator, BiometricFaceConstan
private static final int MSG_SET_FEATURE_COMPLETED = 107;
private static final int MSG_CHALLENGE_GENERATED = 108;
- private IFaceService mService;
+ private final IFaceService mService;
private final Context mContext;
private IBinder mToken = new Binder();
private AuthenticationCallback mAuthenticationCallback;
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 4ca75d9a09cf..7b1048ed1de5 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -19,6 +19,7 @@ package android.hardware.fingerprint;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.MANAGE_FINGERPRINT;
import static android.Manifest.permission.USE_BIOMETRIC;
+import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.Manifest.permission.USE_FINGERPRINT;
import android.annotation.NonNull;
@@ -685,6 +686,75 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
}
/**
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ public boolean isUdfps() {
+ if (mService == null) {
+ Slog.w(TAG, "isUdfps: no fingerprint service");
+ return false;
+ }
+
+ try {
+ return mService.isUdfps();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return false;
+ }
+
+ /**
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ public void setUdfpsOverlayController(IUdfpsOverlayController controller) {
+ if (mService == null) {
+ Slog.w(TAG, "setUdfpsOverlayController: no fingerprint service");
+ return;
+ }
+
+ try {
+ mService.setUdfpsOverlayController(controller);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ public void onFingerDown(int x, int y, float minor, float major) {
+ if (mService == null) {
+ Slog.w(TAG, "onFingerDown: no fingerprint service");
+ return;
+ }
+
+ try {
+ mService.onFingerDown(x, y, minor, major);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ public void onFingerUp() {
+ if (mService == null) {
+ Slog.w(TAG, "onFingerDown: no fingerprint service");
+ return;
+ }
+
+ try {
+ mService.onFingerUp();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Determine if there is at least one fingerprint enrolled.
*
* @return true if at least one fingerprint is enrolled, false otherwise
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 38d7d2bc9e27..21ac687166f0 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -114,8 +114,8 @@ interface IFingerprintService {
// Notifies about a finger leaving the sensor area.
void onFingerUp();
- // Returns whether the specified sensor is an under-display fingerprint sensor (UDFPS).
- boolean isUdfps(int sensorId);
+ // Returns whether the sensor is an under-display fingerprint sensor (UDFPS).
+ boolean isUdfps();
// Sets the controller for managing the UDFPS overlay.
void setUdfpsOverlayController(in IUdfpsOverlayController controller);
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index dea932dd6178..43bd76de1f46 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -208,8 +208,15 @@ public final class PermissionControllerManager {
ServiceConnector<IPermissionController> remoteService = sRemoteServices.get(key);
if (remoteService == null) {
Intent intent = new Intent(SERVICE_INTERFACE);
- intent.setPackage(context.getPackageManager().getPermissionControllerPackageName());
+ String pkgName = context.getPackageManager().getPermissionControllerPackageName();
+ intent.setPackage(pkgName);
ResolveInfo serviceInfo = context.getPackageManager().resolveService(intent, 0);
+ if (serviceInfo == null) {
+ String errorMsg = "No PermissionController package (" + pkgName + ") for user "
+ + context.getUserId();
+ Log.wtf(TAG, errorMsg);
+ throw new IllegalStateException(errorMsg);
+ }
remoteService = new ServiceConnector.Impl<IPermissionController>(
ActivityThread.currentApplication() /* context */,
new Intent(SERVICE_INTERFACE)
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index feebb9485167..e1d48860b1e2 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8298,6 +8298,13 @@ public final class Settings {
public static final String CAMERA_GESTURE_DISABLED = "camera_gesture_disabled";
/**
+ * Whether the panic button (emergency sos) gesture should be enabled.
+ *
+ * @hide
+ */
+ public static final String PANIC_GESTURE_ENABLED = "panic_gesture_enabled";
+
+ /**
* Whether the camera launch gesture to double tap the power button when the screen is off
* should be disabled.
*
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 31da83ad5137..69220722fc05 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -308,7 +308,7 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll
false /* isScreenRound */,
false /* alwaysConsumeSystemBars */, null /* displayCutout */,
LayoutParams.SOFT_INPUT_ADJUST_RESIZE /* legacySoftInputMode*/,
- 0 /* legacySystemUiFlags */, typeSideMap)
+ 0 /* legacyWindowFlags */, 0 /* legacySystemUiFlags */, typeSideMap)
.getInsets(mTypes);
}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index a679b3740fd9..c383bc7a4d70 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -501,6 +501,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
private PendingControlRequest mPendingImeControlRequest;
private int mLastLegacySoftInputMode;
+ private int mLastLegacyWindowFlags;
private int mLastLegacySystemUiFlags;
private DisplayCutout mLastDisplayCutout;
private boolean mStartingAnimation;
@@ -569,8 +570,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
WindowInsets insets = state.calculateInsets(mFrame, mState /* ignoringVisibilityState*/,
mLastInsets.isRound(), mLastInsets.shouldAlwaysConsumeSystemBars(),
- mLastDisplayCutout, mLastLegacySoftInputMode, mLastLegacySystemUiFlags,
- null /* typeSideMap */);
+ mLastDisplayCutout, mLastLegacySoftInputMode, mLastLegacyWindowFlags,
+ mLastLegacySystemUiFlags, null /* typeSideMap */);
mHost.dispatchWindowInsetsAnimationProgress(insets, mUnmodifiableTmpRunningAnims);
if (DEBUG) {
for (WindowInsetsAnimation anim : mUnmodifiableTmpRunningAnims) {
@@ -706,13 +707,14 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
@VisibleForTesting
public WindowInsets calculateInsets(boolean isScreenRound,
boolean alwaysConsumeSystemBars, DisplayCutout cutout,
- int legacySoftInputMode, int legacySystemUiFlags) {
+ int legacySoftInputMode, int legacyWindowFlags, int legacySystemUiFlags) {
mLastLegacySoftInputMode = legacySoftInputMode;
+ mLastLegacyWindowFlags = legacyWindowFlags;
mLastLegacySystemUiFlags = legacySystemUiFlags;
mLastDisplayCutout = cutout;
mLastInsets = mState.calculateInsets(mFrame, null /* ignoringVisibilityState*/,
isScreenRound, alwaysConsumeSystemBars, cutout,
- legacySoftInputMode, legacySystemUiFlags,
+ legacySoftInputMode, legacyWindowFlags, legacySystemUiFlags,
null /* typeSideMap */);
return mLastInsets;
}
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 91e7591193f1..6b0b509932a8 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -22,13 +22,14 @@ import static android.view.ViewRootImpl.NEW_INSETS_MODE_IME;
import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
import static android.view.ViewRootImpl.sNewInsetsMode;
import static android.view.WindowInsets.Type.MANDATORY_SYSTEM_GESTURES;
-import static android.view.WindowInsets.Type.SIZE;
import static android.view.WindowInsets.Type.SYSTEM_GESTURES;
import static android.view.WindowInsets.Type.displayCutout;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.indexOf;
import static android.view.WindowInsets.Type.isVisibleInsetsType;
+import static android.view.WindowInsets.Type.statusBars;
import static android.view.WindowInsets.Type.systemBars;
+import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
@@ -38,7 +39,6 @@ import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
-import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.SparseIntArray;
import android.view.WindowInsets.Type;
@@ -171,7 +171,7 @@ public class InsetsState implements Parcelable {
*/
public WindowInsets calculateInsets(Rect frame, @Nullable InsetsState ignoringVisibilityState,
boolean isScreenRound, boolean alwaysConsumeSystemBars, DisplayCutout cutout,
- int legacySoftInputMode, int legacySystemUiFlags,
+ int legacySoftInputMode, int legacyWindowFlags, int legacySystemUiFlags,
@Nullable @InternalInsetsSide SparseIntArray typeSideMap) {
Insets[] typeInsetsMap = new Insets[Type.SIZE];
Insets[] typeMaxInsetsMap = new Insets[Type.SIZE];
@@ -218,10 +218,17 @@ public class InsetsState implements Parcelable {
}
}
final int softInputAdjustMode = legacySoftInputMode & SOFT_INPUT_MASK_ADJUST;
+
+ @InsetsType int compatInsetsTypes = systemBars() | displayCutout();
+ if (softInputAdjustMode == SOFT_INPUT_ADJUST_RESIZE) {
+ compatInsetsTypes |= ime();
+ }
+ if ((legacyWindowFlags & FLAG_FULLSCREEN) != 0) {
+ compatInsetsTypes &= ~statusBars();
+ }
+
return new WindowInsets(typeInsetsMap, typeMaxInsetsMap, typeVisibilityMap, isScreenRound,
- alwaysConsumeSystemBars, cutout, softInputAdjustMode == SOFT_INPUT_ADJUST_RESIZE
- ? systemBars() | displayCutout() | ime()
- : systemBars() | displayCutout(),
+ alwaysConsumeSystemBars, cutout, compatInsetsTypes,
sNewInsetsMode == NEW_INSETS_MODE_FULL
&& (legacySystemUiFlags & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0);
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index f2cec250d47f..fe6f33da6d2a 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2257,7 +2257,8 @@ public final class ViewRootImpl implements ViewParent,
mLastWindowInsets = mInsetsController.calculateInsets(
mContext.getResources().getConfiguration().isScreenRound(),
mAttachInfo.mAlwaysConsumeSystemBars, mPendingDisplayCutout.get(),
- mWindowAttributes.softInputMode, (mWindowAttributes.systemUiVisibility
+ mWindowAttributes.softInputMode, mWindowAttributes.flags,
+ (mWindowAttributes.systemUiVisibility
| mWindowAttributes.subtreeSystemUiVisibility));
Rect visibleInsets = mInsetsController.calculateVisibleInsets(
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 4d6b72f96aab..5e94758b003c 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -17,7 +17,6 @@
package android.view;
-import static android.view.WindowInsets.Type.CAPTION_BAR;
import static android.view.WindowInsets.Type.DISPLAY_CUTOUT;
import static android.view.WindowInsets.Type.FIRST;
import static android.view.WindowInsets.Type.IME;
@@ -95,7 +94,7 @@ public final class WindowInsets {
private final boolean mStableInsetsConsumed;
private final boolean mDisplayCutoutConsumed;
- private final int mCompatInsetTypes;
+ private final int mCompatInsetsTypes;
private final boolean mCompatIgnoreVisibility;
/**
@@ -150,8 +149,8 @@ public final class WindowInsets {
@Nullable Insets[] typeMaxInsetsMap,
boolean[] typeVisibilityMap,
boolean isRound,
- boolean alwaysConsumeSystemBars, DisplayCutout displayCutout, int compatInsetTypes,
- boolean compatIgnoreVisibility) {
+ boolean alwaysConsumeSystemBars, DisplayCutout displayCutout,
+ @InsetsType int compatInsetsTypes, boolean compatIgnoreVisibility) {
mSystemWindowInsetsConsumed = typeInsetsMap == null;
mTypeInsetsMap = mSystemWindowInsetsConsumed
? new Insets[SIZE]
@@ -165,7 +164,7 @@ public final class WindowInsets {
mTypeVisibilityMap = typeVisibilityMap;
mIsRound = isRound;
mAlwaysConsumeSystemBars = alwaysConsumeSystemBars;
- mCompatInsetTypes = compatInsetTypes;
+ mCompatInsetsTypes = compatInsetsTypes;
mCompatIgnoreVisibility = compatIgnoreVisibility;
mDisplayCutoutConsumed = displayCutout == null;
@@ -183,7 +182,7 @@ public final class WindowInsets {
src.mStableInsetsConsumed ? null : src.mTypeMaxInsetsMap,
src.mTypeVisibilityMap, src.mIsRound,
src.mAlwaysConsumeSystemBars, displayCutoutCopyConstructorArgument(src),
- src.mCompatInsetTypes,
+ src.mCompatInsetsTypes,
src.mCompatIgnoreVisibility);
}
@@ -310,11 +309,11 @@ public final class WindowInsets {
@NonNull
public Insets getSystemWindowInsets() {
Insets result = mCompatIgnoreVisibility
- ? getInsetsIgnoringVisibility(mCompatInsetTypes & ~ime())
- : getInsets(mCompatInsetTypes);
+ ? getInsetsIgnoringVisibility(mCompatInsetsTypes & ~ime())
+ : getInsets(mCompatInsetsTypes);
// We can't query max insets for IME, so we need to add it manually after.
- if ((mCompatInsetTypes & ime()) != 0 && mCompatIgnoreVisibility) {
+ if ((mCompatInsetsTypes & ime()) != 0 && mCompatIgnoreVisibility) {
result = Insets.max(result, getInsets(ime()));
}
return result;
@@ -503,7 +502,7 @@ public final class WindowInsets {
mTypeVisibilityMap,
mIsRound, mAlwaysConsumeSystemBars,
null /* displayCutout */,
- mCompatInsetTypes, mCompatIgnoreVisibility);
+ mCompatInsetsTypes, mCompatIgnoreVisibility);
}
@@ -554,7 +553,7 @@ public final class WindowInsets {
mTypeVisibilityMap,
mIsRound, mAlwaysConsumeSystemBars,
displayCutoutCopyConstructorArgument(this),
- mCompatInsetTypes, mCompatIgnoreVisibility);
+ mCompatInsetsTypes, mCompatIgnoreVisibility);
}
// TODO(b/119190588): replace @code with @link below
@@ -627,7 +626,7 @@ public final class WindowInsets {
@Deprecated
@NonNull
public Insets getStableInsets() {
- return getInsets(mTypeMaxInsetsMap, mCompatInsetTypes);
+ return getInsets(mTypeMaxInsetsMap, mCompatInsetsTypes);
}
/**
@@ -939,7 +938,7 @@ public final class WindowInsets {
: mDisplayCutout == null
? DisplayCutout.NO_CUTOUT
: mDisplayCutout.inset(left, top, right, bottom),
- mCompatInsetTypes, mCompatIgnoreVisibility);
+ mCompatInsetsTypes, mCompatIgnoreVisibility);
}
@Override
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 28a18da37b3e..2fe7c021bb21 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -276,7 +276,7 @@ public final class WindowManagerImpl implements WindowManager {
if (ViewRootImpl.sNewInsetsMode == NEW_INSETS_MODE_FULL) {
return insetsState.calculateInsets(bounds, null /* ignoringVisibilityState*/,
isScreenRound, alwaysConsumeSystemBars, displayCutout.get(),
- SOFT_INPUT_ADJUST_NOTHING,
+ SOFT_INPUT_ADJUST_NOTHING, attrs.flags,
SYSTEM_UI_FLAG_VISIBLE, null /* typeSideMap */);
} else {
return new WindowInsets.Builder()
diff --git a/core/java/com/android/internal/view/IInlineSuggestionsRequestCallback.aidl b/core/java/com/android/internal/view/IInlineSuggestionsRequestCallback.aidl
index 03948a92bcab..6c97962ac057 100644
--- a/core/java/com/android/internal/view/IInlineSuggestionsRequestCallback.aidl
+++ b/core/java/com/android/internal/view/IInlineSuggestionsRequestCallback.aidl
@@ -22,41 +22,54 @@ import android.view.inputmethod.InlineSuggestionsRequest;
import com.android.internal.view.IInlineSuggestionsResponseCallback;
/**
- * Binder interface for the IME service to send an inline suggestion request to the system.
+ * Binder interface for the IME service to send {@link InlineSuggestionsRequest} or notify other IME
+ * service events to the system.
* {@hide}
*/
oneway interface IInlineSuggestionsRequestCallback {
- // Indicates that the current IME does not support inline suggestion.
+ /** Indicates that the current IME does not support inline suggestion. */
void onInlineSuggestionsUnsupported();
- // Sends the inline suggestions request from IME to Autofill. Calling this method indicates
- // that the IME input is started on the view corresponding to the request.
+ /**
+ * Sends the inline suggestions request from IME to Autofill. Calling this method indicates
+ * that the IME input is started on the view corresponding to the request.
+ */
void onInlineSuggestionsRequest(in InlineSuggestionsRequest request,
in IInlineSuggestionsResponseCallback callback);
- // Signals that {@link android.inputmethodservice.InputMethodService
- // #onStartInput(EditorInfo, boolean)} is called on the given focused field.
+ /**
+ * Signals that {@link android.inputmethodservice.InputMethodService
+ * #onStartInput(EditorInfo, boolean)} is called on the given focused field.
+ */
void onInputMethodStartInput(in AutofillId imeFieldId);
- // Signals that {@link android.inputmethodservice.InputMethodService
- // #dispatchOnShowInputRequested(int, boolean)} is called and shares the call result.
- // The true value of {@code requestResult} means the IME is about to be shown, while
- // false value means the IME will not be shown.
+ /**
+ * Signals that {@link android.inputmethodservice.InputMethodService
+ * #dispatchOnShowInputRequested(int, boolean)} is called and shares the call result.
+ * The true value of {@code requestResult} means the IME is about to be shown, while
+ * false value means the IME will not be shown.
+ */
void onInputMethodShowInputRequested(boolean requestResult);
- // Signals that {@link android.inputmethodservice.InputMethodService
- // #onStartInputView(EditorInfo, boolean)} is called on the field specified by the earlier
- // {@link #onInputMethodStartInput(AutofillId)}.
+ /**
+ * Signals that {@link android.inputmethodservice.InputMethodService
+ * #onStartInputView(EditorInfo, boolean)} is called on the field specified by the earlier
+ * {@link #onInputMethodStartInput(AutofillId)}.
+ */
void onInputMethodStartInputView();
- // Signals that {@link android.inputmethodservice.InputMethodService
- // #onFinishInputView(boolean)} is called on the field specified by the earlier
- // {@link #onInputMethodStartInput(AutofillId)}.
+ /**
+ * Signals that {@link android.inputmethodservice.InputMethodService
+ * #onFinishInputView(boolean)} is called on the field specified by the earlier
+ * {@link #onInputMethodStartInput(AutofillId)}.
+ */
void onInputMethodFinishInputView();
- // Signals that {@link android.inputmethodservice.InputMethodService
- // #onFinishInput()} is called on the field specified by the earlier
- // {@link #onInputMethodStartInput(AutofillId)}.
+ /**
+ * Signals that {@link android.inputmethodservice.InputMethodService
+ * #onFinishInput()} is called on the field specified by the earlier
+ * {@link #onInputMethodStartInput(AutofillId)}.
+ */
void onInputMethodFinishInput();
// Indicates that the current IME changes inline suggestion session.
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index edafb0c19da8..ca4dc18689bc 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -208,6 +208,13 @@ message SecureSettingsProto {
optional Doze doze = 21;
optional SettingProto emergency_assistance_application = 22 [ (android.privacy).dest = DEST_AUTOMATIC ];
+
+ message EmergencyResponse {
+ optional SettingProto panic_gesture_enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ }
+
+ optional EmergencyResponse emergency_response = 83;
+
optional SettingProto enhanced_voice_privacy_enabled = 23 [ (android.privacy).dest = DEST_AUTOMATIC ];
message Gesture {
@@ -606,5 +613,5 @@ message SecureSettingsProto {
// Please insert fields in alphabetical order and group them into messages
// if possible (to avoid reaching the method limit).
- // Next tag = 83;
+ // Next tag = 84;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 0985fea3d26a..ef50fc8e2b6e 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -227,6 +227,8 @@
<protected-broadcast android:name="android.bluetooth.mapmce.profile.action.MESSAGE_RECEIVED" />
<protected-broadcast android:name="android.bluetooth.mapmce.profile.action.MESSAGE_SENT_SUCCESSFULLY" />
<protected-broadcast android:name="android.bluetooth.mapmce.profile.action.MESSAGE_DELIVERED_SUCCESSFULLY" />
+ <protected-broadcast android:name="android.bluetooth.mapmce.profile.action.MESSAGE_READ_STATUS_CHANGED" />
+ <protected-broadcast android:name="android.bluetooth.mapmce.profile.action.MESSAGE_DELETED_STATUS_CHANGED" />
<protected-broadcast
android:name="com.android.bluetooth.BluetoothMapContentObserver.action.MESSAGE_SENT" />
<protected-broadcast
diff --git a/core/res/res/values-television/config.xml b/core/res/res/values-television/config.xml
index 3ecb1dddd916..0382dd3b9103 100644
--- a/core/res/res/values-television/config.xml
+++ b/core/res/res/values-television/config.xml
@@ -42,4 +42,13 @@
<!-- Allow SystemUI to show the shutdown dialog -->
<bool name="config_showSysuiShutdown">true</bool>
+
+ <!-- Control the behavior when the user long presses the power button.
+ 0 - Nothing
+ 1 - Global actions menu
+ 2 - Power off (with confirmation)
+ 3 - Power off (without confirmation)
+ 4 - Go to voice assist
+ 5 - Go to assistant (Settings.Secure.ASSISTANT -->
+ <integer name="config_longPressOnPowerBehavior">3</integer>
</resources>
diff --git a/core/tests/bluetoothtests/AndroidManifest.xml b/core/tests/bluetoothtests/AndroidManifest.xml
index 7f9d8749358c..6849a90f5010 100644
--- a/core/tests/bluetoothtests/AndroidManifest.xml
+++ b/core/tests/bluetoothtests/AndroidManifest.xml
@@ -15,14 +15,18 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.bluetooth.tests" >
+ package="com.android.bluetooth.tests"
+ android:sharedUserId="android.uid.bluetooth" >
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+ <uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.LOCAL_MAC_ADDRESS" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
+ <uses-permission android:name="android.permission.RECEIVE_SMS" />
+ <uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java
index 4b32ceae0617..89dbe3f75b56 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java
@@ -360,6 +360,30 @@ public class BluetoothStressTest extends InstrumentationTestCase {
mTestUtils.unpair(mAdapter, device);
}
+ /* Make sure there is at least 1 unread message in the last week on remote device */
+ public void testMceSetMessageStatus() {
+ int iterations = BluetoothTestRunner.sMceSetMessageStatusIterations;
+ if (iterations == 0) {
+ return;
+ }
+
+ BluetoothDevice device = mAdapter.getRemoteDevice(BluetoothTestRunner.sDeviceAddress);
+ mTestUtils.enable(mAdapter);
+ mTestUtils.connectProfile(mAdapter, device, BluetoothProfile.MAP_CLIENT, null);
+ mTestUtils.mceGetUnreadMessage(mAdapter, device);
+
+ for (int i = 0; i < iterations; i++) {
+ mTestUtils.mceSetMessageStatus(mAdapter, device, BluetoothMapClient.READ);
+ mTestUtils.mceSetMessageStatus(mAdapter, device, BluetoothMapClient.UNREAD);
+ }
+
+ /**
+ * It is hard to find device to support set undeleted status, so just
+ * set deleted in 1 iteration
+ **/
+ mTestUtils.mceSetMessageStatus(mAdapter, device, BluetoothMapClient.DELETED);
+ }
+
private void sleep(long time) {
try {
Thread.sleep(time);
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestRunner.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestRunner.java
index 56e691d8c246..d19c2c3e7e24 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestRunner.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestRunner.java
@@ -40,6 +40,7 @@ import android.util.Log;
* [-e connect_input_iterations <iterations>] \
* [-e connect_pan_iterations <iterations>] \
* [-e start_stop_sco_iterations <iterations>] \
+ * [-e mce_set_message_status_iterations <iterations>] \
* [-e pair_address <address>] \
* [-e headset_address <address>] \
* [-e a2dp_address <address>] \
@@ -64,6 +65,7 @@ public class BluetoothTestRunner extends InstrumentationTestRunner {
public static int sConnectInputIterations = 100;
public static int sConnectPanIterations = 100;
public static int sStartStopScoIterations = 100;
+ public static int sMceSetMessageStatusIterations = 100;
public static String sDeviceAddress = "";
public static byte[] sDevicePairPin = {'1', '2', '3', '4'};
@@ -173,6 +175,15 @@ public class BluetoothTestRunner extends InstrumentationTestRunner {
}
}
+ val = arguments.getString("mce_set_message_status_iterations");
+ if (val != null) {
+ try {
+ sMceSetMessageStatusIterations = Integer.parseInt(val);
+ } catch (NumberFormatException e) {
+ // Invalid argument, fall back to default value
+ }
+ }
+
val = arguments.getString("device_address");
if (val != null) {
sDeviceAddress = val;
diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
index ed613c36b89b..409025bc670d 100644
--- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
+++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java
@@ -56,6 +56,10 @@ public class BluetoothTestUtils extends Assert {
private static final int CONNECT_PROXY_TIMEOUT = 5000;
/** Time between polls in ms. */
private static final int POLL_TIME = 100;
+ /** Timeout to get map message in ms. */
+ private static final int GET_UNREAD_MESSAGE_TIMEOUT = 10000;
+ /** Timeout to set map message status in ms. */
+ private static final int SET_MESSAGE_STATUS_TIMEOUT = 2000;
private abstract class FlagReceiver extends BroadcastReceiver {
private int mExpectedFlags = 0;
@@ -98,6 +102,8 @@ public class BluetoothTestUtils extends Assert {
private static final int STATE_TURNING_ON_FLAG = 1 << 6;
private static final int STATE_ON_FLAG = 1 << 7;
private static final int STATE_TURNING_OFF_FLAG = 1 << 8;
+ private static final int STATE_GET_MESSAGE_FINISHED_FLAG = 1 << 9;
+ private static final int STATE_SET_MESSAGE_STATUS_FINISHED_FLAG = 1 << 10;
public BluetoothReceiver(int expectedFlags) {
super(expectedFlags);
@@ -231,6 +237,9 @@ public class BluetoothTestUtils extends Assert {
case BluetoothProfile.PAN:
mConnectionAction = BluetoothPan.ACTION_CONNECTION_STATE_CHANGED;
break;
+ case BluetoothProfile.MAP_CLIENT:
+ mConnectionAction = BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED;
+ break;
default:
mConnectionAction = null;
}
@@ -308,6 +317,34 @@ public class BluetoothTestUtils extends Assert {
}
}
+
+ private class MceSetMessageStatusReceiver extends FlagReceiver {
+ private static final int MESSAGE_RECEIVED_FLAG = 1;
+ private static final int STATUS_CHANGED_FLAG = 1 << 1;
+
+ public MceSetMessageStatusReceiver(int expectedFlags) {
+ super(expectedFlags);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (BluetoothMapClient.ACTION_MESSAGE_RECEIVED.equals(intent.getAction())) {
+ String handle = intent.getStringExtra(BluetoothMapClient.EXTRA_MESSAGE_HANDLE);
+ assertNotNull(handle);
+ setFiredFlag(MESSAGE_RECEIVED_FLAG);
+ mMsgHandle = handle;
+ } else if (BluetoothMapClient.ACTION_MESSAGE_DELETED_STATUS_CHANGED.equals(intent.getAction())) {
+ int result = intent.getIntExtra(BluetoothMapClient.EXTRA_RESULT_CODE, BluetoothMapClient.RESULT_FAILURE);
+ assertEquals(result, BluetoothMapClient.RESULT_SUCCESS);
+ setFiredFlag(STATUS_CHANGED_FLAG);
+ } else if (BluetoothMapClient.ACTION_MESSAGE_READ_STATUS_CHANGED.equals(intent.getAction())) {
+ int result = intent.getIntExtra(BluetoothMapClient.EXTRA_RESULT_CODE, BluetoothMapClient.RESULT_FAILURE);
+ assertEquals(result, BluetoothMapClient.RESULT_SUCCESS);
+ setFiredFlag(STATUS_CHANGED_FLAG);
+ }
+ }
+ }
+
private BluetoothProfile.ServiceListener mServiceListener =
new BluetoothProfile.ServiceListener() {
@Override
@@ -326,6 +363,9 @@ public class BluetoothTestUtils extends Assert {
case BluetoothProfile.PAN:
mPan = (BluetoothPan) proxy;
break;
+ case BluetoothProfile.MAP_CLIENT:
+ mMce = (BluetoothMapClient) proxy;
+ break;
}
}
}
@@ -346,6 +386,9 @@ public class BluetoothTestUtils extends Assert {
case BluetoothProfile.PAN:
mPan = null;
break;
+ case BluetoothProfile.MAP_CLIENT:
+ mMce = null;
+ break;
}
}
}
@@ -362,6 +405,8 @@ public class BluetoothTestUtils extends Assert {
private BluetoothHeadset mHeadset = null;
private BluetoothHidHost mInput = null;
private BluetoothPan mPan = null;
+ private BluetoothMapClient mMce = null;
+ private String mMsgHandle = null;
/**
* Creates a utility instance for testing Bluetooth.
@@ -898,7 +943,7 @@ public class BluetoothTestUtils extends Assert {
* @param adapter The BT adapter.
* @param device The remote device.
* @param profile The profile to connect. One of {@link BluetoothProfile#A2DP},
- * {@link BluetoothProfile#HEADSET}, or {@link BluetoothProfile#HID_HOST}.
+ * {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#HID_HOST} or {@link BluetoothProfile#MAP_CLIENT}..
* @param methodName The method name to printed in the logs. If null, will be
* "connectProfile(profile=&lt;profile&gt;, device=&lt;device&gt;)"
*/
@@ -941,6 +986,8 @@ public class BluetoothTestUtils extends Assert {
assertTrue(((BluetoothHeadset)proxy).connect(device));
} else if (profile == BluetoothProfile.HID_HOST) {
assertTrue(((BluetoothHidHost)proxy).connect(device));
+ } else if (profile == BluetoothProfile.MAP_CLIENT) {
+ assertTrue(((BluetoothMapClient)proxy).connect(device));
}
break;
default:
@@ -1016,6 +1063,8 @@ public class BluetoothTestUtils extends Assert {
assertTrue(((BluetoothHeadset)proxy).disconnect(device));
} else if (profile == BluetoothProfile.HID_HOST) {
assertTrue(((BluetoothHidHost)proxy).disconnect(device));
+ } else if (profile == BluetoothProfile.MAP_CLIENT) {
+ assertTrue(((BluetoothMapClient)proxy).disconnect(device));
}
break;
case BluetoothProfile.STATE_DISCONNECTED:
@@ -1373,6 +1422,89 @@ public class BluetoothTestUtils extends Assert {
}
}
+ public void mceGetUnreadMessage(BluetoothAdapter adapter, BluetoothDevice device) {
+ int mask;
+ String methodName = "getUnreadMessage";
+
+ if (!adapter.isEnabled()) {
+ fail(String.format("%s bluetooth not enabled", methodName));
+ }
+
+ if (!adapter.getBondedDevices().contains(device)) {
+ fail(String.format("%s device not paired", methodName));
+ }
+
+ mMce = (BluetoothMapClient) connectProxy(adapter, BluetoothProfile.MAP_CLIENT);
+ assertNotNull(mMce);
+
+ if (mMce.getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) {
+ fail(String.format("%s device is not connected", methodName));
+ }
+
+ mMsgHandle = null;
+ mask = MceSetMessageStatusReceiver.MESSAGE_RECEIVED_FLAG;
+ MceSetMessageStatusReceiver receiver = getMceSetMessageStatusReceiver(device, mask);
+ assertTrue(mMce.getUnreadMessages(device));
+
+ long s = System.currentTimeMillis();
+ while (System.currentTimeMillis() - s < GET_UNREAD_MESSAGE_TIMEOUT) {
+ if ((receiver.getFiredFlags() & mask) == mask) {
+ writeOutput(String.format("%s completed", methodName));
+ removeReceiver(receiver);
+ return;
+ }
+ sleep(POLL_TIME);
+ }
+ int firedFlags = receiver.getFiredFlags();
+ removeReceiver(receiver);
+ fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%s)",
+ methodName, mMce.getConnectionState(device), BluetoothMapClient.STATE_CONNECTED, firedFlags, mask));
+ }
+
+ /**
+ * Set a message to read/unread/deleted/undeleted
+ */
+ public void mceSetMessageStatus(BluetoothAdapter adapter, BluetoothDevice device, int status) {
+ int mask;
+ String methodName = "setMessageStatus";
+
+ if (!adapter.isEnabled()) {
+ fail(String.format("%s bluetooth not enabled", methodName));
+ }
+
+ if (!adapter.getBondedDevices().contains(device)) {
+ fail(String.format("%s device not paired", methodName));
+ }
+
+ mMce = (BluetoothMapClient) connectProxy(adapter, BluetoothProfile.MAP_CLIENT);
+ assertNotNull(mMce);
+
+ if (mMce.getConnectionState(device) != BluetoothProfile.STATE_CONNECTED) {
+ fail(String.format("%s device is not connected", methodName));
+ }
+
+ assertNotNull(mMsgHandle);
+ mask = MceSetMessageStatusReceiver.STATUS_CHANGED_FLAG;
+ MceSetMessageStatusReceiver receiver = getMceSetMessageStatusReceiver(device, mask);
+
+ assertTrue(mMce.setMessageStatus(device, mMsgHandle, status));
+
+ long s = System.currentTimeMillis();
+ while (System.currentTimeMillis() - s < SET_MESSAGE_STATUS_TIMEOUT) {
+ if ((receiver.getFiredFlags() & mask) == mask) {
+ writeOutput(String.format("%s completed", methodName));
+ removeReceiver(receiver);
+ return;
+ }
+ sleep(POLL_TIME);
+ }
+
+ int firedFlags = receiver.getFiredFlags();
+ removeReceiver(receiver);
+ fail(String.format("%s timeout: state=%d (expected %d), flags=0x%x (expected 0x%s)",
+ methodName, mMce.getConnectionState(device), BluetoothPan.STATE_CONNECTED, firedFlags, mask));
+ }
+
private void addReceiver(BroadcastReceiver receiver, String[] actions) {
IntentFilter filter = new IntentFilter();
for (String action: actions) {
@@ -1408,7 +1540,8 @@ public class BluetoothTestUtils extends Assert {
String[] actions = {
BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED,
BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED,
- BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED};
+ BluetoothHidHost.ACTION_CONNECTION_STATE_CHANGED,
+ BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED};
ConnectProfileReceiver receiver = new ConnectProfileReceiver(device, profile,
expectedFlags);
addReceiver(receiver, actions);
@@ -1430,6 +1563,16 @@ public class BluetoothTestUtils extends Assert {
return receiver;
}
+ private MceSetMessageStatusReceiver getMceSetMessageStatusReceiver(BluetoothDevice device,
+ int expectedFlags) {
+ String[] actions = {BluetoothMapClient.ACTION_MESSAGE_RECEIVED,
+ BluetoothMapClient.ACTION_MESSAGE_READ_STATUS_CHANGED,
+ BluetoothMapClient.ACTION_MESSAGE_DELETED_STATUS_CHANGED};
+ MceSetMessageStatusReceiver receiver = new MceSetMessageStatusReceiver(expectedFlags);
+ addReceiver(receiver, actions);
+ return receiver;
+ }
+
private void removeReceiver(BroadcastReceiver receiver) {
mContext.unregisterReceiver(receiver);
mReceivers.remove(receiver);
@@ -1456,6 +1599,10 @@ public class BluetoothTestUtils extends Assert {
if (mPan != null) {
return mPan;
}
+ case BluetoothProfile.MAP_CLIENT:
+ if (mMce != null) {
+ return mMce;
+ }
break;
default:
return null;
@@ -1483,6 +1630,11 @@ public class BluetoothTestUtils extends Assert {
sleep(POLL_TIME);
}
return mPan;
+ case BluetoothProfile.MAP_CLIENT:
+ while (mMce == null && System.currentTimeMillis() - s < CONNECT_PROXY_TIMEOUT) {
+ sleep(POLL_TIME);
+ }
+ return mMce;
default:
return null;
}
diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
index bfcf52af80bf..eb695258142a 100644
--- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
@@ -86,7 +86,7 @@ public class ImeInsetsSourceConsumerTest {
false,
new DisplayCutout(
Insets.of(10, 10, 10, 10), rect, rect, rect, rect),
- SOFT_INPUT_ADJUST_RESIZE, 0);
+ SOFT_INPUT_ADJUST_RESIZE, 0, 0);
mImeConsumer = (ImeInsetsSourceConsumer) mController.getSourceConsumer(ITYPE_IME);
});
}
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index c36f1067149e..801cd4ddb94e 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -165,7 +165,7 @@ public class InsetsControllerTest {
false,
new DisplayCutout(
Insets.of(10, 10, 10, 10), rect, rect, rect, rect),
- SOFT_INPUT_ADJUST_RESIZE, 0);
+ SOFT_INPUT_ADJUST_RESIZE, 0, 0);
mController.onFrameChanged(new Rect(0, 0, 100, 100));
});
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index 5260ef83cc4f..c7d835ca7c7e 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -29,6 +29,7 @@ import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
import static android.view.WindowInsets.Type.ime;
import static android.view.WindowInsets.Type.navigationBars;
import static android.view.WindowInsets.Type.statusBars;
+import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN;
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
@@ -77,7 +78,7 @@ public class InsetsStateTest {
mState.getSource(ITYPE_IME).setVisible(true);
SparseIntArray typeSideMap = new SparseIntArray();
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, typeSideMap);
+ false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, 0, typeSideMap);
assertEquals(Insets.of(0, 100, 0, 100), insets.getSystemWindowInsets());
assertEquals(Insets.of(0, 100, 0, 100), insets.getInsets(Type.all()));
assertEquals(ISIDE_TOP, typeSideMap.get(ITYPE_STATUS_BAR));
@@ -96,7 +97,7 @@ public class InsetsStateTest {
mState.getSource(ITYPE_IME).setFrame(new Rect(0, 100, 100, 300));
mState.getSource(ITYPE_IME).setVisible(true);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, null);
+ false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, 0, null);
assertEquals(100, insets.getStableInsetBottom());
assertEquals(Insets.of(0, 0, 0, 100), insets.getInsetsIgnoringVisibility(Type.systemBars()));
assertEquals(Insets.of(0, 0, 0, 200), insets.getSystemWindowInsets());
@@ -115,7 +116,7 @@ public class InsetsStateTest {
mState.getSource(ITYPE_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300));
mState.getSource(ITYPE_NAVIGATION_BAR).setVisible(true);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, DisplayCutout.NO_CUTOUT, 0, 0, null);
+ false, DisplayCutout.NO_CUTOUT, 0, 0, 0, null);
assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.statusBars()));
assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(Type.navigationBars()));
@@ -131,7 +132,7 @@ public class InsetsStateTest {
mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300));
mState.getSource(ITYPE_IME).setVisible(true);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, 0, null);
+ false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, 0, 0, null);
assertEquals(0, insets.getSystemWindowInsetBottom());
assertEquals(100, insets.getInsets(ime()).bottom);
assertTrue(insets.isVisible(ime()));
@@ -147,11 +148,28 @@ public class InsetsStateTest {
mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300));
mState.getSource(ITYPE_IME).setVisible(true);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING,
+ false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, 0,
SYSTEM_UI_FLAG_LAYOUT_STABLE, null);
assertEquals(100, insets.getSystemWindowInsetTop());
insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, false,
- DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING,
+ DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, 0,
+ 0 /* legacySystemUiFlags */, null);
+ assertEquals(0, insets.getSystemWindowInsetTop());
+ }
+ }
+
+ @Test
+ public void testCalculateInsets_systemUiFlagLayoutStable_windowFlagFullscreen() {
+ try (final InsetsModeSession session =
+ new InsetsModeSession(ViewRootImpl.NEW_INSETS_MODE_FULL)) {
+ mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
+ mState.getSource(ITYPE_STATUS_BAR).setVisible(false);
+ WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
+ false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, FLAG_FULLSCREEN,
+ SYSTEM_UI_FLAG_LAYOUT_STABLE, null);
+ assertEquals(0, insets.getSystemWindowInsetTop());
+ insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, false,
+ DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, 0,
0 /* legacySystemUiFlags */, null);
assertEquals(0, insets.getSystemWindowInsetTop());
}
@@ -195,7 +213,7 @@ public class InsetsStateTest {
mState.getSource(ITYPE_EXTRA_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300));
mState.getSource(ITYPE_EXTRA_NAVIGATION_BAR).setVisible(true);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, DisplayCutout.NO_CUTOUT, 0, 0, null);
+ false, DisplayCutout.NO_CUTOUT, 0, 0, 0, null);
assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.statusBars()));
assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(Type.navigationBars()));
@@ -211,7 +229,7 @@ public class InsetsStateTest {
mState.getSource(ITYPE_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300));
mState.getSource(ITYPE_NAVIGATION_BAR).setVisible(true);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, DisplayCutout.NO_CUTOUT, 0, 0, null);
+ false, DisplayCutout.NO_CUTOUT, 0, 0, 0, null);
assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.statusBars()));
assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(Type.navigationBars()));
@@ -226,7 +244,7 @@ public class InsetsStateTest {
mState.getSource(ITYPE_IME).setVisible(true);
mState.removeSource(ITYPE_IME);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, false,
- DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, null);
+ DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, 0, null);
assertEquals(0, insets.getSystemWindowInsetBottom());
}
diff --git a/data/etc/preinstalled-packages-platform-overlays.xml b/data/etc/preinstalled-packages-platform-overlays.xml
index ecd7d40ff21d..84c1897a2b62 100644
--- a/data/etc/preinstalled-packages-platform-overlays.xml
+++ b/data/etc/preinstalled-packages-platform-overlays.xml
@@ -19,32 +19,250 @@
<config>
<install-in-user-type package="com.android.internal.display.cutout.emulation.corner">
<install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.internal.display.cutout.emulation.double">
<install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.internal.display.cutout.emulation.hole">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.internal.display.cutout.emulation.tall">
<install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.internal.display.cutout.emulation.waterfall">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.internal.systemui.navbar.twobutton">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.internal.systemui.navbar.threebutton">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.internal.systemui.navbar.gestural">
<install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.internal.systemui.navbar.gestural_extra_wide_back">
<install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.internal.systemui.navbar.gestural_narrow_back">
<install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.internal.systemui.navbar.gestural_wide_back">
<install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
</install-in-user-type>
- <install-in-user-type package="com.android.internal.systemui.navbar.threebutton">
+ <install-in-user-type package="com.android.internal.systemui.onehanded.gestural">
<install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
</install-in-user-type>
- <install-in-user-type package="com.android.internal.systemui.navbar.twobutton">
+ <install-in-user-type package="com.android.theme.color.amethyst">
<install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
</install-in-user-type>
- <install-in-user-type package="com.android.internal.systemui.onehanded.gestural">
+ <install-in-user-type package="com.android.theme.color.aquamarine">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.color.black">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.color.carbon">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.color.cinnamon">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.color.green">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.color.ocean">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.color.orchid">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.color.palette">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.color.purple">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.color.sand">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.color.space">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.color.tangerine">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.font.notoserifsource">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.circular.android">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.circular.launcher">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.circular.settings">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.circular.systemui">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.circular.themepicker">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.filled.android">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.filled.launcher">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.filled.settings">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.filled.systemui">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.filled.themepicker">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.kai.android">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.kai.launcher">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.kai.settings">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.kai.systemui">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.kai.themepicker">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.rounded.android">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.rounded.launcher">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.rounded.settings">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.rounded.systemui">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.rounded.themepicker">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.sam.android">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.sam.launcher">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.sam.settings">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.sam.systemui">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.sam.themepicker">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.victor.android">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.victor.launcher">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.victor.settings">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.victor.systemui">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon_pack.victor.themepicker">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon.pebble">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon.roundedrect">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon.squircle">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon.taperedrect">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon.teardrop">
+ <install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
+ </install-in-user-type>
+ <install-in-user-type package="com.android.theme.icon.vessel">
<install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
</install-in-user-type>
</config>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index c710bed29361..06791421d60f 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -198,6 +198,8 @@ applications that come with the platform
<permission name="android.permission.MANAGE_USERS" />
<permission name="android.permission.UPDATE_APP_OPS_STATS"/>
<permission name="android.permission.USE_RESERVED_DISK"/>
+ <permission name="android.permission.LOG_COMPAT_CHANGE" />
+ <permission name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
</privapp-permissions>
<privapp-permissions package="com.android.providers.contacts">
diff --git a/libs/hwui/hwui/MinikinSkia.cpp b/libs/hwui/hwui/MinikinSkia.cpp
index 6a12a203b9f8..a6137b073d5a 100644
--- a/libs/hwui/hwui/MinikinSkia.cpp
+++ b/libs/hwui/hwui/MinikinSkia.cpp
@@ -125,22 +125,22 @@ const std::vector<minikin::FontVariation>& MinikinFontSkia::GetAxes() const {
std::shared_ptr<minikin::MinikinFont> MinikinFontSkia::createFontWithVariation(
const std::vector<minikin::FontVariation>& variations) const {
- SkFontArguments params;
+ SkFontArguments args;
int ttcIndex;
std::unique_ptr<SkStreamAsset> stream(mTypeface->openStream(&ttcIndex));
LOG_ALWAYS_FATAL_IF(stream == nullptr, "openStream failed");
- params.setCollectionIndex(ttcIndex);
- std::vector<SkFontArguments::Axis> skAxes;
- skAxes.resize(variations.size());
+ args.setCollectionIndex(ttcIndex);
+ std::vector<SkFontArguments::VariationPosition::Coordinate> skVariation;
+ skVariation.resize(variations.size());
for (size_t i = 0; i < variations.size(); i++) {
- skAxes[i].fTag = variations[i].axisTag;
- skAxes[i].fStyleValue = SkFloatToScalar(variations[i].value);
+ skVariation[i].axis = variations[i].axisTag;
+ skVariation[i].value = SkFloatToScalar(variations[i].value);
}
- params.setAxes(skAxes.data(), skAxes.size());
+ args.setVariationDesignPosition({skVariation.data(), static_cast<int>(skVariation.size())});
sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
- sk_sp<SkTypeface> face(fm->makeFromStream(std::move(stream), params));
+ sk_sp<SkTypeface> face(fm->makeFromStream(std::move(stream), args));
return std::make_shared<MinikinFontSkia>(std::move(face), mFontData, mFontSize, mFilePath,
ttcIndex, variations);
diff --git a/libs/hwui/jni/FontFamily.cpp b/libs/hwui/jni/FontFamily.cpp
index a2fef1e19328..68eaa0a3ca54 100644
--- a/libs/hwui/jni/FontFamily.cpp
+++ b/libs/hwui/jni/FontFamily.cpp
@@ -104,21 +104,21 @@ static jlong FontFamily_getFamilyReleaseFunc(CRITICAL_JNI_PARAMS) {
static bool addSkTypeface(NativeFamilyBuilder* builder, sk_sp<SkData>&& data, int ttcIndex,
jint weight, jint italic) {
- FatVector<SkFontArguments::Axis, 2> skiaAxes;
+ FatVector<SkFontArguments::VariationPosition::Coordinate, 2> skVariation;
for (const auto& axis : builder->axes) {
- skiaAxes.emplace_back(SkFontArguments::Axis{axis.axisTag, axis.value});
+ skVariation.push_back({axis.axisTag, axis.value});
}
const size_t fontSize = data->size();
const void* fontPtr = data->data();
std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(std::move(data)));
- SkFontArguments params;
- params.setCollectionIndex(ttcIndex);
- params.setAxes(skiaAxes.data(), skiaAxes.size());
+ SkFontArguments args;
+ args.setCollectionIndex(ttcIndex);
+ args.setVariationDesignPosition({skVariation.data(), static_cast<int>(skVariation.size())});
sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
- sk_sp<SkTypeface> face(fm->makeFromStream(std::move(fontData), params));
+ sk_sp<SkTypeface> face(fm->makeFromStream(std::move(fontData), args));
if (face == NULL) {
ALOGE("addFont failed to create font, invalid request");
builder->axes.clear();
diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp
index 5714cd1d0390..996cdceed8a7 100644
--- a/libs/hwui/jni/fonts/Font.cpp
+++ b/libs/hwui/jni/fonts/Font.cpp
@@ -93,19 +93,19 @@ static jlong Font_Builder_build(JNIEnv* env, jobject clazz, jlong builderPtr, jo
sk_sp<SkData> data(SkData::MakeWithProc(fontPtr, fontSize,
release_global_ref, reinterpret_cast<void*>(fontRef)));
- FatVector<SkFontArguments::Axis, 2> skiaAxes;
+ FatVector<SkFontArguments::VariationPosition::Coordinate, 2> skVariation;
for (const auto& axis : builder->axes) {
- skiaAxes.emplace_back(SkFontArguments::Axis{axis.axisTag, axis.value});
+ skVariation.push_back({axis.axisTag, axis.value});
}
std::unique_ptr<SkStreamAsset> fontData(new SkMemoryStream(std::move(data)));
- SkFontArguments params;
- params.setCollectionIndex(ttcIndex);
- params.setAxes(skiaAxes.data(), skiaAxes.size());
+ SkFontArguments args;
+ args.setCollectionIndex(ttcIndex);
+ args.setVariationDesignPosition({skVariation.data(), static_cast<int>(skVariation.size())});
sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault());
- sk_sp<SkTypeface> face(fm->makeFromStream(std::move(fontData), params));
+ sk_sp<SkTypeface> face(fm->makeFromStream(std::move(fontData), args));
if (face == nullptr) {
jniThrowException(env, "java/lang/IllegalArgumentException",
"Failed to create internal object. maybe invalid font data.");
diff --git a/location/java/android/location/package.html b/location/java/android/location/package.html
index 2355e725b6c5..20c5c54d6921 100644
--- a/location/java/android/location/package.html
+++ b/location/java/android/location/package.html
@@ -6,7 +6,7 @@
<p class="warning">
<strong>This API is not the recommended method for accessing Android location.</strong><br>
The
-<a href="{@docRoot}reference/com/google/android/gms/location/package-summary.html">Google Location Services API</a>,
+<a href="https://developers.google.com/android/reference/com/google/android/gms/location/package-summary">Google Location Services API</a>,
part of Google Play services, is the preferred way to add location-awareness to
your app. It offers a simpler API, higher accuracy, low-power geofencing, and
more. If you are currently using the android.location API, you are strongly
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 482a3812b838..18c2957b1adc 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -173,5 +173,6 @@ public class SecureSettings {
Settings.Secure.ONE_HANDED_MODE_TIMEOUT,
Settings.Secure.TAPS_APP_TO_EXIT,
Settings.Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED,
+ Settings.Secure.PANIC_GESTURE_ENABLED,
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index b337e609e281..91f3f4af0566 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -260,5 +260,6 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.ONE_HANDED_MODE_TIMEOUT, ANY_INTEGER_VALIDATOR);
VALIDATORS.put(Secure.TAPS_APP_TO_EXIT, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.PANIC_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index a994fdaa4591..27576b107efd 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -2026,6 +2026,13 @@ class SettingsProtoDumpUtil {
dumpSetting(s, p,
Settings.Secure.EMERGENCY_ASSISTANCE_APPLICATION,
SecureSettingsProto.EMERGENCY_ASSISTANCE_APPLICATION);
+
+ final long emergencyResponseToken = p.start(SecureSettingsProto.EMERGENCY_RESPONSE);
+ dumpSetting(s, p,
+ Settings.Secure.PANIC_GESTURE_ENABLED,
+ SecureSettingsProto.EmergencyResponse.PANIC_GESTURE_ENABLED);
+ p.end(emergencyResponseToken);
+
dumpSetting(s, p,
Settings.Secure.ENHANCED_VOICE_PRIVACY_ENABLED,
SecureSettingsProto.ENHANCED_VOICE_PRIVACY_ENABLED);
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 6ecf303c6dc8..dfc47587b91c 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -140,7 +140,8 @@ android_library {
"testables",
"truth-prebuilt",
"dagger2",
- "jsr330"
+ "jsr330",
+ "WindowManager-Shell",
],
libs: [
"android.test.runner",
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index 2a2ba1b0ccaa..14097b12e730 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -39,4 +39,6 @@
-keep public class * extends com.android.systemui.SystemUI {
public <init>(android.content.Context);
-} \ No newline at end of file
+}
+
+-keep class com.android.wm.shell.* \ No newline at end of file
diff --git a/packages/SystemUI/res/values/config_tv.xml b/packages/SystemUI/res/values/config_tv.xml
index ffd58dcfd50d..d8c9428f2676 100644
--- a/packages/SystemUI/res/values/config_tv.xml
+++ b/packages/SystemUI/res/values/config_tv.xml
@@ -22,4 +22,13 @@
<!-- Bounds [left top right bottom] on screen for picture-in-picture (PIP) windows,
when the PIP menu is shown in center. -->
<string translatable="false" name="pip_menu_bounds">"596 280 1324 690"</string>
+
+ <!-- Whether to enable microphone disclosure indicator
+ (com.android.systemui.statusbar.tv.micdisclosure.AudioRecordingDisclosureBar). -->
+ <bool name="audio_recording_disclosure_enabled">true</bool>
+
+ <!-- Whether the indicator should expand and show the recording application's label.
+ When disabled (false) the "minimized" indicator would appear on the screen whenever an
+ application is recording, but will not reveal to the user what application this is. -->
+ <bool name="audio_recording_disclosure_reveal_packages">false</bool>
</resources>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 8212d6159737..a56f6f56836a 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -92,6 +92,8 @@
<item type="id" name="requires_remeasuring"/>
+ <item type="id" name="secondary_home_handle" />
+
<!-- Whether the icon is from a notification for which targetSdk < L -->
<item type="id" name="icon_is_pre_L"/>
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
index 332a00d1d4e7..398a2c9c9d41 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
@@ -16,6 +16,7 @@
package com.android.systemui.accessibility;
+import android.annotation.NonNull;
import android.content.Context;
import android.graphics.PixelFormat;
import android.provider.Settings;
@@ -23,6 +24,7 @@ import android.view.Gravity;
import android.view.WindowManager;
import android.widget.ImageView;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
/**
@@ -42,23 +44,36 @@ class MagnificationModeSwitch {
private boolean mIsVisible = false;
MagnificationModeSwitch(Context context) {
+ this(context, createView(context));
+ }
+
+ @VisibleForTesting
+ MagnificationModeSwitch(Context context, @NonNull ImageView imageView) {
mContext = context;
mWindowManager = (WindowManager) mContext.getSystemService(
Context.WINDOW_SERVICE);
mParams = createLayoutParams();
- mImageView = createView(mContext, mMagnificationMode);
+ mImageView = imageView;
+ applyResourcesValues();
mImageView.setOnClickListener(
view -> {
removeButton();
toggleMagnificationMode();
});
+ mImageView.setImageResource(getIconResId(mMagnificationMode));
+ }
+
+ private void applyResourcesValues() {
+ final int padding = mContext.getResources().getDimensionPixelSize(
+ R.dimen.magnification_switch_button_padding);
+ mImageView.setPadding(padding, padding, padding, padding);
}
void removeButton() {
- mImageView.animate().cancel();
if (!mIsVisible) {
return;
}
+ mImageView.animate().cancel();
mWindowManager.removeView(mImageView);
mIsVisible = false;
}
@@ -72,7 +87,7 @@ class MagnificationModeSwitch {
mWindowManager.addView(mImageView, mParams);
mIsVisible = true;
}
-
+ mImageView.setAlpha(1.0f);
// TODO(b/143852371): use accessibility timeout as a delay.
// Dismiss the magnification switch button after the button is displayed for a period of
// time.
@@ -82,30 +97,29 @@ class MagnificationModeSwitch {
.setStartDelay(START_DELAY_MS)
.setDuration(DURATION_MS)
.withEndAction(
- () -> removeButton());
+ () -> removeButton())
+ .start();
}
private void toggleMagnificationMode() {
final int newMode =
mMagnificationMode ^ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL;
mMagnificationMode = newMode;
+ mImageView.setImageResource(getIconResId(newMode));
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, newMode);
}
- private static ImageView createView(Context context, int mode) {
- final int padding = context.getResources().getDimensionPixelSize(
- R.dimen.magnification_switch_button_padding);
+ private static ImageView createView(Context context) {
ImageView imageView = new ImageView(context);
- imageView.setImageResource(getIconResId(mode));
imageView.setClickable(true);
imageView.setFocusable(true);
- imageView.setPadding(padding, padding, padding, padding);
imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
return imageView;
}
- private static int getIconResId(int mode) {
+ @VisibleForTesting
+ static int getIconResId(int mode) {
return (mode == Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN)
? R.drawable.ic_open_in_new_window
: R.drawable.ic_open_in_new_fullscreen;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 7e9faff8c296..8b6581ff1856 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -258,8 +258,8 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSPARENT);
params.gravity = Gravity.TOP | Gravity.LEFT;
- params.x = mMagnificationFrame.left;
- params.y = mMagnificationFrame.top;
+ params.x = mMagnificationFrame.left - mMirrorSurfaceMargin;
+ params.y = mMagnificationFrame.top - mMirrorSurfaceMargin;
params.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
params.setTitle(mContext.getString(R.string.magnification_window_title));
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 7b441d64af06..42911374dc87 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -246,11 +246,6 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
IActivityTaskManager getActivityTaskManager() {
return ActivityTaskManager.getService();
}
-
- IFingerprintService getFingerprintService() {
- return IFingerprintService.Stub.asInterface(
- ServiceManager.getService(Context.FINGERPRINT_SERVICE));
- }
}
@Inject
@@ -276,23 +271,12 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
mActivityTaskManager = mInjector.getActivityTaskManager();
- if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
- IFingerprintService fingerprintService = mInjector.getFingerprintService();
- if (fingerprintService == null) {
- Log.e(TAG, "FEATURE_FINGERPRINT is available, but FingerprintService is null");
- } else {
- boolean isUdfps = false;
- try {
- // TODO(b/160024833): Enumerate through all of the sensors and check whether
- // at least one of them is UDFPS.
- isUdfps = fingerprintService.isUdfps(0 /* sensorId */);
- } catch (RemoteException e) {
- Log.w(TAG, "Unable to check whether the sensor is a UDFPS", e);
- }
- if (isUdfps) {
- mUdfpsController = new UdfpsController(mContext, fingerprintService,
- mWindowManager);
- }
+ final FingerprintManager fpm = mContext.getSystemService(FingerprintManager.class);
+ if (fpm != null && fpm.isHardwareDetected()) {
+ // TODO(b/160024833): Enumerate through all of the sensors and check whether
+ // at least one of them is UDFPS.
+ if (fpm.isUdfps()) {
+ mUdfpsController = new UdfpsController(mContext, mWindowManager);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 97e97ffaae0c..739c2b155444 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -20,6 +20,7 @@ import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.PixelFormat;
import android.graphics.Point;
+import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.IFingerprintService;
import android.hardware.fingerprint.IUdfpsOverlayController;
import android.os.Handler;
@@ -44,7 +45,7 @@ class UdfpsController {
private static final String TAG = "UdfpsController";
private final Context mContext;
- private final IFingerprintService mFingerprintService;
+ private final FingerprintManager mFingerprintManager;
private final WindowManager mWindowManager;
private final Handler mHandler;
@@ -94,10 +95,9 @@ class UdfpsController {
}
};
- UdfpsController(Context context, IFingerprintService fingerprintService,
- WindowManager windowManager) {
+ UdfpsController(Context context, WindowManager windowManager) {
mContext = context;
- mFingerprintService = fingerprintService;
+ mFingerprintManager = context.getSystemService(FingerprintManager.class);
mWindowManager = windowManager;
mHandler = new Handler(Looper.getMainLooper());
start();
@@ -133,11 +133,7 @@ class UdfpsController {
mHbmEnableCommand = mContext.getResources().getString(R.string.udfps_hbm_enable_command);
mHbmDisableCommand = mContext.getResources().getString(R.string.udfps_hbm_disable_command);
- try {
- mFingerprintService.setUdfpsOverlayController(new UdfpsOverlayController());
- } catch (RemoteException e) {
- Log.e(TAG, "start | failed to set UDFPS controller", e);
- }
+ mFingerprintManager.setUdfpsOverlayController(new UdfpsOverlayController());
mIsOverlayShowing = false;
}
@@ -156,6 +152,7 @@ class UdfpsController {
}
private void hideUdfpsOverlay() {
+ onFingerUp();
mHandler.post(() -> {
Log.v(TAG, "hideUdfpsOverlay | removing window");
if (mIsOverlayShowing) {
@@ -174,19 +171,11 @@ class UdfpsController {
Log.e(TAG, "onFingerDown | failed to enable HBM: " + e.getMessage());
}
mView.onFingerDown();
- try {
- mFingerprintService.onFingerDown(x, y, minor, major);
- } catch (RemoteException e) {
- Log.e(TAG, "onFingerDown | failed to propagate onFingerDown", e);
- }
+ mFingerprintManager.onFingerDown(x, y, minor, major);
}
private void onFingerUp() {
- try {
- mFingerprintService.onFingerUp();
- } catch (RemoteException e) {
- Log.e(TAG, "onFingeUp | failed to propagate onFingerUp", e);
- }
+ mFingerprintManager.onFingerUp();
mView.onFingerUp();
try {
FileWriter fw = new FileWriter(mHbmPath);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
index dfc7d53f08a7..8190550a74fc 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
@@ -143,12 +143,12 @@ public class UdfpsView extends View {
void onFingerDown() {
mIsFingerDown = true;
mSensorPaint.setStyle(Paint.Style.FILL);
- invalidate();
+ postInvalidate();
}
void onFingerUp() {
mIsFingerDown = false;
mSensorPaint.setStyle(Paint.Style.STROKE);
- invalidate();
+ postInvalidate();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java
index 57e2362bd511..d017bc0e31c2 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java
@@ -24,6 +24,8 @@ import android.content.pm.ShortcutInfo;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
@@ -40,15 +42,15 @@ import com.android.systemui.R;
*/
public class BubbleIconFactory extends BaseIconFactory {
+ private int mBadgeSize;
+
protected BubbleIconFactory(Context context) {
super(context, context.getResources().getConfiguration().densityDpi,
context.getResources().getDimensionPixelSize(R.dimen.individual_bubble_size));
- }
-
- int getBadgeSize() {
- return mContext.getResources().getDimensionPixelSize(
+ mBadgeSize = mContext.getResources().getDimensionPixelSize(
com.android.launcher3.icons.R.dimen.profile_badge_size);
}
+
/**
* Returns the drawable that the developer has provided to display in the bubble.
*/
@@ -78,18 +80,20 @@ public class BubbleIconFactory extends BaseIconFactory {
* will include the workprofile indicator on the badge if appropriate.
*/
BitmapInfo getBadgeBitmap(Drawable userBadgedAppIcon, boolean isImportantConversation) {
- Bitmap userBadgedBitmap = createIconBitmap(
- userBadgedAppIcon, 1f, getBadgeSize());
- ShadowGenerator shadowGenerator = new ShadowGenerator(getBadgeSize());
- if (!isImportantConversation) {
- Canvas c = new Canvas();
- c.setBitmap(userBadgedBitmap);
- shadowGenerator.recreateIcon(Bitmap.createBitmap(userBadgedBitmap), c);
- return createIconBitmap(userBadgedBitmap);
- } else {
- float ringStrokeWidth = mContext.getResources().getDimensionPixelSize(
+ ShadowGenerator shadowGenerator = new ShadowGenerator(mBadgeSize);
+ Bitmap userBadgedBitmap = createIconBitmap(userBadgedAppIcon, 1f, mBadgeSize);
+
+ if (userBadgedAppIcon instanceof AdaptiveIconDrawable) {
+ userBadgedBitmap = Bitmap.createScaledBitmap(
+ getCircleBitmap((AdaptiveIconDrawable) userBadgedAppIcon, /* size */
+ userBadgedAppIcon.getIntrinsicWidth()),
+ mBadgeSize, mBadgeSize, /* filter */ true);
+ }
+
+ if (isImportantConversation) {
+ final float ringStrokeWidth = mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.importance_ring_stroke_width);
- int importantConversationColor = mContext.getResources().getColor(
+ final int importantConversationColor = mContext.getResources().getColor(
com.android.settingslib.R.color.important_conversation, null);
Bitmap badgeAndRing = Bitmap.createBitmap(userBadgedBitmap.getWidth(),
userBadgedBitmap.getHeight(), userBadgedBitmap.getConfig());
@@ -114,9 +118,45 @@ public class BubbleIconFactory extends BaseIconFactory {
shadowGenerator.recreateIcon(Bitmap.createBitmap(badgeAndRing), c);
return createIconBitmap(badgeAndRing);
+ } else {
+ Canvas c = new Canvas();
+ c.setBitmap(userBadgedBitmap);
+ shadowGenerator.recreateIcon(Bitmap.createBitmap(userBadgedBitmap), c);
+ return createIconBitmap(userBadgedBitmap);
}
}
+ public Bitmap getCircleBitmap(AdaptiveIconDrawable icon, int size) {
+ Drawable foreground = icon.getForeground();
+ Drawable background = icon.getBackground();
+ Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas();
+ canvas.setBitmap(bitmap);
+
+ // Clip canvas to circle.
+ Path circlePath = new Path();
+ circlePath.addCircle(/* x */ size / 2f,
+ /* y */ size / 2f,
+ /* radius */ size / 2f,
+ Path.Direction.CW);
+ canvas.clipPath(circlePath);
+
+ // Draw background.
+ background.setBounds(0, 0, size, size);
+ background.draw(canvas);
+
+ // Draw foreground. The foreground and background drawables are derived from adaptive icons
+ // Some icon shapes fill more space than others, so adaptive icons are normalized to about
+ // the same size. This size is smaller than the original bounds, so we estimate
+ // the difference in this offset.
+ int offset = size / 5;
+ foreground.setBounds(-offset, -offset, size + offset, size + offset);
+ foreground.draw(canvas);
+
+ canvas.setBitmap(null);
+ return bitmap;
+ }
+
/**
* Returns a {@link BitmapInfo} for the entire bubble icon including the badge.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
index 78d70877a90e..e8dba8f3b585 100644
--- a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
@@ -217,10 +217,10 @@ class LogBuffer(
private fun dumpMessage(message: LogMessage, pw: PrintWriter) {
pw.print(DATE_FORMAT.format(message.timestamp))
pw.print(" ")
- pw.print(message.level)
+ pw.print(message.level.shortString)
pw.print(" ")
pw.print(message.tag)
- pw.print(" ")
+ pw.print(": ")
pw.println(message.printer(message))
}
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogLevel.kt b/packages/SystemUI/src/com/android/systemui/log/LogLevel.kt
index 7b9af0f91200..53f231c9f9d2 100644
--- a/packages/SystemUI/src/com/android/systemui/log/LogLevel.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/LogLevel.kt
@@ -21,11 +21,14 @@ import android.util.Log
/**
* Enum version of @Log.Level
*/
-enum class LogLevel(@Log.Level val nativeLevel: Int) {
- VERBOSE(Log.VERBOSE),
- DEBUG(Log.DEBUG),
- INFO(Log.INFO),
- WARNING(Log.WARN),
- ERROR(Log.ERROR),
- WTF(Log.ASSERT)
+enum class LogLevel(
+ @Log.Level val nativeLevel: Int,
+ val shortString: String
+) {
+ VERBOSE(Log.VERBOSE, "V"),
+ DEBUG(Log.DEBUG, "D"),
+ INFO(Log.INFO, "I"),
+ WARNING(Log.WARN, "W"),
+ ERROR(Log.ERROR, "E"),
+ WTF(Log.ASSERT, "WTF")
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index 370f9a762402..d9d46605f209 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -529,6 +529,7 @@ public class Divider extends SystemUI implements DividerView.DividerCallbacks,
updateVisibility(false /* visible */);
mMinimized = false;
removeDivider();
+ mImePositionProcessor.reset();
}
void ensureMinimizedSplit() {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java
index 9db389eba3d8..5aeca5e07bdd 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java
@@ -117,6 +117,18 @@ class DividerImeController implements DisplayImeController.ImePositionProcessor
&& (imeSplit.asBinder() == mSplits.mSecondary.token.asBinder());
}
+ void reset() {
+ mPaused = true;
+ mPausedTargetAdjusted = false;
+ mAdjustedWhileHidden = false;
+ mAnimation = null;
+ mAdjusted = mTargetAdjusted = false;
+ mImeWasShown = mTargetShown = false;
+ mTargetPrimaryDim = mTargetSecondaryDim = mLastPrimaryDim = mLastSecondaryDim = 0.f;
+ mSecondaryHasFocus = false;
+ mLastAdjustTop = -1;
+ }
+
private void updateDimTargets() {
final boolean splitIsVisible = !getView().isHidden();
mTargetPrimaryDim = (mSecondaryHasFocus && mTargetShown && splitIsVisible)
@@ -126,18 +138,20 @@ class DividerImeController implements DisplayImeController.ImePositionProcessor
}
@Override
- public void onImeStartPositioning(int displayId, int hiddenTop, int shownTop,
- boolean imeShouldShow, SurfaceControl.Transaction t) {
+ @ImeAnimationFlags
+ public int onImeStartPositioning(int displayId, int hiddenTop, int shownTop,
+ boolean imeShouldShow, boolean imeIsFloating, SurfaceControl.Transaction t) {
mHiddenTop = hiddenTop;
mShownTop = shownTop;
mTargetShown = imeShouldShow;
if (!isDividerVisible()) {
- return;
+ return 0;
}
final boolean splitIsVisible = !getView().isHidden();
mSecondaryHasFocus = getSecondaryHasFocus(displayId);
final boolean targetAdjusted = splitIsVisible && imeShouldShow && mSecondaryHasFocus
- && !getLayout().mDisplayLayout.isLandscape() && !mSplits.mDivider.isMinimized();
+ && !imeIsFloating && !getLayout().mDisplayLayout.isLandscape()
+ && !mSplits.mDivider.isMinimized();
if (mLastAdjustTop < 0) {
mLastAdjustTop = imeShouldShow ? hiddenTop : shownTop;
} else if (mLastAdjustTop != (imeShouldShow ? mShownTop : mHiddenTop)) {
@@ -155,7 +169,7 @@ class DividerImeController implements DisplayImeController.ImePositionProcessor
if (mPaused) {
mPausedTargetAdjusted = targetAdjusted;
if (DEBUG) Slog.d(TAG, " ime starting but paused " + dumpState());
- return;
+ return (targetAdjusted || mAdjusted) ? IME_ANIMATION_NO_ALPHA : 0;
}
mTargetAdjusted = targetAdjusted;
updateDimTargets();
@@ -174,6 +188,7 @@ class DividerImeController implements DisplayImeController.ImePositionProcessor
} else {
mAdjustedWhileHidden = true;
}
+ return (mTargetAdjusted || mAdjusted) ? IME_ANIMATION_NO_ALPHA : 0;
}
private void updateImeAdjustState() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index cdcfac87f1c8..d264af94947d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -84,7 +84,6 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
private final Map<View, MenuItem> mMenuItemsByView = new ArrayMap<>();
private OnMenuEventListener mMenuListener;
private boolean mDismissRtl;
- private boolean mIsForeground;
private ValueAnimator mFadeAnimator;
private boolean mAnimating;
@@ -191,9 +190,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
@Override
public void createMenu(ViewGroup parent, StatusBarNotification sbn) {
mParent = (ExpandableNotificationRow) parent;
- createMenuViews(true /* resetState */,
- sbn != null && (sbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE)
- != 0);
+ createMenuViews(true /* resetState */);
}
@Override
@@ -237,8 +234,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
// Menu hasn't been created yet, no need to do anything.
return;
}
- createMenuViews(!isMenuVisible() /* resetState */,
- (sbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0);
+ createMenuViews(!isMenuVisible() /* resetState */);
}
@Override
@@ -253,9 +249,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
mParent.removeListener();
}
- private void createMenuViews(boolean resetState, final boolean isForeground) {
- mIsForeground = isForeground;
-
+ private void createMenuViews(boolean resetState) {
final Resources res = mContext.getResources();
mHorizSpaceForIcon = res.getDimensionPixelSize(R.dimen.notification_menu_icon_size);
mVertSpaceForIcons = res.getDimensionPixelSize(R.dimen.notification_min_height);
@@ -266,7 +260,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
SHOW_NOTIFICATION_SNOOZE, 0) == 1;
// Construct the menu items based on the notification
- if (!isForeground && showSnooze) {
+ if (showSnooze) {
// Only show snooze for non-foreground notifications, and if the setting is on
mSnoozeItem = createSnoozeItem(mContext);
}
@@ -283,7 +277,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
mInfoItem = createInfoItem(mContext);
}
- if (!isForeground && showSnooze) {
+ if (showSnooze) {
mRightMenuItems.add(mSnoozeItem);
}
mRightMenuItems.add(mInfoItem);
@@ -789,7 +783,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
public void setDismissRtl(boolean dismissRtl) {
mDismissRtl = dismissRtl;
if (mMenuContainer != null) {
- createMenuViews(true, mIsForeground);
+ createMenuViews(true);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 27daf8615a31..837543c9bdae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -591,6 +591,7 @@ public class NavigationBarFragment extends LifecycleFragment implements Callback
.registerDisplayListener(this, new Handler(Looper.getMainLooper()));
mOrientationHandle = new QuickswitchOrientedNavHandle(getContext());
+ mOrientationHandle.setId(R.id.secondary_home_handle);
getBarTransitions().addDarkIntensityListener(mOrientationHandleIntensityListener);
mOrientationParams = new WindowManager.LayoutParams(0, 0,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java
index 8fcaa67e7614..23d03a4b225a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonDrawable.java
@@ -80,6 +80,22 @@ public class KeyButtonDrawable extends Drawable {
private final Paint mShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
private final ShadowDrawableState mState;
private AnimatedVectorDrawable mAnimatedDrawable;
+ private final Callback mAnimatedDrawableCallback = new Callback() {
+ @Override
+ public void invalidateDrawable(@NonNull Drawable who) {
+ invalidateSelf();
+ }
+
+ @Override
+ public void scheduleDrawable(@NonNull Drawable who, @NonNull Runnable what, long when) {
+ scheduleSelf(what, when);
+ }
+
+ @Override
+ public void unscheduleDrawable(@NonNull Drawable who, @NonNull Runnable what) {
+ unscheduleSelf(what);
+ }
+ };
public KeyButtonDrawable(Drawable d, @ColorInt int lightColor, @ColorInt int darkColor,
boolean horizontalFlip, Color ovalBackgroundColor) {
@@ -97,6 +113,7 @@ public class KeyButtonDrawable extends Drawable {
}
if (canAnimate()) {
mAnimatedDrawable = (AnimatedVectorDrawable) mState.mChildState.newDrawable().mutate();
+ mAnimatedDrawable.setCallback(mAnimatedDrawableCallback);
setDrawableBounds(mAnimatedDrawable);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 6bc0565510a2..c0602762ef29 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -26,6 +26,7 @@ import android.os.ServiceManager;
import android.os.UserHandle;
import com.android.internal.statusbar.IStatusBarService;
+import com.android.systemui.R;
import com.android.systemui.SystemUI;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.statusbar.CommandQueue;
@@ -72,6 +73,11 @@ public class TvStatusBar extends SystemUI implements CommandQueue.Callbacks {
} catch (RemoteException ex) {
// If the system process isn't there we're doomed anyway.
}
+
+ if (mContext.getResources().getBoolean(R.bool.audio_recording_disclosure_enabled)) {
+ // Creating AudioRecordingDisclosureBar and just letting it run
+ new AudioRecordingDisclosureBar(mContext);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java
index 36e360da857f..8e4e12358836 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/micdisclosure/AudioRecordingDisclosureBar.java
@@ -112,6 +112,13 @@ public class AudioRecordingDisclosureBar implements
*/
private final AudioActivityObserver[] mAudioActivityObservers;
/**
+ * Whether the indicator should expand and show the recording application's label.
+ * If disabled ({@code false}) the "minimized" ({@link #STATE_MINIMIZED}) indicator would appear
+ * on the screen whenever an application is recording, but will not reveal to the user what
+ * application this is.
+ */
+ private final boolean mRevealRecordingPackages;
+ /**
* Set of applications that we've notified the user about since the indicator came up. Meaning
* that if an application is in this list then at some point since the indicator came up, it
* was expanded showing this application's title.
@@ -137,6 +144,8 @@ public class AudioRecordingDisclosureBar implements
public AudioRecordingDisclosureBar(Context context) {
mContext = context;
+ mRevealRecordingPackages = mContext.getResources().getBoolean(
+ R.bool.audio_recording_disclosure_reveal_packages);
mExemptPackages = new ArraySet<>(
Arrays.asList(mContext.getResources().getStringArray(
R.array.audio_recording_disclosure_exempt_apps)));
@@ -149,11 +158,6 @@ public class AudioRecordingDisclosureBar implements
};
}
- private String[] getGlobalStringArray(String setting) {
- String result = Settings.Global.getString(mContext.getContentResolver(), setting);
- return TextUtils.isEmpty(result) ? new String[0] : result.split(",");
- }
-
@UiThread
@Override
public void onAudioActivityStateChange(boolean active, String packageName) {
@@ -190,7 +194,9 @@ public class AudioRecordingDisclosureBar implements
break;
case STATE_MINIMIZED:
- expand(packageName);
+ if (mRevealRecordingPackages) {
+ expand(packageName);
+ }
break;
case STATE_DISAPPEARING:
@@ -228,9 +234,8 @@ public class AudioRecordingDisclosureBar implements
@UiThread
private void show(String packageName) {
- final String label = getApplicationLabel(packageName);
if (DEBUG) {
- Log.d(TAG, "Showing indicator for " + packageName + " (" + label + ")...");
+ Log.d(TAG, "Showing indicator for " + packageName);
}
mIsLtr = mContext.getResources().getConfiguration().getLayoutDirection()
@@ -247,19 +252,31 @@ public class AudioRecordingDisclosureBar implements
mTextView = mTextsContainers.findViewById(R.id.text);
mBgEnd = mIndicatorView.findViewById(R.id.bg_end);
- // Swap background drawables depending on layout directions (both drawables have rounded
- // corners only on one side)
- if (mIsLtr) {
- mBgEnd.setBackgroundResource(R.drawable.tv_rect_dark_right_rounded);
- mIconContainerBg.setBackgroundResource(R.drawable.tv_rect_dark_left_rounded);
+ // Set up the notification text
+ if (mRevealRecordingPackages) {
+ // Swap background drawables depending on layout directions (both drawables have rounded
+ // corners only on one side)
+ if (mIsLtr) {
+ mBgEnd.setBackgroundResource(R.drawable.tv_rect_dark_right_rounded);
+ mIconContainerBg.setBackgroundResource(R.drawable.tv_rect_dark_left_rounded);
+ } else {
+ mBgEnd.setBackgroundResource(R.drawable.tv_rect_dark_left_rounded);
+ mIconContainerBg.setBackgroundResource(R.drawable.tv_rect_dark_right_rounded);
+ }
+
+ final String label = getApplicationLabel(packageName);
+ mTextView.setText(mContext.getString(R.string.app_accessed_mic, label));
} else {
- mBgEnd.setBackgroundResource(R.drawable.tv_rect_dark_left_rounded);
- mIconContainerBg.setBackgroundResource(R.drawable.tv_rect_dark_right_rounded);
+ mTextsContainers.setVisibility(View.GONE);
+ mIconContainerBg.setVisibility(View.GONE);
+ mTextView.setVisibility(View.GONE);
+ mBgEnd.setVisibility(View.GONE);
+ mTextsContainers = null;
+ mIconContainerBg = null;
+ mTextView = null;
+ mBgEnd = null;
}
- // Set up the notification text
- mTextView.setText(mContext.getString(R.string.app_accessed_mic, label));
-
// Initially change the visibility to INVISIBLE, wait until and receives the size and
// then animate it moving from "off" the screen correctly
mIndicatorView.setVisibility(View.INVISIBLE);
@@ -296,7 +313,11 @@ public class AudioRecordingDisclosureBar implements
@Override
public void onAnimationEnd(Animator animation) {
startPulsatingAnimation();
- onExpanded();
+ if (mRevealRecordingPackages) {
+ onExpanded();
+ } else {
+ onMinimized();
+ }
}
});
set.start();
@@ -321,6 +342,8 @@ public class AudioRecordingDisclosureBar implements
@UiThread
private void expand(String packageName) {
+ assertRevealingRecordingPackages();
+
final String label = getApplicationLabel(packageName);
if (DEBUG) {
Log.d(TAG, "Expanding for " + packageName + " (" + label + ")...");
@@ -348,6 +371,8 @@ public class AudioRecordingDisclosureBar implements
@UiThread
private void minimize() {
+ assertRevealingRecordingPackages();
+
if (DEBUG) Log.d(TAG, "Minimizing...");
final int targetOffset = (mIsLtr ? 1 : -1) * mTextsContainers.getWidth();
final AnimatorSet set = new AnimatorSet();
@@ -393,6 +418,8 @@ public class AudioRecordingDisclosureBar implements
@UiThread
private void onExpanded() {
+ assertRevealingRecordingPackages();
+
if (DEBUG) Log.d(TAG, "Expanded");
mState = STATE_SHOWN;
@@ -404,11 +431,13 @@ public class AudioRecordingDisclosureBar implements
if (DEBUG) Log.d(TAG, "Minimized");
mState = STATE_MINIMIZED;
- if (!mPendingNotificationPackages.isEmpty()) {
- // There is a new application that started recording, tell the user about it.
- expand(mPendingNotificationPackages.poll());
- } else {
- hideIndicatorIfNeeded();
+ if (mRevealRecordingPackages) {
+ if (!mPendingNotificationPackages.isEmpty()) {
+ // There is a new application that started recording, tell the user about it.
+ expand(mPendingNotificationPackages.poll());
+ } else {
+ hideIndicatorIfNeeded();
+ }
}
}
@@ -451,7 +480,14 @@ public class AudioRecordingDisclosureBar implements
animator.start();
}
+ private String[] getGlobalStringArray(String setting) {
+ String result = Settings.Global.getString(mContext.getContentResolver(), setting);
+ return TextUtils.isEmpty(result) ? new String[0] : result.split(",");
+ }
+
private String getApplicationLabel(String packageName) {
+ assertRevealingRecordingPackages();
+
final PackageManager pm = mContext.getPackageManager();
final ApplicationInfo appInfo;
try {
@@ -461,4 +497,11 @@ public class AudioRecordingDisclosureBar implements
}
return pm.getApplicationLabel(appInfo).toString();
}
+
+ private void assertRevealingRecordingPackages() {
+ if (!mRevealRecordingPackages) {
+ Log.e(TAG, "Not revealing recording packages",
+ DEBUG ? new RuntimeException("Should not be called") : null);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
index 3402a52f056a..89f469a438a9 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
@@ -19,6 +19,7 @@ package com.android.systemui.wm;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
+import android.annotation.IntDef;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Point;
@@ -136,12 +137,16 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
}
}
- private void dispatchStartPositioning(int displayId, int hiddenTop, int shownTop,
- boolean show, SurfaceControl.Transaction t) {
+ @ImePositionProcessor.ImeAnimationFlags
+ private int dispatchStartPositioning(int displayId, int hiddenTop, int shownTop,
+ boolean show, boolean isFloating, SurfaceControl.Transaction t) {
synchronized (mPositionProcessors) {
+ int flags = 0;
for (ImePositionProcessor pp : mPositionProcessors) {
- pp.onImeStartPositioning(displayId, hiddenTop, shownTop, show, t);
+ flags |= pp.onImeStartPositioning(
+ displayId, hiddenTop, shownTop, show, isFloating, t);
}
+ return flags;
}
}
@@ -184,6 +189,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
int mRotation = Surface.ROTATION_0;
boolean mImeShowing = false;
final Rect mImeFrame = new Rect();
+ boolean mAnimateAlpha = true;
PerDisplay(int displayId, int initialRotation) {
mDisplayId = displayId;
@@ -273,15 +279,29 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
return mImeFrame.top + (int) surfaceOffset;
}
+ private boolean calcIsFloating(InsetsSource imeSource) {
+ final Rect frame = imeSource.getFrame();
+ if (frame.height() == 0) {
+ return true;
+ }
+ // Some Floating Input Methods will still report a frame, but the frame is actually
+ // a nav-bar inset created by WM and not part of the IME (despite being reported as
+ // an IME inset). For now, we assume that no non-floating IME will be <= this nav bar
+ // frame height so any reported frame that is <= nav-bar frame height is assumed to
+ // be floating.
+ return frame.height() <= mSystemWindows.mDisplayController.getDisplayLayout(mDisplayId)
+ .navBarFrameHeight();
+ }
+
private void startAnimation(final boolean show, final boolean forceRestart) {
final InsetsSource imeSource = mInsetsState.getSource(InsetsState.ITYPE_IME);
if (imeSource == null || mImeSourceControl == null) {
return;
}
final Rect newFrame = imeSource.getFrame();
- final boolean isFloating = newFrame.height() == 0 && show;
+ final boolean isFloating = calcIsFloating(imeSource) && show;
if (isFloating) {
- // This is likely a "floating" or "expanded" IME, so to get animations, just
+ // This is a "floating" or "expanded" IME, so to get animations, just
// pretend the ime has some size just below the screen.
mImeFrame.set(newFrame);
final int floatingInset = (int) (
@@ -334,7 +354,8 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
SurfaceControl.Transaction t = mTransactionPool.acquire();
float value = (float) animation.getAnimatedValue();
t.setPosition(mImeSourceControl.getLeash(), x, value);
- final float alpha = isFloating ? (value - hiddenY) / (shownY - hiddenY) : 1.f;
+ final float alpha = (mAnimateAlpha || isFloating)
+ ? (value - hiddenY) / (shownY - hiddenY) : 1.f;
t.setAlpha(mImeSourceControl.getLeash(), alpha);
dispatchPositionChanged(mDisplayId, imeTop(value), t);
t.apply();
@@ -347,16 +368,18 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
public void onAnimationStart(Animator animation) {
SurfaceControl.Transaction t = mTransactionPool.acquire();
t.setPosition(mImeSourceControl.getLeash(), x, startY);
- final float alpha = isFloating ? (startY - hiddenY) / (shownY - hiddenY) : 1.f;
- t.setAlpha(mImeSourceControl.getLeash(), alpha);
if (DEBUG) {
Slog.d(TAG, "onAnimationStart d:" + mDisplayId + " top:"
+ imeTop(hiddenY) + "->" + imeTop(shownY)
+ " showing:" + (mAnimationDirection == DIRECTION_SHOW));
}
- dispatchStartPositioning(mDisplayId, imeTop(hiddenY),
- imeTop(shownY), mAnimationDirection == DIRECTION_SHOW,
- t);
+ int flags = dispatchStartPositioning(mDisplayId, imeTop(hiddenY),
+ imeTop(shownY), mAnimationDirection == DIRECTION_SHOW, isFloating, t);
+ mAnimateAlpha = (flags & ImePositionProcessor.IME_ANIMATION_NO_ALPHA) == 0;
+ final float alpha = (mAnimateAlpha || isFloating)
+ ? (startY - hiddenY) / (shownY - hiddenY)
+ : 1.f;
+ t.setAlpha(mImeSourceControl.getLeash(), alpha);
if (mAnimationDirection == DIRECTION_SHOW) {
t.show(mImeSourceControl.getLeash());
}
@@ -419,15 +442,33 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged
*/
public interface ImePositionProcessor {
/**
+ * Indicates that ime shouldn't animate alpha. It will always be opaque. Used when stuff
+ * behind the IME shouldn't be visible (for example during split-screen adjustment where
+ * there is nothing behind the ime).
+ */
+ int IME_ANIMATION_NO_ALPHA = 1;
+
+ /** @hide */
+ @IntDef(prefix = { "IME_ANIMATION_" }, value = {
+ IME_ANIMATION_NO_ALPHA,
+ })
+ @interface ImeAnimationFlags {}
+
+ /**
* Called when the IME position is starting to animate.
*
* @param hiddenTop The y position of the top of the IME surface when it is hidden.
* @param shownTop The y position of the top of the IME surface when it is shown.
* @param showing {@code true} when we are animating from hidden to shown, {@code false}
* when animating from shown to hidden.
+ * @param isFloating {@code true} when the ime is a floating ime (doesn't inset).
+ * @return flags that may alter how ime itself is animated (eg. no-alpha).
*/
- default void onImeStartPositioning(int displayId, int hiddenTop, int shownTop,
- boolean showing, SurfaceControl.Transaction t) {}
+ @ImeAnimationFlags
+ default int onImeStartPositioning(int displayId, int hiddenTop, int shownTop,
+ boolean showing, boolean isFloating, SurfaceControl.Transaction t) {
+ return 0;
+ }
/**
* Called when the ime position changed. This is expected to be a synchronous call on the
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java
index cfec1c07ff1d..a341f3050ea6 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java
@@ -79,6 +79,7 @@ public class DisplayLayout {
private final Rect mStableInsets = new Rect();
private boolean mHasNavigationBar = false;
private boolean mHasStatusBar = false;
+ private int mNavBarFrameHeight = 0;
/**
* Create empty layout.
@@ -146,6 +147,7 @@ public class DisplayLayout {
if (mHasStatusBar) {
convertNonDecorInsetsToStableInsets(res, mStableInsets, mWidth, mHeight, mHasStatusBar);
}
+ mNavBarFrameHeight = getNavigationBarFrameHeight(res, mWidth > mHeight);
}
/**
@@ -214,6 +216,11 @@ public class DisplayLayout {
return mWidth > mHeight;
}
+ /** Get the navbar frame height (used by ime). */
+ public int navBarFrameHeight() {
+ return mNavBarFrameHeight;
+ }
+
/** Gets the orientation of this layout */
public int getOrientation() {
return (mWidth > mHeight) ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
@@ -483,6 +490,7 @@ public class DisplayLayout {
} else {
return res.getDimensionPixelSize(R.dimen.navigation_bar_width_car_mode);
}
+
} else {
if (navBarSide == NAV_BAR_BOTTOM) {
return res.getDimensionPixelSize(landscape
@@ -493,4 +501,11 @@ public class DisplayLayout {
}
}
}
+
+ /** @see com.android.server.wm.DisplayPolicy#getNavigationBarFrameHeight */
+ public static int getNavigationBarFrameHeight(Resources res, boolean landscape) {
+ return res.getDimensionPixelSize(landscape
+ ? R.dimen.navigation_bar_frame_height_landscape
+ : R.dimen.navigation_bar_frame_height);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
new file mode 100644
index 000000000000..71f3d5bee225
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2020 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.accessibility;
+
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
+import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW;
+
+import static com.android.systemui.accessibility.MagnificationModeSwitch.getIconResId;
+
+import static junit.framework.Assert.assertEquals;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.provider.Settings;
+import android.testing.AndroidTestingRunner;
+import android.view.View;
+import android.view.ViewPropertyAnimator;
+import android.view.WindowManager;
+import android.widget.ImageView;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class MagnificationModeSwitchTest extends SysuiTestCase {
+
+ @Mock
+ private ImageView mMockImageView;
+ @Mock
+ private WindowManager mWindowManager;
+ @Mock
+ private ViewPropertyAnimator mViewPropertyAnimator;
+ private MagnificationModeSwitch mMagnificationModeSwitch;
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
+
+ when(mViewPropertyAnimator.setDuration(anyLong())).thenReturn(mViewPropertyAnimator);
+ when(mViewPropertyAnimator.alpha(anyFloat())).thenReturn(mViewPropertyAnimator);
+ when(mViewPropertyAnimator.setStartDelay(anyLong())).thenReturn(mViewPropertyAnimator);
+ when(mViewPropertyAnimator.withEndAction(any(Runnable.class))).thenReturn(
+ mViewPropertyAnimator);
+
+ when(mMockImageView.animate()).thenReturn(mViewPropertyAnimator);
+
+ mMagnificationModeSwitch = new MagnificationModeSwitch(mContext, mMockImageView);
+ }
+
+ @Test
+ public void removeButton_removeView() {
+ mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
+
+ mMagnificationModeSwitch.removeButton();
+
+ verify(mWindowManager).removeView(mMockImageView);
+ // First invocation is in showButton.
+ verify(mViewPropertyAnimator, times(2)).cancel();
+ }
+
+ @Test
+ public void showWindowModeButton_fullscreenMode_addViewAndSetImageResource() {
+ mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
+
+ verify(mMockImageView).setAlpha(1.0f);
+ verify(mMockImageView).setImageResource(
+ getIconResId(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW));
+ verify(mViewPropertyAnimator).cancel();
+ verify(mViewPropertyAnimator).setDuration(anyLong());
+ verify(mViewPropertyAnimator).setStartDelay(anyLong());
+ verify(mViewPropertyAnimator).alpha(anyFloat());
+ ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
+ verify(mViewPropertyAnimator).withEndAction(captor.capture());
+ verify(mWindowManager).addView(eq(mMockImageView), any(WindowManager.LayoutParams.class));
+
+ captor.getValue().run();
+
+ // First invocation is in showButton.
+ verify(mViewPropertyAnimator, times(2)).cancel();
+ verify(mWindowManager).removeView(mMockImageView);
+ }
+
+ @Test
+ public void performClick_fullscreenMode_removeViewAndChangeSettingsValue() {
+ ArgumentCaptor<View.OnClickListener> captor = ArgumentCaptor.forClass(
+ View.OnClickListener.class);
+ verify(mMockImageView).setOnClickListener(captor.capture());
+ mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+
+ captor.getValue().onClick(mMockImageView);
+
+ // First invocation is in showButton.
+ verify(mViewPropertyAnimator, times(2)).cancel();
+ verify(mMockImageView).setImageResource(
+ getIconResId(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW));
+ verify(mWindowManager).removeView(mMockImageView);
+ final int actualMode = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0);
+ assertEquals(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW, actualMode);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java
index 7cf351f3efdd..84a261b6e7d2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java
@@ -44,6 +44,7 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.wm.DisplayController;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -135,6 +136,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
assertThat(mDisplayAreaOrganizer.mDisplayAreaMap.containsKey(mDisplayAreaInfo)).isTrue();
}
+ @Ignore("b/160848002")
@Test
public void testScheduleOffset() {
final int xOffSet = 0;
@@ -148,6 +150,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
OneHandedDisplayAreaOrganizer.MSG_OFFSET_ANIMATE)).isEqualTo(true);
}
+ @Ignore("b/160848002")
@Test
public void testRotation_portraitToLandscape() {
when(mMockLeash.isValid()).thenReturn(false);
@@ -180,6 +183,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(true);
}
+ @Ignore("b/160848002")
@Test
public void testRotation_landscapeToPortrait() {
when(mMockLeash.isValid()).thenReturn(false);
@@ -212,6 +216,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(true);
}
+ @Ignore("b/160848002")
@Test
public void testRotation_portraitToPortrait() {
when(mMockLeash.isValid()).thenReturn(false);
@@ -244,6 +249,7 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase {
OneHandedDisplayAreaOrganizer.MSG_RESET_IMMEDIATE)).isEqualTo(false);
}
+ @Ignore("b/160848002")
@Test
public void testRotation_landscapeToLandscape() {
when(mMockLeash.isValid()).thenReturn(false);
diff --git a/packages/Tethering/common/TetheringLib/api/module-lib-current.txt b/packages/Tethering/common/TetheringLib/api/module-lib-current.txt
index 754584e70fad..6ddb122936e7 100644
--- a/packages/Tethering/common/TetheringLib/api/module-lib-current.txt
+++ b/packages/Tethering/common/TetheringLib/api/module-lib-current.txt
@@ -1,24 +1,6 @@
// Signature format: 2.0
package android.net {
- public final class TetheredClient implements android.os.Parcelable {
- ctor public TetheredClient(@NonNull android.net.MacAddress, @NonNull java.util.Collection<android.net.TetheredClient.AddressInfo>, int);
- method public int describeContents();
- method @NonNull public java.util.List<android.net.TetheredClient.AddressInfo> getAddresses();
- method @NonNull public android.net.MacAddress getMacAddress();
- method public int getTetheringType();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient> CREATOR;
- }
-
- public static final class TetheredClient.AddressInfo implements android.os.Parcelable {
- method public int describeContents();
- method @NonNull public android.net.LinkAddress getAddress();
- method @Nullable public String getHostname();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient.AddressInfo> CREATOR;
- }
-
public final class TetheringConstants {
field public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
field public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
@@ -38,69 +20,15 @@ package android.net {
method @NonNull public String[] getTetheringErroredIfaces();
method public boolean isTetheringSupported();
method public boolean isTetheringSupported(@NonNull String);
- method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback);
- method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener);
method public void requestLatestTetheringEntitlementResult(int, @NonNull android.os.ResultReceiver, boolean);
method @Deprecated public int setUsbTethering(boolean);
- method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(int, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
- method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering();
- method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int);
method @Deprecated public int tether(@NonNull String);
- method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback);
method @Deprecated public int untether(@NonNull String);
- field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED";
- field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY";
- field public static final String EXTRA_ACTIVE_TETHER = "tetherArray";
- field public static final String EXTRA_AVAILABLE_TETHER = "availableArray";
- field public static final String EXTRA_ERRORED_TETHER = "erroredArray";
- field public static final int TETHERING_BLUETOOTH = 2; // 0x2
- field public static final int TETHERING_ETHERNET = 5; // 0x5
- field public static final int TETHERING_INVALID = -1; // 0xffffffff
- field public static final int TETHERING_NCM = 4; // 0x4
- field public static final int TETHERING_USB = 1; // 0x1
- field public static final int TETHERING_WIFI = 0; // 0x0
- field public static final int TETHERING_WIFI_P2P = 3; // 0x3
- field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc
- field public static final int TETHER_ERROR_DISABLE_FORWARDING_ERROR = 9; // 0x9
- field public static final int TETHER_ERROR_ENABLE_FORWARDING_ERROR = 8; // 0x8
- field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd
- field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa
- field public static final int TETHER_ERROR_INTERNAL_ERROR = 5; // 0x5
- field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf
- field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe
- field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0
- field public static final int TETHER_ERROR_PROVISIONING_FAILED = 11; // 0xb
- field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2
- field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6
- field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4
- field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1
- field public static final int TETHER_ERROR_UNKNOWN_TYPE = 16; // 0x10
- field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3
- field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7
- field public static final int TETHER_HARDWARE_OFFLOAD_FAILED = 2; // 0x2
- field public static final int TETHER_HARDWARE_OFFLOAD_STARTED = 1; // 0x1
- field public static final int TETHER_HARDWARE_OFFLOAD_STOPPED = 0; // 0x0
- }
-
- public static interface TetheringManager.OnTetheringEntitlementResultListener {
- method public void onTetheringEntitlementResult(int);
- }
-
- public static interface TetheringManager.StartTetheringCallback {
- method public default void onTetheringFailed(int);
- method public default void onTetheringStarted();
}
public static interface TetheringManager.TetheringEventCallback {
- method public default void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>);
- method public default void onError(@NonNull String, int);
- method public default void onOffloadStatusChanged(int);
method public default void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps);
- method public default void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>);
- method public default void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>);
- method public default void onTetheringSupported(boolean);
- method public default void onUpstreamChanged(@Nullable android.net.Network);
}
public static class TetheringManager.TetheringInterfaceRegexps {
@@ -109,21 +37,5 @@ package android.net {
method @NonNull public java.util.List<java.lang.String> getTetherableWifiRegexs();
}
- public static class TetheringManager.TetheringRequest {
- method @Nullable public android.net.LinkAddress getClientStaticIpv4Address();
- method @Nullable public android.net.LinkAddress getLocalIpv4Address();
- method public boolean getShouldShowEntitlementUi();
- method public int getTetheringType();
- method public boolean isExemptFromEntitlementCheck();
- }
-
- public static class TetheringManager.TetheringRequest.Builder {
- ctor public TetheringManager.TetheringRequest.Builder(int);
- method @NonNull public android.net.TetheringManager.TetheringRequest build();
- method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean);
- method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setShouldShowEntitlementUi(boolean);
- method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress);
- }
-
}
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
index 48be0d96425c..645b00001330 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
@@ -16,8 +16,6 @@
package android.net;
-import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -36,7 +34,6 @@ import java.util.Objects;
* @hide
*/
@SystemApi
-@SystemApi(client = MODULE_LIBRARIES)
@TestApi
public final class TetheredClient implements Parcelable {
@NonNull
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
index 7483bac11662..db8436859281 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
@@ -55,7 +55,6 @@ import java.util.function.Supplier;
* @hide
*/
@SystemApi
-@SystemApi(client = MODULE_LIBRARIES)
@TestApi
public class TetheringManager {
private static final String TAG = TetheringManager.class.getSimpleName();
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index dad061262d49..d50e9d720861 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -175,7 +175,7 @@ public class FullScreenMagnificationGestureHandler extends MagnificationGestureH
*/
public FullScreenMagnificationGestureHandler(Context context,
FullScreenMagnificationController fullScreenMagnificationController,
- MagnificationGestureHandler.ScaleChangedListener listener,
+ ScaleChangedListener listener,
boolean detectTripleTap,
boolean detectShortcutTrigger,
int displayId) {
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
index f38c38ff7d26..d6f53d2c225c 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationGestureHandler.java
@@ -23,7 +23,7 @@ import com.android.server.accessibility.BaseEventStreamTransformation;
*/
public abstract class MagnificationGestureHandler extends BaseEventStreamTransformation {
- protected final MagnificationGestureHandler.ScaleChangedListener mListener;
+ protected final ScaleChangedListener mListener;
protected MagnificationGestureHandler(ScaleChangedListener listener) {
mListener = listener;
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
index 56c05199d0ca..bd25f2bea881 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationGestureHandler.java
@@ -100,7 +100,7 @@ public class WindowMagnificationGestureHandler extends MagnificationGestureHandl
*/
public WindowMagnificationGestureHandler(Context context,
WindowMagnificationManager windowMagnificationMgr,
- MagnificationGestureHandler.ScaleChangedListener listener, boolean detectTripleTap,
+ ScaleChangedListener listener, boolean detectTripleTap,
boolean detectShortcutTrigger, int displayId) {
super(listener);
if (DEBUG_ALL) {
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
index a3c5d1edd351..a08c2dd4292d 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/WindowMagnificationManager.java
@@ -17,7 +17,10 @@
package com.android.server.accessibility.magnification;
import android.annotation.Nullable;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
@@ -52,17 +55,28 @@ public class WindowMagnificationManager implements
static final float MAX_SCALE = FullScreenMagnificationController.MAX_SCALE;
static final float MIN_SCALE = FullScreenMagnificationController.MIN_SCALE;
- private final Object mLock = new Object();;
+ private final Object mLock = new Object();
private final Context mContext;
@VisibleForTesting
@GuardedBy("mLock")
- @Nullable WindowMagnificationConnectionWrapper mConnectionWrapper;
+ @Nullable
+ WindowMagnificationConnectionWrapper mConnectionWrapper;
@GuardedBy("mLock")
private ConnectionCallback mConnectionCallback;
@GuardedBy("mLock")
private SparseArray<WindowMagnifier> mWindowMagnifiers = new SparseArray<>();
private int mUserId;
+ @VisibleForTesting
+ protected final BroadcastReceiver mScreenStateReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final int displayId = context.getDisplayId();
+ removeMagnificationButton(displayId);
+ disableWindowMagnification(displayId);
+ }
+ };
+
public WindowMagnificationManager(Context context, int userId) {
mContext = context;
mUserId = userId;
@@ -133,8 +147,12 @@ public class WindowMagnificationManager implements
if (connect == isConnected()) {
return false;
}
- if (!connect) {
+ if (connect) {
+ final IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
+ mContext.registerReceiver(mScreenStateReceiver, intentFilter);
+ } else {
disableAllWindowMagnifiers();
+ mContext.unregisterReceiver(mScreenStateReceiver);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 25e6eb6e943f..fa18ccb53408 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -6012,9 +6012,9 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
- private boolean isAppBad(ApplicationInfo info) {
+ private boolean isAppBad(final String processName, final int uid) {
synchronized (this) {
- return mAppErrors.isBadProcessLocked(info);
+ return mAppErrors.isBadProcessLocked(processName, uid);
}
}
@@ -6082,7 +6082,7 @@ public class ActivityManagerService extends IActivityManager.Stub
mPendingStartActivityUids.isPendingTopPid(pr.uid, pids[i]);
states[i] = isPendingTop ? PROCESS_STATE_TOP : pr.getCurProcState();
if (scores != null) {
- scores[i] = isPendingTop ? ProcessList.FOREGROUND_APP_ADJ : pr.curAdj;
+ scores[i] = isPendingTop ? (ProcessList.FOREGROUND_APP_ADJ - 1) : pr.curAdj;
}
} else {
states[i] = PROCESS_STATE_NONEXISTENT;
@@ -19713,8 +19713,8 @@ public class ActivityManagerService extends IActivityManager.Stub
}
@Override
- public boolean isAppBad(ApplicationInfo info) {
- return ActivityManagerService.this.isAppBad(info);
+ public boolean isAppBad(final String processName, final int uid) {
+ return ActivityManagerService.this.isAppBad(processName, uid);
}
@Override
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 50d2cab0af81..a36a18b4cf5c 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -33,7 +33,6 @@ import android.app.ApplicationExitInfo;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ApplicationInfo;
import android.content.pm.VersionedPackage;
import android.net.Uri;
import android.os.Binder;
@@ -263,16 +262,16 @@ class AppErrors {
return needSep;
}
- boolean isBadProcessLocked(ApplicationInfo info) {
- return mBadProcesses.get(info.processName, info.uid) != null;
+ boolean isBadProcessLocked(final String processName, final int uid) {
+ return mBadProcesses.get(processName, uid) != null;
}
- void clearBadProcessLocked(ApplicationInfo info) {
- mBadProcesses.remove(info.processName, info.uid);
+ void clearBadProcessLocked(final String processName, final int uid) {
+ mBadProcesses.remove(processName, uid);
}
- void resetProcessCrashTimeLocked(ApplicationInfo info) {
- mProcessCrashTimes.remove(info.processName, info.uid);
+ void resetProcessCrashTimeLocked(final String processName, final int uid) {
+ mProcessCrashTimes.remove(processName, uid);
}
void resetProcessCrashTimeLocked(boolean resetEntireUser, int appId, int userId) {
@@ -548,7 +547,7 @@ class AppErrors {
if (r != null && !r.isolated && res != AppErrorDialog.RESTART) {
// XXX Can't keep track of crash time for isolated processes,
// since they don't have a persistent identity.
- mProcessCrashTimes.put(r.info.processName, r.uid,
+ mProcessCrashTimes.put(r.processName, r.uid,
SystemClock.uptimeMillis());
}
}
@@ -695,8 +694,8 @@ class AppErrors {
boolean tryAgain = false;
if (!app.isolated) {
- crashTime = mProcessCrashTimes.get(app.info.processName, app.uid);
- crashTimePersistent = mProcessCrashTimesPersistent.get(app.info.processName, app.uid);
+ crashTime = mProcessCrashTimes.get(app.processName, app.uid);
+ crashTimePersistent = mProcessCrashTimesPersistent.get(app.processName, app.uid);
} else {
crashTime = crashTimePersistent = null;
}
@@ -723,10 +722,10 @@ class AppErrors {
if (crashTime != null && now < crashTime + ProcessList.MIN_CRASH_INTERVAL) {
// The process crashed again very quickly. If it was a bound foreground service, let's
// try to restart again in a while, otherwise the process loses!
- Slog.w(TAG, "Process " + app.info.processName
+ Slog.w(TAG, "Process " + app.processName
+ " has crashed too many times: killing!");
EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH,
- app.userId, app.info.processName, app.uid);
+ app.userId, app.processName, app.uid);
mService.mAtmInternal.onHandleAppCrash(app.getWindowProcessController());
if (!app.isPersistent()) {
// We don't want to start this process again until the user
@@ -734,13 +733,13 @@ class AppErrors {
// need to keep it running. If a persistent process is actually
// repeatedly crashing, then badness for everyone.
EventLog.writeEvent(EventLogTags.AM_PROC_BAD, app.userId, app.uid,
- app.info.processName);
+ app.processName);
if (!app.isolated) {
// XXX We don't have a way to mark isolated processes
// as bad, since they don't have a peristent identity.
- mBadProcesses.put(app.info.processName, app.uid,
+ mBadProcesses.put(app.processName, app.uid,
new BadProcessInfo(now, shortMsg, longMsg, stackTrace));
- mProcessCrashTimes.remove(app.info.processName, app.uid);
+ mProcessCrashTimes.remove(app.processName, app.uid);
}
app.bad = true;
app.removed = true;
@@ -785,8 +784,8 @@ class AppErrors {
if (!app.isolated) {
// XXX Can't keep track of crash times for isolated processes,
// because they don't have a persistent identity.
- mProcessCrashTimes.put(app.info.processName, app.uid, now);
- mProcessCrashTimesPersistent.put(app.info.processName, app.uid, now);
+ mProcessCrashTimes.put(app.processName, app.uid, now);
+ mProcessCrashTimesPersistent.put(app.processName, app.uid, now);
}
if (app.crashHandler != null) mService.mHandler.post(app.crashHandler);
@@ -829,7 +828,7 @@ class AppErrors {
}
Long crashShowErrorTime = null;
if (!proc.isolated) {
- crashShowErrorTime = mProcessCrashShowDialogTimes.get(proc.info.processName,
+ crashShowErrorTime = mProcessCrashShowDialogTimes.get(proc.processName,
proc.uid);
}
final boolean showFirstCrash = Settings.Global.getInt(
@@ -850,7 +849,7 @@ class AppErrors {
&& (showFirstCrash || showFirstCrashDevOption || data.repeating)) {
proc.getDialogController().showCrashDialogs(data);
if (!proc.isolated) {
- mProcessCrashShowDialogTimes.put(proc.info.processName, proc.uid, now);
+ mProcessCrashShowDialogTimes.put(proc.processName, proc.uid, now);
}
} else {
// The device is asleep, so just pretend that the user
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index ebff0691c1f7..b6ad1a526165 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -2359,9 +2359,9 @@ public final class ProcessList {
if ((intentFlags & Intent.FLAG_FROM_BACKGROUND) != 0) {
// If we are in the background, then check to see if this process
// is bad. If so, we will just silently fail.
- if (mService.mAppErrors.isBadProcessLocked(info)) {
+ if (mService.mAppErrors.isBadProcessLocked(processName, info.uid)) {
if (DEBUG_PROCESSES) Slog.v(TAG, "Bad process: " + info.uid
- + "/" + info.processName);
+ + "/" + processName);
return null;
}
} else {
@@ -2370,13 +2370,13 @@ public final class ProcessList {
// least one crash dialog again, and make the process good again
// if it had been bad.
if (DEBUG_PROCESSES) Slog.v(TAG, "Clearing bad process: " + info.uid
- + "/" + info.processName);
- mService.mAppErrors.resetProcessCrashTimeLocked(info);
- if (mService.mAppErrors.isBadProcessLocked(info)) {
+ + "/" + processName);
+ mService.mAppErrors.resetProcessCrashTimeLocked(processName, info.uid);
+ if (mService.mAppErrors.isBadProcessLocked(processName, info.uid)) {
EventLog.writeEvent(EventLogTags.AM_PROC_GOOD,
UserHandle.getUserId(info.uid), info.uid,
info.processName);
- mService.mAppErrors.clearBadProcessLocked(info);
+ mService.mAppErrors.clearBadProcessLocked(processName, info.uid);
if (app != null) {
app.bad = false;
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 2eca00e27ddf..b3231909e4b7 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -44,6 +44,7 @@ import static android.app.AppOpsManager.RestrictionBypass;
import static android.app.AppOpsManager.SAMPLING_STRATEGY_BOOT_TIME_SAMPLING;
import static android.app.AppOpsManager.SAMPLING_STRATEGY_RARELY_USED;
import static android.app.AppOpsManager.SAMPLING_STRATEGY_UNIFORM;
+import static android.app.AppOpsManager.SAMPLING_STRATEGY_UNIFORM_OPS;
import static android.app.AppOpsManager.SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE;
import static android.app.AppOpsManager.UID_STATE_BACKGROUND;
import static android.app.AppOpsManager.UID_STATE_CACHED;
@@ -5936,11 +5937,13 @@ public class AppOpsService extends IAppOpsService.Stub {
int newLeftDistance = AppOpsManager.leftCircularDistance(opCode,
mSampledAppOpCode, _NUM_OP);
- if (mAcceptableLeftDistance < newLeftDistance) {
+ if (mAcceptableLeftDistance < newLeftDistance
+ && mSamplingStrategy != SAMPLING_STRATEGY_UNIFORM_OPS) {
return;
}
- if (mAcceptableLeftDistance > newLeftDistance) {
+ if (mAcceptableLeftDistance > newLeftDistance
+ && mSamplingStrategy != SAMPLING_STRATEGY_UNIFORM_OPS) {
mAcceptableLeftDistance = newLeftDistance;
mMessagesCollectedCount = 0.0f;
}
@@ -5978,13 +5981,13 @@ public class AppOpsService extends IAppOpsService.Stub {
if (mSampledPackage == null) {
if (ThreadLocalRandom.current().nextFloat() < 0.5f) {
mSamplingStrategy = SAMPLING_STRATEGY_BOOT_TIME_SAMPLING;
- resampleAppOpForPackageLocked(packageName);
+ resampleAppOpForPackageLocked(packageName, true);
}
} else if (mRarelyUsedPackages.contains(packageName)) {
mRarelyUsedPackages.remove(packageName);
if (ThreadLocalRandom.current().nextFloat() < 0.5f) {
mSamplingStrategy = SAMPLING_STRATEGY_RARELY_USED;
- resampleAppOpForPackageLocked(packageName);
+ resampleAppOpForPackageLocked(packageName, true);
}
}
}
@@ -6001,16 +6004,22 @@ public class AppOpsService extends IAppOpsService.Stub {
/** Resamples package and appop to watch from the list provided. */
private void resamplePackageAndAppOpLocked(@NonNull List<String> packageNames) {
if (!packageNames.isEmpty()) {
- mSamplingStrategy = SAMPLING_STRATEGY_UNIFORM;
- resampleAppOpForPackageLocked(packageNames.get(
- ThreadLocalRandom.current().nextInt(packageNames.size())));
+ if (ThreadLocalRandom.current().nextFloat() < 0.5f) {
+ mSamplingStrategy = SAMPLING_STRATEGY_UNIFORM;
+ resampleAppOpForPackageLocked(packageNames.get(
+ ThreadLocalRandom.current().nextInt(packageNames.size())), true);
+ } else {
+ mSamplingStrategy = SAMPLING_STRATEGY_UNIFORM_OPS;
+ resampleAppOpForPackageLocked(packageNames.get(
+ ThreadLocalRandom.current().nextInt(packageNames.size())), false);
+ }
}
}
/** Resamples appop for the chosen package and initializes sampling state */
- private void resampleAppOpForPackageLocked(@NonNull String packageName) {
+ private void resampleAppOpForPackageLocked(@NonNull String packageName, boolean pickOp) {
mMessagesCollectedCount = 0.0f;
- mSampledAppOpCode = ThreadLocalRandom.current().nextInt(_NUM_OP);
+ mSampledAppOpCode = pickOp ? ThreadLocalRandom.current().nextInt(_NUM_OP) : OP_NONE;
mAcceptableLeftDistance = _NUM_OP;
mSampledPackage = packageName;
}
diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java
index 29b8493b8035..03ca8fa28a9c 100644
--- a/services/core/java/com/android/server/biometrics/Utils.java
+++ b/services/core/java/com/android/server/biometrics/Utils.java
@@ -31,6 +31,7 @@ import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_NOT_ENROLLED;
import static com.android.server.biometrics.PreAuthInfo.BIOMETRIC_NO_HARDWARE;
import static com.android.server.biometrics.PreAuthInfo.CREDENTIAL_NOT_ENROLLED;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
@@ -52,6 +53,7 @@ import android.provider.Settings;
import android.util.Slog;
import com.android.internal.R;
+import com.android.server.biometrics.sensors.ClientMonitor;
public class Utils {
@@ -395,4 +397,8 @@ public class Utils {
? keyguardComponent.getPackageName() : null;
return hasPermission && keyguardPackage != null && keyguardPackage.equals(clientPackage);
}
+
+ public static String getClientName(@Nullable ClientMonitor<?> client) {
+ return client != null ? client.getClass().getSimpleName() : "null";
+ }
}
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 14baaa72494f..f8e8dd979e4d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
@@ -45,7 +45,7 @@ public abstract class AcquisitionClient<T> extends ClientMonitor<T> implements I
private final PowerManager mPowerManager;
private final VibrationEffect mSuccessVibrationEffect;
private final VibrationEffect mErrorVibrationEffect;
- private boolean mErrorAlreadySent;
+ private boolean mShouldSendErrorToClient;
/**
* Stops the HAL operation specific to the ClientMonitor subclass.
@@ -84,11 +84,11 @@ public abstract class AcquisitionClient<T> extends ClientMonitor<T> implements I
// case (success, failure, or error) is received from the HAL (e.g. versions of fingerprint
// that do not handle lockout under the HAL. In these cases, ensure that the framework only
// sends errors once per ClientMonitor.
- if (!mErrorAlreadySent) {
+ if (!mShouldSendErrorToClient) {
logOnError(getContext(), errorCode, vendorCode, getTargetUserId());
try {
if (getListener() != null) {
- mErrorAlreadySent = true;
+ mShouldSendErrorToClient = true;
getListener().onError(getSensorId(), getCookie(), errorCode, vendorCode);
}
} catch (RemoteException e) {
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 5392f0ff89af..fdc3def93d06 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java
@@ -17,6 +17,7 @@
package com.android.server.biometrics.sensors;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
import android.app.TaskStackListener;
@@ -41,7 +42,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> {
private final boolean mIsStrongBiometric;
private final boolean mRequireConfirmation;
private final IActivityTaskManager mActivityTaskManager;
- private final TaskStackListener mTaskStackListener;
+ @Nullable private final TaskStackListener mTaskStackListener;
private final LockoutTracker mLockoutTracker;
private final boolean mIsRestricted;
@@ -56,7 +57,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> {
@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, @NonNull TaskStackListener taskStackListener,
+ int statsModality, int statsClient, @Nullable TaskStackListener taskStackListener,
@NonNull LockoutTracker lockoutTracker) {
super(context, lazyDaemon, token, listener, targetUserId, owner, cookie, sensorId,
statsModality, BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient);
@@ -133,10 +134,12 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> {
vibrateSuccess();
}
- try {
- mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
- } catch (RemoteException e) {
- Slog.e(TAG, "Could not unregister task stack listener", e);
+ if (mTaskStackListener != null) {
+ try {
+ mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Could not unregister task stack listener", e);
+ }
}
final byte[] byteToken = new byte[hardwareAuthToken.size()];
@@ -221,10 +224,12 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> {
return;
}
- try {
- mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
- } catch (RemoteException e) {
- Slog.e(TAG, "Could not register task stack listener", e);
+ if (mTaskStackListener != null) {
+ try {
+ mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Could not register task stack listener", e);
+ }
}
if (DEBUG) Slog.w(TAG, "Requesting auth for " + getOwnerString());
@@ -241,10 +246,12 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T> {
return;
}
- try {
- mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
- } catch (RemoteException e) {
- Slog.e(TAG, "Could not unregister task stack listener", e);
+ if (mTaskStackListener != null) {
+ try {
+ mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Could not unregister task stack listener", e);
+ }
}
if (DEBUG) Slog.w(TAG, "Requesting cancel for " + getOwnerString());
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
index c9ab3138b410..6bdd7834c000 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java
@@ -28,7 +28,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Slog;
-import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityTracker;
+import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -37,7 +37,6 @@ import java.text.SimpleDateFormat;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Date;
-import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Queue;
@@ -133,7 +132,7 @@ public class BiometricScheduler {
@Override
public void run() {
if (operation.state != Operation.STATE_FINISHED) {
- Slog.e(tag, "[Watchdog] Running for: " + operation);
+ Slog.e(tag, "[Watchdog Triggered]: " + operation);
operation.clientMonitor.mFinishCallback
.onClientFinished(operation.clientMonitor, false /* success */);
}
@@ -173,7 +172,7 @@ public class BiometricScheduler {
}
@NonNull private final String mBiometricTag;
- @Nullable private final GestureAvailabilityTracker mGestureAvailabilityTracker;
+ @Nullable private final GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
@NonNull private final IBiometricService mBiometricService;
@NonNull private final Handler mHandler = new Handler(Looper.getMainLooper());
@NonNull private final InternalFinishCallback mInternalFinishCallback;
@@ -195,6 +194,8 @@ public class BiometricScheduler {
return;
}
+ mCurrentOperation.state = Operation.STATE_FINISHED;
+
if (mCurrentOperation.clientFinishCallback != null) {
mCurrentOperation.clientFinishCallback.onClientFinished(clientMonitor, success);
}
@@ -206,12 +207,11 @@ public class BiometricScheduler {
}
Slog.d(getTag(), "[Finished] " + clientMonitor + ", success: " + success);
- if (mGestureAvailabilityTracker != null) {
- mGestureAvailabilityTracker.markSensorActive(
+ if (mGestureAvailabilityDispatcher != null) {
+ mGestureAvailabilityDispatcher.markSensorActive(
mCurrentOperation.clientMonitor.getSensorId(), false /* active */);
}
- mCurrentOperation.state = Operation.STATE_FINISHED;
mCurrentOperation = null;
startNextOperationIfIdle();
});
@@ -221,15 +221,15 @@ public class BiometricScheduler {
/**
* Creates a new scheduler.
* @param tag for the specific instance of the scheduler. Should be unique.
- * @param gestureAvailabilityTracker may be null if the sensor does not support gestures (such
- * as fingerprint swipe).
+ * @param gestureAvailabilityDispatcher may be null if the sensor does not support gestures
+ * (such as fingerprint swipe).
*/
public BiometricScheduler(@NonNull String tag,
- @Nullable GestureAvailabilityTracker gestureAvailabilityTracker) {
+ @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
mBiometricTag = tag;
mInternalFinishCallback = new InternalFinishCallback();
- mGestureAvailabilityTracker = gestureAvailabilityTracker;
- mPendingOperations = new LinkedList<>();
+ mGestureAvailabilityDispatcher = gestureAvailabilityDispatcher;
+ mPendingOperations = new ArrayDeque<>();
mBiometricService = IBiometricService.Stub.asInterface(
ServiceManager.getService(Context.BIOMETRIC_SERVICE));
mCrashStates = new ArrayDeque<>();
@@ -268,9 +268,9 @@ public class BiometricScheduler {
return;
}
- if (mGestureAvailabilityTracker != null
+ if (mGestureAvailabilityDispatcher != null
&& mCurrentOperation.clientMonitor instanceof AcquisitionClient) {
- mGestureAvailabilityTracker.markSensorActive(
+ mGestureAvailabilityDispatcher.markSensorActive(
mCurrentOperation.clientMonitor.getSensorId(),
true /* active */);
}
@@ -297,15 +297,23 @@ public class BiometricScheduler {
* Starts the {@link #mCurrentOperation} if
* 1) its state is {@link Operation#STATE_WAITING_FOR_COOKIE} and
* 2) its cookie matches this cookie
+ *
+ * This is currently only used by {@link com.android.server.biometrics.BiometricService}, which
+ * requests sensors to prepare for authentication with a cookie. Once sensor(s) are ready (e.g.
+ * the BiometricService client becomes the current client in the scheduler), the cookie is
+ * returned to BiometricService. Once BiometricService decides that authentication can start,
+ * it invokes this code path.
+ *
* @param cookie of the operation to be started
*/
public void startPreparedClient(int cookie) {
if (mCurrentOperation == null) {
- Slog.e(getTag(), "Current operation null");
+ Slog.e(getTag(), "Current operation is null");
return;
}
if (mCurrentOperation.state != Operation.STATE_WAITING_FOR_COOKIE) {
- Slog.e(getTag(), "Operation in wrong state: " + mCurrentOperation);
+ Slog.e(getTag(), "Operation is in the wrong state: " + mCurrentOperation
+ + ", expected STATE_WAITING_FOR_COOKIE");
return;
}
if (mCurrentOperation.clientMonitor.getCookie() != cookie) {
@@ -354,7 +362,8 @@ public class BiometricScheduler {
// If the current operation is cancellable, start the cancellation process.
if (mCurrentOperation != null && mCurrentOperation.clientMonitor instanceof Interruptable
- && mCurrentOperation.state != Operation.STATE_STARTED_CANCELING) {
+ && mCurrentOperation.state == Operation.STATE_STARTED) {
+ Slog.d(getTag(), "[Cancelling Interruptable]: " + mCurrentOperation);
cancelInternal(mCurrentOperation);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java
deleted file mode 100644
index 9aa72a76aed2..000000000000
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricServiceBase.java
+++ /dev/null
@@ -1,681 +0,0 @@
-/*
- * Copyright (C) 2020 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;
-
-import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE;
-
-import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
-import android.app.AppOpsManager;
-import android.app.IActivityTaskManager;
-import android.app.SynchronousUserSwitchObserver;
-import android.app.TaskStackListener;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
-import android.hardware.biometrics.BiometricAuthenticator;
-import android.hardware.biometrics.BiometricConstants;
-import android.hardware.biometrics.BiometricsProtoEnums;
-import android.hardware.biometrics.IBiometricService;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.IHwBinder;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.Slog;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.util.FrameworkStatsLog;
-import com.android.server.SystemService;
-import com.android.server.biometrics.Utils;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Abstract base class containing all of the business logic for biometric services, e.g.
- * Fingerprint, Face, Iris.
- *
- * @hide
- */
-public abstract class BiometricServiceBase<T> extends SystemService
- implements IHwBinder.DeathRecipient {
-
- protected static final boolean DEBUG = true;
-
- private static final int MSG_USER_SWITCHING = 10;
- private static final long CANCEL_TIMEOUT_LIMIT = 3000; // max wait for onCancel() from HAL,in ms
-
- private final Context mContext;
- protected final IActivityTaskManager mActivityTaskManager;
- protected final BiometricTaskStackListener mTaskStackListener =
- new BiometricTaskStackListener();
- private final ResetClientStateRunnable mResetClientState = new ResetClientStateRunnable();
-
- protected final IStatusBarService mStatusBarService;
- protected final Map<Integer, Long> mAuthenticatorIds =
- Collections.synchronizedMap(new HashMap<>());
- protected final AppOpsManager mAppOps;
-
- /**
- * Handler which all subclasses should post events to.
- */
- protected final Handler mHandler = new Handler(Looper.getMainLooper()) {
- @Override
- public void handleMessage(android.os.Message msg) {
- switch (msg.what) {
- case MSG_USER_SWITCHING:
- handleUserSwitching(msg.arg1);
- break;
- default:
- Slog.w(getTag(), "Unknown message:" + msg.what);
- }
- }
- };
-
- protected final ClientMonitor.FinishCallback mClientFinishCallback =
- (clientMonitor, success) -> {
- removeClient(clientMonitor);
- // When enrollment finishes, update this group's authenticator id, as the HAL has
- // already generated a new authenticator id when the new biometric is enrolled.
- if (clientMonitor instanceof EnrollClient) {
- updateActiveGroup(clientMonitor.getTargetUserId());
- }
- };
-
- private IBiometricService mBiometricService;
- private ClientMonitor<T> mCurrentClient;
- private ClientMonitor<T> mPendingClient;
- private PerformanceTracker mPerformanceTracker;
- private int mSensorId;
- protected int mCurrentUserId = UserHandle.USER_NULL;
-
- /**
- * @return the log tag.
- */
- protected abstract String getTag();
-
- /**
- * @return a fresh reference to the biometric HAL
- */
- protected abstract T getDaemon();
-
- /**
- * @return the biometric utilities for a specific implementation.
- */
- protected abstract BiometricUtils getBiometricUtils();
-
- /**
- * @param userId
- * @return true if the enrollment limit has been reached.
- */
- protected abstract boolean hasReachedEnrollmentLimit(int userId);
-
- /**
- * Notifies the HAL that the user has changed.
- * @param userId
- */
- protected abstract void updateActiveGroup(int userId);
-
- /**
- * @param userId
- * @return Returns true if the user has any enrolled biometrics.
- */
- protected abstract boolean hasEnrolledBiometrics(int userId);
-
- /**
- * @return Returns the MANAGE_* permission string, which is required for enrollment, removal
- * etc.
- */
- protected abstract String getManageBiometricPermission();
-
- protected abstract List<? extends BiometricAuthenticator.Identifier> getEnrolledTemplates(
- int userId);
-
- /**
- * Notifies clients of any change in the biometric state (active / idle). This is mainly for
- * Fingerprint navigation gestures.
- * @param isActive
- */
- protected void notifyClientActiveCallbacks(boolean isActive) {}
-
- protected abstract int statsModality();
-
- private final Runnable mOnTaskStackChangedRunnable = new Runnable() {
- @Override
- public void run() {
- try {
- if (!(mCurrentClient instanceof AuthenticationClient)) {
- return;
- }
- final String currentClient = mCurrentClient.getOwnerString();
- if (isKeyguard(currentClient)) {
- return; // Keyguard is always allowed
- }
- List<ActivityManager.RunningTaskInfo> runningTasks =
- mActivityTaskManager.getTasks(1);
- if (!runningTasks.isEmpty()) {
- final String topPackage = runningTasks.get(0).topActivity.getPackageName();
- if (!topPackage.contentEquals(currentClient)
- && !mCurrentClient.isAlreadyDone()) {
- Slog.e(getTag(), "Stopping background authentication, top: "
- + topPackage + " currentClient: " + currentClient);
- ((AuthenticationClient) mCurrentClient).cancel();
- }
- }
- } catch (RemoteException e) {
- Slog.e(getTag(), "Unable to get running tasks", e);
- }
- }
- };
-
- private final class BiometricTaskStackListener extends TaskStackListener {
- @Override
- public void onTaskStackChanged() {
- mHandler.post(mOnTaskStackChangedRunnable);
- }
- }
-
- private final class ResetClientStateRunnable implements Runnable {
- @Override
- public void run() {
- /**
- * Warning: if we get here, the driver never confirmed our call to cancel the current
- * operation (authenticate, enroll, remove, enumerate, etc), which is
- * really bad. The result will be a 3-second delay in starting each new client.
- * If you see this on a device, make certain the driver notifies with
- * {@link BiometricConstants#BIOMETRIC_ERROR_CANCELED} in response to cancel()
- * once it has successfully switched to the IDLE state in the HAL.
- * Additionally,{@link BiometricConstants#BIOMETRIC_ERROR_CANCELED} should only be sent
- * in response to an actual cancel() call.
- */
- Slog.w(getTag(), "Client "
- + (mCurrentClient != null ? mCurrentClient.getOwnerString() : "null")
- + " failed to respond to cancel, starting client "
- + (mPendingClient != null ? mPendingClient.getOwnerString() : "null"));
-
- FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
- statsModality(), BiometricsProtoEnums.ISSUE_CANCEL_TIMED_OUT);
-
- ClientMonitor<T> newClient = mPendingClient;
- mCurrentClient = null;
- mPendingClient = null;
- startClient(newClient, false);
- }
- }
-
- /**
- * Initializes the system service.
- * <p>
- * Subclasses must define a single argument constructor that accepts the context
- * and passes it to super.
- * </p>
- *
- * @param context The system server context.
- */
- public BiometricServiceBase(Context context) {
- super(context);
- mContext = context;
- mStatusBarService = IStatusBarService.Stub.asInterface(
- ServiceManager.getService(Context.STATUS_BAR_SERVICE));
- mAppOps = context.getSystemService(AppOpsManager.class);
- mActivityTaskManager = ActivityTaskManager.getService();
- mPerformanceTracker = PerformanceTracker.getInstanceForSensorId(getSensorId());
- }
-
- @Override
- public void onStart() {
- listenForUserSwitches();
- }
-
- @Override
- public void serviceDied(long cookie) {
- Slog.e(getTag(), "HAL died");
- mPerformanceTracker.incrementHALDeathCount();
- mCurrentUserId = UserHandle.USER_NULL;
-
- // All client lifecycle must be managed on the handler.
- mHandler.post(() -> {
- Slog.e(getTag(), "Sending BIOMETRIC_ERROR_HW_UNAVAILABLE after HAL crash");
- handleError(BIOMETRIC_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */);
- });
-
- FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
- statsModality(), BiometricsProtoEnums.ISSUE_HAL_DEATH);
- }
-
- protected void initializeConfigurationInternal(int sensorId) {
- if (DEBUG) {
- Slog.d(getTag(), "initializeConfigurationInternal(" + sensorId + ")");
- }
- mSensorId = sensorId;
- }
-
- protected ClientMonitor<?> getCurrentClient() {
- return mCurrentClient;
- }
-
- protected boolean isStrongBiometric() {
- return Utils.isStrongBiometric(mSensorId);
- }
-
- protected int getSensorId() {
- return mSensorId;
- }
-
- /**
- * Callback handlers from the daemon. The caller must put this on a handler.
- */
-
- protected void handleAcquired(int acquiredInfo, int vendorCode) {
- final ClientMonitor<?> client = mCurrentClient;
- if (!(client instanceof AcquisitionClient)) {
- final String clientName = client != null ? client.getClass().getSimpleName() : "null";
- Slog.e(getTag(), "handleAcquired for non-acquire consumer: " + clientName);
- return;
- }
-
- final AcquisitionClient<?> acquisitionClient = (AcquisitionClient<?>) client;
- acquisitionClient.onAcquired(acquiredInfo, vendorCode);
- }
-
- protected void handleAuthenticated(BiometricAuthenticator.Identifier identifier,
- ArrayList<Byte> token) {
- final ClientMonitor<?> client = mCurrentClient;
- if (!(client instanceof AuthenticationClient)) {
- final String clientName = client != null ? client.getClass().getSimpleName() : "null";
- Slog.e(getTag(), "handleAuthenticated for non-authentication client: " + clientName);
- return;
- }
-
- final AuthenticationClient<?> authenticationClient = (AuthenticationClient<?>) client;
- final boolean authenticated = identifier.getBiometricId() != 0;
- authenticationClient.onAuthenticated(identifier, authenticated, token);
- }
-
- protected void handleEnrollResult(BiometricAuthenticator.Identifier identifier,
- int remaining) {
- final ClientMonitor<?> client = mCurrentClient;
- if (!(client instanceof EnrollClient)) {
- final String clientName = client != null ? client.getClass().getSimpleName() : "null";
- Slog.e(getTag(), "handleEnrollResult for non-enroll client: " + clientName);
- return;
- }
-
- final EnrollClient<?> enrollClient = (EnrollClient<?>) client;
- enrollClient.onEnrollResult(identifier, remaining);
- }
-
- protected void handleError(int error, int vendorCode) {
- final ClientMonitor<?> client = mCurrentClient;
-
- if (DEBUG) Slog.v(getTag(), "handleError(client="
- + (client != null ? client.getOwnerString() : "null") + ", error = " + error + ")");
-
- if (!(client instanceof Interruptable)) {
- Slog.e(getTag(), "error received for non-ErrorConsumer");
- return;
- }
-
- final Interruptable interruptable = (Interruptable) client;
- interruptable.onError(error, vendorCode);
-
- if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
- mHandler.removeCallbacks(mResetClientState);
- if (mPendingClient != null) {
- if (DEBUG) Slog.v(getTag(), "start pending client " +
- mPendingClient.getOwnerString());
- startClient(mPendingClient, false);
- mPendingClient = null;
- }
- }
- }
-
- protected void handleRemoved(BiometricAuthenticator.Identifier identifier,
- final int remaining) {
- if (DEBUG) Slog.w(getTag(), "Removed: fid=" + identifier.getBiometricId()
- + ", dev=" + identifier.getDeviceId()
- + ", rem=" + remaining);
-
- final ClientMonitor<?> client = mCurrentClient;
- if (!(client instanceof RemovalConsumer)) {
- final String clientName = client != null ? client.getClass().getSimpleName() : "null";
- Slog.e(getTag(), "handleRemoved for non-removal consumer: " + clientName);
- return;
- }
-
- final RemovalConsumer removalConsumer = (RemovalConsumer) client;
- removalConsumer.onRemoved(identifier, remaining);
- }
-
- protected void handleEnumerate(BiometricAuthenticator.Identifier identifier, int remaining) {
- final ClientMonitor<?> client = mCurrentClient;
- if (!(client instanceof EnumerateConsumer)) {
- final String clientName = client != null ? client.getClass().getSimpleName() : "null";
- Slog.e(getTag(), "handleEnumerate for non-enumerate consumer: "
- + clientName);
- return;
- }
-
- final EnumerateConsumer enumerateConsumer = (EnumerateConsumer) client;
- enumerateConsumer.onEnumerationResult(identifier, remaining);
- }
-
- /**
- * Calls from the Manager. These are still on the calling binder's thread.
- */
-
- protected void enrollInternal(EnrollClient<T> client, int userId) {
- if (hasReachedEnrollmentLimit(userId)) {
- return;
- }
-
- // Group ID is arbitrarily set to parent profile user ID. It just represents
- // the default biometrics for the user.
- if (!Utils.isCurrentUserOrProfile(mContext, userId)) {
- return;
- }
-
- mHandler.post(() -> {
- startClient(client, true /* initiatedByClient */);
- });
- }
-
- protected void cancelEnrollmentInternal(IBinder token) {
- mHandler.post(() -> {
- ClientMonitor<?> client = mCurrentClient;
- if (client instanceof EnrollClient && client.getToken() == token) {
- if (DEBUG) Slog.v(getTag(), "Cancelling enrollment");
- ((EnrollClient<?>) client).cancel();
- }
- });
- }
-
- protected void generateChallengeInternal(GenerateChallengeClient<T> client) {
- mHandler.post(() -> {
- startClient(client, true /* initiatedByClient */);
- });
- }
-
- protected void revokeChallengeInternal(RevokeChallengeClient<T> client) {
- mHandler.post(() -> {
- startClient(client, true /* initiatedByClient */);
- });
- }
-
- protected void authenticateInternal(AuthenticationClient<T> client, String opPackageName) {
- mHandler.post(() -> {
- startAuthentication(client, opPackageName);
- });
- }
-
- protected void cancelAuthenticationInternal(final IBinder token, final String opPackageName,
- boolean fromClient) {
-
- if (DEBUG) Slog.v(getTag(), "cancelAuthentication(" + opPackageName + ")");
-
- mHandler.post(() -> {
- ClientMonitor<?> client = mCurrentClient;
- if (client instanceof AuthenticationClient) {
- if (client.getToken() == token || !fromClient) {
- if (DEBUG) Slog.v(getTag(), "Stopping client " + client.getOwnerString()
- + ", fromClient: " + fromClient);
- // If cancel was from BiometricService, it means the dialog was dismissed
- // and authentication should be canceled.
- ((AuthenticationClient<?>) client).cancel();
- } else {
- if (DEBUG) Slog.v(getTag(), "Can't stop client " + client.getOwnerString()
- + " since tokens don't match. fromClient: " + fromClient);
- }
- } else if (client != null) {
- if (DEBUG) Slog.v(getTag(), "Can't cancel non-authenticating client "
- + client.getOwnerString());
- }
- });
- }
-
- protected void removeInternal(RemovalClient<T> client) {
- mHandler.post(() -> {
- startClient(client, true /* initiatedByClient */);
- });
- }
-
- protected void cleanupInternal(
- InternalCleanupClient<? extends BiometricAuthenticator.Identifier, T> client) {
- mHandler.post(() -> {
- if (DEBUG) {
- Slog.v(getTag(), "Cleaning up templates for user("
- + client.getTargetUserId() + ")");
- }
- startClient(client, true /* initiatedByClient */);
- });
- }
-
- // Should be done on a handler thread - not on the Binder's thread.
- private void startAuthentication(AuthenticationClient<T> client, String opPackageName) {
- if (DEBUG) Slog.v(getTag(), "startAuthentication(" + opPackageName + ")");
-
- startClient(client, true /* initiatedByClient */);
- }
-
- /**
- * Helper methods.
- */
-
- /**
- * @return true if this is keyguard package
- */
- public boolean isKeyguard(String clientPackage) {
- return Utils.isKeyguard(mContext, clientPackage);
- }
-
- /**
- * Calls the HAL to switch states to the new task. If there's already a current task,
- * it calls cancel() and sets mPendingClient to begin when the current task finishes
- * ({@link BiometricConstants#BIOMETRIC_ERROR_CANCELED}).
- *
- * @param newClient the new client that wants to connect
- * @param initiatedByClient true for authenticate, remove and enroll
- */
- @VisibleForTesting
- protected void startClient(ClientMonitor<T> newClient, boolean initiatedByClient) {
- ClientMonitor<?> currentClient = mCurrentClient;
- if (currentClient != null) {
- if (DEBUG) Slog.v(getTag(), "request stop current client " +
- currentClient.getOwnerString());
- if (currentClient instanceof InternalCleanupClient) {
- // This condition means we're currently running internal diagnostics to
- // remove extra templates in the hardware and/or the software
- // TODO: design an escape hatch in case client never finishes
- if (newClient != null) {
- Slog.w(getTag(), "Internal cleanup in progress but trying to start client "
- + newClient.getClass().getSuperclass().getSimpleName()
- + "(" + newClient.getOwnerString() + ")"
- + ", initiatedByClient = " + initiatedByClient);
- }
- } else if (currentClient instanceof Interruptable) {
- ((Interruptable) currentClient).cancel();
-
- // Only post the reset runnable for non-cleanup clients. Cleanup clients should
- // never be forcibly stopped since they ensure synchronization between HAL and
- // framework. Thus, we should instead just start the pending client once cleanup
- // finishes instead of using the reset runnable.
- mHandler.removeCallbacks(mResetClientState);
- mHandler.postDelayed(mResetClientState, CANCEL_TIMEOUT_LIMIT);
- }
- mPendingClient = newClient;
- } else if (newClient != null) {
- // For BiometricPrompt clients, do not start until
- // <Biometric>Service#startPreparedClient is called. BiometricService waits until all
- // modalities are ready before initiating authentication.
- if (newClient instanceof AuthenticationClient) {
- AuthenticationClient<?> client = (AuthenticationClient<?>) newClient;
- if (client.isBiometricPrompt()) {
- if (DEBUG) Slog.v(getTag(), "Returning cookie: " + client.getCookie());
- mCurrentClient = newClient;
- if (mBiometricService == null) {
- mBiometricService = IBiometricService.Stub.asInterface(
- ServiceManager.getService(Context.BIOMETRIC_SERVICE));
- }
- try {
- mBiometricService.onReadyForAuthentication(client.getCookie());
- } catch (RemoteException e) {
- Slog.e(getTag(), "Remote exception", e);
- }
- return;
- }
- }
-
- // We are not a BiometricPrompt client, start the client immediately
- mCurrentClient = newClient;
- startCurrentClient(mCurrentClient.getCookie());
- }
- }
-
- protected void startCurrentClient(int cookie) {
- if (mCurrentClient == null) {
- Slog.e(getTag(), "Trying to start null client!");
- return;
- }
-
- if (DEBUG) Slog.v(getTag(), "Starting client "
- + mCurrentClient.getClass().getSimpleName()
- + "(" + mCurrentClient.getOwnerString() + ")"
- + " targetUserId: " + mCurrentClient.getTargetUserId()
- + " currentUserId: " + mCurrentUserId
- + " cookie: " + cookie + "/" + mCurrentClient.getCookie());
-
- if (cookie != mCurrentClient.getCookie()) {
- Slog.e(getTag(), "Mismatched cookie");
- return;
- }
-
- final T daemon = mCurrentClient.getFreshDaemon();
- if (daemon == null) {
- Slog.e(getTag(), "Daemon null, unable to start: "
- + mCurrentClient.getClass().getSimpleName());
- mCurrentClient.unableToStart();
- mCurrentClient = null;
- return;
- }
-
- mCurrentClient.start(mClientFinishCallback);
- notifyClientActiveCallbacks(true);
- }
-
- protected void removeClient(ClientMonitor<?> client) {
- if (client != null) {
- client.destroy();
- if (client != mCurrentClient && mCurrentClient != null) {
- Slog.w(getTag(), "Unexpected client: " + client.getOwnerString() + "expected: "
- + mCurrentClient.getOwnerString());
- }
- }
- if (mCurrentClient != null) {
- if (DEBUG) Slog.v(getTag(), "Done with client: "
- + mCurrentClient.getClass().getSimpleName()
- + "(" + mCurrentClient.getOwnerString() + ")");
- mCurrentClient = null;
- }
- if (mPendingClient == null) {
- notifyClientActiveCallbacks(false);
- }
- }
-
- /**
- * Populates existing authenticator ids. To be used only during the start of the service.
- */
- protected void loadAuthenticatorIds() {
- // This operation can be expensive, so keep track of the elapsed time. Might need to move to
- // background if it takes too long.
- long t = System.currentTimeMillis();
- mAuthenticatorIds.clear();
- for (UserInfo user : UserManager.get(getContext()).getUsers(true /* excludeDying */)) {
- int userId = user.id;
- if (!mAuthenticatorIds.containsKey(userId)) {
- updateActiveGroup(userId);
- }
- }
-
- t = System.currentTimeMillis() - t;
- if (t > 1000) {
- Slog.w(getTag(), "loadAuthenticatorIds() taking too long: " + t + "ms");
- }
- }
-
- protected boolean isRestricted() {
- // Only give privileged apps (like Settings) access to biometric info
- final boolean restricted = !hasPermission(getManageBiometricPermission());
- return restricted;
- }
-
- protected boolean hasPermission(String permission) {
- return getContext().checkCallingOrSelfPermission(permission)
- == PackageManager.PERMISSION_GRANTED;
- }
-
- protected void checkPermission(String permission) {
- getContext().enforceCallingOrSelfPermission(permission,
- "Must have " + permission + " permission.");
- }
-
- /**
- * @return authenticator id for the calling user
- */
- protected long getAuthenticatorId(int callingUserId) {
- return mAuthenticatorIds.getOrDefault(callingUserId, 0L);
- }
-
- /**
- * This method should be called upon connection to the daemon, and when user switches.
- */
- protected abstract void doTemplateCleanupForUser(int userId);
-
- /**
- * This method is called when the user switches. Implementations should probably notify the
- * HAL.
- */
- protected void handleUserSwitching(int userId) {
- if (getCurrentClient() instanceof InternalCleanupClient) {
- Slog.w(getTag(), "User switched while performing cleanup");
- }
- updateActiveGroup(userId);
- doTemplateCleanupForUser(userId);
- }
-
- private void listenForUserSwitches() {
- try {
- ActivityManager.getService().registerUserSwitchObserver(
- new SynchronousUserSwitchObserver() {
- @Override
- public void onUserSwitching(int newUserId) {
- mHandler.obtainMessage(MSG_USER_SWITCHING, newUserId, 0 /* unused */)
- .sendToTarget();
- }
- }, getTag());
- } catch (RemoteException e) {
- Slog.w(getTag(), "Failed to listen for user switching event" ,e);
- }
- }
-}
diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
index 8cabdf52314c..8b27781940ac 100644
--- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitor.java
@@ -34,7 +34,7 @@ import java.util.NoSuchElementException;
public abstract class ClientMonitor<T> extends LoggableMonitor implements IBinder.DeathRecipient {
private static final String TAG = "Biometrics/ClientMonitor";
- protected static final boolean DEBUG = BiometricServiceBase.DEBUG;
+ protected static final boolean DEBUG = true;
// Counter used to distinguish between ClientMonitor instances to help debugging.
private static int sCount = 0;
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 a3d96778d177..163f29ea5184 100644
--- a/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/EnrollClient.java
@@ -35,12 +35,17 @@ public abstract class EnrollClient<T> extends AcquisitionClient<T> {
protected final byte[] mHardwareAuthToken;
protected final int mTimeoutSec;
- private final BiometricUtils mBiometricUtils;
+ protected final BiometricUtils mBiometricUtils;
private final boolean mShouldVibrate;
private long mEnrollmentStartTimeMs;
private boolean mAlreadyCancelled;
+ /**
+ * @return true if the user has already enrolled the maximum number of templates.
+ */
+ protected abstract boolean hasReachedEnrollmentLimit();
+
public EnrollClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils utils,
@@ -82,6 +87,12 @@ public abstract class EnrollClient<T> extends AcquisitionClient<T> {
public void start(@NonNull FinishCallback finishCallback) {
super.start(finishCallback);
+ if (hasReachedEnrollmentLimit()) {
+ Slog.e(TAG, "Reached enrollment limit");
+ finishCallback.onClientFinished(this, false /* success */);
+ return;
+ }
+
mEnrollmentStartTimeMs = System.currentTimeMillis();
startHalOperation();
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/LockoutResetTracker.java b/services/core/java/com/android/server/biometrics/sensors/LockoutResetDispatcher.java
index 22c10b350e7d..f4997d4abe24 100644
--- a/services/core/java/com/android/server/biometrics/sensors/LockoutResetTracker.java
+++ b/services/core/java/com/android/server/biometrics/sensors/LockoutResetDispatcher.java
@@ -32,7 +32,7 @@ import java.util.ArrayList;
* ends. This class keeps track of all client callbacks. Individual sensors should notify this
* when lockout for a specific sensor has been reset.
*/
-public class LockoutResetTracker implements IBinder.DeathRecipient {
+public class LockoutResetDispatcher implements IBinder.DeathRecipient {
private static final String TAG = "LockoutResetTracker";
@@ -79,7 +79,7 @@ public class LockoutResetTracker implements IBinder.DeathRecipient {
}
}
- public LockoutResetTracker(Context context) {
+ public LockoutResetDispatcher(Context context) {
mContext = context;
mClientCallbacks = new ArrayList<>();
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java b/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java
index a7c63f75d707..1a4216f9d43c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java
+++ b/services/core/java/com/android/server/biometrics/sensors/LoggableMonitor.java
@@ -39,10 +39,6 @@ public abstract class LoggableMonitor {
private final int mStatsClient;
private long mFirstAcquireTimeMs;
- protected long getFirstAcquireTimeMs() {
- return mFirstAcquireTimeMs;
- }
-
/**
* Only valid for AuthenticationClient.
* @return true if the client is authenticating for a crypto operation.
@@ -62,6 +58,12 @@ public abstract class LoggableMonitor {
mStatsClient = statsClient;
}
+ private boolean isAnyFieldUnknown() {
+ return mStatsModality == BiometricsProtoEnums.MODALITY_UNKNOWN
+ || mStatsAction == BiometricsProtoEnums.ACTION_UNKNOWN
+ || mStatsClient == BiometricsProtoEnums.CLIENT_UNKNOWN;
+ }
+
protected final void logOnAcquired(Context context, int acquiredInfo, int vendorCode,
int targetUserId) {
@@ -86,6 +88,11 @@ public abstract class LoggableMonitor {
+ ", AcquiredInfo: " + acquiredInfo
+ ", VendorCode: " + vendorCode);
}
+
+ if (isAnyFieldUnknown()) {
+ return;
+ }
+
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ACQUIRED,
mStatsModality,
targetUserId,
@@ -114,6 +121,11 @@ public abstract class LoggableMonitor {
} else {
Slog.v(TAG, "Error latency: " + latency);
}
+
+ if (isAnyFieldUnknown()) {
+ return;
+ }
+
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ERROR_OCCURRED,
mStatsModality,
targetUserId,
@@ -157,6 +169,10 @@ public abstract class LoggableMonitor {
Slog.v(TAG, "Authentication latency: " + latency);
}
+ if (isAnyFieldUnknown()) {
+ return;
+ }
+
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_AUTHENTICATED,
mStatsModality,
targetUserId,
@@ -179,6 +195,10 @@ public abstract class LoggableMonitor {
Slog.v(TAG, "Enroll latency: " + latency);
}
+ if (isAnyFieldUnknown()) {
+ return;
+ }
+
FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_ENROLLED,
mStatsModality,
targetUserId,
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/Face10.java
new file mode 100644
index 000000000000..c4ea0a8f8fc7
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/Face10.java
@@ -0,0 +1,649 @@
+/*
+ * Copyright (C) 2020 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;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.NotificationManager;
+import android.app.SynchronousUserSwitchObserver;
+import android.app.UserSwitchObserver;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.biometrics.face.V1_0.IBiometricsFace;
+import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback;
+import android.hardware.face.Face;
+import android.hardware.face.IFaceServiceReceiver;
+import android.os.Build;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IHwBinder;
+import android.os.Looper;
+import android.os.NativeHandle;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.util.Slog;
+
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.biometrics.Utils;
+import com.android.server.biometrics.sensors.AcquisitionClient;
+import com.android.server.biometrics.sensors.BiometricScheduler;
+import com.android.server.biometrics.sensors.ClientMonitor;
+import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
+import com.android.server.biometrics.sensors.EnumerateConsumer;
+import com.android.server.biometrics.sensors.Interruptable;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
+import com.android.server.biometrics.sensors.LockoutTracker;
+import com.android.server.biometrics.sensors.PerformanceTracker;
+import com.android.server.biometrics.sensors.RemovalConsumer;
+import com.android.server.biometrics.sensors.fingerprint.FingerprintUpdateActiveUserClient;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Supports a single instance of the {@link android.hardware.biometrics.face.V1_0} or
+ * its extended minor versions.
+ */
+class Face10 implements IHwBinder.DeathRecipient {
+
+ private static final String TAG = "Face10";
+ private static final int ENROLL_TIMEOUT_SEC = 75;
+ static final String NOTIFICATION_TAG = "FaceService";
+ static final int NOTIFICATION_ID = 1;
+
+ @NonNull private final Context mContext;
+ @NonNull private final BiometricScheduler mScheduler;
+ @NonNull private final Handler mHandler;
+ @NonNull private final ClientMonitor.LazyDaemon<IBiometricsFace> mLazyDaemon;
+ @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher;
+ @NonNull private final LockoutHalImpl mLockoutTracker;
+ @NonNull private final UsageStats mUsageStats;
+ @NonNull private NotificationManager mNotificationManager;
+ private final int mSensorId;
+ @NonNull private final Map<Integer, Long> mAuthenticatorIds;
+
+ @Nullable private IBiometricsFace mDaemon;
+ private int mCurrentUserId = UserHandle.USER_NULL;
+
+ private final UserSwitchObserver mUserSwitchObserver = new SynchronousUserSwitchObserver() {
+ @Override
+ public void onUserSwitching(int newUserId) {
+ scheduleInternalCleanup(newUserId);
+ }
+ };
+
+ private final IBiometricsFaceClientCallback mDaemonCallback =
+ new IBiometricsFaceClientCallback.Stub() {
+ @Override
+ public void onEnrollResult(long deviceId, int faceId, int userId, int remaining) {
+ mHandler.post(() -> {
+ final CharSequence name = FaceUtils.getInstance()
+ .getUniqueName(mContext, userId);
+ final Face face = new Face(name, faceId, deviceId);
+
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof FaceEnrollClient)) {
+ Slog.e(TAG, "onEnrollResult for non-enroll client: "
+ + Utils.getClientName(client));
+ return;
+ }
+
+ final FaceEnrollClient enrollClient = (FaceEnrollClient) client;
+ enrollClient.onEnrollResult(face, remaining);
+ });
+ }
+
+ @Override
+ public void onAuthenticated(long deviceId, int faceId, int userId, ArrayList<Byte> token) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof FaceAuthenticationClient)) {
+ Slog.e(TAG, "onAuthenticated for non-authentication client: "
+ + Utils.getClientName(client));
+ return;
+ }
+
+ final FaceAuthenticationClient authenticationClient =
+ (FaceAuthenticationClient) client;
+ final boolean authenticated = faceId != 0;
+ final Face face = new Face("", faceId, deviceId);
+ authenticationClient.onAuthenticated(face, authenticated, token);
+ });
+ }
+
+ @Override
+ public void onAcquired(long deviceId, int userId, int acquiredInfo, int vendorCode) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof AcquisitionClient)) {
+ Slog.e(TAG, "onAcquired for non-acquire client: "
+ + Utils.getClientName(client));
+ return;
+ }
+
+ final AcquisitionClient<?> acquisitionClient = (AcquisitionClient<?>) client;
+ acquisitionClient.onAcquired(acquiredInfo, vendorCode);
+ });
+ }
+
+ @Override
+ public void onError(long deviceId, int userId, int error, int vendorCode) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ Slog.d(TAG, "handleError"
+ + ", client: " + (client != null ? client.getOwnerString() : null)
+ + ", error: " + error
+ + ", vendorCode: " + vendorCode);
+ if (!(client instanceof Interruptable)) {
+ Slog.e(TAG, "onError for non-error consumer: " + Utils.getClientName(client));
+ return;
+ }
+
+ final Interruptable interruptable = (Interruptable) client;
+ interruptable.onError(error, vendorCode);
+
+ if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) {
+ Slog.e(TAG, "Got ERROR_HW_UNAVAILABLE");
+ mDaemon = null;
+ mCurrentUserId = UserHandle.USER_NULL;
+ }
+ });
+ }
+
+ @Override
+ public void onRemoved(long deviceId, ArrayList<Integer> removed, int userId) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof RemovalConsumer)) {
+ Slog.e(TAG, "onRemoved for non-removal consumer: "
+ + Utils.getClientName(client));
+ return;
+ }
+
+ final RemovalConsumer removalConsumer = (RemovalConsumer) client;
+
+ if (!removed.isEmpty()) {
+ // Convert to old fingerprint-like behavior, where remove() receives one removal
+ // at a time. This way, remove can share some more common code.
+ for (int i = 0; i < removed.size(); i++) {
+ final int id = removed.get(i);
+ final Face face = new Face("", id, deviceId);
+ final int remaining = removed.size() - i - 1;
+ Slog.d(TAG, "Removed, faceId: " + id + ", remaining: " + remaining);
+ removalConsumer.onRemoved(face, remaining);
+ }
+ } else {
+ final Face face = new Face("", 0 /* identifier */, deviceId);
+ removalConsumer.onRemoved(face, 0 /* remaining */);
+ }
+
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.FACE_UNLOCK_RE_ENROLL, 0, UserHandle.USER_CURRENT);
+ });
+ }
+
+ @Override
+ public void onEnumerate(long deviceId, ArrayList<Integer> faceIds, int userId) {
+ mHandler.post(() -> {
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (!(client instanceof EnumerateConsumer)) {
+ Slog.e(TAG, "onEnumerate for non-enumerate consumer: "
+ + Utils.getClientName(client));
+ return;
+ }
+
+ final EnumerateConsumer enumerateConsumer = (EnumerateConsumer) client;
+
+ if (!faceIds.isEmpty()) {
+ // Convert to old fingerprint-like behavior, where enumerate() receives one
+ // template at a time. This way, enumerate can share some more common code.
+ for (int i = 0; i < faceIds.size(); i++) {
+ final Face face = new Face("", faceIds.get(i), deviceId);
+ enumerateConsumer.onEnumerationResult(face, faceIds.size() - i - 1);
+ }
+ } else {
+ // For face, the HIDL contract is to receive an empty list when there are no
+ // templates enrolled. Send a null identifier since we don't consume them
+ // anywhere, and send remaining == 0 so this code can be shared with
+ // Fingerprint@2.1
+ enumerateConsumer.onEnumerationResult(null /* identifier */, 0);
+ }
+ });
+ }
+
+ @Override
+ public void onLockoutChanged(long duration) {
+ mHandler.post(() -> {
+ Slog.d(TAG, "onLockoutChanged: " + duration);
+ final @LockoutTracker.LockoutMode int lockoutMode;
+ if (duration == 0) {
+ lockoutMode = LockoutTracker.LOCKOUT_NONE;
+ } else if (duration == -1 || duration == Long.MAX_VALUE) {
+ lockoutMode = LockoutTracker.LOCKOUT_PERMANENT;
+ } else {
+ lockoutMode = LockoutTracker.LOCKOUT_TIMED;
+ }
+
+ mLockoutTracker.setCurrentUserLockoutMode(lockoutMode);
+
+ if (duration == 0) {
+ mLockoutResetDispatcher.notifyLockoutResetCallbacks(mSensorId);
+ }
+ });
+ }
+ };
+
+ Face10(@NonNull Context context, int sensorId,
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher) {
+ mContext = context;
+ mSensorId = sensorId;
+ mScheduler = new BiometricScheduler(TAG, null /* gestureAvailabilityTracker */);
+ mHandler = new Handler(Looper.getMainLooper());
+ mUsageStats = new UsageStats(context);
+ mAuthenticatorIds = new HashMap<>();
+ mLazyDaemon = Face10.this::getDaemon;
+ mNotificationManager = mContext.getSystemService(NotificationManager.class);
+ mLockoutTracker = new LockoutHalImpl();
+ mLockoutResetDispatcher = lockoutResetDispatcher;
+
+ try {
+ ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to register user switch observer");
+ }
+ }
+
+ @Override
+ public void serviceDied(long cookie) {
+ Slog.e(TAG, "HAL died");
+ mHandler.post(() -> {
+ PerformanceTracker.getInstanceForSensorId(mSensorId)
+ .incrementHALDeathCount();
+ mDaemon = null;
+ mCurrentUserId = UserHandle.USER_NULL;
+
+ final ClientMonitor<?> client = mScheduler.getCurrentClient();
+ if (client instanceof Interruptable) {
+ Slog.e(TAG, "Sending ERROR_HW_UNAVAILABLE for client: " + client);
+ final Interruptable interruptable = (Interruptable) client;
+ interruptable.onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+ 0 /* vendorCode */);
+
+ mScheduler.recordCrashState();
+
+ FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED,
+ BiometricsProtoEnums.MODALITY_FACE,
+ BiometricsProtoEnums.ISSUE_HAL_DEATH);
+ }
+ });
+ }
+
+ private synchronized IBiometricsFace getDaemon() {
+ if (mDaemon != null) {
+ return mDaemon;
+ }
+
+ Slog.d(TAG, "Daemon was null, reconnecting, current operation: "
+ + mScheduler.getCurrentClient());
+
+ try {
+ mDaemon = IBiometricsFace.getService();
+ } catch (java.util.NoSuchElementException e) {
+ // Service doesn't exist or cannot be opened.
+ Slog.w(TAG, "NoSuchElementException", e);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get face HAL", e);
+ }
+
+ if (mDaemon == null) {
+ Slog.w(TAG, "Face HAL not available");
+ return null;
+ }
+
+ mDaemon.asBinder().linkToDeath(this, 0 /* flags */);
+
+ // HAL ID for these HIDL versions are only used to determine if callbacks have been
+ // successfully set.
+ long halId = 0;
+ try {
+ halId = mDaemon.setCallback(mDaemonCallback).value;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to set callback for face HAL", e);
+ mDaemon = null;
+ }
+
+ Slog.d(TAG, "Face HAL ready, HAL ID: " + halId);
+ if (halId != 0) {
+ scheduleLoadAuthenticatorIds();
+ scheduleInternalCleanup(ActivityManager.getCurrentUser());
+ } else {
+ Slog.e(TAG, "Unable to set callback");
+ mDaemon = null;
+ }
+
+ return mDaemon;
+ }
+
+ @LockoutTracker.LockoutMode int getLockoutModeForUser(int userId) {
+ return mLockoutTracker.getLockoutModeForUser(userId);
+ }
+
+ private void scheduleLoadAuthenticatorIds() {
+ // Note that this can be performed on the scheduler (as opposed to being done immediately
+ // when the HAL is (re)loaded, since
+ // 1) If this is truly the first time it's being performed (e.g. system has just started),
+ // this will be run very early and way before any applications need to generate keys.
+ // 2) If this is being performed to refresh the authenticatorIds (e.g. HAL crashed and has
+ // just been reloaded), the framework already has a cache of the authenticatorIds. This
+ // is safe because authenticatorIds only change when A) new template has been enrolled,
+ // or B) all templates are removed.
+ mHandler.post(() -> {
+ for (UserInfo user : UserManager.get(mContext).getUsers(true /* excludeDying */)) {
+ final int targetUserId = user.id;
+ if (!mAuthenticatorIds.containsKey(targetUserId)) {
+ scheduleUpdateActiveUserWithoutHandler(targetUserId);
+ }
+ }
+ });
+ }
+
+ /**
+ * Schedules the {@link FingerprintUpdateActiveUserClient} without posting the work onto the
+ * handler. Many/most APIs are user-specific. However, the HAL requires explicit "setActiveUser"
+ * invocation prior to authenticate/enroll/etc. Thus, internally we usually want to schedule
+ * this operation on the same lambda/runnable as those operations so that the ordering is
+ * correct.
+ */
+ private void scheduleUpdateActiveUserWithoutHandler(int targetUserId) {
+ final boolean hasEnrolled = !getEnrolledFaces(targetUserId).isEmpty();
+ final FaceUpdateActiveUserClient client = new FaceUpdateActiveUserClient(mContext,
+ mLazyDaemon, targetUserId, mContext.getOpPackageName(), mSensorId, mCurrentUserId,
+ hasEnrolled, mAuthenticatorIds);
+ mScheduler.scheduleClientMonitor(client, (clientMonitor, success) -> {
+ if (success) {
+ mCurrentUserId = targetUserId;
+ }
+ });
+ }
+
+ void scheduleResetLockout(int userId, @NonNull byte[] hardwareAuthToken) {
+ mHandler.post(() -> {
+ if (getEnrolledFaces(userId).isEmpty()) {
+ Slog.w(TAG, "Ignoring lockout reset, no templates enrolled for user: " + userId);
+ return;
+ }
+
+ scheduleUpdateActiveUserWithoutHandler(userId);
+
+ final FaceResetLockoutClient client = new FaceResetLockoutClient(mContext,
+ mLazyDaemon, userId, mContext.getOpPackageName(), mSensorId,
+ hardwareAuthToken);
+ mScheduler.scheduleClientMonitor(client);
+ });
+ }
+
+ void scheduleSetFeature(@NonNull IBinder token, int userId, int feature, boolean enabled,
+ @NonNull byte[] hardwareAuthToken, @NonNull IFaceServiceReceiver receiver,
+ @NonNull String opPackageName) {
+ mHandler.post(() -> {
+ final List<Face> faces = getEnrolledFaces(userId);
+ if (faces.isEmpty()) {
+ Slog.w(TAG, "Ignoring setFeature, no templates enrolled for user: " + userId);
+ return;
+ }
+
+ scheduleUpdateActiveUserWithoutHandler(userId);
+
+ 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);
+ mScheduler.scheduleClientMonitor(client);
+ });
+ }
+
+ void scheduleGetFeature(@NonNull IBinder token, int userId, int feature,
+ @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
+ mHandler.post(() -> {
+ final List<Face> faces = getEnrolledFaces(userId);
+ if (faces.isEmpty()) {
+ Slog.w(TAG, "Ignoring getFeature, no templates enrolled for user: " + userId);
+ return;
+ }
+
+ scheduleUpdateActiveUserWithoutHandler(userId);
+
+ final int faceId = faces.get(0).getBiometricId();
+ final FaceGetFeatureClient client = new FaceGetFeatureClient(mContext,
+ mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
+ opPackageName, mSensorId, feature, faceId);
+ mScheduler.scheduleClientMonitor(client);
+ });
+ }
+
+ void scheduleGenerateChallenge(@NonNull IBinder token, @NonNull IFaceServiceReceiver receiver,
+ @NonNull String opPackageName) {
+ mHandler.post(() -> {
+ final FaceGenerateChallengeClient client = new FaceGenerateChallengeClient(mContext,
+ mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), opPackageName,
+ mSensorId);
+ mScheduler.scheduleClientMonitor(client);
+ });
+ }
+
+ void scheduleRevokeChallenge(@NonNull IBinder token, @NonNull String owner) {
+ mHandler.post(() -> {
+ final FaceRevokeChallengeClient client = new FaceRevokeChallengeClient(mContext,
+ mLazyDaemon, token, owner, mSensorId);
+ mScheduler.scheduleClientMonitor(client);
+ });
+ }
+
+ void scheduleEnroll(@NonNull IBinder token, @NonNull byte[] hardwareAuthToken, int userId,
+ @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName,
+ @NonNull int[] disabledFeatures, @Nullable NativeHandle surfaceHandle) {
+ mHandler.post(() -> {
+ scheduleUpdateActiveUserWithoutHandler(userId);
+
+ mNotificationManager.cancelAsUser(NOTIFICATION_TAG, NOTIFICATION_ID,
+ UserHandle.CURRENT);
+
+ final FaceEnrollClient client = new FaceEnrollClient(mContext, mLazyDaemon, token,
+ new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
+ opPackageName, FaceUtils.getInstance(), disabledFeatures, ENROLL_TIMEOUT_SEC,
+ surfaceHandle, mSensorId);
+
+ mScheduler.scheduleClientMonitor(client, ((clientMonitor, success) -> {
+ if (success) {
+ // Update authenticatorIds
+ scheduleUpdateActiveUserWithoutHandler(client.getTargetUserId());
+ }
+ }));
+ });
+ }
+
+ void cancelEnrollment(@NonNull IBinder token) {
+ mHandler.post(() -> {
+ mScheduler.cancelEnrollment(token);
+ });
+ }
+
+ void scheduleAuthenticate(@NonNull IBinder token, long operationId, int userId, int cookie,
+ @NonNull ClientMonitorCallbackConverter receiver, @NonNull String opPackageName,
+ boolean restricted, int statsClient) {
+ mHandler.post(() -> {
+ scheduleUpdateActiveUserWithoutHandler(userId);
+
+ final boolean isStrongBiometric = Utils.isStrongBiometric(mSensorId);
+ final FaceAuthenticationClient client = new FaceAuthenticationClient(mContext,
+ mLazyDaemon, token, receiver, userId, operationId, restricted, opPackageName,
+ cookie, false /* requireConfirmation */, mSensorId, isStrongBiometric,
+ statsClient, mLockoutTracker, mUsageStats);
+ mScheduler.scheduleClientMonitor(client);
+ });
+ }
+
+ void startPreparedClient(int cookie) {
+ mHandler.post(() -> {
+ mScheduler.startPreparedClient(cookie);
+ });
+ }
+
+ void cancelAuthentication(@NonNull IBinder token) {
+ mHandler.post(() -> {
+ mScheduler.cancelAuthentication(token);
+ });
+ }
+
+ void scheduleRemove(@NonNull IBinder token, int faceId, int userId,
+ @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) {
+ mHandler.post(() -> {
+ scheduleUpdateActiveUserWithoutHandler(userId);
+
+ final FaceRemovalClient client = new FaceRemovalClient(mContext, mLazyDaemon, token,
+ new ClientMonitorCallbackConverter(receiver), faceId, userId, opPackageName,
+ FaceUtils.getInstance(), mSensorId, mAuthenticatorIds);
+ mScheduler.scheduleClientMonitor(client);
+ });
+ }
+
+ private void scheduleInternalCleanup(int userId) {
+ mHandler.post(() -> {
+ scheduleUpdateActiveUserWithoutHandler(userId);
+
+ final List<Face> enrolledList = getEnrolledFaces(userId);
+ final FaceInternalCleanupClient client = new FaceInternalCleanupClient(mContext,
+ mLazyDaemon, userId, mContext.getOpPackageName(), mSensorId, enrolledList,
+ FaceUtils.getInstance(), mAuthenticatorIds);
+ mScheduler.scheduleClientMonitor(client);
+ });
+ }
+
+ boolean isHardwareDetected() {
+ final IBiometricsFace daemon = getDaemon();
+ return daemon != null;
+ }
+
+ List<Face> getEnrolledFaces(int userId) {
+ return FaceUtils.getInstance().getBiometricsForUser(mContext, userId);
+ }
+
+ long getAuthenticatorId(int userId) {
+ return mAuthenticatorIds.get(userId);
+ }
+
+ public void dump(@NonNull PrintWriter pw) {
+ PerformanceTracker performanceTracker =
+ PerformanceTracker.getInstanceForSensorId(mSensorId);
+
+ JSONObject dump = new JSONObject();
+ try {
+ dump.put("service", "Face Manager");
+
+ JSONArray sets = new JSONArray();
+ for (UserInfo user : UserManager.get(mContext).getUsers()) {
+ final int userId = user.getUserHandle().getIdentifier();
+ final int N = FaceUtils.getInstance().getBiometricsForUser(mContext, userId).size();
+ JSONObject set = new JSONObject();
+ set.put("id", userId);
+ set.put("count", N);
+ set.put("accept", performanceTracker.getAcceptForUser(userId));
+ set.put("reject", performanceTracker.getRejectForUser(userId));
+ set.put("acquire", performanceTracker.getAcquireForUser(userId));
+ set.put("lockout", performanceTracker.getTimedLockoutForUser(userId));
+ set.put("permanentLockout", performanceTracker.getPermanentLockoutForUser(userId));
+ // cryptoStats measures statistics about secure face transactions
+ // (e.g. to unlock password storage, make secure purchases, etc.)
+ set.put("acceptCrypto", performanceTracker.getAcceptCryptoForUser(userId));
+ set.put("rejectCrypto", performanceTracker.getRejectCryptoForUser(userId));
+ set.put("acquireCrypto", performanceTracker.getAcquireCryptoForUser(userId));
+ sets.put(set);
+ }
+
+ dump.put("prints", sets);
+ } catch (JSONException e) {
+ Slog.e(TAG, "dump formatting failure", e);
+ }
+ pw.println(dump);
+ pw.println("HAL deaths since last reboot: " + performanceTracker.getHALDeathCount());
+
+ mUsageStats.print(pw);
+ }
+
+ public void dumpHal(@NonNull FileDescriptor fd, @NonNull String[] args) {
+ // WARNING: CDD restricts image data from leaving TEE unencrypted on
+ // production devices:
+ // [C-1-10] MUST not allow unencrypted access to identifiable biometric
+ // data or any data derived from it (such as embeddings) to the
+ // Application Processor outside the context of the TEE.
+ // As such, this API should only be enabled for testing purposes on
+ // engineering and userdebug builds. All modules in the software stack
+ // MUST enforce final build products do NOT have this functionality.
+ // Additionally, the following check MUST NOT be removed.
+ if (!(Build.IS_ENG || Build.IS_USERDEBUG)) {
+ return;
+ }
+
+ // Additionally, this flag allows turning off face for a device
+ // (either permanently through the build or on an individual device).
+ if (SystemProperties.getBoolean("ro.face.disable_debug_data", false)
+ || SystemProperties.getBoolean("persist.face.disable_debug_data", false)) {
+ return;
+ }
+
+ // The debug method takes two file descriptors. The first is for text
+ // output, which we will drop. The second is for binary data, which
+ // will be the protobuf data.
+ final IBiometricsFace daemon = getDaemon();
+ if (daemon != null) {
+ FileOutputStream devnull = null;
+ try {
+ devnull = new FileOutputStream("/dev/null");
+ final NativeHandle handle = new NativeHandle(
+ new FileDescriptor[] { devnull.getFD(), fd },
+ new int[0], false);
+ daemon.debug(handle, new ArrayList<String>(Arrays.asList(args)));
+ } catch (IOException | RemoteException ex) {
+ Slog.d(TAG, "error while reading face debugging data", ex);
+ } finally {
+ if (devnull != null) {
+ try {
+ devnull.close();
+ } catch (IOException ex) {
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticationClient.java
index 118cadc9f9fa..21bda74bc6b9 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceAuthenticationClient.java
@@ -21,7 +21,6 @@ import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.app.TaskStackListener;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
@@ -67,11 +66,10 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> {
@NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
boolean restricted, String owner, int cookie, boolean requireConfirmation, int sensorId,
boolean isStrongBiometric, int statsClient,
- @NonNull TaskStackListener taskStackListener,
@NonNull LockoutTracker lockoutTracker, @NonNull UsageStats usageStats) {
super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted,
owner, cookie, requireConfirmation, sensorId, isStrongBiometric,
- BiometricsProtoEnums.MODALITY_FACE, statsClient, taskStackListener,
+ BiometricsProtoEnums.MODALITY_FACE, statsClient, null /* taskStackListener */,
lockoutTracker);
mNotificationManager = context.getSystemService(NotificationManager.class);
mUsageStats = usageStats;
@@ -221,8 +219,8 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> {
.build();
mNotificationManager.createNotificationChannel(channel);
- mNotificationManager.notifyAsUser(FaceService.NOTIFICATION_TAG,
- FaceService.NOTIFICATION_ID, notification,
+ mNotificationManager.notifyAsUser(Face10.NOTIFICATION_TAG,
+ Face10.NOTIFICATION_ID, notification,
UserHandle.CURRENT);
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java
index 4f9f46aff4c0..52a822675c2f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java
@@ -68,6 +68,19 @@ public class FaceEnrollClient extends EnrollClient<IBiometricsFace> {
}
@Override
+ protected boolean hasReachedEnrollmentLimit() {
+ final int limit = getContext().getResources().getInteger(
+ com.android.internal.R.integer.config_faceMaxTemplatesPerUser);
+ final int enrolled = mBiometricUtils.getBiometricsForUser(getContext(), getTargetUserId())
+ .size();
+ if (enrolled >= limit) {
+ Slog.w(TAG, "Too many faces registered, user: " + getTargetUserId());
+ return true;
+ }
+ return false;
+ }
+
+ @Override
public void onAcquired(int acquireInfo, int vendorCode) {
final boolean shouldSend;
if (acquireInfo == FaceManager.FACE_ACQUIRED_VENDOR) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index 7a051921f403..1c81fe1b484b 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -16,64 +16,32 @@
package com.android.server.biometrics.sensors.face;
-import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.MANAGE_BIOMETRIC;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
-import android.app.ActivityManager;
-import android.app.NotificationManager;
+import android.annotation.NonNull;
import android.content.Context;
-import android.content.pm.UserInfo;
-import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.IBiometricSensorReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
-import android.hardware.biometrics.face.V1_0.IBiometricsFace;
-import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback;
import android.hardware.face.Face;
import android.hardware.face.IFaceService;
import android.hardware.face.IFaceServiceReceiver;
import android.os.Binder;
-import android.os.Build;
-import android.os.Environment;
import android.os.IBinder;
import android.os.NativeHandle;
-import android.os.RemoteException;
-import android.os.SELinux;
-import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.Settings;
import android.util.Slog;
import android.view.Surface;
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.DumpUtils;
-import com.android.server.SystemServerInitThreadPool;
-import com.android.server.biometrics.sensors.AuthenticationClient;
-import com.android.server.biometrics.sensors.BiometricServiceBase;
-import com.android.server.biometrics.sensors.BiometricUtils;
-import com.android.server.biometrics.sensors.ClientMonitor;
+import com.android.server.SystemService;
+import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
-import com.android.server.biometrics.sensors.EnrollClient;
-import com.android.server.biometrics.sensors.GenerateChallengeClient;
-import com.android.server.biometrics.sensors.LockoutResetTracker;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
-import com.android.server.biometrics.sensors.PerformanceTracker;
-import com.android.server.biometrics.sensors.RemovalClient;
-import com.android.server.biometrics.sensors.RevokeChallengeClient;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.io.File;
import java.io.FileDescriptor;
-import java.io.FileOutputStream;
-import java.io.IOException;
import java.io.PrintWriter;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -82,177 +50,115 @@ import java.util.List;
* The service is responsible for maintaining a list of clients and dispatching all
* face-related events.
*/
-public class FaceService extends BiometricServiceBase<IBiometricsFace> {
+public class FaceService extends SystemService {
protected static final String TAG = "FaceService";
- private static final boolean DEBUG = true;
- private static final String FACE_DATA_DIR = "facedata";
- private static final String ACTION_LOCKOUT_RESET =
- "com.android.server.biometrics.face.ACTION_LOCKOUT_RESET";
-
-
- static final String NOTIFICATION_TAG = "FaceService";
- static final int NOTIFICATION_ID = 1;
- private final LockoutResetTracker mLockoutResetTracker;
- private final ClientMonitor.LazyDaemon<IBiometricsFace> mLazyDaemon;
+ private Face10 mFace10;
+ private final LockoutResetDispatcher mLockoutResetDispatcher;
/**
* Receives the incoming binder calls from FaceManager.
*/
private final class FaceServiceWrapper extends IFaceService.Stub {
- private static final int ENROLL_TIMEOUT_SEC = 75;
-
- /**
- * The following methods contain common code which is shared in biometrics/common.
- */
-
@Override // Binder call
public void generateChallenge(IBinder token, IFaceServiceReceiver receiver,
String opPackageName) {
- checkPermission(MANAGE_BIOMETRIC);
-
- final FaceGenerateChallengeClient client = new FaceGenerateChallengeClient(getContext(),
- mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), opPackageName,
- getSensorId());
- generateChallengeInternal(client);
+ Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
+ mFace10.scheduleGenerateChallenge(token, receiver, opPackageName);
}
@Override // Binder call
public void revokeChallenge(IBinder token, String owner) {
- checkPermission(MANAGE_BIOMETRIC);
-
- final FaceRevokeChallengeClient client = new FaceRevokeChallengeClient(getContext(),
- mLazyDaemon, token, owner, getSensorId());
-
- // TODO(b/137106905): Schedule binder calls in FaceService to avoid deadlocks.
- if (getCurrentClient() == null) {
- // if we aren't handling any other HIDL calls (mCurrentClient == null), revoke
- // the challenge right away.
- revokeChallengeInternal(client);
- } else {
- // postpone revoking the challenge until we finish processing the current HIDL
- // call.
- mPendingRevokeChallenge = client;
- }
+ Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
+ mFace10.scheduleRevokeChallenge(token, owner);
}
@Override // Binder call
public void enroll(int userId, final IBinder token, final byte[] hardwareAuthToken,
final IFaceServiceReceiver receiver, final String opPackageName,
final int[] disabledFeatures, Surface surface) {
- checkPermission(MANAGE_BIOMETRIC);
- updateActiveGroup(userId);
-
- mHandler.post(() -> {
- mNotificationManager.cancelAsUser(NOTIFICATION_TAG, NOTIFICATION_ID,
- UserHandle.CURRENT);
- });
-
- final FaceEnrollClient client = new FaceEnrollClient(getContext(), mLazyDaemon, token,
- new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
- opPackageName, getBiometricUtils(), disabledFeatures, ENROLL_TIMEOUT_SEC,
- convertSurfaceToNativeHandle(surface), getSensorId());
-
- enrollInternal(client, userId);
+ Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
+ mFace10.scheduleEnroll(token, hardwareAuthToken, userId, receiver, opPackageName,
+ disabledFeatures, convertSurfaceToNativeHandle(surface));
}
@Override // Binder call
public void enrollRemotely(int userId, final IBinder token, final byte[] hardwareAuthToken,
final IFaceServiceReceiver receiver, final String opPackageName,
final int[] disabledFeatures) {
- checkPermission(MANAGE_BIOMETRIC);
+ Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
// TODO(b/145027036): Implement this.
}
@Override // Binder call
public void cancelEnrollment(final IBinder token) {
- checkPermission(MANAGE_BIOMETRIC);
- cancelEnrollmentInternal(token);
+ Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
+ mFace10.cancelEnrollment(token);
}
@Override // Binder call
- public void authenticate(final IBinder token, final long opId, int userId,
+ public void authenticate(final IBinder token, final long operationId, int userId,
final IFaceServiceReceiver receiver, final String opPackageName) {
- checkPermission(USE_BIOMETRIC_INTERNAL);
- updateActiveGroup(userId);
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
- final boolean restricted = isRestricted();
- final int statsClient = isKeyguard(opPackageName) ? BiometricsProtoEnums.CLIENT_KEYGUARD
+ final boolean restricted = false; // Face APIs are private
+ final int statsClient = Utils.isKeyguard(getContext(), opPackageName)
+ ? BiometricsProtoEnums.CLIENT_KEYGUARD
: BiometricsProtoEnums.CLIENT_UNKNOWN;
- final FaceAuthenticationClient client = new FaceAuthenticationClient(getContext(),
- mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId, opId,
- restricted, opPackageName, 0 /* cookie */, false /* requireConfirmation */,
- getSensorId(), isStrongBiometric(), statsClient, mTaskStackListener,
- mLockoutTracker, mUsageStats);
- authenticateInternal(client, opPackageName);
+ mFace10.scheduleAuthenticate(token, operationId, userId, 0 /* cookie */,
+ new ClientMonitorCallbackConverter(receiver), opPackageName, restricted,
+ statsClient);
}
@Override // Binder call
- public void prepareForAuthentication(boolean requireConfirmation, IBinder token, long opId,
- int userId, IBiometricSensorReceiver sensorReceiver,
+ public void prepareForAuthentication(boolean requireConfirmation, IBinder token,
+ long operationId, int userId, IBiometricSensorReceiver sensorReceiver,
String opPackageName, int cookie, int callingUid, int callingPid,
int callingUserId) {
- checkPermission(USE_BIOMETRIC_INTERNAL);
- updateActiveGroup(userId);
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
final boolean restricted = true; // BiometricPrompt is always restricted
- final FaceAuthenticationClient client = new FaceAuthenticationClient(getContext(),
- mLazyDaemon, token, new ClientMonitorCallbackConverter(sensorReceiver), userId,
- opId, restricted, opPackageName, cookie, requireConfirmation, getSensorId(),
- isStrongBiometric(), BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
- mTaskStackListener, mLockoutTracker, mUsageStats);
- authenticateInternal(client, opPackageName);
+ mFace10.scheduleAuthenticate(token, operationId, userId, cookie,
+ new ClientMonitorCallbackConverter(sensorReceiver), opPackageName,
+ restricted, BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT);
}
@Override // Binder call
public void startPreparedClient(int cookie) {
- checkPermission(MANAGE_BIOMETRIC);
- startCurrentClient(cookie);
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ mFace10.startPreparedClient(cookie);
}
@Override // Binder call
public void cancelAuthentication(final IBinder token, final String opPackageName) {
- checkPermission(USE_BIOMETRIC_INTERNAL);
- cancelAuthenticationInternal(token, opPackageName, true /* fromClient */);
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ mFace10.cancelAuthentication(token);
}
@Override // Binder call
public void cancelAuthenticationFromService(final IBinder token, final String opPackageName,
int callingUid, int callingPid, int callingUserId) {
- checkPermission(USE_BIOMETRIC_INTERNAL);
- // Cancellation is from system server in this case.
- cancelAuthenticationInternal(token, opPackageName, false /* fromClient */);
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ mFace10.cancelAuthentication(token);
}
@Override // Binder call
public void remove(final IBinder token, final int faceId, final int userId,
final IFaceServiceReceiver receiver, final String opPackageName) {
- checkPermission(MANAGE_BIOMETRIC);
- updateActiveGroup(userId);
-
- if (token == null) {
- Slog.w(TAG, "remove(): token is null");
- return;
- }
-
- final FaceRemovalClient client = new FaceRemovalClient(getContext(), mLazyDaemon, token,
- new ClientMonitorCallbackConverter(receiver), faceId, userId, opPackageName,
- getBiometricUtils(), getSensorId(), mAuthenticatorIds);
- removeInternal(client);
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ mFace10.scheduleRemove(token, faceId, userId, receiver, opPackageName);
}
@Override
public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback,
final String opPackageName) {
- checkPermission(USE_BIOMETRIC_INTERNAL);
- mHandler.post(() -> {
- mLockoutResetTracker.addCallback(callback, opPackageName);
- });
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ mLockoutResetDispatcher.addCallback(callback, opPackageName);
}
@Override // Binder call
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
return;
}
@@ -260,28 +166,25 @@ public class FaceService extends BiometricServiceBase<IBiometricsFace> {
final long ident = Binder.clearCallingIdentity();
try {
if (args.length > 1 && "--hal".equals(args[0])) {
- dumpHal(fd, Arrays.copyOfRange(args, 1, args.length, args.getClass()));
+ mFace10.dumpHal(fd, Arrays.copyOfRange(args, 1, args.length, args.getClass()));
} else {
- dumpInternal(pw);
+ mFace10.dump(pw);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
- /**
- * The following methods don't use any common code from BiometricService
- */
-
- // TODO: refactor out common code here
@Override // Binder call
public boolean isHardwareDetected(String opPackageName) {
- checkPermission(USE_BIOMETRIC_INTERNAL);
-
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ if (mFace10 == null) {
+ Slog.wtf(TAG, "No HAL, caller: " + opPackageName);
+ return false;
+ }
final long token = Binder.clearCallingIdentity();
try {
- IBiometricsFace daemon = getFaceDaemon();
- return daemon != null;
+ return mFace10.isHardwareDetected();
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -289,512 +192,66 @@ public class FaceService extends BiometricServiceBase<IBiometricsFace> {
@Override // Binder call
public List<Face> getEnrolledFaces(int userId, String opPackageName) {
- checkPermission(MANAGE_BIOMETRIC);
- return FaceService.this.getEnrolledTemplates(userId);
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ return mFace10.getEnrolledFaces(userId);
}
@Override // Binder call
public boolean hasEnrolledFaces(int userId, String opPackageName) {
- checkPermission(USE_BIOMETRIC_INTERNAL);
- return FaceService.this.hasEnrolledBiometrics(userId);
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ return !mFace10.getEnrolledFaces(userId).isEmpty();
}
@Override
public @LockoutTracker.LockoutMode int getLockoutModeForUser(int userId) {
- checkPermission(USE_BIOMETRIC_INTERNAL);
- return mLockoutTracker.getLockoutModeForUser(userId);
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ return mFace10.getLockoutModeForUser(userId);
}
@Override // Binder call
- public long getAuthenticatorId(int callingUserId) {
- checkPermission(USE_BIOMETRIC_INTERNAL);
- return FaceService.this.getAuthenticatorId(callingUserId);
+ public long getAuthenticatorId(int userId) {
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ return mFace10.getAuthenticatorId(userId);
}
@Override // Binder call
public void resetLockout(int userId, byte[] hardwareAuthToken) {
- checkPermission(MANAGE_BIOMETRIC);
- mHandler.post(() -> {
- if (!FaceService.this.hasEnrolledBiometrics(userId)) {
- Slog.w(TAG, "Ignoring lockout reset, no templates enrolled");
- return;
- }
-
- Slog.d(TAG, "Resetting lockout for user: " + userId);
-
- updateActiveGroup(userId);
- final FaceResetLockoutClient client = new FaceResetLockoutClient(getContext(),
- mLazyDaemon, userId, getContext().getOpPackageName(), getSensorId(),
- hardwareAuthToken);
- startClient(client, true /* initiatedByClient */);
- });
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ mFace10.scheduleResetLockout(userId, hardwareAuthToken);
}
@Override
public void setFeature(final IBinder token, int userId, int feature, boolean enabled,
final byte[] hardwareAuthToken, IFaceServiceReceiver receiver,
final String opPackageName) {
- checkPermission(MANAGE_BIOMETRIC);
-
- mHandler.post(() -> {
- if (DEBUG) {
- Slog.d(TAG, "setFeature for user(" + userId + ")");
- }
- updateActiveGroup(userId);
- if (!FaceService.this.hasEnrolledBiometrics(mCurrentUserId)) {
- Slog.e(TAG, "No enrolled biometrics while setting feature: " + feature);
- return;
- }
-
- final int faceId = getFirstTemplateForUser(mCurrentUserId);
-
- final FaceSetFeatureClient client = new FaceSetFeatureClient(getContext(),
- mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
- opPackageName, getSensorId(), feature, enabled, hardwareAuthToken, faceId);
- startClient(client, true /* initiatedByClient */);
- });
-
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ mFace10.scheduleSetFeature(token, userId, feature, enabled, hardwareAuthToken, receiver,
+ opPackageName);
}
@Override
public void getFeature(final IBinder token, int userId, int feature,
IFaceServiceReceiver receiver, final String opPackageName) {
- checkPermission(MANAGE_BIOMETRIC);
-
- mHandler.post(() -> {
- if (DEBUG) {
- Slog.d(TAG, "getFeature for user(" + userId + ")");
- }
- updateActiveGroup(userId);
- // This should ideally return tri-state, but the user isn't shown settings unless
- // they are enrolled so it's fine for now.
- if (!FaceService.this.hasEnrolledBiometrics(mCurrentUserId)) {
- Slog.e(TAG, "No enrolled biometrics while getting feature: " + feature);
- return;
- }
-
- // TODO: Support multiple faces
- final int faceId = getFirstTemplateForUser(mCurrentUserId);
-
- final FaceGetFeatureClient client = new FaceGetFeatureClient(getContext(),
- mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
- opPackageName, getSensorId(), feature, faceId);
- startClient(client, true /* initiatedByClient */);
- });
-
- }
-
- // TODO: Support multiple faces
- private int getFirstTemplateForUser(int user) {
- final List<Face> faces = FaceService.this.getEnrolledTemplates(user);
- if (!faces.isEmpty()) {
- return faces.get(0).getBiometricId();
- }
- return 0;
+ Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
+ mFace10.scheduleGetFeature(token, userId, feature, receiver, opPackageName);
}
@Override // Binder call
public void initializeConfiguration(int sensorId) {
- checkPermission(USE_BIOMETRIC_INTERNAL);
- initializeConfigurationInternal(sensorId);
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
+ mFace10 = new Face10(getContext(), sensorId, mLockoutResetDispatcher);
}
}
- private final LockoutHalImpl mLockoutTracker;
-
- @GuardedBy("this")
- private IBiometricsFace mDaemon;
- private UsageStats mUsageStats;
- private FaceRevokeChallengeClient mPendingRevokeChallenge;
-
- private NotificationManager mNotificationManager;
-
- /**
- * Receives callbacks from the HAL.
- */
- private IBiometricsFaceClientCallback mDaemonCallback =
- new IBiometricsFaceClientCallback.Stub() {
- @Override
- public void onEnrollResult(final long deviceId, int faceId, int userId,
- int remaining) {
- mHandler.post(() -> {
- final Face face = new Face(getBiometricUtils()
- .getUniqueName(getContext(), userId), faceId, deviceId);
- FaceService.super.handleEnrollResult(face, remaining);
-
- // Enrollment changes the authenticatorId, so update it here.
- IBiometricsFace daemon = getFaceDaemon();
- if (remaining == 0 && daemon != null) {
- try {
- mAuthenticatorIds.put(userId,
- hasEnrolledBiometrics(userId) ? daemon.getAuthenticatorId().value
- : 0L);
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to get authenticatorId", e);
- }
- }
- });
- }
-
- @Override
- public void onAcquired(final long deviceId, final int userId, final int acquiredInfo,
- final int vendorCode) {
- mHandler.post(() -> {
- FaceService.super.handleAcquired(acquiredInfo, vendorCode);
- });
- }
-
- @Override
- public void onAuthenticated(final long deviceId, final int faceId, final int userId,
- ArrayList<Byte> token) {
- mHandler.post(() -> {
- Face face = new Face("", faceId, deviceId);
- FaceService.super.handleAuthenticated(face, token);
- });
- }
-
- @Override
- public void onError(final long deviceId, final int userId, final int error,
- final int vendorCode) {
- mHandler.post(() -> {
- FaceService.super.handleError(error, vendorCode);
-
- // TODO: this chunk of code should be common to all biometric services
- if (error == BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE) {
- // If we get HW_UNAVAILABLE, try to connect again later...
- Slog.w(TAG, "Got ERROR_HW_UNAVAILABLE; try reconnecting next client.");
- synchronized (this) {
- mDaemon = null;
- mCurrentUserId = UserHandle.USER_NULL;
- }
- }
- });
- }
-
- @Override
- public void onRemoved(final long deviceId, ArrayList<Integer> faceIds, final int userId) {
- mHandler.post(() -> {
- if (!faceIds.isEmpty()) {
- for (int i = 0; i < faceIds.size(); i++) {
- final Face face = new Face("", faceIds.get(i), deviceId);
- // Convert to old behavior
- FaceService.super.handleRemoved(face, faceIds.size() - i - 1);
- }
- } else {
- final Face face = new Face("", 0 /* identifier */, deviceId);
- FaceService.super.handleRemoved(face, 0 /* remaining */);
- }
- Settings.Secure.putIntForUser(getContext().getContentResolver(),
- Settings.Secure.FACE_UNLOCK_RE_ENROLL, 0, UserHandle.USER_CURRENT);
- });
- }
-
- @Override
- public void onEnumerate(long deviceId, ArrayList<Integer> faceIds, int userId)
- throws RemoteException {
- mHandler.post(() -> {
- if (!faceIds.isEmpty()) {
- for (int i = 0; i < faceIds.size(); i++) {
- final Face face = new Face("", faceIds.get(i), deviceId);
- // Convert to old old behavior
- FaceService.super.handleEnumerate(face, faceIds.size() - i - 1);
- }
- } else {
- // For face, the HIDL contract is to receive an empty list when there are no
- // templates enrolled. Send a null identifier since we don't consume them
- // anywhere, and send remaining == 0 to plumb this with existing common code.
- FaceService.super.handleEnumerate(null /* identifier */, 0);
- }
- });
- }
-
- @Override
- public void onLockoutChanged(long duration) {
- Slog.d(TAG, "onLockoutChanged: " + duration);
-
- final @LockoutTracker.LockoutMode int lockoutMode;
- if (duration == 0) {
- lockoutMode = LockoutTracker.LOCKOUT_NONE;
- } else if (duration == -1 || duration == Long.MAX_VALUE) {
- lockoutMode = LockoutTracker.LOCKOUT_PERMANENT;
- } else {
- lockoutMode = LockoutTracker.LOCKOUT_TIMED;
- }
- mLockoutTracker.setCurrentUserLockoutMode(lockoutMode);
-
- mHandler.post(() -> {
- if (duration == 0) {
- mLockoutResetTracker.notifyLockoutResetCallbacks(getSensorId());
- }
- });
- }
- };
-
public FaceService(Context context) {
super(context);
- mLazyDaemon = FaceService.this::getFaceDaemon;
- mLockoutResetTracker = new LockoutResetTracker(context);
- mLockoutTracker = new LockoutHalImpl();
- mUsageStats = new UsageStats(context);
- mNotificationManager = getContext().getSystemService(NotificationManager.class);
- }
-
- @Override
- protected void removeClient(ClientMonitor<?> client) {
- super.removeClient(client);
- if (mPendingRevokeChallenge != null) {
- revokeChallengeInternal(mPendingRevokeChallenge);
- mPendingRevokeChallenge = null;
- }
+ mLockoutResetDispatcher = new LockoutResetDispatcher(context);
}
@Override
public void onStart() {
- super.onStart();
publishBinderService(Context.FACE_SERVICE, new FaceServiceWrapper());
- // Get the face daemon on FaceService's on thread so SystemServerInitThreadPool isn't
- // blocked
- SystemServerInitThreadPool.submit(() -> mHandler.post(this::getFaceDaemon),
- TAG + ".onStart");
- }
-
- @Override
- public String getTag() {
- return TAG;
- }
-
- @Override
- protected IBiometricsFace getDaemon() {
- return getFaceDaemon();
- }
-
- @Override
- protected BiometricUtils getBiometricUtils() {
- return FaceUtils.getInstance();
- }
-
- @Override
- protected boolean hasReachedEnrollmentLimit(int userId) {
- final int limit = getContext().getResources().getInteger(
- com.android.internal.R.integer.config_faceMaxTemplatesPerUser);
- final int enrolled = FaceService.this.getEnrolledTemplates(userId).size();
- if (enrolled >= limit) {
- Slog.w(TAG, "Too many faces registered, user: " + userId);
- return true;
- }
- return false;
- }
-
- @Override
- public void serviceDied(long cookie) {
- super.serviceDied(cookie);
- mDaemon = null;
-
- mCurrentUserId = UserHandle.USER_NULL; // Force updateActiveGroup() to re-evaluate
- }
-
- @Override
- protected void updateActiveGroup(int userId) {
- IBiometricsFace daemon = getFaceDaemon();
-
- if (daemon != null) {
- try {
- if (userId != mCurrentUserId) {
- final File baseDir = Environment.getDataVendorDeDirectory(userId);
- final File faceDir = new File(baseDir, FACE_DATA_DIR);
- if (!faceDir.exists()) {
- if (!faceDir.mkdir()) {
- Slog.v(TAG, "Cannot make directory: " + faceDir.getAbsolutePath());
- return;
- }
- // Calling mkdir() from this process will create a directory with our
- // permissions (inherited from the containing dir). This command fixes
- // the label.
- if (!SELinux.restorecon(faceDir)) {
- Slog.w(TAG, "Restorecons failed. Directory will have wrong label.");
- return;
- }
- }
-
- daemon.setActiveUser(userId, faceDir.getAbsolutePath());
- mCurrentUserId = userId;
- mAuthenticatorIds.put(userId,
- hasEnrolledBiometrics(userId) ? daemon.getAuthenticatorId().value : 0L);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to setActiveUser():", e);
- }
- }
- }
-
- @Override
- protected void handleUserSwitching(int userId) {
- super.handleUserSwitching(userId);
- // Will be updated when we get the callback from HAL
- mLockoutTracker.setCurrentUserLockoutMode(LockoutTracker.LOCKOUT_NONE);
- }
-
- @Override
- protected boolean hasEnrolledBiometrics(int userId) {
- if (userId != UserHandle.getCallingUserId()) {
- checkPermission(INTERACT_ACROSS_USERS);
- }
- return getBiometricUtils().getBiometricsForUser(getContext(), userId).size() > 0;
- }
-
- @Override
- protected String getManageBiometricPermission() {
- return MANAGE_BIOMETRIC;
- }
-
- @Override
- protected List<Face> getEnrolledTemplates(int userId) {
- return FaceUtils.getInstance().getBiometricsForUser(getContext(), userId);
- }
-
- @Override
- protected void notifyClientActiveCallbacks(boolean isActive) {
- // noop for Face.
- }
-
- @Override
- protected int statsModality() {
- return BiometricsProtoEnums.MODALITY_FACE;
- }
-
- @Override
- protected void doTemplateCleanupForUser(int userId) {
- final List<Face> enrolledList = getEnrolledTemplates(userId);
- final FaceInternalCleanupClient client = new FaceInternalCleanupClient(getContext(),
- mLazyDaemon, userId, getContext().getOpPackageName(), getSensorId(), enrolledList,
- getBiometricUtils(), mAuthenticatorIds);
- cleanupInternal(client);
- }
-
-
- /** Gets the face daemon */
- private synchronized IBiometricsFace getFaceDaemon() {
- if (mDaemon == null) {
- Slog.v(TAG, "mDaemon was null, reconnect to face");
- try {
- mDaemon = IBiometricsFace.getService();
- } catch (java.util.NoSuchElementException e) {
- // Service doesn't exist or cannot be opened. Logged below.
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to get biometric interface", e);
- }
- if (mDaemon == null) {
- Slog.w(TAG, "face HIDL not available");
- return null;
- }
-
- mDaemon.asBinder().linkToDeath(this, 0);
-
- long halId = 0;
- try {
- halId = mDaemon.setCallback(mDaemonCallback).value;
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to open face HAL", e);
- mDaemon = null; // try again later!
- }
-
- if (DEBUG) Slog.v(TAG, "Face HAL id: " + halId);
- if (halId != 0) {
- loadAuthenticatorIds();
- updateActiveGroup(ActivityManager.getCurrentUser());
- doTemplateCleanupForUser(ActivityManager.getCurrentUser());
- } else {
- Slog.w(TAG, "Failed to open Face HAL!");
- MetricsLogger.count(getContext(), "faced_openhal_error", 1);
- mDaemon = null;
- }
- }
- return mDaemon;
}
private native NativeHandle convertSurfaceToNativeHandle(Surface surface);
-
- private void dumpInternal(PrintWriter pw) {
- PerformanceTracker performanceTracker =
- PerformanceTracker.getInstanceForSensorId(getSensorId());
-
- JSONObject dump = new JSONObject();
- try {
- dump.put("service", "Face Manager");
-
- JSONArray sets = new JSONArray();
- for (UserInfo user : UserManager.get(getContext()).getUsers()) {
- final int userId = user.getUserHandle().getIdentifier();
- final int N = getBiometricUtils().getBiometricsForUser(getContext(), userId).size();
- JSONObject set = new JSONObject();
- set.put("id", userId);
- set.put("count", N);
- set.put("accept", performanceTracker.getAcceptForUser(userId));
- set.put("reject", performanceTracker.getRejectForUser(userId));
- set.put("acquire", performanceTracker.getAcquireForUser(userId));
- set.put("lockout", performanceTracker.getTimedLockoutForUser(userId));
- set.put("permanentLockout", performanceTracker.getPermanentLockoutForUser(userId));
- // cryptoStats measures statistics about secure face transactions
- // (e.g. to unlock password storage, make secure purchases, etc.)
- set.put("acceptCrypto", performanceTracker.getAcceptCryptoForUser(userId));
- set.put("rejectCrypto", performanceTracker.getRejectCryptoForUser(userId));
- set.put("acquireCrypto", performanceTracker.getAcquireCryptoForUser(userId));
- sets.put(set);
- }
-
- dump.put("prints", sets);
- } catch (JSONException e) {
- Slog.e(TAG, "dump formatting failure", e);
- }
- pw.println(dump);
- pw.println("HAL deaths since last reboot: " + performanceTracker.getHALDeathCount());
-
- mUsageStats.print(pw);
- }
-
- private void dumpHal(FileDescriptor fd, String[] args) {
- // WARNING: CDD restricts image data from leaving TEE unencrypted on
- // production devices:
- // [C-1-10] MUST not allow unencrypted access to identifiable biometric
- // data or any data derived from it (such as embeddings) to the
- // Application Processor outside the context of the TEE.
- // As such, this API should only be enabled for testing purposes on
- // engineering and userdebug builds. All modules in the software stack
- // MUST enforce final build products do NOT have this functionality.
- // Additionally, the following check MUST NOT be removed.
- if (!(Build.IS_ENG || Build.IS_USERDEBUG)) {
- return;
- }
-
- // Additionally, this flag allows turning off face for a device
- // (either permanently through the build or on an individual device).
- if (SystemProperties.getBoolean("ro.face.disable_debug_data", false)
- || SystemProperties.getBoolean("persist.face.disable_debug_data", false)) {
- return;
- }
-
- // The debug method takes two file descriptors. The first is for text
- // output, which we will drop. The second is for binary data, which
- // will be the protobuf data.
- final IBiometricsFace daemon = getFaceDaemon();
- if (daemon != null) {
- FileOutputStream devnull = null;
- try {
- devnull = new FileOutputStream("/dev/null");
- final NativeHandle handle = new NativeHandle(
- new FileDescriptor[] { devnull.getFD(), fd },
- new int[0], false);
- daemon.debug(handle, new ArrayList<String>(Arrays.asList(args)));
- } catch (IOException | RemoteException ex) {
- Slog.d(TAG, "error while reading face debugging data", ex);
- } finally {
- if (devnull != null) {
- try {
- devnull.close();
- } catch (IOException ex) {
- }
- }
- }
- }
- }
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceUpdateActiveUserClient.java
new file mode 100644
index 000000000000..bcf304e47816
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceUpdateActiveUserClient.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2020 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;
+
+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.sensors.ClientMonitor;
+
+import java.io.File;
+import java.util.Map;
+
+public class FaceUpdateActiveUserClient extends ClientMonitor<IBiometricsFace> {
+ private static final String TAG = "FaceUpdateActiveUserClient";
+ private static final String FACE_DATA_DIR = "facedata";
+
+ private final int mCurrentUserId;
+ private final boolean mHasEnrolledBiometrics;
+ @NonNull private final Map<Integer, Long> mAuthenticatorIds;
+
+ FaceUpdateActiveUserClient(@NonNull Context context,
+ @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, int userId, @NonNull String owner,
+ int sensorId, int currentUserId, 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);
+ mCurrentUserId = currentUserId;
+ mHasEnrolledBiometrics = hasEnrolledBIometrics;
+ mAuthenticatorIds = authenticatorIds;
+ }
+
+ @Override
+ public void start(@NonNull FinishCallback finishCallback) {
+ super.start(finishCallback);
+
+ if (mCurrentUserId == getTargetUserId()) {
+ Slog.d(TAG, "Already user: " + mCurrentUserId + ", refreshing authenticatorId");
+ try {
+ mAuthenticatorIds.put(getTargetUserId(), mHasEnrolledBiometrics
+ ? getFreshDaemon().getAuthenticatorId().value : 0L);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to refresh authenticatorId", e);
+ }
+ finishCallback.onClientFinished(this, true /* success */);
+ return;
+ }
+
+ startHalOperation();
+ }
+
+ @Override
+ public void unableToStart() {
+ // Nothing to do here
+ }
+
+ @Override
+ protected void startHalOperation() {
+ final File storePath = new File(Environment.getDataVendorDeDirectory(getTargetUserId()),
+ FACE_DATA_DIR);
+ if (!storePath.exists()) {
+ Slog.e(TAG, "vold has not created the directory?");
+ mFinishCallback.onClientFinished(this, false /* success */);
+ return;
+ }
+
+ try {
+ getFreshDaemon().setActiveUser(getTargetUserId(), storePath.getAbsolutePath());
+ mFinishCallback.onClientFinished(this, true /* success */);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to setActiveUser: " + e);
+ mFinishCallback.onClientFinished(this, false /* success */);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java
index 0a9497291f37..9f94c88193ff 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/Fingerprint21.java
@@ -56,7 +56,7 @@ import com.android.server.biometrics.sensors.ClientMonitor;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.EnumerateConsumer;
import com.android.server.biometrics.sensors.Interruptable;
-import com.android.server.biometrics.sensors.LockoutResetTracker;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
import com.android.server.biometrics.sensors.PerformanceTracker;
import com.android.server.biometrics.sensors.RemovalConsumer;
@@ -83,17 +83,17 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
private static final int ENROLL_TIMEOUT_SEC = 60;
private final Context mContext;
- private final IActivityTaskManager mActivityTaskManager;;
+ private final IActivityTaskManager mActivityTaskManager;
private final SensorProperties mSensorProperties;
private final BiometricScheduler mScheduler;
private final Handler mHandler;
- private final LockoutResetTracker mLockoutResetTracker;
+ private final LockoutResetDispatcher mLockoutResetDispatcher;
private final LockoutFrameworkImpl mLockoutTracker;
private final BiometricTaskStackListener mTaskStackListener;
private final ClientMonitor.LazyDaemon<IBiometricsFingerprint> mLazyDaemon;
private final Map<Integer, Long> mAuthenticatorIds;
- private IBiometricsFingerprint mDaemon;
+ @Nullable private IBiometricsFingerprint mDaemon;
@Nullable private IUdfpsOverlayController mUdfpsOverlayController;
private int mCurrentUserId = UserHandle.USER_NULL;
@@ -102,9 +102,9 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
*/
private static final class SensorProperties {
final int sensorId;
- final Boolean isUdfps;
+ final boolean isUdfps;
- SensorProperties(int sensorId, Boolean isUdfps) {
+ SensorProperties(int sensorId, boolean isUdfps) {
this.sensorId = sensorId;
this.isUdfps = isUdfps;
}
@@ -146,7 +146,7 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
new LockoutFrameworkImpl.LockoutResetCallback() {
@Override
public void onLockoutReset(int userId) {
- mLockoutResetTracker.notifyLockoutResetCallbacks(mSensorProperties.sensorId);
+ mLockoutResetDispatcher.notifyLockoutResetCallbacks(mSensorProperties.sensorId);
}
};
@@ -157,9 +157,8 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
}
};
- private IBiometricsFingerprintClientCallback mDaemonCallback =
+ private final IBiometricsFingerprintClientCallback mDaemonCallback =
new IBiometricsFingerprintClientCallback.Stub() {
-
@Override
public void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining) {
mHandler.post(() -> {
@@ -169,19 +168,13 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
final ClientMonitor<?> client = mScheduler.getCurrentClient();
if (!(client instanceof FingerprintEnrollClient)) {
- final String clientName = client != null
- ? client.getClass().getSimpleName(): "null";
- Slog.e(TAG, "onEnrollResult for non-enroll client: " + clientName);
+ Slog.e(TAG, "onEnrollResult for non-enroll client: "
+ + Utils.getClientName(client));
return;
}
final FingerprintEnrollClient enrollClient = (FingerprintEnrollClient) client;
enrollClient.onEnrollResult(fingerprint, remaining);
-
- if (remaining == 0) {
- // Update the framework's authenticatorId cache for this user
- scheduleUpdateActiveUserWithoutHandler(mCurrentUserId);
- }
});
}
@@ -195,9 +188,8 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
mHandler.post(() -> {
final ClientMonitor<?> client = mScheduler.getCurrentClient();
if (!(client instanceof AcquisitionClient)) {
- final String clientName = client != null
- ? client.getClass().getSimpleName(): "null";
- Slog.e(TAG, "onAcquired for non-acquisition client: " + clientName);
+ Slog.e(TAG, "onAcquired for non-acquisition client: "
+ + Utils.getClientName(client));
return;
}
@@ -212,9 +204,8 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
mHandler.post(() -> {
final ClientMonitor<?> client = mScheduler.getCurrentClient();
if (!(client instanceof FingerprintAuthenticationClient)) {
- final String clientName = client != null
- ? client.getClass().getSimpleName(): "null";
- Slog.e(TAG, "onAuthenticated for non-authentication client: " + clientName);
+ Slog.e(TAG, "onAuthenticated for non-authentication client: "
+ + Utils.getClientName(client));
return;
}
@@ -235,9 +226,7 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
+ ", error: " + error
+ ", vendorCode: " + vendorCode);
if (!(client instanceof Interruptable)) {
- final String clientName = client != null
- ? client.getClass().getSimpleName(): "null";
- Slog.e(TAG, "onError for non-error consumer: " + clientName);
+ Slog.e(TAG, "onError for non-error consumer: " + Utils.getClientName(client));
return;
}
@@ -258,9 +247,8 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
Slog.d(TAG, "Removed, fingerId: " + fingerId + ", remaining: " + remaining);
final ClientMonitor<?> client = mScheduler.getCurrentClient();
if (!(client instanceof RemovalConsumer)) {
- final String clientName = client != null
- ? client.getClass().getSimpleName(): "null";
- Slog.e(TAG, "onRemoved for non-removal consumer: " + clientName);
+ Slog.e(TAG, "onRemoved for non-removal consumer: "
+ + Utils.getClientName(client));
return;
}
@@ -275,9 +263,8 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
mHandler.post(() -> {
final ClientMonitor<?> client = mScheduler.getCurrentClient();
if (!(client instanceof EnumerateConsumer)) {
- final String clientName = client != null
- ? client.getClass().getSimpleName(): "null";
- Slog.e(TAG, "onEnumerate for non-enumerate consumer: " + clientName);
+ Slog.e(TAG, "onEnumerate for non-enumerate consumer: "
+ + Utils.getClientName(client));
return;
}
@@ -289,17 +276,17 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
};
Fingerprint21(@NonNull Context context, int sensorId,
- @NonNull LockoutResetTracker lockoutResetTracker,
- @NonNull GestureAvailabilityTracker gestureAvailabilityTracker) {
+ @NonNull LockoutResetDispatcher lockoutResetDispatcher,
+ @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) {
mContext = context;
mActivityTaskManager = ActivityTaskManager.getService();
mHandler = new Handler(Looper.getMainLooper());
mTaskStackListener = new BiometricTaskStackListener();
mAuthenticatorIds = Collections.synchronizedMap(new HashMap<>());
mLazyDaemon = Fingerprint21.this::getDaemon;
- mLockoutResetTracker = lockoutResetTracker;
+ mLockoutResetDispatcher = lockoutResetDispatcher;
mLockoutTracker = new LockoutFrameworkImpl(context, mLockoutResetCallback);
- mScheduler = new BiometricScheduler(TAG, gestureAvailabilityTracker);
+ mScheduler = new BiometricScheduler(TAG, gestureAvailabilityDispatcher);
try {
ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG);
@@ -308,7 +295,7 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
}
final IBiometricsFingerprint daemon = getDaemon();
- Boolean isUdfps = false;
+ boolean isUdfps = false;
android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint extension =
android.hardware.biometrics.fingerprint.V2_3.IBiometricsFingerprint.castFrom(
daemon);
@@ -317,7 +304,7 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
isUdfps = extension.isUdfps(sensorId);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception while quering udfps", e);
- isUdfps = null;
+ isUdfps = false;
}
}
mSensorProperties = new SensorProperties(sensorId, isUdfps);
@@ -414,9 +401,15 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
}
}
});
-
}
+ /**
+ * Schedules the {@link FingerprintUpdateActiveUserClient} without posting the work onto the
+ * handler. Many/most APIs are user-specific. However, the HAL requires explicit "setActiveUser"
+ * invocation prior to authenticate/enroll/etc. Thus, internally we usually want to schedule
+ * this operation on the same lambda/runnable as those operations so that the ordering is
+ * correct.
+ */
private void scheduleUpdateActiveUserWithoutHandler(int targetUserId) {
final boolean hasEnrolled = !getEnrolledFingerprints(targetUserId).isEmpty();
final FingerprintUpdateActiveUserClient client =
@@ -467,9 +460,13 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
mLazyDaemon, token, new ClientMonitorCallbackConverter(receiver), userId,
hardwareAuthToken, opPackageName, FingerprintUtils.getInstance(),
ENROLL_TIMEOUT_SEC, mSensorProperties.sensorId, mUdfpsOverlayController);
- mScheduler.scheduleClientMonitor(client);
+ mScheduler.scheduleClientMonitor(client, ((clientMonitor, success) -> {
+ if (success) {
+ // Update authenticatorIds
+ scheduleUpdateActiveUserWithoutHandler(clientMonitor.getTargetUserId());
+ }
+ }));
});
-
}
void cancelEnrollment(@NonNull IBinder token) {
@@ -533,7 +530,7 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
}
boolean isHardwareDetected() {
- IBiometricsFingerprint daemon = getDaemon();
+ final IBiometricsFingerprint daemon = getDaemon();
return daemon != null;
}
@@ -619,7 +616,7 @@ class Fingerprint21 implements IHwBinder.DeathRecipient {
tracker.clear();
}
- void dumpInternal(PrintWriter pw) {
+ void dumpInternal(@NonNull PrintWriter pw) {
PerformanceTracker performanceTracker =
PerformanceTracker.getInstanceForSensorId(mSensorProperties.sensorId);
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java
index c26dbfdaa75b..25e949a6fa60 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintEnrollClient.java
@@ -76,6 +76,19 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint
}
@Override
+ protected boolean hasReachedEnrollmentLimit() {
+ final int limit = getContext().getResources().getInteger(
+ com.android.internal.R.integer.config_fingerprintMaxTemplatesPerUser);
+ final int enrolled = mBiometricUtils.getBiometricsForUser(getContext(), getTargetUserId())
+ .size();
+ if (enrolled >= limit) {
+ Slog.w(TAG, "Too many faces registered, user: " + getTargetUserId());
+ return true;
+ }
+ return false;
+ }
+
+ @Override
protected void startHalOperation() {
showUdfpsOverlay();
try {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
index d092bfd02b7b..ce9565ac0f7d 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java
@@ -25,6 +25,7 @@ import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.Manifest.permission.USE_FINGERPRINT;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
+import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.content.Context;
@@ -50,7 +51,7 @@ import com.android.internal.util.DumpUtils;
import com.android.server.SystemService;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
-import com.android.server.biometrics.sensors.LockoutResetTracker;
+import com.android.server.biometrics.sensors.LockoutResetDispatcher;
import com.android.server.biometrics.sensors.LockoutTracker;
import java.io.FileDescriptor;
@@ -68,8 +69,8 @@ public class FingerprintService extends SystemService {
protected static final String TAG = "FingerprintService";
private final AppOpsManager mAppOps;
- private final LockoutResetTracker mLockoutResetTracker;
- private final GestureAvailabilityTracker mGestureAvailabilityTracker;
+ private final LockoutResetDispatcher mLockoutResetDispatcher;
+ private final GestureAvailabilityDispatcher mGestureAvailabilityDispatcher;
private Fingerprint21 mFingerprint21;
/**
@@ -181,11 +182,11 @@ public class FingerprintService extends SystemService {
public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback,
final String opPackageName) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
- mLockoutResetTracker.addCallback(callback, opPackageName);
+ mLockoutResetDispatcher.addCallback(callback, opPackageName);
}
@Override // Binder call
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
return;
}
@@ -212,6 +213,10 @@ public class FingerprintService extends SystemService {
final long token = Binder.clearCallingIdentity();
try {
+ if (mFingerprint21 == null) {
+ Slog.e(TAG, "No HAL");
+ return false;
+ }
return mFingerprint21.isHardwareDetected();
} finally {
Binder.restoreCallingIdentity(token);
@@ -277,26 +282,26 @@ public class FingerprintService extends SystemService {
@Override
public boolean isClientActive() {
Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
- return mGestureAvailabilityTracker.isAnySensorActive();
+ return mGestureAvailabilityDispatcher.isAnySensorActive();
}
@Override
public void addClientActiveCallback(IFingerprintClientActiveCallback callback) {
Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
- mGestureAvailabilityTracker.registerCallback(callback);
+ mGestureAvailabilityDispatcher.registerCallback(callback);
}
@Override
public void removeClientActiveCallback(IFingerprintClientActiveCallback callback) {
Utils.checkPermission(getContext(), MANAGE_FINGERPRINT);
- mGestureAvailabilityTracker.removeCallback(callback);
+ mGestureAvailabilityDispatcher.removeCallback(callback);
}
@Override // Binder call
public void initializeConfiguration(int sensorId) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
- mFingerprint21 = new Fingerprint21(getContext(), sensorId, mLockoutResetTracker,
- mGestureAvailabilityTracker);
+ mFingerprint21 = new Fingerprint21(getContext(), sensorId, mLockoutResetDispatcher,
+ mGestureAvailabilityDispatcher);
}
@Override
@@ -312,7 +317,7 @@ public class FingerprintService extends SystemService {
}
@Override
- public boolean isUdfps(int sensorId) {
+ public boolean isUdfps() {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
return mFingerprint21.isUdfps();
}
@@ -327,8 +332,8 @@ public class FingerprintService extends SystemService {
public FingerprintService(Context context) {
super(context);
mAppOps = context.getSystemService(AppOpsManager.class);
- mGestureAvailabilityTracker = new GestureAvailabilityTracker();
- mLockoutResetTracker = new LockoutResetTracker(context);
+ mGestureAvailabilityDispatcher = new GestureAvailabilityDispatcher();
+ mLockoutResetDispatcher = new LockoutResetDispatcher(context);
}
@Override
@@ -336,6 +341,9 @@ public class FingerprintService extends SystemService {
publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper());
}
+ /**
+ * Checks for public API invocations to ensure that permissions, etc are granted/correct.
+ */
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
private boolean canUseFingerprint(String opPackageName, boolean requireForeground, int uid,
int pid, int userId) {
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/GestureAvailabilityTracker.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/GestureAvailabilityDispatcher.java
index 5023c83cd585..7bda33da8126 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/GestureAvailabilityTracker.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/GestureAvailabilityDispatcher.java
@@ -28,7 +28,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
* Keeps track of sensor gesture availability (e.g. swipe), and notifies clients when its
* availability changes
*/
-public class GestureAvailabilityTracker {
+public class GestureAvailabilityDispatcher {
private static final String TAG = "GestureAvailabilityTracker";
private final CopyOnWriteArrayList<IFingerprintClientActiveCallback> mClientActiveCallbacks;
@@ -36,7 +36,7 @@ public class GestureAvailabilityTracker {
private boolean mIsActive;
- GestureAvailabilityTracker() {
+ GestureAvailabilityDispatcher() {
mClientActiveCallbacks = new CopyOnWriteArrayList<>();
mActiveSensors = new HashMap<>();
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/iris/IrisService.java b/services/core/java/com/android/server/biometrics/sensors/iris/IrisService.java
index 58ab20d8d56d..bcf63dcdd67f 100644
--- a/services/core/java/com/android/server/biometrics/sensors/iris/IrisService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/iris/IrisService.java
@@ -18,17 +18,12 @@ package com.android.server.biometrics.sensors.iris;
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
+import android.annotation.NonNull;
import android.content.Context;
-import android.hardware.biometrics.BiometricAuthenticator;
-import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.iris.IIrisService;
-import com.android.server.biometrics.sensors.BiometricServiceBase;
-import com.android.server.biometrics.sensors.BiometricUtils;
-import com.android.server.biometrics.sensors.LockoutTracker;
-import com.android.server.biometrics.sensors.fingerprint.FingerprintService;
-
-import java.util.List;
+import com.android.server.SystemService;
+import com.android.server.biometrics.Utils;
/**
* A service to manage multiple clients that want to access the Iris HAL API.
@@ -36,11 +31,9 @@ import java.util.List;
* iris-related events.
*
* TODO: The vendor is expected to fill in the service. See
- * {@link FingerprintService}
- *
- * @hide
+ * {@link com.android.server.biometrics.sensors.face.FaceService}
*/
-public class IrisService extends BiometricServiceBase {
+public class IrisService extends SystemService {
private static final String TAG = "IrisService";
@@ -50,77 +43,16 @@ public class IrisService extends BiometricServiceBase {
private final class IrisServiceWrapper extends IIrisService.Stub {
@Override // Binder call
public void initializeConfiguration(int sensorId) {
- checkPermission(USE_BIOMETRIC_INTERNAL);
- initializeConfigurationInternal(sensorId);
+ Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
}
}
- /**
- * Initializes the system service.
- * <p>
- * Subclasses must define a single argument constructor that accepts the context
- * and passes it to super.
- * </p>
- *
- * @param context The system server context.
- */
- public IrisService(Context context) {
+ public IrisService(@NonNull Context context) {
super(context);
}
@Override
public void onStart() {
- super.onStart();
publishBinderService(Context.IRIS_SERVICE, new IrisServiceWrapper());
}
-
- @Override
- protected void doTemplateCleanupForUser(int userId) {
-
- }
-
- @Override
- protected String getTag() {
- return TAG;
- }
-
- @Override
- protected Object getDaemon() {
- return null;
- }
-
- @Override
- protected BiometricUtils getBiometricUtils() {
- return null;
- }
-
- @Override
- protected boolean hasReachedEnrollmentLimit(int userId) {
- return false;
- }
-
- @Override
- protected void updateActiveGroup(int userId) {
-
- }
-
- @Override
- protected boolean hasEnrolledBiometrics(int userId) {
- return false;
- }
-
- @Override
- protected String getManageBiometricPermission() {
- return null;
- }
-
- @Override
- protected List<? extends BiometricAuthenticator.Identifier> getEnrolledTemplates(int userId) {
- return null;
- }
-
- @Override
- protected int statsModality() {
- return BiometricsProtoEnums.MODALITY_IRIS;
- }
}
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index b279b370c611..ed3a223b5dd7 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -400,7 +400,7 @@ public class ClipboardService extends SystemService {
final int intendingUid = getIntendingUid(callingPackage, userId);
final int intendingUserId = UserHandle.getUserId(intendingUid);
if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, callingPackage,
- intendingUid, intendingUserId)
+ intendingUid, intendingUserId, false)
|| isDeviceLocked(intendingUserId)) {
return null;
}
@@ -416,7 +416,7 @@ public class ClipboardService extends SystemService {
final int intendingUid = getIntendingUid(callingPackage, userId);
final int intendingUserId = UserHandle.getUserId(intendingUid);
if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, callingPackage,
- intendingUid, intendingUserId)
+ intendingUid, intendingUserId, false)
|| isDeviceLocked(intendingUserId)) {
return false;
}
@@ -450,7 +450,7 @@ public class ClipboardService extends SystemService {
final int intendingUid = getIntendingUid(callingPackage, userId);
final int intendingUserId = UserHandle.getUserId(intendingUid);
if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, callingPackage,
- intendingUid, intendingUserId)
+ intendingUid, intendingUserId, false)
|| isDeviceLocked(intendingUserId)) {
return false;
}
@@ -740,14 +740,21 @@ public class ClipboardService extends SystemService {
private boolean clipboardAccessAllowed(int op, String callingPackage, int uid,
@UserIdInt int userId) {
- // Check the AppOp.
- if (mAppOps.noteOp(op, uid, callingPackage) != AppOpsManager.MODE_ALLOWED) {
- return false;
- }
+ return clipboardAccessAllowed(op, callingPackage, uid, userId, true);
+ }
+
+ private boolean clipboardAccessAllowed(int op, String callingPackage, int uid,
+ @UserIdInt int userId, boolean shouldNoteOp) {
+
+ boolean allowed = false;
+
+ // First, verify package ownership to ensure use below is safe.
+ mAppOps.checkPackage(uid, callingPackage);
+
// Shell can access the clipboard for testing purposes.
if (mPm.checkPermission(android.Manifest.permission.READ_CLIPBOARD_IN_BACKGROUND,
callingPackage) == PackageManager.PERMISSION_GRANTED) {
- return true;
+ allowed = true;
}
// The default IME is always allowed to access the clipboard.
String defaultIme = Settings.Secure.getStringForUser(getContext().getContentResolver(),
@@ -755,7 +762,7 @@ public class ClipboardService extends SystemService {
if (!TextUtils.isEmpty(defaultIme)) {
final String imePkg = ComponentName.unflattenFromString(defaultIme).getPackageName();
if (imePkg.equals(callingPackage)) {
- return true;
+ allowed = true;
}
}
@@ -766,8 +773,10 @@ public class ClipboardService extends SystemService {
// at the same time. e.x. SystemUI. It needs to check the window focus of
// Binder.getCallingUid(). Without checking, the user X can't copy any thing from
// INTERNAL_SYSTEM_WINDOW to the other applications.
- boolean allowed = mWm.isUidFocused(uid)
- || isInternalSysWindowAppWithWindowFocus(callingPackage);
+ if (!allowed) {
+ allowed = mWm.isUidFocused(uid)
+ || isInternalSysWindowAppWithWindowFocus(callingPackage);
+ }
if (!allowed && mContentCaptureInternal != null) {
// ...or the Content Capture Service
// The uid parameter of mContentCaptureInternal.isContentCaptureServiceForUser
@@ -786,17 +795,28 @@ public class ClipboardService extends SystemService {
// userId must pass intending userId. i.e. user#10.
allowed = mAutofillInternal.isAugmentedAutofillServiceForUser(uid, userId);
}
- if (!allowed) {
- Slog.e(TAG, "Denying clipboard access to " + callingPackage
- + ", application is not in focus neither is a system service for "
- + "user " + userId);
- }
- return allowed;
+ break;
case AppOpsManager.OP_WRITE_CLIPBOARD:
// Writing is allowed without focus.
- return true;
+ allowed = true;
+ break;
default:
throw new IllegalArgumentException("Unknown clipboard appop " + op);
}
+ if (!allowed) {
+ Slog.e(TAG, "Denying clipboard access to " + callingPackage
+ + ", application is not in focus nor is it a system service for "
+ + "user " + userId);
+ return false;
+ }
+ // Finally, check the app op.
+ int appOpsResult;
+ if (shouldNoteOp) {
+ appOpsResult = mAppOps.noteOp(op, uid, callingPackage);
+ } else {
+ appOpsResult = mAppOps.checkOp(op, uid, callingPackage);
+ }
+
+ return appOpsResult == AppOpsManager.MODE_ALLOWED;
}
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index ca16d5730f78..a864aa65fe4a 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -403,6 +403,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
@GuardedBy("mLock")
private boolean mVerityFound;
+ @GuardedBy("mLock")
private boolean mDataLoaderFinished = false;
@GuardedBy("mLock")
@@ -1118,7 +1119,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (hasParentSessionId()) {
throw new IllegalStateException(
"Session " + sessionId + " is a child of multi-package session "
- + mParentSessionId + " and may not be committed directly.");
+ + getParentSessionId() + " and may not be committed directly.");
}
if (!markAsSealed(statusReceiver, forTransfer)) {
@@ -1589,12 +1590,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
private void onStorageUnhealthy() {
- if (TextUtils.isEmpty(mPackageName)) {
+ final String packageName = getPackageName();
+ if (TextUtils.isEmpty(packageName)) {
// The package has not been installed.
return;
}
final PackageManagerService packageManagerService = mPm;
- final String packageName = mPackageName;
mHandler.post(() -> {
if (packageManagerService.deletePackageX(packageName,
PackageManager.VERSION_CODE_HIGHEST, UserHandle.USER_SYSTEM,
@@ -1686,7 +1687,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
private void handleInstall() {
- if (isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked()) {
+ final boolean needsLogging;
+ synchronized (mLock) {
+ needsLogging = isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked();
+ }
+ if (needsLogging) {
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.INSTALL_PACKAGE)
.setAdmin(mInstallSource.installerPackageName)
@@ -1929,19 +1934,20 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// Skip logging the side-loaded app installations, as those are private and aren't reported
// anywhere; app stores already have a record of the installation and that's why reporting
// it here is fine
+ final String packageName = getPackageName();
final String packageNameToLog =
- (params.installFlags & PackageManager.INSTALL_FROM_ADB) == 0 ? mPackageName : "";
+ (params.installFlags & PackageManager.INSTALL_FROM_ADB) == 0 ? packageName : "";
final long currentTimestamp = System.currentTimeMillis();
FrameworkStatsLog.write(FrameworkStatsLog.PACKAGE_INSTALLER_V2_REPORTED,
isIncrementalInstallation(),
packageNameToLog,
currentTimestamp - createdMillis,
returnCode,
- getApksSize());
+ getApksSize(packageName));
}
- private long getApksSize() {
- final PackageSetting ps = mPm.getPackageSetting(mPackageName);
+ private long getApksSize(String packageName) {
+ final PackageSetting ps = mPm.getPackageSetting(packageName);
if (ps == null) {
return 0;
}
@@ -2577,7 +2583,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
void setPermissionsResult(boolean accepted) {
- if (!mSealed) {
+ if (!isSealed()) {
throw new SecurityException("Must be sealed to accept permissions");
}
@@ -2653,7 +2659,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (hasParentSessionId()) {
throw new IllegalStateException(
"Session " + sessionId + " is a child of multi-package session "
- + mParentSessionId + " and may not be abandoned directly.");
+ + getParentSessionId() + " and may not be abandoned directly.");
}
List<PackageInstallerSession> childSessions = getChildSessionsNotLocked();
@@ -2823,7 +2829,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
return;
}
- if (mDestroyed || mDataLoaderFinished) {
+ final boolean isDestroyedOrDataLoaderFinished;
+ synchronized (mLock) {
+ isDestroyedOrDataLoaderFinished = mDestroyed || mDataLoaderFinished;
+ }
+ if (isDestroyedOrDataLoaderFinished) {
switch (status) {
case IDataLoaderStatusListener.DATA_LOADER_UNRECOVERABLE:
onStorageUnhealthy();
@@ -2835,7 +2845,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
try {
IDataLoader dataLoader = dataLoaderManager.getDataLoader(dataLoaderId);
if (dataLoader == null) {
- mDataLoaderFinished = true;
+ synchronized (mLock) {
+ mDataLoaderFinished = true;
+ }
dispatchSessionVerificationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE,
"Failure to obtain data loader");
return;
@@ -2868,10 +2880,12 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
break;
}
case IDataLoaderStatusListener.DATA_LOADER_IMAGE_READY: {
- mDataLoaderFinished = true;
+ synchronized (mLock) {
+ mDataLoaderFinished = true;
+ }
if (hasParentSessionId()) {
mSessionProvider.getSession(
- mParentSessionId).dispatchStreamValidateAndCommit();
+ getParentSessionId()).dispatchStreamValidateAndCommit();
} else {
dispatchStreamValidateAndCommit();
}
@@ -2881,7 +2895,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
break;
}
case IDataLoaderStatusListener.DATA_LOADER_IMAGE_NOT_READY: {
- mDataLoaderFinished = true;
+ synchronized (mLock) {
+ mDataLoaderFinished = true;
+ }
dispatchSessionVerificationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE,
"Failed to prepare image.");
if (manualStartAndDestroy) {
@@ -2895,7 +2911,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
break;
}
case IDataLoaderStatusListener.DATA_LOADER_UNRECOVERABLE:
- mDataLoaderFinished = true;
+ synchronized (mLock) {
+ mDataLoaderFinished = true;
+ }
dispatchSessionVerificationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE,
"DataLoader reported unrecoverable failure.");
break;
@@ -2919,7 +2937,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
final IStorageHealthListener healthListener = new IStorageHealthListener.Stub() {
@Override
public void onHealthStatus(int storageId, int status) {
- if (mDestroyed || mDataLoaderFinished) {
+ final boolean isDestroyedOrDataLoaderFinished;
+ synchronized (mLock) {
+ isDestroyedOrDataLoaderFinished = mDestroyed || mDataLoaderFinished;
+ }
+ if (isDestroyedOrDataLoaderFinished) {
// App's installed.
switch (status) {
case IStorageHealthListener.HEALTH_STATUS_UNHEALTHY:
@@ -2941,7 +2963,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
// fallthrough
case IStorageHealthListener.HEALTH_STATUS_UNHEALTHY:
// Even ADB installation can't wait for missing pages for too long.
- mDataLoaderFinished = true;
+ synchronized (mLock) {
+ mDataLoaderFinished = true;
+ }
dispatchSessionVerificationFailure(INSTALL_FAILED_MEDIA_UNAVAILABLE,
"Image is missing pages required for installation.");
break;
@@ -2984,13 +3008,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
return EMPTY_CHILD_SESSION_ARRAY;
}
+ private boolean canBeAddedAsChild(int parentCandidate) {
+ synchronized (mLock) {
+ return (!hasParentSessionId() || mParentSessionId == parentCandidate)
+ && !mCommitted && !mDestroyed;
+ }
+ }
+
@Override
public void addChildSessionId(int childSessionId) {
final PackageInstallerSession childSession = mSessionProvider.getSession(childSessionId);
- if (childSession == null
- || (childSession.hasParentSessionId() && childSession.mParentSessionId != sessionId)
- || childSession.mCommitted
- || childSession.mDestroyed) {
+ if (childSession == null || !childSession.canBeAddedAsChild(sessionId)) {
throw new IllegalStateException("Unable to add child session " + childSessionId
+ " as it does not exist or is in an invalid state.");
}
@@ -3039,12 +3067,16 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
boolean hasParentSessionId() {
- return mParentSessionId != SessionInfo.INVALID_ID;
+ synchronized (mLock) {
+ return mParentSessionId != SessionInfo.INVALID_ID;
+ }
}
@Override
public int getParentSessionId() {
- return mParentSessionId;
+ synchronized (mLock) {
+ return mParentSessionId;
+ }
}
private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) {
@@ -3134,27 +3166,37 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
/** {@hide} */
boolean isStagedSessionReady() {
- return mStagedSessionReady;
+ synchronized (mLock) {
+ return mStagedSessionReady;
+ }
}
/** {@hide} */
boolean isStagedSessionApplied() {
- return mStagedSessionApplied;
+ synchronized (mLock) {
+ return mStagedSessionApplied;
+ }
}
/** {@hide} */
boolean isStagedSessionFailed() {
- return mStagedSessionFailed;
+ synchronized (mLock) {
+ return mStagedSessionFailed;
+ }
}
/** {@hide} */
@StagedSessionErrorCode int getStagedSessionErrorCode() {
- return mStagedSessionErrorCode;
+ synchronized (mLock) {
+ return mStagedSessionErrorCode;
+ }
}
/** {@hide} */
String getStagedSessionErrorMessage() {
- return mStagedSessionErrorMessage;
+ synchronized (mLock) {
+ return mStagedSessionErrorMessage;
+ }
}
private void destroyInternal() {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 9f3db704253e..17b805f4dfa3 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -7158,8 +7158,9 @@ public class PackageManagerService extends IPackageManager.Stub
&& ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp)
|| (matchVisibleToInstantAppOnly && isCallerInstantApp
&& isTargetHiddenFromInstantApp));
- final boolean blockNormalResolution = !isTargetInstantApp && !isCallerInstantApp
- && shouldFilterApplicationLocked(
+ final boolean blockNormalResolution =
+ !resolveForStart && !isTargetInstantApp && !isCallerInstantApp
+ && shouldFilterApplicationLocked(
getPackageSettingInternal(ai.applicationInfo.packageName,
Process.SYSTEM_UID), filterCallingUid, userId);
if (!blockInstantResolution && !blockNormalResolution) {
@@ -7252,8 +7253,8 @@ public class PackageManagerService extends IPackageManager.Stub
final PackageSetting setting =
getPackageSettingInternal(pkgName, Process.SYSTEM_UID);
result = null;
- if (setting != null && setting.pkg != null
- && !shouldFilterApplicationLocked(setting, filterCallingUid, userId)) {
+ if (setting != null && setting.pkg != null && (resolveForStart
+ || !shouldFilterApplicationLocked(setting, filterCallingUid, userId))) {
result = filterIfNotSystemUser(mComponentResolver.queryActivities(
intent, resolvedType, flags, setting.pkg.getActivities(), userId),
userId);
@@ -25545,7 +25546,8 @@ public class PackageManagerService extends IPackageManager.Stub
private void applyMimeGroupChanges(String packageName, String mimeGroup) {
if (mComponentResolver.updateMimeGroup(packageName, mimeGroup)) {
- clearPackagePreferredActivities(packageName, UserHandle.USER_ALL);
+ Binder.withCleanCallingIdentity(() ->
+ clearPackagePreferredActivities(packageName, UserHandle.USER_ALL));
}
mPmInternal.writeSettings(false);
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 9963cf7e212d..bc7554c54eb0 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -4224,6 +4224,20 @@ public class PermissionManagerService extends IPermissionManager.Stub {
revokePermissionFromPackageForUser(p.getPackageName(),
bp.getName(), true, userId, callback));
}
+ } else {
+ mPackageManagerInt.forEachPackage(p -> {
+ PackageSetting ps = mPackageManagerInt.getPackageSetting(
+ p.getPackageName());
+ if (ps == null) {
+ return;
+ }
+ PermissionsState permissionsState = ps.getPermissionsState();
+ if (permissionsState.getInstallPermissionState(bp.getName()) != null) {
+ permissionsState.revokeInstallPermission(bp);
+ permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
+ MASK_PERMISSION_FLAGS_ALL, 0);
+ }
+ });
}
it.remove();
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java
index 72f8027e664f..761858ccd238 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Enforcer.java
@@ -192,10 +192,7 @@ public class SoundTriggerHw2Enforcer implements ISoundTriggerHw2 {
}
private static RuntimeException handleException(RuntimeException e) {
- // TODO(b/160169016): There is currently no other way to distinguish dead object from other
- // exceptions.
- if (e.getCause() instanceof RemoteException &&
- e.getCause().getMessage().equals("HwBinder Error: (-32)")) {
+ if (e.getCause() instanceof DeadObjectException) {
// Server is dead, no need to reboot.
Log.e(TAG, "HAL died");
} else {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 4e355f0dadfc..bec0ce944e3f 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -7465,7 +7465,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
/**
* Determines whether this ActivityRecord can turn the screen on. It checks whether the flag
* {@link ActivityRecord#getTurnScreenOnFlag} is set and checks whether the ActivityRecord
- * should be visible depending on Keyguard state
+ * should be visible depending on Keyguard and window state.
*
* @return true if the screen can be turned on, false otherwise.
*/
@@ -7474,8 +7474,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return false;
}
final ActivityStack stack = getRootTask();
- return stack != null &&
- stack.checkKeyguardVisibility(this, true /* shouldBeVisible */,
+ return stack != null
+ && !stack.inMultiWindowMode()
+ && stack.checkKeyguardVisibility(this, true /* shouldBeVisible */,
stack.topRunningActivity() == this /* isTop */);
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 1d1c31b8226c..de91af752512 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3459,9 +3459,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
InsetsControlTarget getImeFallback() {
// host is in non-default display that doesn't support system decor, default to
// default display's StatusBar to control IME (when available), else let system control it.
- WindowState statusBar =
- mWmService.getDefaultDisplayContentLocked().getDisplayPolicy().getStatusBar();
- return statusBar != null ? statusBar : mRemoteInsetsControlTarget;
+ final DisplayContent defaultDc = mWmService.getDefaultDisplayContentLocked();
+ WindowState statusBar = defaultDc.getDisplayPolicy().getStatusBar();
+ return statusBar != null ? statusBar : defaultDc.mRemoteInsetsControlTarget;
}
boolean canShowIme() {
@@ -3521,7 +3521,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
*/
@VisibleForTesting
InsetsControlTarget computeImeControlTarget() {
- if (!isImeControlledByApp() && mRemoteInsetsControlTarget != null) {
+ if (!isImeControlledByApp() && mRemoteInsetsControlTarget != null
+ || (mInputMethodInputTarget != null
+ && getImeHostOrFallback(mInputMethodInputTarget.getWindow())
+ == mRemoteInsetsControlTarget)) {
return mRemoteInsetsControlTarget;
} else {
// Now, a special case -- if the last target's window is in the process of exiting, but
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 99ee5e121b7a..e7fbc334306e 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -32,7 +32,7 @@ import java.io.PrintWriter;
*/
class ImeInsetsSourceProvider extends InsetsSourceProvider {
- private WindowState mImeTargetFromIme;
+ private InsetsControlTarget mImeTargetFromIme;
private Runnable mShowImeRunner;
private boolean mIsImeLayoutDrawn;
@@ -47,10 +47,12 @@ class ImeInsetsSourceProvider extends InsetsSourceProvider {
*
* @param imeTarget imeTarget on which IME request is coming from.
*/
- void scheduleShowImePostLayout(WindowState imeTarget) {
+ void scheduleShowImePostLayout(InsetsControlTarget imeTarget) {
boolean targetChanged = mImeTargetFromIme != imeTarget
&& mImeTargetFromIme != null && imeTarget != null && mShowImeRunner != null
- && mImeTargetFromIme.mActivityRecord == imeTarget.mActivityRecord;
+ && imeTarget.getWindow() != null && mImeTargetFromIme.getWindow() != null
+ && mImeTargetFromIme.getWindow().mActivityRecord
+ == imeTarget.getWindow().mActivityRecord;
mImeTargetFromIme = imeTarget;
if (targetChanged) {
// target changed, check if new target can show IME.
@@ -62,7 +64,8 @@ class ImeInsetsSourceProvider extends InsetsSourceProvider {
return;
}
- ProtoLog.d(WM_DEBUG_IME, "Schedule IME show for %s", mImeTargetFromIme.getName());
+ ProtoLog.d(WM_DEBUG_IME, "Schedule IME show for %s", mImeTargetFromIme.getWindow() == null
+ ? mImeTargetFromIme : mImeTargetFromIme.getWindow().getName());
mShowImeRunner = () -> {
ProtoLog.d(WM_DEBUG_IME, "Run showImeRunner");
// Target should still be the same.
@@ -127,13 +130,17 @@ class ImeInsetsSourceProvider extends InsetsSourceProvider {
return false;
}
ProtoLog.d(WM_DEBUG_IME, "dcTarget: %s mImeTargetFromIme: %s",
- dcTarget.getName(), mImeTargetFromIme.getName());
+ dcTarget.getName(), mImeTargetFromIme.getWindow() == null
+ ? mImeTargetFromIme : mImeTargetFromIme.getWindow().getName());
return (!dcTarget.isClosing() && mImeTargetFromIme == dcTarget)
- || (mImeTargetFromIme != null && dcTarget.getParentWindow() == mImeTargetFromIme
- && dcTarget.mSubLayer > mImeTargetFromIme.mSubLayer)
+ || (mImeTargetFromIme != null && mImeTargetFromIme.getWindow() != null
+ && dcTarget.getParentWindow() == mImeTargetFromIme
+ && dcTarget.mSubLayer > mImeTargetFromIme.getWindow().mSubLayer)
|| mImeTargetFromIme == mDisplayContent.getImeFallback()
- || (!mImeTargetFromIme.isClosing() && controlTarget == mImeTargetFromIme);
+ || controlTarget == mImeTargetFromIme
+ && (mImeTargetFromIme.getWindow() == null
+ || !mImeTargetFromIme.getWindow().isClosing());
}
@Override
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index ab2151974ce9..f3e23169fee0 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2269,6 +2269,10 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
final DisplayContent display = getChildAt(displayNdx);
+ if (display.shouldSleep()) {
+ continue;
+ }
+
final boolean curResult = result;
boolean resumedOnDisplay = display.reduceOnAllTaskDisplayAreas(
(taskDisplayArea, resumed) -> {
@@ -2360,7 +2364,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
// process the keyguard going away, which can happen before the sleep
// token is released. As a result, it is important we resume the
// activity here.
- resumeFocusedStacksTopActivities();
+ stack.resumeTopActivityUncheckedLocked(null, null);
}
// The visibility update must not be called before resuming the top, so the
// display orientation can be updated first if needed. Otherwise there may
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index f13dfdd1156f..68445f6970fb 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -615,8 +615,9 @@ class TaskSnapshotController {
static Rect getSystemBarInsets(Rect frame, InsetsState state) {
return state.calculateInsets(frame, null /* ignoringVisibilityState */,
false /* isScreenRound */, false /* alwaysConsumeSystemBars */,
- null /* displayCutout */, 0 /* legacySoftInputMode */, 0 /* legacySystemUiFlags */,
- null /* typeSideMap */).getInsets(WindowInsets.Type.systemBars()).toRect();
+ null /* displayCutout */, 0 /* legacySoftInputMode */, 0 /* legacyWindowFlags */,
+ 0 /* legacySystemUiFlags */, null /* typeSideMap */).getInsets(
+ WindowInsets.Type.systemBars()).toRect();
}
void dump(PrintWriter pw, String prefix) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 85972bffd755..4df48dc11636 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7599,13 +7599,14 @@ public class WindowManagerService extends IWindowManager.Stub
if (imeTarget == null) {
return;
}
- imeTarget = imeTarget.getImeControlTarget().getWindow();
+ final InsetsControlTarget controlTarget = imeTarget.getImeControlTarget();
+ imeTarget = controlTarget.getWindow();
// If InsetsControlTarget doesn't have a window, its using remoteControlTarget which
// is controlled by default display
final DisplayContent dc = imeTarget != null
? imeTarget.getDisplayContent() : getDefaultDisplayContentLocked();
dc.getInsetsStateController().getImeSourceProvider()
- .scheduleShowImePostLayout(imeTarget);
+ .scheduleShowImePostLayout(controlTarget);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
index c9e628480a52..70e6a340816a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/WindowMagnificationManagerTest.java
@@ -32,7 +32,10 @@ import static org.testng.Assert.assertTrue;
import static java.lang.Float.NaN;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.IBinder;
@@ -300,6 +303,26 @@ public class WindowMagnificationManagerTest {
assertFalse(mWindowMagnificationManager.isConnected());
}
+ @Test
+ public void requestConnection_registerAndUnregisterBroadcastReceiver() {
+ assertTrue(mWindowMagnificationManager.requestConnection(true));
+ verify(mContext).registerReceiver(any(BroadcastReceiver.class), any(IntentFilter.class));
+
+ assertTrue(mWindowMagnificationManager.requestConnection(false));
+ verify(mContext).unregisterReceiver(any(BroadcastReceiver.class));
+ }
+
+ @Test
+ public void onReceiveScreenOff_removeMagnificationButtonAndDisableWindowMagnification()
+ throws RemoteException {
+ mWindowMagnificationManager.requestConnection(true);
+ mWindowMagnificationManager.mScreenStateReceiver.onReceive(mContext,
+ new Intent(Intent.ACTION_SCREEN_OFF));
+
+ verify(mMockConnection.getConnection()).removeMagnificationButton(TEST_DISPLAY);
+ verify(mMockConnection.getConnection()).disableWindowMagnification(TEST_DISPLAY);
+ }
+
private MotionEvent generatePointersDownEvent(PointF[] pointersLocation) {
final int len = pointersLocation.length;
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricServiceBaseTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricServiceBaseTest.java
deleted file mode 100644
index 0c66ee75eab0..000000000000
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricServiceBaseTest.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2020 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;
-
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.hardware.biometrics.BiometricAuthenticator;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.List;
-
-@Presubmit
-@SmallTest
-public class BiometricServiceBaseTest {
- private static class TestableBiometricServiceBase extends BiometricServiceBase {
- TestableBiometricServiceBase(Context context) {
- super(context);
- }
-
- @Override
- protected void doTemplateCleanupForUser(int userId) {
- }
-
- @Override
- protected String getTag() {
- return null;
- }
-
- @Override
- protected Object getDaemon() {
- return null;
- }
-
- @Override
- protected BiometricUtils getBiometricUtils() {
- return null;
- }
-
- @Override
- protected boolean hasReachedEnrollmentLimit(int userId) {
- return false;
- }
-
- @Override
- protected void updateActiveGroup(int userId) {
- }
-
- @Override
- protected boolean hasEnrolledBiometrics(int userId) {
- return false;
- }
-
- @Override
- protected String getManageBiometricPermission() {
- return null;
- }
-
- @Override
- protected List<? extends BiometricAuthenticator.Identifier> getEnrolledTemplates(
- int userId) {
- return null;
- }
-
- @Override
- protected int statsModality() {
- return 0;
- }
- }
-
- private static final int CLIENT_COOKIE = 0xc00c1e;
-
- private BiometricServiceBase mBiometricServiceBase;
-
- @Mock
- private Context mContext;
- @Mock
- private Resources mResources;
- @Mock
- private BiometricAuthenticator.Identifier mIdentifier;
- @Mock
- private ClientMonitor mClient;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- when(mContext.getResources()).thenReturn(mResources);
- when(mResources.getString(anyInt())).thenReturn("");
- when(mClient.getCookie()).thenReturn(CLIENT_COOKIE);
-
- mBiometricServiceBase = new TestableBiometricServiceBase(mContext);
- }
-
- @Test
- public void testHandleEnumerate_doesNotCrash_withNullClient() {
- mBiometricServiceBase.handleEnumerate(mIdentifier, 0 /* remaining */);
- }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index c7b45efb2de1..76b1a4d69f05 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -17,6 +17,8 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
@@ -65,9 +67,13 @@ import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import android.app.ActivityManager.TaskSnapshot;
@@ -405,7 +411,7 @@ public class ActivityRecordTests extends ActivityTestsBase {
@Test
public void ignoreRequestedOrientationInFreeformWindows() {
- mStack.setWindowingMode(WindowConfiguration.WINDOWING_MODE_FREEFORM);
+ mStack.setWindowingMode(WINDOWING_MODE_FREEFORM);
final Rect stableRect = new Rect();
mStack.getDisplay().mDisplayContent.getStableRect(stableRect);
@@ -1657,6 +1663,26 @@ public class ActivityRecordTests extends ActivityTestsBase {
.diff(wpc.getRequestedOverrideConfiguration()));
}
+ @Test
+ public void testCanTurnScreenOn() {
+ mStack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ doReturn(true).when(mStack).checkKeyguardVisibility(
+ same(mActivity), eq(true) /* shouldBeVisible */, anyBoolean());
+ doReturn(true).when(mActivity).getTurnScreenOnFlag();
+
+ assertTrue(mActivity.canTurnScreenOn());
+ }
+
+ @Test
+ public void testFreeformWindowCantTurnScreenOn() {
+ mStack.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ doReturn(true).when(mStack).checkKeyguardVisibility(
+ same(mActivity), eq(true) /* shouldBeVisible */, anyBoolean());
+ doReturn(true).when(mActivity).getTurnScreenOnFlag();
+
+ assertFalse(mActivity.canTurnScreenOn());
+ }
+
/**
* Creates an activity on display. For non-default display request it will also create a new
* display with custom DisplayInfo.
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index 4f5b3ec8bdac..4aac47cf006a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -891,6 +891,24 @@ public class RootActivityContainerTests extends ActivityTestsBase {
assertEquals(taskDisplayArea.getTopStack(), taskDisplayArea.getRootHomeTask());
}
+ @Test
+ public void testResumeFocusedStackOnSleepingDisplay() {
+ // Create an activity on secondary display.
+ final TestDisplayContent secondDisplay = addNewDisplayContentAt(
+ DisplayContent.POSITION_TOP);
+ final ActivityStack stack = secondDisplay.getDefaultTaskDisplayArea()
+ .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ final ActivityRecord activity = new ActivityBuilder(mService).setStack(stack).build();
+ spyOn(activity);
+ spyOn(stack);
+
+ // Cannot resumed activities on secondary display if the display should sleep.
+ doReturn(true).when(secondDisplay).shouldSleep();
+ mRootWindowContainer.resumeFocusedStacksTopActivities();
+ verify(stack, never()).resumeTopActivityUncheckedLocked(any(), any());
+ verify(activity, never()).makeActiveIfNeeded(any());
+ }
+
/**
* Mock {@link RootWindowContainer#resolveHomeActivity} for returning consistent activity
* info for test cases.
diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index bc987a6282c7..71a1964210b0 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -623,6 +623,10 @@ public final class TelephonyPermissions {
}
private static int getCarrierPrivilegeStatus(Context context, int subId, int uid) {
+ if (uid == Process.SYSTEM_UID || uid == Process.PHONE_UID) {
+ // Skip the check if it's one of these special uids
+ return TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
+ }
final long identity = Binder.clearCallingIdentity();
try {
TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index b376660f839e..183fdcce1ca4 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -670,10 +670,12 @@ public final class SmsManager {
}
if (priority < 0x00 || priority > 0x03) {
+ Log.e(TAG, "Invalid Priority " + priority);
priority = SMS_MESSAGE_PRIORITY_NOT_SPECIFIED;
}
if (validityPeriod < 0x05 || validityPeriod > 0x09b0a0) {
+ Log.e(TAG, "Invalid Validity Period " + validityPeriod);
validityPeriod = SMS_MESSAGE_PERIOD_NOT_SPECIFIED;
}
@@ -1231,10 +1233,12 @@ public final class SmsManager {
}
if (priority < 0x00 || priority > 0x03) {
+ Log.e(TAG, "Invalid Priority " + priority);
priority = SMS_MESSAGE_PRIORITY_NOT_SPECIFIED;
}
if (validityPeriod < 0x05 || validityPeriod > 0x09b0a0) {
+ Log.e(TAG, "Invalid Validity Period " + validityPeriod);
validityPeriod = SMS_MESSAGE_PERIOD_NOT_SPECIFIED;
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index d96e024d4c60..f9148d0c44c4 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -113,8 +113,6 @@ import com.android.internal.telephony.RILConstants;
import com.android.internal.telephony.SmsApplication;
import com.android.telephony.Rlog;
-import java.io.FileInputStream;
-import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -127,8 +125,6 @@ import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
/**
* Provides access to information about the telephony services on
@@ -2540,7 +2536,8 @@ public class TelephonyManager {
return PhoneConstants.PHONE_TYPE_CDMA;
case RILConstants.NETWORK_MODE_LTE_ONLY:
- if (getLteOnCdmaModeStatic() == PhoneConstants.LTE_ON_CDMA_TRUE) {
+ if (TelephonyProperties.lte_on_cdma_device().orElse(
+ PhoneConstants.LTE_ON_CDMA_FALSE) == PhoneConstants.LTE_ON_CDMA_TRUE) {
return PhoneConstants.PHONE_TYPE_CDMA;
} else {
return PhoneConstants.PHONE_TYPE_GSM;
@@ -2551,35 +2548,6 @@ public class TelephonyManager {
}
/**
- * The contents of the /proc/cmdline file
- */
- @UnsupportedAppUsage
- private static String getProcCmdLine()
- {
- String cmdline = "";
- FileInputStream is = null;
- try {
- is = new FileInputStream("/proc/cmdline");
- byte [] buffer = new byte[2048];
- int count = is.read(buffer);
- if (count > 0) {
- cmdline = new String(buffer, 0, count);
- }
- } catch (IOException e) {
- Rlog.d(TAG, "No /proc/cmdline exception=" + e);
- } finally {
- if (is != null) {
- try {
- is.close();
- } catch (IOException e) {
- }
- }
- }
- Rlog.d(TAG, "/proc/cmdline=" + cmdline);
- return cmdline;
- }
-
- /**
* @return The max value for the timeout passed in {@link #requestNumberVerification}.
* @hide
*/
@@ -2588,56 +2556,6 @@ public class TelephonyManager {
return MAX_NUMBER_VERIFICATION_TIMEOUT_MILLIS;
}
- /** Kernel command line */
- private static final String sKernelCmdLine = getProcCmdLine();
-
- /** Pattern for selecting the product type from the kernel command line */
- private static final Pattern sProductTypePattern =
- Pattern.compile("\\sproduct_type\\s*=\\s*(\\w+)");
-
- /** The ProductType used for LTE on CDMA devices */
- private static final String sLteOnCdmaProductType =
- TelephonyProperties.lte_on_cdma_product_type().orElse("");
-
- /**
- * Return if the current radio is LTE on CDMA. This
- * is a tri-state return value as for a period of time
- * the mode may be unknown.
- *
- * @return {@link PhoneConstants#LTE_ON_CDMA_UNKNOWN}, {@link PhoneConstants#LTE_ON_CDMA_FALSE}
- * or {@link PhoneConstants#LTE_ON_CDMA_TRUE}
- *
- * @hide
- */
- @UnsupportedAppUsage
- public static int getLteOnCdmaModeStatic() {
- int retVal;
- int curVal;
- String productType = "";
-
- curVal = TelephonyProperties.lte_on_cdma_device().orElse(
- PhoneConstants.LTE_ON_CDMA_UNKNOWN);
- retVal = curVal;
- if (retVal == PhoneConstants.LTE_ON_CDMA_UNKNOWN) {
- Matcher matcher = sProductTypePattern.matcher(sKernelCmdLine);
- if (matcher.find()) {
- productType = matcher.group(1);
- if (sLteOnCdmaProductType.equals(productType)) {
- retVal = PhoneConstants.LTE_ON_CDMA_TRUE;
- } else {
- retVal = PhoneConstants.LTE_ON_CDMA_FALSE;
- }
- } else {
- retVal = PhoneConstants.LTE_ON_CDMA_FALSE;
- }
- }
-
- Rlog.d(TAG, "getLteOnCdmaMode=" + retVal + " curVal=" + curVal +
- " product_type='" + productType +
- "' lteOnCdmaProductType='" + sLteOnCdmaProductType + "'");
- return retVal;
- }
-
//
//
// Current Network
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
index c7ad2bbb043f..d186fcf63cfe 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -1100,7 +1100,7 @@ public final class BearerData {
bData.hasUserDataHeader = (inStream.read(1) == 1);
inStream.skip(3);
}
- if ((! decodeSuccess) || (paramBits > 0)) {
+ if ((!decodeSuccess) || (paramBits > 0)) {
Rlog.d(LOG_TAG, "MESSAGE_IDENTIFIER decode " +
(decodeSuccess ? "succeeded" : "failed") +
" (extra bits = " + paramBits + ")");
@@ -1469,7 +1469,7 @@ public final class BearerData {
bData.reportReq = (inStream.read(1) == 1);
inStream.skip(4);
}
- if ((! decodeSuccess) || (paramBits > 0)) {
+ if ((!decodeSuccess) || (paramBits > 0)) {
Rlog.d(LOG_TAG, "REPLY_OPTION decode " +
(decodeSuccess ? "succeeded" : "failed") +
" (extra bits = " + paramBits + ")");
@@ -1488,7 +1488,7 @@ public final class BearerData {
decodeSuccess = true;
bData.numberOfMessages = IccUtils.cdmaBcdByteToInt((byte)inStream.read(8));
}
- if ((! decodeSuccess) || (paramBits > 0)) {
+ if ((!decodeSuccess) || (paramBits > 0)) {
Rlog.d(LOG_TAG, "NUMBER_OF_MESSAGES decode " +
(decodeSuccess ? "succeeded" : "failed") +
" (extra bits = " + paramBits + ")");
@@ -1507,7 +1507,7 @@ public final class BearerData {
decodeSuccess = true;
bData.depositIndex = (inStream.read(8) << 8) | inStream.read(8);
}
- if ((! decodeSuccess) || (paramBits > 0)) {
+ if ((!decodeSuccess) || (paramBits > 0)) {
Rlog.d(LOG_TAG, "MESSAGE_DEPOSIT_INDEX decode " +
(decodeSuccess ? "succeeded" : "failed") +
" (extra bits = " + paramBits + ")");
@@ -1594,7 +1594,7 @@ public final class BearerData {
bData.errorClass = inStream.read(2);
bData.messageStatus = inStream.read(6);
}
- if ((! decodeSuccess) || (paramBits > 0)) {
+ if ((!decodeSuccess) || (paramBits > 0)) {
Rlog.d(LOG_TAG, "MESSAGE_STATUS decode " +
(decodeSuccess ? "succeeded" : "failed") +
" (extra bits = " + paramBits + ")");
@@ -1614,7 +1614,7 @@ public final class BearerData {
decodeSuccess = true;
bData.msgCenterTimeStamp = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8));
}
- if ((! decodeSuccess) || (paramBits > 0)) {
+ if ((!decodeSuccess) || (paramBits > 0)) {
Rlog.d(LOG_TAG, "MESSAGE_CENTER_TIME_STAMP decode " +
(decodeSuccess ? "succeeded" : "failed") +
" (extra bits = " + paramBits + ")");
@@ -1633,7 +1633,7 @@ public final class BearerData {
decodeSuccess = true;
bData.validityPeriodAbsolute = TimeStamp.fromByteArray(inStream.readByteArray(6 * 8));
}
- if ((! decodeSuccess) || (paramBits > 0)) {
+ if ((!decodeSuccess) || (paramBits > 0)) {
Rlog.d(LOG_TAG, "VALIDITY_PERIOD_ABSOLUTE decode " +
(decodeSuccess ? "succeeded" : "failed") +
" (extra bits = " + paramBits + ")");
@@ -1653,7 +1653,7 @@ public final class BearerData {
bData.deferredDeliveryTimeAbsolute = TimeStamp.fromByteArray(
inStream.readByteArray(6 * 8));
}
- if ((! decodeSuccess) || (paramBits > 0)) {
+ if ((!decodeSuccess) || (paramBits > 0)) {
Rlog.d(LOG_TAG, "DEFERRED_DELIVERY_TIME_ABSOLUTE decode " +
(decodeSuccess ? "succeeded" : "failed") +
" (extra bits = " + paramBits + ")");
@@ -1672,7 +1672,7 @@ public final class BearerData {
decodeSuccess = true;
bData.deferredDeliveryTimeRelative = inStream.read(8);
}
- if ((! decodeSuccess) || (paramBits > 0)) {
+ if ((!decodeSuccess) || (paramBits > 0)) {
Rlog.d(LOG_TAG, "VALIDITY_PERIOD_RELATIVE decode " +
(decodeSuccess ? "succeeded" : "failed") +
" (extra bits = " + paramBits + ")");
@@ -1692,7 +1692,7 @@ public final class BearerData {
decodeSuccess = true;
bData.validityPeriodRelative = inStream.read(8);
}
- if ((! decodeSuccess) || (paramBits > 0)) {
+ if ((!decodeSuccess) || (paramBits > 0)) {
Rlog.d(LOG_TAG, "DEFERRED_DELIVERY_TIME_RELATIVE decode " +
(decodeSuccess ? "succeeded" : "failed") +
" (extra bits = " + paramBits + ")");
@@ -1713,7 +1713,7 @@ public final class BearerData {
bData.privacy = inStream.read(2);
inStream.skip(6);
}
- if ((! decodeSuccess) || (paramBits > 0)) {
+ if ((!decodeSuccess) || (paramBits > 0)) {
Rlog.d(LOG_TAG, "PRIVACY_INDICATOR decode " +
(decodeSuccess ? "succeeded" : "failed") +
" (extra bits = " + paramBits + ")");
@@ -1733,7 +1733,7 @@ public final class BearerData {
decodeSuccess = true;
bData.language = inStream.read(8);
}
- if ((! decodeSuccess) || (paramBits > 0)) {
+ if ((!decodeSuccess) || (paramBits > 0)) {
Rlog.d(LOG_TAG, "LANGUAGE_INDICATOR decode " +
(decodeSuccess ? "succeeded" : "failed") +
" (extra bits = " + paramBits + ")");
@@ -1754,7 +1754,7 @@ public final class BearerData {
bData.displayMode = inStream.read(2);
inStream.skip(6);
}
- if ((! decodeSuccess) || (paramBits > 0)) {
+ if ((!decodeSuccess) || (paramBits > 0)) {
Rlog.d(LOG_TAG, "DISPLAY_MODE decode " +
(decodeSuccess ? "succeeded" : "failed") +
" (extra bits = " + paramBits + ")");
@@ -1775,7 +1775,7 @@ public final class BearerData {
bData.priority = inStream.read(2);
inStream.skip(6);
}
- if ((! decodeSuccess) || (paramBits > 0)) {
+ if ((!decodeSuccess) || (paramBits > 0)) {
Rlog.d(LOG_TAG, "PRIORITY_INDICATOR decode " +
(decodeSuccess ? "succeeded" : "failed") +
" (extra bits = " + paramBits + ")");
@@ -1796,7 +1796,7 @@ public final class BearerData {
bData.alert = inStream.read(2);
inStream.skip(6);
}
- if ((! decodeSuccess) || (paramBits > 0)) {
+ if ((!decodeSuccess) || (paramBits > 0)) {
Rlog.d(LOG_TAG, "ALERT_ON_MESSAGE_DELIVERY decode " +
(decodeSuccess ? "succeeded" : "failed") +
" (extra bits = " + paramBits + ")");
@@ -1816,7 +1816,7 @@ public final class BearerData {
decodeSuccess = true;
bData.userResponseCode = inStream.read(8);
}
- if ((! decodeSuccess) || (paramBits > 0)) {
+ if ((!decodeSuccess) || (paramBits > 0)) {
Rlog.d(LOG_TAG, "USER_RESPONSE_CODE decode " +
(decodeSuccess ? "succeeded" : "failed") +
" (extra bits = " + paramBits + ")");
@@ -1878,7 +1878,7 @@ public final class BearerData {
decodeSuccess = true;
}
- if ((! decodeSuccess) || (paramBits > 0)) {
+ if ((!decodeSuccess) || (paramBits > 0)) {
Rlog.d(LOG_TAG, "SERVICE_CATEGORY_PROGRAM_DATA decode " +
(decodeSuccess ? "succeeded" : "failed") +
" (extra bits = " + paramBits + ')');
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index f297ba74914e..7e31c4633050 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -92,14 +92,15 @@ public class SmsMessage extends SmsMessageBase {
private int mVoiceMailCount = 0;
+ /** TP-Validity-Period-Format (TP-VPF). See TS 23.040, 9.2.3.3 */
private static final int VALIDITY_PERIOD_FORMAT_NONE = 0x00;
private static final int VALIDITY_PERIOD_FORMAT_ENHANCED = 0x01;
private static final int VALIDITY_PERIOD_FORMAT_RELATIVE = 0x02;
private static final int VALIDITY_PERIOD_FORMAT_ABSOLUTE = 0x03;
- //Validity Period min - 5 mins
+ // Validity Period min - 5 mins
private static final int VALIDITY_PERIOD_MIN = 5;
- //Validity Period max - 63 weeks
+ // Validity Period max - 63 weeks
private static final int VALIDITY_PERIOD_MAX = 635040;
private static final int INVALID_VALIDITY_PERIOD = -1;
@@ -193,20 +194,20 @@ public class SmsMessage extends SmsMessageBase {
}
/**
- * Get Encoded Relative Validty Period Value from Validity period in mins.
+ * Gets Encoded Relative Validity Period Value from Validity period in mins.
*
* @param validityPeriod Validity period in mins.
*
* Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
- * ||relValidityPeriod (TP-VP) || || validityPeriod ||
- *
- * 0 to 143 ---> (TP-VP + 1) x 5 minutes
- *
- * 144 to 167 ---> 12 hours + ((TP-VP -143) x 30 minutes)
- *
- * 168 to 196 ---> (TP-VP - 166) x 1 day
- *
- * 197 to 255 ---> (TP-VP - 192) x 1 week
+ * ------------------------------------------------------------
+ * TP-VP | Validity period
+ * (Relative format) | value
+ * ------------------------------------------------------------
+ * 0 to 143 | (TP-VP + 1) x 5 minutes
+ * 144 to 167 | 12 hours + ((TP-VP -143) x 30 minutes)
+ * 168 to 196 | (TP-VP - 166) x 1 day
+ * 197 to 255 | (TP-VP - 192) x 1 week
+ * ------------------------------------------------------------
*
* @return relValidityPeriod Encoded Relative Validity Period Value.
* @hide
@@ -214,19 +215,16 @@ public class SmsMessage extends SmsMessageBase {
public static int getRelativeValidityPeriod(int validityPeriod) {
int relValidityPeriod = INVALID_VALIDITY_PERIOD;
- if (validityPeriod < VALIDITY_PERIOD_MIN || validityPeriod > VALIDITY_PERIOD_MAX) {
- Rlog.e(LOG_TAG,"Invalid Validity Period" + validityPeriod);
- return relValidityPeriod;
- }
-
- if (validityPeriod <= 720) {
- relValidityPeriod = (validityPeriod / 5) - 1;
- } else if (validityPeriod <= 1440) {
- relValidityPeriod = ((validityPeriod - 720) / 30) + 143;
- } else if (validityPeriod <= 43200) {
- relValidityPeriod = (validityPeriod / 1440) + 166;
- } else if (validityPeriod <= 635040) {
- relValidityPeriod = (validityPeriod / 10080) + 192;
+ if (validityPeriod >= VALIDITY_PERIOD_MIN) {
+ if (validityPeriod <= 720) {
+ relValidityPeriod = (validityPeriod / 5) - 1;
+ } else if (validityPeriod <= 1440) {
+ relValidityPeriod = ((validityPeriod - 720) / 30) + 143;
+ } else if (validityPeriod <= 43200) {
+ relValidityPeriod = (validityPeriod / 1440) + 166;
+ } else if (validityPeriod <= VALIDITY_PERIOD_MAX) {
+ relValidityPeriod = (validityPeriod / 10080) + 192;
+ }
}
return relValidityPeriod;
}
@@ -336,17 +334,19 @@ public class SmsMessage extends SmsMessageBase {
SubmitPdu ret = new SubmitPdu();
- int validityPeriodFormat = VALIDITY_PERIOD_FORMAT_NONE;
- int relativeValidityPeriod = INVALID_VALIDITY_PERIOD;
+ int relativeValidityPeriod = getRelativeValidityPeriod(validityPeriod);
+
+ byte mtiByte = 0x01; // SMS-SUBMIT
- // TP-Validity-Period-Format (TP-VPF) in 3GPP TS 23.040 V6.8.1 section 9.2.3.3
- //bit 4:3 = 10 - TP-VP field present - relative format
- if((relativeValidityPeriod = getRelativeValidityPeriod(validityPeriod)) >= 0) {
- validityPeriodFormat = VALIDITY_PERIOD_FORMAT_RELATIVE;
+ if (header != null) {
+ // Set TP-UDHI
+ mtiByte |= 0x40;
}
- byte mtiByte = (byte)(0x01 | (validityPeriodFormat << 0x03) |
- (header != null ? 0x40 : 0x00));
+ if (relativeValidityPeriod != INVALID_VALIDITY_PERIOD) {
+ // Set TP-Validity-Period-Format (TP-VPF)
+ mtiByte |= VALIDITY_PERIOD_FORMAT_RELATIVE << 3;
+ }
ByteArrayOutputStream bo = getSubmitPduHead(
scAddress, destinationAddress, mtiByte,
@@ -418,8 +418,8 @@ public class SmsMessage extends SmsMessageBase {
bo.write(0x08);
}
- if (validityPeriodFormat == VALIDITY_PERIOD_FORMAT_RELATIVE) {
- // ( TP-Validity-Period - relative format)
+ // TP-Validity-Period (TP-VP)
+ if (relativeValidityPeriod != INVALID_VALIDITY_PERIOD) {
bo.write(relativeValidityPeriod);
}
@@ -1352,23 +1352,17 @@ public class SmsMessage extends SmsMessageBase {
// TP-Validity-Period-Format
int validityPeriodLength = 0;
- int validityPeriodFormat = ((firstByte>>3) & 0x3);
- if (0x0 == validityPeriodFormat) /* 00, TP-VP field not present*/
- {
+ int validityPeriodFormat = ((firstByte >> 3) & 0x3);
+ if (validityPeriodFormat == VALIDITY_PERIOD_FORMAT_NONE) {
validityPeriodLength = 0;
- }
- else if (0x2 == validityPeriodFormat) /* 10, TP-VP: relative format*/
- {
+ } else if (validityPeriodFormat == VALIDITY_PERIOD_FORMAT_RELATIVE) {
validityPeriodLength = 1;
- }
- else /* other case, 11 or 01, TP-VP: absolute or enhanced format*/
- {
+ } else { // VALIDITY_PERIOD_FORMAT_ENHANCED or VALIDITY_PERIOD_FORMAT_ABSOLUTE
validityPeriodLength = 7;
}
// TP-Validity-Period is not used on phone, so just ignore it for now.
- while (validityPeriodLength-- > 0)
- {
+ while (validityPeriodLength-- > 0) {
p.getByte();
}
diff --git a/tools/validatekeymaps/Android.bp b/tools/validatekeymaps/Android.bp
index 819e75ba1fed..61ce44c3b5b9 100644
--- a/tools/validatekeymaps/Android.bp
+++ b/tools/validatekeymaps/Android.bp
@@ -20,6 +20,7 @@ cc_binary_host {
"libutils",
"libcutils",
"liblog",
+ "libui-types",
],
// This tool is prebuilt if we're doing an app-only build.
diff --git a/wifi/TEST_MAPPING b/wifi/TEST_MAPPING
new file mode 100644
index 000000000000..fde3a6aa993c
--- /dev/null
+++ b/wifi/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsWifiTestCases",
+ "options": [
+ {
+ "exclude-annotation": "android.net.wifi.cts.VirtualDeviceNotSupported"
+ }
+ ]
+ }
+ ]
+}