diff options
184 files changed, 5006 insertions, 1564 deletions
diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java index 398ccb69fbe8..2ce85ee57205 100644 --- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java +++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java @@ -3,7 +3,6 @@ package com.android.server.usage; import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.usage.AppStandbyInfo; -import android.app.usage.UsageEvents; import android.app.usage.UsageStatsManager.StandbyBuckets; import android.app.usage.UsageStatsManager.SystemForcedReasons; import android.content.Context; @@ -68,8 +67,6 @@ public interface AppStandbyInternal { */ void postOneTimeCheckIdleStates(); - void reportEvent(UsageEvents.Event event, int userId); - void setLastJobRunTime(String packageName, int userId, long elapsedRealtime); long getTimeSinceLastJobRun(String packageName, int userId); diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index 1157ee905b86..0b0923a67de6 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -62,6 +62,7 @@ import android.app.usage.AppStandbyInfo; import android.app.usage.UsageEvents; import android.app.usage.UsageStatsManager.StandbyBuckets; import android.app.usage.UsageStatsManager.SystemForcedReasons; +import android.app.usage.UsageStatsManagerInternal; import android.appwidget.AppWidgetManager; import android.content.BroadcastReceiver; import android.content.ContentResolver; @@ -128,9 +129,10 @@ import java.util.concurrent.CountDownLatch; * Manages the standby state of an app, listening to various events. * * Unit test: - atest com.android.server.usage.AppStandbyControllerTests + * atest com.android.server.usage.AppStandbyControllerTests */ -public class AppStandbyController implements AppStandbyInternal { +public class AppStandbyController + implements AppStandbyInternal, UsageStatsManagerInternal.UsageEventListener { private static final String TAG = "AppStandbyController"; // Do not submit with true. @@ -468,10 +470,21 @@ public class AppStandbyController implements AppStandbyInternal { @VisibleForTesting void setAppIdleEnabled(boolean enabled) { + // Don't call out to USM with the lock held. Also, register the listener before we + // change our internal state so no events fall through the cracks. + final UsageStatsManagerInternal usmi = + LocalServices.getService(UsageStatsManagerInternal.class); + if (enabled) { + usmi.registerListener(this); + } else { + usmi.unregisterListener(this); + } + synchronized (mAppIdleLock) { if (mAppIdleEnabled != enabled) { final boolean oldParoleState = isInParole(); mAppIdleEnabled = enabled; + if (isInParole() != oldParoleState) { postParoleStateChanged(); } @@ -489,6 +502,11 @@ public class AppStandbyController implements AppStandbyInternal { mInjector.onBootPhase(phase); if (phase == PHASE_SYSTEM_SERVICES_READY) { Slog.d(TAG, "Setting app idle enabled state"); + + if (mAppIdleEnabled) { + LocalServices.getService(UsageStatsManagerInternal.class).registerListener(this); + } + // Observe changes to the threshold ConstantsObserver settingsObserver = new ConstantsObserver(mHandler); settingsObserver.start(); @@ -912,8 +930,10 @@ public class AppStandbyController implements AppStandbyInternal { } } - @Override - public void reportEvent(UsageEvents.Event event, int userId) { + /** + * Callback to inform listeners of a new event. + */ + public void onUsageEvent(int userId, @NonNull UsageEvents.Event event) { if (!mAppIdleEnabled) return; final int eventType = event.getEventType(); if ((eventType == UsageEvents.Event.ACTIVITY_RESUMED diff --git a/cmds/statsd/src/OWNERS b/cmds/statsd/src/OWNERS new file mode 100644 index 000000000000..0f3ddf7388d9 --- /dev/null +++ b/cmds/statsd/src/OWNERS @@ -0,0 +1,6 @@ +# Temporary OWNERS Block to assist with migration +# bug: 167962588 +per-file *atoms.proto = set noparent +per-file *atom_field_options.proto = set noparent +per-file *atoms.proto = baligh@google.com, yro@google.com, singhtejinder@google.com, jeffreyhuang@google.com +per-file *atom_field_options.proto = baligh@google.com, yro@google.com, singhtejinder@google.com, jeffreyhuang@google.com diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 0b044bee9e64..7d82f43af975 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -228,6 +228,7 @@ package android { field public static final String SHOW_KEYGUARD_MESSAGE = "android.permission.SHOW_KEYGUARD_MESSAGE"; field public static final String SHUTDOWN = "android.permission.SHUTDOWN"; field public static final String START_ACTIVITIES_FROM_BACKGROUND = "android.permission.START_ACTIVITIES_FROM_BACKGROUND"; + field public static final String START_FOREGROUND_SERVICES_FROM_BACKGROUND = "android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND"; field public static final String STATUS_BAR_SERVICE = "android.permission.STATUS_BAR_SERVICE"; field public static final String STOP_APP_SWITCHES = "android.permission.STOP_APP_SWITCHES"; field public static final String SUBSTITUTE_NOTIFICATION_APP_NAME = "android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"; @@ -603,7 +604,7 @@ package android.app { method public static android.app.BroadcastOptions makeBasic(); method @RequiresPermission(android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND) public void setBackgroundActivityStartsAllowed(boolean); method public void setDontSendToRestrictedApps(boolean); - method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void setTemporaryAppWhitelistDuration(long); + method @RequiresPermission(anyOf={android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) public void setTemporaryAppWhitelistDuration(long); method public android.os.Bundle toBundle(); } @@ -1764,6 +1765,7 @@ package android.content { field public static final String BACKUP_SERVICE = "backup"; field public static final String BATTERY_STATS_SERVICE = "batterystats"; field public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 1048576; // 0x100000 + field public static final int BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND = 262144; // 0x40000 field public static final String BUGREPORT_SERVICE = "bugreport"; field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions"; field public static final String CONTEXTHUB_SERVICE = "contexthub"; @@ -2280,6 +2282,7 @@ package android.content.pm { field public static final int PROTECTION_FLAG_DOCUMENTER = 262144; // 0x40000 field public static final int PROTECTION_FLAG_INCIDENT_REPORT_APPROVER = 1048576; // 0x100000 field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000 + field public static final int PROTECTION_FLAG_RECENTS = 33554432; // 0x2000000 field public static final int PROTECTION_FLAG_RETAIL_DEMO = 16777216; // 0x1000000 field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000 field public static final int PROTECTION_FLAG_WELLBEING = 131072; // 0x20000 diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java index 03dca30f8560..298c45582174 100644 --- a/core/java/android/app/BroadcastOptions.java +++ b/core/java/android/app/BroadcastOptions.java @@ -90,7 +90,9 @@ public class BroadcastOptions { * power allowlist when this broadcast is being delivered to it. * @param duration The duration in milliseconds; 0 means to not place on allowlist. */ - @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) + @RequiresPermission(anyOf = {android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, + android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND, + android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND}) public void setTemporaryAppWhitelistDuration(long duration) { mTemporaryAppWhitelistDuration = duration; } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 2bf5368b691b..a886beddf64c 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -4901,7 +4901,7 @@ public class Notification implements Parcelable bindNotificationHeader(contentView, p); bindLargeIconAndApplyMargin(contentView, p, result); boolean showProgress = handleProgressBar(contentView, ex, p); - if (p.title != null && p.title.length() > 0 && !p.mHasCustomContent) { + if (p.hasTitle()) { contentView.setViewVisibility(R.id.title, View.VISIBLE); contentView.setTextViewText(R.id.title, processTextSpans(p.title)); setTextViewColorPrimary(contentView, R.id.title, p); @@ -4909,7 +4909,7 @@ public class Notification implements Parcelable ? ViewGroup.LayoutParams.WRAP_CONTENT : ViewGroup.LayoutParams.MATCH_PARENT); } - if (p.text != null) { + if (p.text != null && p.text.length() != 0) { int textId = showProgress ? com.android.internal.R.id.text_line_1 : com.android.internal.R.id.text; contentView.setTextViewText(textId, processTextSpans(p.text)); @@ -5296,7 +5296,7 @@ public class Notification implements Parcelable contentView.setViewVisibility(R.id.app_name_text, View.GONE); return false; } - if (p.mHeaderless && !p.mHasCustomContent) { + if (p.mHeaderless && p.hasTitle()) { contentView.setViewVisibility(R.id.app_name_text, View.GONE); // the headerless template will have the TITLE in this position; return true to // keep the divider visible between that title and the next text element. @@ -11073,6 +11073,10 @@ public class Notification implements Parcelable return this; } + final boolean hasTitle() { + return title != null && title.length() != 0 && !mHasCustomContent; + } + final StandardTemplateParams viewType(int viewType) { mViewType = viewType; return this; diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java index b96b54ad8d21..3798de921dc7 100644 --- a/core/java/android/app/Service.java +++ b/core/java/android/app/Service.java @@ -697,6 +697,10 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac * service element of manifest file. The value of attribute * {@link android.R.attr#foregroundServiceType} can be multiple flags ORed together.</p> * + * @throws IllegalStateException If the app targeting API is + * {@link android.os.Build.VERSION_CODES#S} or later, and the service is restricted from + * becoming foreground service due to background restriction. + * * @param id The identifier for this notification as per * {@link NotificationManager#notify(int, Notification) * NotificationManager.notify(int, Notification)}; must not be 0. diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 16ae081049ef..56a95921091d 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -8505,6 +8505,8 @@ public class DevicePolicyManager { * it previously set with {@link #addUserRestriction(ComponentName, String)}. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @return a {@link Bundle} whose keys are the user restrictions, and the values a + * {@code boolean} indicating whether the restriction is set. * @throws SecurityException if {@code admin} is not a device or profile owner. */ public @NonNull Bundle getUserRestrictions(@NonNull ComponentName admin) { diff --git a/core/java/android/bluetooth/BluetoothCodecStatus.java b/core/java/android/bluetooth/BluetoothCodecStatus.java index 3a65aaa0d16c..7764ebeb2e33 100644 --- a/core/java/android/bluetooth/BluetoothCodecStatus.java +++ b/core/java/android/bluetooth/BluetoothCodecStatus.java @@ -18,7 +18,6 @@ package android.bluetooth; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; -import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -40,7 +39,7 @@ public final class BluetoothCodecStatus implements Parcelable { * This extra represents the current codec status of the A2DP * profile. */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public static final String EXTRA_CODEC_STATUS = "android.bluetooth.extra.CODEC_STATUS"; @@ -199,7 +198,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return the current codec configuration */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public @Nullable BluetoothCodecConfig getCodecConfig() { return mCodecConfig; } @@ -209,7 +208,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return an array with the codecs local capabilities */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public @Nullable BluetoothCodecConfig[] getCodecsLocalCapabilities() { return mCodecsLocalCapabilities; } @@ -219,7 +218,7 @@ public final class BluetoothCodecStatus implements Parcelable { * * @return an array with the codecs selectable capabilities */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) + @UnsupportedAppUsage public @Nullable BluetoothCodecConfig[] getCodecsSelectableCapabilities() { return mCodecsSelectableCapabilities; } diff --git a/core/java/android/companion/OWNERS b/core/java/android/companion/OWNERS new file mode 100644 index 000000000000..da723b3b67da --- /dev/null +++ b/core/java/android/companion/OWNERS @@ -0,0 +1 @@ +eugenesusla@google.com
\ No newline at end of file diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 6a3f6b4034ff..8f92bf1e3253 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -367,6 +367,16 @@ public abstract class Context { /*********** Hidden flags below this line ***********/ /** + * Flag for {@link #bindService}: allow background foreground service starts from the bound + * service's process. + * This flag is only respected if the caller is holding + * {@link android.Manifest.permission#START_FOREGROUND_SERVICES_FROM_BACKGROUND}. + * @hide + */ + @SystemApi + public static final int BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND = 0x00040000; + + /** * Flag for {@link #bindService}: This flag is intended to be used only by the system to adjust * the scheduling policy for IMEs (and any other out-of-process user-visible components that * work closely with the top app) so that UI hosted in such services can have the same @@ -3107,6 +3117,10 @@ public abstract class Context { * @throws SecurityException If the caller does not have permission to access the service * or the service can not be found. * + * @throws IllegalStateException If the caller app's targeting API is + * {@link android.os.Build.VERSION_CODES#S} or later, and the foreground service is restricted + * from start due to background restriction. + * * @see #stopService * @see android.app.Service#startForeground(int, android.app.Notification) */ diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java index c6450ffdf91c..cd9ba6a5b451 100644 --- a/core/java/android/content/pm/PermissionInfo.java +++ b/core/java/android/content/pm/PermissionInfo.java @@ -251,6 +251,16 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { @SystemApi public static final int PROTECTION_FLAG_RETAIL_DEMO = 0x1000000; + /** + * Additional flag for {@link #protectionLevel}, corresponding + * to the <code>recents</code> value of + * {@link android.R.attr#protectionLevel}. + * + * @hide + */ + @SystemApi + public static final int PROTECTION_FLAG_RECENTS = 0x2000000; + /** @hide */ @IntDef(flag = true, prefix = { "PROTECTION_FLAG_" }, value = { PROTECTION_FLAG_PRIVILEGED, @@ -274,6 +284,7 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { PROTECTION_FLAG_APP_PREDICTOR, PROTECTION_FLAG_COMPANION, PROTECTION_FLAG_RETAIL_DEMO, + PROTECTION_FLAG_RECENTS, }) @Retention(RetentionPolicy.SOURCE) public @interface ProtectionFlags {} @@ -532,6 +543,9 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { if ((level & PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO) != 0) { protLevel.append("|retailDemo"); } + if ((level & PermissionInfo.PROTECTION_FLAG_RECENTS) != 0) { + protLevel.append("|recents"); + } return protLevel.toString(); } diff --git a/core/java/android/hardware/biometrics/SensorPropertiesInternal.java b/core/java/android/hardware/biometrics/SensorPropertiesInternal.java index 2189de0827b7..0b81c6c8cc25 100644 --- a/core/java/android/hardware/biometrics/SensorPropertiesInternal.java +++ b/core/java/android/hardware/biometrics/SensorPropertiesInternal.java @@ -16,13 +16,10 @@ package android.hardware.biometrics; -import android.annotation.IntDef; +import android.annotation.NonNull; import android.os.Parcel; import android.os.Parcelable; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - /** * The base class containing all modality-agnostic information. This is a superset of the * {@link android.hardware.biometrics.common.CommonProps}, and provides backwards-compatible @@ -35,6 +32,11 @@ public class SensorPropertiesInternal implements Parcelable { @SensorProperties.Strength public final int sensorStrength; public final int maxEnrollmentsPerUser; + public static SensorPropertiesInternal from(@NonNull SensorPropertiesInternal prop) { + return new SensorPropertiesInternal(prop.sensorId, prop.sensorStrength, + prop.maxEnrollmentsPerUser); + } + protected SensorPropertiesInternal(int sensorId, @SensorProperties.Strength int sensorStrength, int maxEnrollmentsPerUser) { this.sensorId = sensorId; @@ -72,4 +74,10 @@ public class SensorPropertiesInternal implements Parcelable { dest.writeInt(sensorStrength); dest.writeInt(maxEnrollmentsPerUser); } + + @Override + public String toString() { + return "ID: " + sensorId + ", Strength: " + sensorStrength + + ", MaxEnrollmentsPerUser: " + maxEnrollmentsPerUser; + } } diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index b046d1df5b8c..7b4889f0a1b3 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -358,7 +358,7 @@ public final class DisplayManagerGlobal { // listener. DisplayInfo display = getDisplayInfoLocked(displayId); if (display != null) { - float refreshRate = display.getMode().getRefreshRate(); + float refreshRate = display.getRefreshRate(); // Signal native callbacks if we ever set a refresh rate. nSignalNativeCallbacks(refreshRate); } @@ -862,7 +862,7 @@ public final class DisplayManagerGlobal { if (display != null) { // We need to tell AChoreographer instances the current refresh rate so that apps // can get it for free once a callback first registers. - float refreshRate = display.getMode().getRefreshRate(); + float refreshRate = display.getRefreshRate(); nSignalNativeCallbacks(refreshRate); } } diff --git a/core/java/android/hardware/face/FaceSensorPropertiesInternal.java b/core/java/android/hardware/face/FaceSensorPropertiesInternal.java index e91554b532b0..b9c0d12de22b 100644 --- a/core/java/android/hardware/face/FaceSensorPropertiesInternal.java +++ b/core/java/android/hardware/face/FaceSensorPropertiesInternal.java @@ -19,7 +19,6 @@ package android.hardware.face; import android.hardware.biometrics.SensorProperties; import android.hardware.biometrics.SensorPropertiesInternal; import android.os.Parcel; -import android.os.Parcelable; /** * Container for face sensor properties. @@ -78,4 +77,9 @@ public class FaceSensorPropertiesInternal extends SensorPropertiesInternal { dest.writeBoolean(supportsFaceDetection); dest.writeBoolean(supportsSelfIllumination); } + + @Override + public String toString() { + return "ID: " + sensorId + ", Strength: " + sensorStrength; + } } diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 00023a5caf75..9c2ae4e7c48a 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -2481,6 +2481,29 @@ public abstract class BatteryStats implements Parcelable { "group", "compl", "dorm", "uninit" }; + /** + * Returned value if energy data is unavailable + * + * {@hide} + */ + public static final long ENERGY_DATA_UNAVAILABLE = -1; + + /** + * Returns the energy in microjoules that the screen consumed while on. + * Will return {@link #ENERGY_DATA_UNAVAILABLE} if data is unavailable + * + * {@hide} + */ + public abstract long getScreenOnEnergy(); + + /** + * Returns the energy in microjoules that the screen consumed while in doze + * Will return {@link #ENERGY_DATA_UNAVAILABLE} if data is unavailable + * + * {@hide} + */ + public abstract long getScreenDozeEnergy(); + public static final BitDescription[] HISTORY_STATE_DESCRIPTIONS = new BitDescription[] { new BitDescription(HistoryItem.STATE_CPU_RUNNING_FLAG, "running", "r"), new BitDescription(HistoryItem.STATE_WAKE_LOCK_FLAG, "wake_lock", "w"), diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java index e32ffa6e9d05..18921639f55d 100644 --- a/core/java/android/security/keymaster/KeymasterDefs.java +++ b/core/java/android/security/keymaster/KeymasterDefs.java @@ -94,6 +94,8 @@ public final class KeymasterDefs { public static final int KM_TAG_ATTESTATION_ID_MEID = KM_BYTES | 715; public static final int KM_TAG_ATTESTATION_ID_MANUFACTURER = KM_BYTES | 716; public static final int KM_TAG_ATTESTATION_ID_MODEL = KM_BYTES | 717; + public static final int KM_TAG_VENDOR_PATCHLEVEL = KM_UINT | 718; + public static final int KM_TAG_BOOT_PATCHLEVEL = KM_UINT | 719; public static final int KM_TAG_DEVICE_UNIQUE_ATTESTATION = KM_BOOL | 720; public static final int KM_TAG_ASSOCIATED_DATA = KM_BYTES | 1000; diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java index 3da3184afae1..59299f6b15eb 100644 --- a/core/java/android/view/Choreographer.java +++ b/core/java/android/view/Choreographer.java @@ -276,7 +276,7 @@ public final class Choreographer { private static float getRefreshRate() { DisplayInfo di = DisplayManagerGlobal.getInstance().getDisplayInfo( Display.DEFAULT_DISPLAY); - return di.getMode().getRefreshRate(); + return di.getRefreshRate(); } /** @@ -944,7 +944,7 @@ public final class Choreographer { private VsyncEventData mLastVsyncEventData = new VsyncEventData(); public FrameDisplayEventReceiver(Looper looper, int vsyncSource) { - super(looper, vsyncSource, CONFIG_CHANGED_EVENT_SUPPRESS); + super(looper, vsyncSource, 0); } // TODO(b/116025192): physicalDisplayId is ignored because SF only emits VSYNC events for diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index 3021aa6a0783..56c7e27151e0 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -456,6 +456,9 @@ public final class Display { // TODO (b/114338689): Remove the flag and use WindowManager#REMOVE_CONTENT_MODE_DESTROY public static final int REMOVE_MODE_DESTROY_CONTENT = 1; + /** @hide */ + public static final int DISPLAY_MODE_ID_FOR_FRAME_RATE_OVERRIDE = 0xFF; + /** * Internal method to create a display. * The display created with this method will have a static {@link DisplayAdjustments} applied. @@ -886,7 +889,7 @@ public final class Display { public float getRefreshRate() { synchronized (this) { updateDisplayInfoLocked(); - return mDisplayInfo.getMode().getRefreshRate(); + return mDisplayInfo.getRefreshRate(); } } @@ -1391,6 +1394,23 @@ public final class Display { } /** + * Returns true if the display is in an off state such as {@link #STATE_OFF}. + * @hide + */ + public static boolean isOffState(int state) { + return state == STATE_OFF; + } + + /** + * Returns true if the display is in an on state such as {@link #STATE_ON} + * or {@link #STATE_VR} or {@link #STATE_ON_SUSPEND}. + * @hide + */ + public static boolean isOnState(int state) { + return state == STATE_ON || state == STATE_VR || state == STATE_ON_SUSPEND; + } + + /** * A mode supported by a given display. * * @see Display#getSupportedModes() @@ -1509,6 +1529,16 @@ public final class Display { Float.floatToIntBits(mRefreshRate) == Float.floatToIntBits(refreshRate); } + /** + * Returns {@code true} if this mode equals to the other mode in all parameters except + * the refresh rate. + * + * @hide + */ + public boolean equalsExceptRefreshRate(@Nullable Display.Mode other) { + return mWidth == other.mWidth && mHeight == other.mHeight; + } + @Override public boolean equals(@Nullable Object other) { if (this == other) { diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java index e8a4ed44b7c8..5d4a4e52975a 100644 --- a/core/java/android/view/DisplayEventReceiver.java +++ b/core/java/android/view/DisplayEventReceiver.java @@ -23,6 +23,8 @@ import android.os.Looper; import android.os.MessageQueue; import android.util.Log; +import com.android.internal.annotations.VisibleForTesting; + import dalvik.annotation.optimization.FastNative; import dalvik.system.CloseGuard; @@ -56,18 +58,18 @@ public abstract class DisplayEventReceiver { public static final int VSYNC_SOURCE_SURFACE_FLINGER = 1; /** - * Specifies to suppress config changed events from being generated from Surface Flinger. + * Specifies to generate config changed events from Surface Flinger. * <p> * Needs to be kept in sync with frameworks/native/include/gui/ISurfaceComposer.h */ - public static final int CONFIG_CHANGED_EVENT_SUPPRESS = 0; + public static final int EVENT_REGISTRATION_CONFIG_CHANGED_FLAG = 0x1; /** - * Specifies to generate config changed events from Surface Flinger. + * Specifies to generate frame rate override events from Surface Flinger. * <p> * Needs to be kept in sync with frameworks/native/include/gui/ISurfaceComposer.h */ - public static final int CONFIG_CHANGED_EVENT_DISPATCH = 1; + public static final int EVENT_REGISTRATION_FRAME_RATE_OVERRIDE_FLAG = 0x2; private static final String TAG = "DisplayEventReceiver"; @@ -81,7 +83,7 @@ public abstract class DisplayEventReceiver { private MessageQueue mMessageQueue; private static native long nativeInit(WeakReference<DisplayEventReceiver> receiver, - MessageQueue messageQueue, int vsyncSource, int configChanged); + MessageQueue messageQueue, int vsyncSource, int eventRegistration); private static native void nativeDispose(long receiverPtr); @FastNative private static native void nativeScheduleVsync(long receiverPtr); @@ -93,7 +95,7 @@ public abstract class DisplayEventReceiver { */ @UnsupportedAppUsage public DisplayEventReceiver(Looper looper) { - this(looper, VSYNC_SOURCE_APP, CONFIG_CHANGED_EVENT_SUPPRESS); + this(looper, VSYNC_SOURCE_APP, 0); } /** @@ -101,17 +103,17 @@ public abstract class DisplayEventReceiver { * * @param looper The looper to use when invoking callbacks. * @param vsyncSource The source of the vsync tick. Must be on of the VSYNC_SOURCE_* values. - * @param configChanged Whether to dispatch config changed events. Must be one of the - * CONFIG_CHANGED_EVENT_* values. + * @param eventRegistration Which events to dispatch. Must be a bitfield consist of the + * EVENT_REGISTRATION_*_FLAG values. */ - public DisplayEventReceiver(Looper looper, int vsyncSource, int configChanged) { + public DisplayEventReceiver(Looper looper, int vsyncSource, int eventRegistration) { if (looper == null) { throw new IllegalArgumentException("looper must not be null"); } mMessageQueue = looper.getQueue(); mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue, - vsyncSource, configChanged); + vsyncSource, eventRegistration); mCloseGuard.open("dispose"); } @@ -206,6 +208,41 @@ public abstract class DisplayEventReceiver { } /** + * Represents a mapping between a UID and an override frame rate + */ + public static class FrameRateOverride { + // The application uid + public final int uid; + + // The frame rate that this application runs at + public final float frameRateHz; + + + @VisibleForTesting + public FrameRateOverride(int uid, float frameRateHz) { + this.uid = uid; + this.frameRateHz = frameRateHz; + } + + @Override + public String toString() { + return "{uid=" + uid + " frameRateHz=" + frameRateHz + "}"; + } + } + + /** + * Called when frame rate override event is received. + * + * @param timestampNanos The timestamp of the event, in the {@link System#nanoTime()} + * timebase. + * @param physicalDisplayId Stable display ID that uniquely describes a (display, port) pair. + * @param overrides The mappings from uid to frame rates + */ + public void onFrameRateOverridesChanged(long timestampNanos, long physicalDisplayId, + FrameRateOverride[] overrides) { + } + + /** * Schedules a single vertical sync pulse to be delivered when the next * display frame begins. */ @@ -240,4 +277,11 @@ public abstract class DisplayEventReceiver { onConfigChanged(timestampNanos, physicalDisplayId, configId); } + // Called from native code. + @SuppressWarnings("unused") + private void dispatchFrameRateOverrides(long timestampNanos, long physicalDisplayId, + FrameRateOverride[] overrides) { + onFrameRateOverridesChanged(timestampNanos, physicalDisplayId, overrides); + } + } diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index fe9a1a76bbaf..020160584f67 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -261,6 +261,11 @@ public final class DisplayInfo implements Parcelable { public String ownerPackageName; /** + * The refresh rate override for this app. 0 means no override. + */ + public float refreshRateOverride; + + /** * @hide * Get current remove mode of the display - what actions should be performed with the display's * content when it is removed. @@ -332,7 +337,8 @@ public final class DisplayInfo implements Parcelable { && state == other.state && ownerUid == other.ownerUid && Objects.equals(ownerPackageName, other.ownerPackageName) - && removeMode == other.removeMode; + && removeMode == other.removeMode + && refreshRateOverride == other.refreshRateOverride; } @Override @@ -376,6 +382,7 @@ public final class DisplayInfo implements Parcelable { ownerUid = other.ownerUid; ownerPackageName = other.ownerPackageName; removeMode = other.removeMode; + refreshRateOverride = other.refreshRateOverride; } public void readFromParcel(Parcel source) { @@ -421,6 +428,7 @@ public final class DisplayInfo implements Parcelable { ownerPackageName = source.readString8(); uniqueId = source.readString8(); removeMode = source.readInt(); + refreshRateOverride = source.readFloat(); } @Override @@ -465,6 +473,7 @@ public final class DisplayInfo implements Parcelable { dest.writeString8(ownerPackageName); dest.writeString8(uniqueId); dest.writeInt(removeMode); + dest.writeFloat(refreshRateOverride); } @Override @@ -472,6 +481,17 @@ public final class DisplayInfo implements Parcelable { return 0; } + /** + * Returns the refresh rate the application would experience. + */ + public float getRefreshRate() { + if (refreshRateOverride > 0) { + return refreshRateOverride; + } + + return getMode().getRefreshRate(); + } + public Display.Mode getMode() { return findMode(modeId); } @@ -675,6 +695,9 @@ public final class DisplayInfo implements Parcelable { } sb.append(", removeMode "); sb.append(removeMode); + sb.append(", refreshRateOverride "); + sb.append(refreshRateOverride); + sb.append("}"); return sb.toString(); } diff --git a/core/java/android/webkit/PacProcessor.java b/core/java/android/webkit/PacProcessor.java index d7b4e8bba884..21fa6fc88c13 100644 --- a/core/java/android/webkit/PacProcessor.java +++ b/core/java/android/webkit/PacProcessor.java @@ -54,7 +54,7 @@ public interface PacProcessor { */ @NonNull static PacProcessor createInstance() { - throw new UnsupportedOperationException("Not implemented"); + return WebViewFactory.getProvider().createPacProcessor(); } /** diff --git a/core/java/android/widget/SelectionActionModeHelper.java b/core/java/android/widget/SelectionActionModeHelper.java index e74ce53306c1..a034a7c2dc7e 100644 --- a/core/java/android/widget/SelectionActionModeHelper.java +++ b/core/java/android/widget/SelectionActionModeHelper.java @@ -156,7 +156,8 @@ public final class SelectionActionModeHelper { mSmartSelectSprite != null ? this::startSelectionActionModeWithSmartSelectAnimation : this::startSelectionActionMode, - mTextClassificationHelper::getOriginalSelection) + mTextClassificationHelper::getOriginalSelection, + mTextClassificationHelper::isTextClassifierDestroyed) .execute(); } } @@ -178,7 +179,8 @@ public final class SelectionActionModeHelper { mTextClassificationHelper.getTimeoutDuration(), mTextClassificationHelper::classifyText, this::startLinkActionMode, - mTextClassificationHelper::getOriginalSelection) + mTextClassificationHelper::getOriginalSelection, + mTextClassificationHelper::isTextClassifierDestroyed) .execute(); } } @@ -194,7 +196,8 @@ public final class SelectionActionModeHelper { mTextClassificationHelper.getTimeoutDuration(), mTextClassificationHelper::classifyText, this::invalidateActionMode, - mTextClassificationHelper::getOriginalSelection) + mTextClassificationHelper::getOriginalSelection, + mTextClassificationHelper::isTextClassifierDestroyed) .execute(); } } @@ -992,6 +995,7 @@ public final class SelectionActionModeHelper { private final Supplier<SelectionResult> mSelectionResultSupplier; private final Consumer<SelectionResult> mSelectionResultCallback; private final Supplier<SelectionResult> mTimeOutResultSupplier; + private final Supplier<Boolean> mIsTextClassifierDestroyedSupplier; private final TextView mTextView; private final String mOriginalText; @@ -1006,13 +1010,16 @@ public final class SelectionActionModeHelper { @NonNull TextView textView, int timeOut, @NonNull Supplier<SelectionResult> selectionResultSupplier, @NonNull Consumer<SelectionResult> selectionResultCallback, - @NonNull Supplier<SelectionResult> timeOutResultSupplier) { + @NonNull Supplier<SelectionResult> timeOutResultSupplier, + @NonNull Supplier<Boolean> isTextClassifierDestroyedSupplier) { super(textView != null ? textView.getHandler() : null); mTextView = Objects.requireNonNull(textView); mTimeOutDuration = timeOut; mSelectionResultSupplier = Objects.requireNonNull(selectionResultSupplier); mSelectionResultCallback = Objects.requireNonNull(selectionResultCallback); mTimeOutResultSupplier = Objects.requireNonNull(timeOutResultSupplier); + mIsTextClassifierDestroyedSupplier = + Objects.requireNonNull(isTextClassifierDestroyedSupplier); // Make a copy of the original text. mOriginalText = getText(mTextView).toString(); } @@ -1022,7 +1029,19 @@ public final class SelectionActionModeHelper { protected SelectionResult doInBackground(Void... params) { final Runnable onTimeOut = this::onTimeOut; mTextView.postDelayed(onTimeOut, mTimeOutDuration); - final SelectionResult result = mSelectionResultSupplier.get(); + SelectionResult result = null; + try { + result = mSelectionResultSupplier.get(); + } catch (IllegalStateException e) { + // Swallows the exception if the text classifier session is destroyed + if (mIsTextClassifierDestroyedSupplier.get()) { + Log.w(LOG_TAG, + "TextClassificationAsyncTask failed because TextClassifier destroyed", + e); + } else { + throw e; + } + } mTextView.removeCallbacks(onTimeOut); return result; } @@ -1154,6 +1173,10 @@ public final class SelectionActionModeHelper { } } + public boolean isTextClassifierDestroyed() { + return mTextClassifier.get().isDestroyed(); + } + private boolean isDarkLaunchEnabled() { return TextClassificationManager.getSettings(mContext).isModelDarkLaunchEnabled(); } diff --git a/core/java/android/window/DisplayAreaOrganizer.java b/core/java/android/window/DisplayAreaOrganizer.java index 6e20452ad061..1ac188cf2486 100644 --- a/core/java/android/window/DisplayAreaOrganizer.java +++ b/core/java/android/window/DisplayAreaOrganizer.java @@ -101,6 +101,19 @@ public class DisplayAreaOrganizer extends WindowOrganizer { public static final int FEATURE_VENDOR_FIRST = FEATURE_SYSTEM_LAST + 1; /** + * Last possible vendor specific display area id. + * @hide + */ + public static final int FEATURE_VENDOR_LAST = FEATURE_VENDOR_FIRST + 10_000; + + /** + * Task display areas that can be created at runtime start with this value. + * @see #createTaskDisplayArea(int, int, String) + * @hide + */ + public static final int FEATURE_RUNTIME_TASK_CONTAINER_FIRST = FEATURE_VENDOR_LAST + 1; + + /** * Registers a DisplayAreaOrganizer to manage display areas for a given feature. A feature can * not be registered by multiple organizers at the same time. * @@ -132,6 +145,50 @@ public class DisplayAreaOrganizer extends WindowOrganizer { } /** + * Creates a persistent task display area. It will be added to be the top most task display area + * in the root. + * + * The new created TDA is organized by the organizer, and will be deleted on calling + * {@link #deleteTaskDisplayArea(WindowContainerToken)} or {@link #unregisterOrganizer()}. + * + * @param displayId the display to create the new task display area in. + * @param rootFeatureId the root display area to create the new task display area in. Caller can + * use {@link #FEATURE_ROOT} as the root of the logical display. + * @param name the name for the new task display area. + * @return the new created task display area. + * @throws IllegalArgumentException if failed to create a new task display area. + * @hide + */ + @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) + @CallSuper + @NonNull + public DisplayAreaAppearedInfo createTaskDisplayArea(int displayId, int rootFeatureId, + @NonNull String name) { + try { + return getController().createTaskDisplayArea( + mInterface, displayId, rootFeatureId, name); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Deletes a persistent task display area. It can only be one that created by an organizer. + * + * @throws IllegalArgumentException if failed to delete the task display area. + * @hide + */ + @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) + @CallSuper + public void deleteTaskDisplayArea(@NonNull WindowContainerToken taskDisplayArea) { + try { + getController().deleteTaskDisplayArea(taskDisplayArea); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Called when a DisplayArea of the registered window type can be controlled by this organizer. * It will not be called for the DisplayAreas that exist when {@link #registerOrganizer(int)} is * called. diff --git a/core/java/android/window/IDisplayAreaOrganizerController.aidl b/core/java/android/window/IDisplayAreaOrganizerController.aidl index edabcf8ad0de..26fa434506cf 100644 --- a/core/java/android/window/IDisplayAreaOrganizerController.aidl +++ b/core/java/android/window/IDisplayAreaOrganizerController.aidl @@ -19,6 +19,7 @@ package android.window; import android.content.pm.ParceledListSlice; import android.window.DisplayAreaAppearedInfo; import android.window.IDisplayAreaOrganizer; +import android.window.WindowContainerToken; /** @hide */ interface IDisplayAreaOrganizerController { @@ -37,4 +38,28 @@ interface IDisplayAreaOrganizerController { * Unregisters a previously registered display area organizer. */ void unregisterOrganizer(in IDisplayAreaOrganizer organizer); + + /** + * Creates a persistent task display area. It will be added to be the top most task display area + * in the root. + * + * The new created TDA is organized by the organizer, and will be deleted on calling + * {@link #deleteTaskDisplayArea(WindowContainerToken)} or {@link #unregisterOrganizer()}. + * + * @param displayId the display to create the new task display area in. + * @param rootFeatureId the root display area to create the new task display area in. Caller can + * use {@link #FEATURE_ROOT} as the root of the logical display. + * @param name the name for the new task display area. + * @return the new created task display area. + * @throws IllegalArgumentException if failed to create a new task display area. + */ + DisplayAreaAppearedInfo createTaskDisplayArea(in IDisplayAreaOrganizer organizer, int displayId, + int rootFeatureId, in String name); + + /** + * Deletes a persistent task display area. It can only be one that created by an organizer. + * + * @throws IllegalArgumentException if failed to delete the task display area. + */ + void deleteTaskDisplayArea(in WindowContainerToken taskDisplayArea); } diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java index 3b5fecfc600a..0ede1b86b524 100644 --- a/core/java/com/android/internal/content/FileSystemProvider.java +++ b/core/java/com/android/internal/content/FileSystemProvider.java @@ -20,15 +20,14 @@ import android.annotation.CallSuper; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ContentResolver; +import android.content.Context; import android.content.Intent; import android.content.res.AssetFileDescriptor; import android.database.Cursor; -import android.database.DatabaseUtils; import android.database.MatrixCursor; import android.database.MatrixCursor.RowBuilder; import android.graphics.Point; import android.net.Uri; -import android.os.Binder; import android.os.Bundle; import android.os.CancellationSignal; import android.os.FileObserver; @@ -39,7 +38,6 @@ import android.provider.DocumentsContract; import android.provider.DocumentsContract.Document; import android.provider.DocumentsProvider; import android.provider.MediaStore; -import android.provider.MediaStore.Files.FileColumns; import android.provider.MetadataReader; import android.system.Int64Ref; import android.text.TextUtils; @@ -66,6 +64,7 @@ import java.nio.file.attribute.BasicFileAttributes; import java.util.Arrays; import java.util.LinkedList; import java.util.List; +import java.util.Locale; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Predicate; @@ -271,8 +270,7 @@ public abstract class FileSystemProvider extends DocumentsProvider { throw new IllegalStateException("Failed to touch " + file + ": " + e); } } - MediaStore.scanFile(getContext().getContentResolver(), file); - + updateMediaStore(getContext(), file); return childId; } @@ -295,7 +293,9 @@ public abstract class FileSystemProvider extends DocumentsProvider { onDocIdChanged(afterDocId); final File afterVisibleFile = getFileForDocId(afterDocId, true); - moveInMediaStore(beforeVisibleFile, afterVisibleFile); + + updateMediaStore(getContext(), beforeVisibleFile); + updateMediaStore(getContext(), afterVisibleFile); if (!TextUtils.equals(docId, afterDocId)) { return afterDocId; @@ -323,17 +323,23 @@ public abstract class FileSystemProvider extends DocumentsProvider { onDocIdChanged(sourceDocumentId); onDocIdDeleted(sourceDocumentId); onDocIdChanged(docId); - moveInMediaStore(visibleFileBefore, getFileForDocId(docId, true)); - + // update the database + updateMediaStore(getContext(), visibleFileBefore); + updateMediaStore(getContext(), getFileForDocId(docId, true)); return docId; } - private void moveInMediaStore(@Nullable File oldVisibleFile, @Nullable File newVisibleFile) { - if (oldVisibleFile != null) { - MediaStore.scanFile(getContext().getContentResolver(), oldVisibleFile); - } - if (newVisibleFile != null) { - MediaStore.scanFile(getContext().getContentResolver(), newVisibleFile); + private static void updateMediaStore(@NonNull Context context, File file) { + if (file != null) { + final ContentResolver resolver = context.getContentResolver(); + final String noMedia = ".nomedia"; + // For file, check whether the file name is .nomedia or not. + // If yes, scan the parent directory to update all files in the directory. + if (!file.isDirectory() && file.getName().toLowerCase(Locale.ROOT).endsWith(noMedia)) { + MediaStore.scanFile(resolver, file.getParentFile()); + } else { + MediaStore.scanFile(resolver, file); + } } } @@ -354,35 +360,7 @@ public abstract class FileSystemProvider extends DocumentsProvider { onDocIdChanged(docId); onDocIdDeleted(docId); - removeFromMediaStore(visibleFile); - } - - private void removeFromMediaStore(@Nullable File visibleFile) - throws FileNotFoundException { - // visibleFolder is null if we're removing a document from external thumb drive or SD card. - if (visibleFile != null) { - final long token = Binder.clearCallingIdentity(); - - try { - final ContentResolver resolver = getContext().getContentResolver(); - final Uri externalUri = MediaStore.Files.getContentUri("external"); - final Bundle queryArgs = new Bundle(); - queryArgs.putInt(MediaStore.QUERY_ARG_MATCH_PENDING, MediaStore.MATCH_INCLUDE); - - // Remove the media store entry corresponding to visibleFile and if it is a - // directory, also remove media store entries for any files inside this directory. - // Logic borrowed from com.android.providers.media.scan.ModernMediaScanner. - final String pathEscapedForLike = DatabaseUtils.escapeForLike( - visibleFile.getAbsolutePath()); - ContentResolver.includeSqlSelectionArgs(queryArgs, - FileColumns.DATA + " LIKE ? ESCAPE '\\' OR " - + FileColumns.DATA + " LIKE ? ESCAPE '\\'", - new String[] {pathEscapedForLike + "/%", pathEscapedForLike}); - resolver.delete(externalUri, queryArgs); - } finally { - Binder.restoreCallingIdentity(token); - } - } + updateMediaStore(getContext(), visibleFile); } @Override diff --git a/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java b/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java index 70505bc5895b..6c01780dac76 100644 --- a/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java +++ b/core/java/com/android/internal/infra/AbstractMultiplePendingRequestsRemoteService.java @@ -33,7 +33,10 @@ import java.util.List; * * @param <S> the concrete remote service class * @param <I> the interface of the binder service + * + * @deprecated Use {@link ServiceConnector} to manage remote service connections */ +@Deprecated public abstract class AbstractMultiplePendingRequestsRemoteService<S extends AbstractMultiplePendingRequestsRemoteService<S, I>, I extends IInterface> extends AbstractRemoteService<S, I> { diff --git a/core/java/com/android/internal/infra/AbstractRemoteService.java b/core/java/com/android/internal/infra/AbstractRemoteService.java index 722e5c102fcf..f63ac2e14e20 100644 --- a/core/java/com/android/internal/infra/AbstractRemoteService.java +++ b/core/java/com/android/internal/infra/AbstractRemoteService.java @@ -58,9 +58,12 @@ import java.util.ArrayList; * @param <S> the concrete remote service class * @param <I> the interface of the binder service * + * @deprecated Use {@link ServiceConnector} to manage remote service connections + * * @hide */ //TODO(b/117779333): improve javadoc above instead of using Autofill as an example +@Deprecated public abstract class AbstractRemoteService<S extends AbstractRemoteService<S, I>, I extends IInterface> implements DeathRecipient { private static final int MSG_BIND = 1; diff --git a/core/java/com/android/internal/infra/AbstractSinglePendingRequestRemoteService.java b/core/java/com/android/internal/infra/AbstractSinglePendingRequestRemoteService.java index 2ebf2fd820d8..0d9af8c8bcf5 100644 --- a/core/java/com/android/internal/infra/AbstractSinglePendingRequestRemoteService.java +++ b/core/java/com/android/internal/infra/AbstractSinglePendingRequestRemoteService.java @@ -33,8 +33,11 @@ import java.io.PrintWriter; * @param <S> the concrete remote service class * @param <I> the interface of the binder service * + * @deprecated Use {@link ServiceConnector} to manage remote service connections + * * @hide */ +@Deprecated public abstract class AbstractSinglePendingRequestRemoteService<S extends AbstractSinglePendingRequestRemoteService<S, I>, I extends IInterface> extends AbstractRemoteService<S, I> { diff --git a/core/java/com/android/internal/infra/OWNERS b/core/java/com/android/internal/infra/OWNERS new file mode 100644 index 000000000000..45503582b2c5 --- /dev/null +++ b/core/java/com/android/internal/infra/OWNERS @@ -0,0 +1,6 @@ +per-file AndroidFuture.java = eugenesusla@google.com +per-file RemoteStream.java = eugenesusla@google.com +per-file PerUser.java = eugenesusla@google.com +per-file ServiceConnector.java = eugenesusla@google.com +per-file AndroidFuture.aidl = eugenesusla@google.com +per-file IAndroidFuture.aidl = eugenesusla@google.com
\ No newline at end of file diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 7c442b408624..7571f5db8cad 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -98,6 +98,8 @@ import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeRead import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader; import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader; import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader; +import com.android.internal.power.MeasuredEnergyArray; +import com.android.internal.power.MeasuredEnergyStats; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FastXmlSerializer; @@ -155,7 +157,7 @@ public class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - static final int VERSION = 189 + (USE_OLD_HISTORY ? 1000 : 0); + static final int VERSION = 190 + (USE_OLD_HISTORY ? 1000 : 0); // The maximum number of names wakelocks we will keep track of // per uid; once the limit is reached, we batch the remaining wakelocks @@ -349,12 +351,20 @@ public class BatteryStatsImpl extends BatteryStats { } /** interface to update rail information for power monitor */ - public interface RailEnergyDataCallback { + public interface MeasuredEnergyRetriever { /** Function to fill the map for the rail data stats * Used for power monitoring feature * @param railStats */ void fillRailDataStats(RailStats railStats); + /** + * Function to get energy consumption data + * + * @return an array of measured energy (in microjoules) since boot, will be null if + * measured energy data is unavailable + */ + @Nullable + MeasuredEnergyArray getEnergyConsumptionData(); } public static abstract class UserInfoProvider { @@ -391,7 +401,7 @@ public class BatteryStatsImpl extends BatteryStats { } }; - public final RailEnergyDataCallback mRailEnergyDataCallback; + public final MeasuredEnergyRetriever mMeasuredEnergyRetriever; /** * This handler is running on {@link BackgroundThread}. @@ -624,8 +634,10 @@ public class BatteryStatsImpl extends BatteryStats { int UPDATE_WIFI = 0x02; int UPDATE_RADIO = 0x04; int UPDATE_BT = 0x08; - int UPDATE_RPM = 0x10; // 16 - int UPDATE_ALL = UPDATE_CPU | UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT | UPDATE_RPM; + int UPDATE_RPM = 0x10; + int UPDATE_ENERGY = 0x20; + int UPDATE_ALL = + UPDATE_CPU | UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT | UPDATE_RPM | UPDATE_ENERGY; Future<?> scheduleSync(String reason, int flags); Future<?> scheduleCpuSyncDueToRemovedUid(int uid); @@ -633,8 +645,11 @@ public class BatteryStatsImpl extends BatteryStats { long delayMillis); Future<?> scheduleCopyFromAllUidsCpuTimes(boolean onBattery, boolean onBatteryScreenOff); Future<?> scheduleCpuSyncDueToSettingChange(); - Future<?> scheduleCpuSyncDueToScreenStateChange(boolean onBattery, - boolean onBatteryScreenOff); + /** + * Schedule a sync because of a screen state change. + */ + Future<?> scheduleSyncDueToScreenStateChange(int flags, boolean onBattery, + boolean onBatteryScreenOff, int screenState); Future<?> scheduleCpuSyncDueToWakelockChange(long delayMillis); void cancelCpuSyncDueToWakelockChange(); Future<?> scheduleSyncDueToBatteryLevelChange(long delayMillis); @@ -932,6 +947,13 @@ public class BatteryStatsImpl extends BatteryStats { int mWifiRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW; /** + * Accumulated energy consumption of various consumers while on battery. + * If energy consumer data is unavailable this will be null. + */ + @GuardedBy("this") + MeasuredEnergyStats mBatteryMeasuredEnergyStats; + + /** * These provide time bases that discount the time the device is plugged * in to power. */ @@ -1123,7 +1145,7 @@ public class BatteryStatsImpl extends BatteryStats { mBatteryStatsHistory = null; mHandler = null; mPlatformIdleStateCallback = null; - mRailEnergyDataCallback = null; + mMeasuredEnergyRetriever = null; mUserInfoProvider = null; mConstants = new Constants(mHandler); clearHistoryLocked(); @@ -1424,7 +1446,8 @@ public class BatteryStatsImpl extends BatteryStats { * @param in the parcel to read from * @return the Counter or null. */ - public static @Nullable Counter readCounterFromParcel(TimeBase timeBase, Parcel in) { + @Nullable + public static Counter readCounterFromParcel(TimeBase timeBase, Parcel in) { if (in.readInt() == 0) { return null; } @@ -3949,7 +3972,7 @@ public class BatteryStatsImpl extends BatteryStats { @GuardedBy("this") public void updateTimeBasesLocked(boolean unplugged, int screenState, long uptimeUs, long realtimeUs) { - final boolean screenOff = !isScreenOn(screenState); + final boolean screenOff = !Display.isOnState(screenState); final boolean updateOnBatteryTimeBase = unplugged != mOnBatteryTimeBase.isRunning(); final boolean updateOnBatteryScreenOffTimeBase = (unplugged && screenOff) != mOnBatteryScreenOffTimeBase.isRunning(); @@ -5012,16 +5035,16 @@ public class BatteryStatsImpl extends BatteryStats { } boolean updateHistory = false; - if (isScreenDoze(state) && !isScreenDoze(oldState)) { + if (Display.isDozeState(state) && !Display.isDozeState(oldState)) { mHistoryCur.states |= HistoryItem.STATE_SCREEN_DOZE_FLAG; mScreenDozeTimer.startRunningLocked(elapsedRealtimeMs); updateHistory = true; - } else if (isScreenDoze(oldState) && !isScreenDoze(state)) { + } else if (Display.isDozeState(oldState) && !Display.isDozeState(state)) { mHistoryCur.states &= ~HistoryItem.STATE_SCREEN_DOZE_FLAG; mScreenDozeTimer.stopRunningLocked(elapsedRealtimeMs); updateHistory = true; } - if (isScreenOn(state)) { + if (Display.isOnState(state)) { mHistoryCur.states |= HistoryItem.STATE_SCREEN_ON_FLAG; if (DEBUG_HISTORY) Slog.v(TAG, "Screen on to: " + Integer.toHexString(mHistoryCur.states)); @@ -5031,7 +5054,7 @@ public class BatteryStatsImpl extends BatteryStats { .startRunningLocked(elapsedRealtimeMs); } updateHistory = true; - } else if (isScreenOn(oldState)) { + } else if (Display.isOnState(oldState)) { mHistoryCur.states &= ~HistoryItem.STATE_SCREEN_ON_FLAG; if (DEBUG_HISTORY) Slog.v(TAG, "Screen off to: " + Integer.toHexString(mHistoryCur.states)); @@ -5047,15 +5070,21 @@ public class BatteryStatsImpl extends BatteryStats { + Display.stateToString(state)); addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs); } - mExternalSync.scheduleCpuSyncDueToScreenStateChange( - mOnBatteryTimeBase.isRunning(), mOnBatteryScreenOffTimeBase.isRunning()); - if (isScreenOn(state)) { + int updateFlag = ExternalStatsSync.UPDATE_CPU; + if (mBatteryMeasuredEnergyStats != null && mBatteryMeasuredEnergyStats.hasSubsystem( + MeasuredEnergyArray.SUBSYSTEM_DISPLAY)) { + updateFlag |= ExternalStatsSync.UPDATE_ENERGY; + } + mExternalSync.scheduleSyncDueToScreenStateChange(updateFlag, + mOnBatteryTimeBase.isRunning(), mOnBatteryScreenOffTimeBase.isRunning(), + mScreenState); + if (Display.isOnState(state)) { updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), state, uptimeMs * 1000, elapsedRealtimeMs * 1000); // Fake a wake lock, so we consider the device waked as long as the screen is on. noteStartWakeLocked(-1, -1, null, "screen", null, WAKE_TYPE_PARTIAL, false, elapsedRealtimeMs, uptimeMs); - } else if (isScreenOn(oldState)) { + } else if (Display.isOnState(oldState)) { noteStopWakeLocked(-1, -1, null, "screen", "screen", WAKE_TYPE_PARTIAL, elapsedRealtimeMs, uptimeMs); updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), state, @@ -7039,6 +7068,26 @@ public class BatteryStatsImpl extends BatteryStats { } } + @Override + public long getScreenOnEnergy() { + if (mBatteryMeasuredEnergyStats == null || !mBatteryMeasuredEnergyStats.hasSubsystem( + MeasuredEnergyArray.SUBSYSTEM_DISPLAY)) { + return ENERGY_DATA_UNAVAILABLE; + } + return mBatteryMeasuredEnergyStats.getAccumulatedBucketEnergy( + MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON); + } + + @Override + public long getScreenDozeEnergy() { + if (mBatteryMeasuredEnergyStats == null || !mBatteryMeasuredEnergyStats.hasSubsystem( + MeasuredEnergyArray.SUBSYSTEM_DISPLAY)) { + return ENERGY_DATA_UNAVAILABLE; + } + return mBatteryMeasuredEnergyStats.getAccumulatedBucketEnergy( + MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_DOZE); + } + @Override public long getStartClockTime() { final long currentTimeMs = System.currentTimeMillis(); if ((currentTimeMs > MILLISECONDS_IN_YEAR @@ -10457,12 +10506,12 @@ public class BatteryStatsImpl extends BatteryStats { } public BatteryStatsImpl(File systemDir, Handler handler, PlatformIdleStateCallback cb, - RailEnergyDataCallback railStatsCb, UserInfoProvider userInfoProvider) { - this(new SystemClocks(), systemDir, handler, cb, railStatsCb, userInfoProvider); + MeasuredEnergyRetriever energyStatsCb, UserInfoProvider userInfoProvider) { + this(new SystemClocks(), systemDir, handler, cb, energyStatsCb, userInfoProvider); } private BatteryStatsImpl(Clocks clocks, File systemDir, Handler handler, - PlatformIdleStateCallback cb, RailEnergyDataCallback railStatsCb, + PlatformIdleStateCallback cb, MeasuredEnergyRetriever energyStatsCb, UserInfoProvider userInfoProvider) { init(clocks); @@ -10563,12 +10612,19 @@ public class BatteryStatsImpl extends BatteryStats { clearHistoryLocked(); updateDailyDeadlineLocked(); mPlatformIdleStateCallback = cb; - mRailEnergyDataCallback = railStatsCb; + mMeasuredEnergyRetriever = energyStatsCb; mUserInfoProvider = userInfoProvider; // Notify statsd that the system is initially not in doze. mDeviceIdleMode = DEVICE_IDLE_MODE_OFF; FrameworkStatsLog.write(FrameworkStatsLog.DEVICE_IDLE_MODE_STATE_CHANGED, mDeviceIdleMode); + + final MeasuredEnergyArray energyStats = mMeasuredEnergyRetriever.getEnergyConsumptionData(); + // If measured energy is not available, it is not supported and + // mBatteryMeasuredEnergyStats should be left null. + if (energyStats != null) { + mBatteryMeasuredEnergyStats = new MeasuredEnergyStats(energyStats, mScreenState); + } } @UnsupportedAppUsage @@ -10588,7 +10644,7 @@ public class BatteryStatsImpl extends BatteryStats { mBatteryStatsHistory = new BatteryStatsHistory(this, mHistoryBuffer); readFromParcel(p); mPlatformIdleStateCallback = null; - mRailEnergyDataCallback = null; + mMeasuredEnergyRetriever = null; } public void setPowerProfileLocked(PowerProfile profile) { @@ -11097,19 +11153,6 @@ public class BatteryStatsImpl extends BatteryStats { return mCharging; } - public boolean isScreenOn(int state) { - return state == Display.STATE_ON || state == Display.STATE_VR - || state == Display.STATE_ON_SUSPEND; - } - - public boolean isScreenOff(int state) { - return state == Display.STATE_OFF; - } - - public boolean isScreenDoze(int state) { - return state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND; - } - void initTimes(long uptimeUs, long realtimeUs) { mStartClockTimeMs = System.currentTimeMillis(); mOnBatteryTimeBase.init(uptimeUs, realtimeUs); @@ -11152,11 +11195,11 @@ public class BatteryStatsImpl extends BatteryStats { mOnBatteryTimeBase.reset(uptimeUs, realtimeUs); mOnBatteryScreenOffTimeBase.reset(uptimeUs, realtimeUs); if ((mHistoryCur.states&HistoryItem.STATE_BATTERY_PLUGGED_FLAG) == 0) { - if (isScreenOn(mScreenState)) { + if (Display.isOnState(mScreenState)) { mDischargeScreenOnUnplugLevel = mHistoryCur.batteryLevel; mDischargeScreenDozeUnplugLevel = 0; mDischargeScreenOffUnplugLevel = 0; - } else if (isScreenDoze(mScreenState)) { + } else if (Display.isDozeState(mScreenState)) { mDischargeScreenOnUnplugLevel = 0; mDischargeScreenDozeUnplugLevel = mHistoryCur.batteryLevel; mDischargeScreenOffUnplugLevel = 0; @@ -11286,6 +11329,11 @@ public class BatteryStatsImpl extends BatteryStats { mTmpRailStats.reset(); + if (mBatteryMeasuredEnergyStats != null) { + mBatteryMeasuredEnergyStats.reset(); + mExternalSync.scheduleSync("reset", ExternalStatsSync.UPDATE_ENERGY); + } + resetIfNotNull(mSystemServerCpuTimesUs, false, elapsedRealtimeUs); resetIfNotNull(mSystemServerThreadCpuTimesUs, false, elapsedRealtimeUs); resetIfNotNull(mBinderThreadCpuTimesUs, false, elapsedRealtimeUs); @@ -11339,19 +11387,19 @@ public class BatteryStatsImpl extends BatteryStats { } private void updateOldDischargeScreenLevelLocked(int state) { - if (isScreenOn(state)) { + if (Display.isOnState(state)) { int diff = mDischargeScreenOnUnplugLevel - mDischargeCurrentLevel; if (diff > 0) { mDischargeAmountScreenOn += diff; mDischargeAmountScreenOnSinceCharge += diff; } - } else if (isScreenDoze(state)) { + } else if (Display.isDozeState(state)) { int diff = mDischargeScreenDozeUnplugLevel - mDischargeCurrentLevel; if (diff > 0) { mDischargeAmountScreenDoze += diff; mDischargeAmountScreenDozeSinceCharge += diff; } - } else if (isScreenOff(state)){ + } else if (Display.isOffState(state)) { int diff = mDischargeScreenOffUnplugLevel - mDischargeCurrentLevel; if (diff > 0) { mDischargeAmountScreenOff += diff; @@ -11361,15 +11409,15 @@ public class BatteryStatsImpl extends BatteryStats { } private void updateNewDischargeScreenLevelLocked(int state) { - if (isScreenOn(state)) { + if (Display.isOnState(state)) { mDischargeScreenOnUnplugLevel = mDischargeCurrentLevel; mDischargeScreenOffUnplugLevel = 0; mDischargeScreenDozeUnplugLevel = 0; - } else if (isScreenDoze(state)){ + } else if (Display.isDozeState(state)) { mDischargeScreenOnUnplugLevel = 0; mDischargeScreenDozeUnplugLevel = mDischargeCurrentLevel; mDischargeScreenOffUnplugLevel = 0; - } else if (isScreenOff(state)) { + } else if (Display.isOffState(state)) { mDischargeScreenOnUnplugLevel = 0; mDischargeScreenDozeUnplugLevel = 0; mDischargeScreenOffUnplugLevel = mDischargeCurrentLevel; @@ -12163,10 +12211,25 @@ public class BatteryStatsImpl extends BatteryStats { * Read and record Rail Energy data. */ public void updateRailStatsLocked() { - if (mRailEnergyDataCallback == null || !mTmpRailStats.isRailStatsAvailable()) { + if (mMeasuredEnergyRetriever == null || !mTmpRailStats.isRailStatsAvailable()) { return; } - mRailEnergyDataCallback.fillRailDataStats(mTmpRailStats); + mMeasuredEnergyRetriever.fillRailDataStats(mTmpRailStats); + } + + /** + * Get energy consumed (in microjoules) by a set of subsystems from the {@link + * MeasuredEnergyRetriever}, if available. + * + * @return a SparseLongArray that maps consumer id to energy consumed. Returns null if data is + * unavailable. + */ + @Nullable + public MeasuredEnergyArray getEnergyConsumptionDataLocked() { + if (mMeasuredEnergyRetriever == null) { + return null; + } + return mMeasuredEnergyRetriever.getEnergyConsumptionData(); } /** @@ -12428,6 +12491,21 @@ public class BatteryStatsImpl extends BatteryStats { } /** + * Update energy consumption data with a new snapshot of energy data. + * Generally this should only be called from BatteryExternalStatsWorker. + * + * @param energyStats latest energy data to update with. + */ + @GuardedBy("this") + public void updateMeasuredEnergyStatsLocked(@NonNull MeasuredEnergyArray energyStats, + int screenState) { + if (mBatteryMeasuredEnergyStats != null) { + mBatteryMeasuredEnergyStats.update(energyStats, screenState, + mOnBatteryTimeBase.isRunning()); + } + } + + /** * Mark the current partial timers as gone through a collection so that they will be * considered in the next cpu times distribution to wakelock holders. */ @@ -12919,11 +12997,11 @@ public class BatteryStatsImpl extends BatteryStats { } addHistoryRecordLocked(mSecRealtime, mSecUptime); mDischargeCurrentLevel = mDischargeUnplugLevel = level; - if (isScreenOn(screenState)) { + if (Display.isOnState(screenState)) { mDischargeScreenOnUnplugLevel = level; mDischargeScreenDozeUnplugLevel = 0; mDischargeScreenOffUnplugLevel = 0; - } else if (isScreenDoze(screenState)) { + } else if (Display.isDozeState(screenState)) { mDischargeScreenOnUnplugLevel = 0; mDischargeScreenDozeUnplugLevel = level; mDischargeScreenOffUnplugLevel = 0; @@ -13079,7 +13157,7 @@ public class BatteryStatsImpl extends BatteryStats { final long chargeDiff = mHistoryCur.batteryChargeUAh - chargeUAh; mDischargeCounter.addCountLocked(chargeDiff); mDischargeScreenOffCounter.addCountLocked(chargeDiff); - if (isScreenDoze(mScreenState)) { + if (Display.isDozeState(mScreenState)) { mDischargeScreenDozeCounter.addCountLocked(chargeDiff); } if (mDeviceIdleMode == DEVICE_IDLE_MODE_LIGHT) { @@ -13130,7 +13208,7 @@ public class BatteryStatsImpl extends BatteryStats { final long chargeDiff = mHistoryCur.batteryChargeUAh - chargeUAh; mDischargeCounter.addCountLocked(chargeDiff); mDischargeScreenOffCounter.addCountLocked(chargeDiff); - if (isScreenDoze(mScreenState)) { + if (Display.isDozeState(mScreenState)) { mDischargeScreenDozeCounter.addCountLocked(chargeDiff); } if (mDeviceIdleMode == DEVICE_IDLE_MODE_LIGHT) { @@ -13584,7 +13662,7 @@ public class BatteryStatsImpl extends BatteryStats { public int getDischargeAmountScreenOn() { synchronized(this) { int val = mDischargeAmountScreenOn; - if (mOnBattery && isScreenOn(mScreenState) + if (mOnBattery && Display.isOnState(mScreenState) && mDischargeCurrentLevel < mDischargeScreenOnUnplugLevel) { val += mDischargeScreenOnUnplugLevel-mDischargeCurrentLevel; } @@ -13596,7 +13674,7 @@ public class BatteryStatsImpl extends BatteryStats { public int getDischargeAmountScreenOnSinceCharge() { synchronized(this) { int val = mDischargeAmountScreenOnSinceCharge; - if (mOnBattery && isScreenOn(mScreenState) + if (mOnBattery && Display.isOnState(mScreenState) && mDischargeCurrentLevel < mDischargeScreenOnUnplugLevel) { val += mDischargeScreenOnUnplugLevel-mDischargeCurrentLevel; } @@ -13609,7 +13687,7 @@ public class BatteryStatsImpl extends BatteryStats { public int getDischargeAmountScreenOff() { synchronized(this) { int val = mDischargeAmountScreenOff; - if (mOnBattery && isScreenOff(mScreenState) + if (mOnBattery && Display.isOffState(mScreenState) && mDischargeCurrentLevel < mDischargeScreenOffUnplugLevel) { val += mDischargeScreenOffUnplugLevel-mDischargeCurrentLevel; } @@ -13622,7 +13700,7 @@ public class BatteryStatsImpl extends BatteryStats { public int getDischargeAmountScreenOffSinceCharge() { synchronized(this) { int val = mDischargeAmountScreenOffSinceCharge; - if (mOnBattery && isScreenOff(mScreenState) + if (mOnBattery && Display.isOffState(mScreenState) && mDischargeCurrentLevel < mDischargeScreenOffUnplugLevel) { val += mDischargeScreenOffUnplugLevel-mDischargeCurrentLevel; } @@ -13635,7 +13713,7 @@ public class BatteryStatsImpl extends BatteryStats { public int getDischargeAmountScreenDoze() { synchronized(this) { int val = mDischargeAmountScreenDoze; - if (mOnBattery && isScreenDoze(mScreenState) + if (mOnBattery && Display.isDozeState(mScreenState) && mDischargeCurrentLevel < mDischargeScreenDozeUnplugLevel) { val += mDischargeScreenDozeUnplugLevel-mDischargeCurrentLevel; } @@ -13647,7 +13725,7 @@ public class BatteryStatsImpl extends BatteryStats { public int getDischargeAmountScreenDozeSinceCharge() { synchronized(this) { int val = mDischargeAmountScreenDozeSinceCharge; - if (mOnBattery && isScreenDoze(mScreenState) + if (mOnBattery && Display.isDozeState(mScreenState) && mDischargeCurrentLevel < mDischargeScreenDozeUnplugLevel) { val += mDischargeScreenDozeUnplugLevel-mDischargeCurrentLevel; } @@ -14129,6 +14207,19 @@ public class BatteryStatsImpl extends BatteryStats { } } + /** + * Dump measured energy stats + */ + @GuardedBy("this") + public void dumpMeasuredEnergyStatsLocked(PrintWriter pw) { + if (mBatteryMeasuredEnergyStats == null) return; + final IndentingPrintWriter iPw = new IndentingPrintWriter(pw, " "); + iPw.println("On battery measured energy stats:"); + iPw.increaseIndent(); + mBatteryMeasuredEnergyStats.dump(iPw); + iPw.decreaseIndent(); + } + final ReentrantLock mWriteLock = new ReentrantLock(); public void writeAsyncLocked() { @@ -14504,6 +14595,8 @@ public class BatteryStatsImpl extends BatteryStats { mNextMaxDailyDeadlineMs = in.readLong(); mBatteryTimeToFullSeconds = in.readLong(); + MeasuredEnergyStats.readSummaryFromParcel(mBatteryMeasuredEnergyStats, in); + mStartCount++; mScreenState = Display.STATE_UNKNOWN; @@ -14997,6 +15090,8 @@ public class BatteryStatsImpl extends BatteryStats { out.writeLong(mNextMaxDailyDeadlineMs); out.writeLong(mBatteryTimeToFullSeconds); + MeasuredEnergyStats.writeSummaryToParcel(mBatteryMeasuredEnergyStats, out); + mScreenOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS); mScreenDozeTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS); for (int i=0; i<NUM_SCREEN_BRIGHTNESS_BINS; i++) { @@ -15581,6 +15676,10 @@ public class BatteryStatsImpl extends BatteryStats { mLastWriteTimeMs = in.readLong(); mBatteryTimeToFullSeconds = in.readLong(); + if (in.readInt() != 0) { + mBatteryMeasuredEnergyStats = new MeasuredEnergyStats(in); + } + mRpmStats.clear(); int NRPMS = in.readInt(); for (int irpm = 0; irpm < NRPMS; irpm++) { @@ -15783,6 +15882,13 @@ public class BatteryStatsImpl extends BatteryStats { out.writeLong(mLastWriteTimeMs); out.writeLong(mBatteryTimeToFullSeconds); + if (mBatteryMeasuredEnergyStats != null) { + out.writeInt(1); + mBatteryMeasuredEnergyStats.writeToParcel(out); + } else { + out.writeInt(0); + } + out.writeInt(mRpmStats.size()); for (Map.Entry<String, SamplingTimer> ent : mRpmStats.entrySet()) { SamplingTimer rpmt = ent.getValue(); @@ -16036,5 +16142,8 @@ public class BatteryStatsImpl extends BatteryStats { pw.println(); dumpConstantsLocked(pw); + + pw.println(); + dumpMeasuredEnergyStatsLocked(pw); } } diff --git a/core/java/com/android/internal/power/MeasuredEnergyArray.java b/core/java/com/android/internal/power/MeasuredEnergyArray.java new file mode 100644 index 000000000000..1f6dc260a197 --- /dev/null +++ b/core/java/com/android/internal/power/MeasuredEnergyArray.java @@ -0,0 +1,66 @@ +/* + * 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.internal.power; + + +import android.annotation.IntDef; + +import com.android.internal.os.RailStats; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Interface to provide subsystem energy data. + * TODO: replace this and {@link RailStats} once b/173077356 is done + */ +public interface MeasuredEnergyArray { + int SUBSYSTEM_UNKNOWN = -1; + int SUBSYSTEM_DISPLAY = 0; + int NUMBER_SUBSYSTEMS = 1; + String[] SUBSYSTEM_NAMES = {"display"}; + + + @IntDef(prefix = { "SUBSYSTEM_" }, value = { + SUBSYSTEM_UNKNOWN, + SUBSYSTEM_DISPLAY, + }) + @Retention(RetentionPolicy.SOURCE) + @interface MeasuredEnergySubsystem {} + + /** + * Get the subsystem at an index in array. + * + * @param index into the array. + * @return subsystem. + */ + @MeasuredEnergySubsystem + int getSubsystem(int index); + + /** + * Get the energy (in microjoules) consumed since boot of the subsystem at an index. + * + * @param index into the array. + * @return energy (in microjoules) consumed since boot. + */ + long getEnergy(int index); + + /** + * Return number of subsystems in the array. + */ + int size(); +} diff --git a/core/java/com/android/internal/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/MeasuredEnergyStats.java new file mode 100644 index 000000000000..7b6e0790e14f --- /dev/null +++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java @@ -0,0 +1,307 @@ +/* + * 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.internal.power; + + +import android.annotation.IntDef; +import android.annotation.Nullable; +import android.os.Parcel; +import android.view.Display; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.power.MeasuredEnergyArray.MeasuredEnergySubsystem; + +import java.io.PrintWriter; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; + +/** + * MeasuredEnergyStats adds up the measured energy usage of various subsystems + */ +@VisibleForTesting +public class MeasuredEnergyStats { + private static final long UNAVAILABLE = -1; + private static final long RESET = -2; + + public static final int ENERGY_BUCKET_UNKNOWN = -1; + public static final int ENERGY_BUCKET_SCREEN_ON = 0; + public static final int ENERGY_BUCKET_SCREEN_DOZE = 1; + public static final int ENERGY_BUCKET_SCREEN_OTHER = 2; + public static final int NUMBER_ENERGY_BUCKETS = 3; + private static final String[] ENERGY_BUCKET_NAMES = + {"screen-on", "screen-doze", "screen-other"}; + + @IntDef(prefix = {"ENERGY_BUCKET_"}, value = { + ENERGY_BUCKET_UNKNOWN, + ENERGY_BUCKET_SCREEN_ON, + ENERGY_BUCKET_SCREEN_DOZE, + ENERGY_BUCKET_SCREEN_OTHER, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface EnergyBucket { + } + + /** + * Energy snapshots from the last time each {@link MeasuredEnergySubsystem} was updated. + * An energy snapshot will be set to {@link #UNAVAILABLE} if the subsystem has never been + * updated. + * An energy snapshot will be set to {@link #RESET} on a reset. A subsystems energy will + * need to be updated at least twice to start accumulating energy again. + */ + private final long[] mMeasuredEnergySnapshots = + new long[MeasuredEnergyArray.NUMBER_SUBSYSTEMS]; + + /** + * Total energy in microjoules since the last reset that an {@link EnergyBucket} has + * accumulated. + * + * Warning: Long array is used for access speed. If the number of supported subsystems + * becomes too large, consider using an alternate data structure. + */ + private final long[] mAccumulatedEnergiesMicroJoules = new long[NUMBER_ENERGY_BUCKETS]; + + /** + * Last known screen state. + */ + private int mLastScreenState; + + public MeasuredEnergyStats(MeasuredEnergyArray energyArray, int screenState) { + Arrays.fill(mMeasuredEnergySnapshots, UNAVAILABLE); + + update(energyArray, screenState, false); + } + + public MeasuredEnergyStats(Parcel in) { + in.readLongArray(mAccumulatedEnergiesMicroJoules); + } + + /** + * Constructor for creating a temp MeasuredEnergyStats + * See {@link #readSummaryFromParcel(MeasuredEnergyStats, Parcel)} + */ + private MeasuredEnergyStats() { + Arrays.fill(mMeasuredEnergySnapshots, UNAVAILABLE); + } + + /** Write to parcel */ + public void writeToParcel(Parcel out) { + out.writeLongArray(mAccumulatedEnergiesMicroJoules); + } + + /** + * Read from summary parcel. + * Note: Measured subsystem availability may be different from when the summary parcel was + * written. + */ + private void readSummaryFromParcel(Parcel in) { + final int size = in.readInt(); + for (int i = 0; i < size; i++) { + final int bucket = in.readInt(); + final long energyUJ = in.readLong(); + + final int subsystem = getSubsystem(bucket); + // Only accept the summary energy if subsystem is currently available + if (subsystem != MeasuredEnergyArray.SUBSYSTEM_UNKNOWN + && mMeasuredEnergySnapshots[subsystem] != UNAVAILABLE) { + mAccumulatedEnergiesMicroJoules[bucket] = energyUJ; + } + } + } + + /** + * Write to summary parcel. + * Note: Measured subsystem availability may be different when the summary parcel is read. + * Note: {@link com.android.internal.os.BatteryStatsImpl#VERSION} must be updated if summary + * parceling changes. + */ + private void writeSummaryToParcel(Parcel out) { + final int sizePos = out.dataPosition(); + out.writeInt(0); + int size = 0; + // Write only the buckets with reported energy + for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { + final int subsystem = getSubsystem(i); + if (mMeasuredEnergySnapshots[subsystem] == UNAVAILABLE) continue; + + out.writeInt(i); + out.writeLong(mAccumulatedEnergiesMicroJoules[i]); + size++; + } + final int currPos = out.dataPosition(); + out.setDataPosition(sizePos); + out.writeInt(size); + out.setDataPosition(currPos); + } + + /** + * Update with the latest measured energies and device state. + * + * @param energyArray measured energy array for some subsystems. + * @param screenState screen state to attribute disaply energy to after this update. + * @param accumulate whether or not to accumulate the latest energy + */ + public void update(MeasuredEnergyArray energyArray, int screenState, boolean accumulate) { + final int size = energyArray.size(); + if (!accumulate) { + for (int i = 0; i < size; i++) { + final int subsystem = energyArray.getSubsystem(i); + mMeasuredEnergySnapshots[subsystem] = energyArray.getEnergy(i); + } + } else { + for (int i = 0; i < size; i++) { + final int subsystem = energyArray.getSubsystem(i); + final long newEnergyUJ = energyArray.getEnergy(i); + final long oldEnergyUJ = mMeasuredEnergySnapshots[subsystem]; + mMeasuredEnergySnapshots[subsystem] = newEnergyUJ; + + // This is the first valid energy, skip accumulating the delta + if (oldEnergyUJ < 0) continue; + final long deltaUJ = newEnergyUJ - oldEnergyUJ; + + final int bucket = getEnergyBucket(subsystem, mLastScreenState); + mAccumulatedEnergiesMicroJoules[bucket] += deltaUJ; + } + } + mLastScreenState = screenState; + } + + /** + * Map {@link MeasuredEnergySubsystem} and device state to an {@link EnergyBucket}. + * Keep in sync with {@link #getSubsystem} + */ + @EnergyBucket + private int getEnergyBucket(@MeasuredEnergySubsystem int subsystem, int screenState) { + switch (subsystem) { + case MeasuredEnergyArray.SUBSYSTEM_DISPLAY: + if (Display.isOnState(screenState)) { + return ENERGY_BUCKET_SCREEN_ON; + } else if (Display.isDozeState(screenState)) { + return ENERGY_BUCKET_SCREEN_DOZE; + } else { + return ENERGY_BUCKET_SCREEN_OTHER; + } + default: + return ENERGY_BUCKET_UNKNOWN; + } + } + + /** + * Map {@link EnergyBucket} to a {@link MeasuredEnergySubsystem}. + * Keep in sync with {@link #getEnergyBucket} + */ + @MeasuredEnergySubsystem + private int getSubsystem(@EnergyBucket int bucket) { + switch (bucket) { + case ENERGY_BUCKET_SCREEN_ON: //fallthrough + case ENERGY_BUCKET_SCREEN_DOZE: //fallthrough + case ENERGY_BUCKET_SCREEN_OTHER: + return MeasuredEnergyArray.SUBSYSTEM_DISPLAY; + default: + return MeasuredEnergyArray.SUBSYSTEM_UNKNOWN; + } + } + + /** + * Check if a subsystem's measured energy is available. + * @param subsystem which subsystem. + * @return true if subsystem is avaiable. + */ + public boolean hasSubsystem(@MeasuredEnergySubsystem int subsystem) { + return mMeasuredEnergySnapshots[subsystem] != UNAVAILABLE; + } + + /** + * Return accumulated energy (in microjoules) since last reset. + */ + public long getAccumulatedBucketEnergy(@EnergyBucket int bucket) { + return mAccumulatedEnergiesMicroJoules[bucket]; + } + + /** + * Populate a MeasuredEnergyStats from a parcel. If the stats is null, consume and + * ignore the parcelled data. + */ + public static void readSummaryFromParcel(@Nullable MeasuredEnergyStats stats, Parcel in) { + // Check if any MeasuredEnergyStats exists on the parcel + if (in.readInt() == 0) return; + + // If stats is null, create a placeholder MeasuredEnergyStats to consume the parcel data + final MeasuredEnergyStats mes = stats != null ? stats : new MeasuredEnergyStats(); + mes.readSummaryFromParcel(in); + } + + /** + * Write a MeasuredEnergyStats to a parcel. If the stats is null, just write a 0. + */ + public static void writeSummaryToParcel(@Nullable MeasuredEnergyStats stats, + Parcel dest) { + if (stats == null) { + dest.writeInt(0); + return; + } + dest.writeInt(1); + stats.writeSummaryToParcel(dest); + } + + /** + * Reset accumulated energy. + */ + public void reset() { + for (int i = 0; i < MeasuredEnergyArray.NUMBER_SUBSYSTEMS; i++) { + // Leave subsystems marked as unavailable alone. + if (mMeasuredEnergySnapshots[i] == UNAVAILABLE) continue; + mMeasuredEnergySnapshots[i] = RESET; + } + Arrays.fill(mAccumulatedEnergiesMicroJoules, 0); + } + + /** + * Dump debug data. + */ + public void dump(PrintWriter pw) { + pw.println("Measured energy snapshot (microjoules):"); + pw.print(" "); + for (int i = 0; i < MeasuredEnergyArray.NUMBER_SUBSYSTEMS; i++) { + final long energyUJ = mMeasuredEnergySnapshots[i]; + if (energyUJ == UNAVAILABLE) continue; + pw.print(MeasuredEnergyArray.SUBSYSTEM_NAMES[i]); + pw.print(" : "); + if (energyUJ == RESET) { + pw.print("reset"); + } else { + pw.print(energyUJ); + } + if (i != MeasuredEnergyArray.NUMBER_SUBSYSTEMS - 1) { + pw.print(", "); + } + } + pw.println(); + + pw.println("Accumulated energy since last reset (microjoules):"); + pw.print(" "); + for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { + pw.print(ENERGY_BUCKET_NAMES[i]); + pw.print(" : "); + pw.print(mAccumulatedEnergiesMicroJoules[i]); + if (i != NUMBER_ENERGY_BUCKETS - 1) { + pw.print(", "); + } + } + pw.println(); + } +} diff --git a/core/java/com/android/internal/util/function/pooled/OWNERS b/core/java/com/android/internal/util/function/pooled/OWNERS new file mode 100644 index 000000000000..da723b3b67da --- /dev/null +++ b/core/java/com/android/internal/util/function/pooled/OWNERS @@ -0,0 +1 @@ +eugenesusla@google.com
\ No newline at end of file diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp index e7e9c31ef90e..6337680147a5 100644 --- a/core/jni/android_view_DisplayEventReceiver.cpp +++ b/core/jni/android_view_DisplayEventReceiver.cpp @@ -41,14 +41,21 @@ static struct { jmethodID dispatchVsync; jmethodID dispatchHotplug; jmethodID dispatchConfigChanged; + jmethodID dispatchFrameRateOverrides; + + struct { + jclass clazz; + jmethodID init; + } frameRateOverrideClassInfo; + } gDisplayEventReceiverClassInfo; class NativeDisplayEventReceiver : public DisplayEventDispatcher { public: - NativeDisplayEventReceiver(JNIEnv* env, - jobject receiverWeak, const sp<MessageQueue>& messageQueue, jint vsyncSource, - jint configChanged); + NativeDisplayEventReceiver(JNIEnv* env, jobject receiverWeak, + const sp<MessageQueue>& messageQueue, jint vsyncSource, + jint eventRegistration); void dispose(); @@ -64,16 +71,17 @@ private: void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId, bool connected) override; void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId, int32_t configId, nsecs_t vsyncPeriod) override; + void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId, + std::vector<FrameRateOverride> overrides) override; void dispatchNullEvent(nsecs_t timestamp, PhysicalDisplayId displayId) override {} }; - -NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env, - jobject receiverWeak, const sp<MessageQueue>& messageQueue, jint vsyncSource, - jint configChanged) : - DisplayEventDispatcher(messageQueue->getLooper(), - static_cast<ISurfaceComposer::VsyncSource>(vsyncSource), - static_cast<ISurfaceComposer::ConfigChanged>(configChanged)), +NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env, jobject receiverWeak, + const sp<MessageQueue>& messageQueue, + jint vsyncSource, jint eventRegistration) + : DisplayEventDispatcher(messageQueue->getLooper(), + static_cast<ISurfaceComposer::VsyncSource>(vsyncSource), + static_cast<ISurfaceComposer::EventRegistration>(eventRegistration)), mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)), mMessageQueue(messageQueue) { ALOGV("receiver %p ~ Initializing display event receiver.", this); @@ -137,16 +145,48 @@ void NativeDisplayEventReceiver::dispatchConfigChanged( mMessageQueue->raiseAndClearException(env, "dispatchConfigChanged"); } -static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, - jobject messageQueueObj, jint vsyncSource, jint configChanged) { +void NativeDisplayEventReceiver::dispatchFrameRateOverrides( + nsecs_t timestamp, PhysicalDisplayId displayId, std::vector<FrameRateOverride> overrides) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + + ScopedLocalRef<jobject> receiverObj(env, jniGetReferent(env, mReceiverWeakGlobal)); + if (receiverObj.get()) { + ALOGV("receiver %p ~ Invoking FrameRateOverride handler.", this); + const auto frameRateOverrideClass = + gDisplayEventReceiverClassInfo.frameRateOverrideClassInfo.clazz; + const auto frameRateOverrideInit = + gDisplayEventReceiverClassInfo.frameRateOverrideClassInfo.init; + auto frameRateOverrideInitObject = + env->NewObject(frameRateOverrideClass, frameRateOverrideInit, 0, 0); + auto frameRateOverrideArray = env->NewObjectArray(overrides.size(), frameRateOverrideClass, + frameRateOverrideInitObject); + for (size_t i = 0; i < overrides.size(); i++) { + auto FrameRateOverrideObject = + env->NewObject(frameRateOverrideClass, frameRateOverrideInit, overrides[i].uid, + overrides[i].frameRateHz); + env->SetObjectArrayElement(frameRateOverrideArray, i, FrameRateOverrideObject); + } + + env->CallVoidMethod(receiverObj.get(), + gDisplayEventReceiverClassInfo.dispatchFrameRateOverrides, timestamp, + displayId.value, frameRateOverrideArray); + ALOGV("receiver %p ~ Returned from FrameRateOverride handler.", this); + } + + mMessageQueue->raiseAndClearException(env, "dispatchConfigChanged"); +} + +static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject messageQueueObj, + jint vsyncSource, jint eventRegistration) { sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj); if (messageQueue == NULL) { jniThrowRuntimeException(env, "MessageQueue is not initialized."); return 0; } - sp<NativeDisplayEventReceiver> receiver = new NativeDisplayEventReceiver(env, - receiverWeak, messageQueue, vsyncSource, configChanged); + sp<NativeDisplayEventReceiver> receiver = + new NativeDisplayEventReceiver(env, receiverWeak, messageQueue, vsyncSource, + eventRegistration); status_t status = receiver->initialize(); if (status) { String8 message; @@ -205,6 +245,18 @@ int register_android_view_DisplayEventReceiver(JNIEnv* env) { gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", "(JJZ)V"); gDisplayEventReceiverClassInfo.dispatchConfigChanged = GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchConfigChanged", "(JJI)V"); + gDisplayEventReceiverClassInfo.dispatchFrameRateOverrides = + GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, + "dispatchFrameRateOverrides", + "(JJ[Landroid/view/DisplayEventReceiver$FrameRateOverride;)V"); + + jclass frameRateOverrideClazz = + FindClassOrDie(env, "android/view/DisplayEventReceiver$FrameRateOverride"); + gDisplayEventReceiverClassInfo.frameRateOverrideClassInfo.clazz = + MakeGlobalRefOrDie(env, frameRateOverrideClazz); + gDisplayEventReceiverClassInfo.frameRateOverrideClassInfo.init = + GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.frameRateOverrideClassInfo.clazz, + "<init>", "(IF)V"); return res; } diff --git a/core/proto/OWNERS b/core/proto/OWNERS index 542d26fa233e..5eeeb048e6d6 100644 --- a/core/proto/OWNERS +++ b/core/proto/OWNERS @@ -25,3 +25,18 @@ hyunyoungs@google.com # Graphics stats jreck@google.com + +# Temporary Block to assist in migration +# Bug: 143080132 +per-file *enums.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com +per-file *media_output_enum.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com +per-file *networkcapabilities.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com +per-file *data_stall_event.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com +per-file *procstats_enum.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com +per-file *usb.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com +per-file *network_stack.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com +per-file *tethering.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com +per-file *dns_resolver.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com +per-file *device_policy.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com +per-file *launcher.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com +per-file *mediametrics.proto = baligh@google.com, yro@google.com, jeffreyhuang@google.com diff --git a/core/proto/android/server/accessibility.proto b/core/proto/android/server/accessibility.proto new file mode 100644 index 000000000000..7fe7f0d5f3cc --- /dev/null +++ b/core/proto/android/server/accessibility.proto @@ -0,0 +1,27 @@ +/* + * 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. + */ + +syntax = "proto2"; + +import "frameworks/base/core/proto/android/typedef.proto"; + +package com.android.server.accessibility; + +option java_multiple_files = true; + +/* The proto format trace entry for accessibility service */ +message AccessibilityDumpProto { +} diff --git a/core/proto/android/server/accessibilitytrace.proto b/core/proto/android/server/accessibilitytrace.proto new file mode 100644 index 000000000000..1fc4a01936b1 --- /dev/null +++ b/core/proto/android/server/accessibilitytrace.proto @@ -0,0 +1,62 @@ +/* + * 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. + */ + +syntax = "proto2"; + +import "frameworks/base/core/proto/android/server/accessibility.proto"; +import "frameworks/base/core/proto/android/server/windowmanagerservice.proto"; + +package com.android.server.accessibility; + +option java_multiple_files = true; + +/* represents a file full of accessibility trace entries. + Encoded, it should start with 0x9 0x41 0x31 0x31 0x59 0x54 0x52 0x41 0x43 (.A11YTRAC), such + that they can be easily identified. */ +message AccessibilityTraceFileProto { + + /* constant; MAGIC_NUMBER = (long) MAGIC_NUMBER_H << 32 | MagicNumber.MAGIC_NUMBER_L + (this is needed because enums have to be 32 bits and there's no nice way to put 64bit + constants into .proto files. */ + enum MagicNumber { + INVALID = 0; + MAGIC_NUMBER_L = 0x59313141; /* A11Y (little-endian ASCII) */ + MAGIC_NUMBER_H = 0x43415254; /* TRAC (little-endian ASCII) */ + } + + optional fixed64 magic_number = 1; /* Must be the first field, set to value in MagicNumber */ + repeated AccessibilityTraceProto entry = 2; +} + +/* one accessibility trace entry. */ +message AccessibilityTraceProto { + /* required: elapsed realtime in nanos since boot of when this entry was logged */ + optional fixed64 elapsed_realtime_nanos = 1; + optional string calendar_time = 2; + + optional string process_name = 3; + optional string thread_id_name = 4; + + /* where the trace originated */ + optional string where = 5; + + optional string calling_pkg = 6; + optional string calling_params = 7; + optional string calling_stacks = 8; + + optional AccessibilityDumpProto accessibility_service = 9; + optional com.android.server.wm.WindowManagerServiceDumpProto window_manager_service = 10; +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index ea667277efee..c9e8f719ebc5 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2557,6 +2557,10 @@ <permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" android:protectionLevel="signature|privileged|vendorPrivileged|oem|verifier" /> + <!-- @SystemApi @hide Allows an application to start foreground services from background --> + <permission android:name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND" + android:protectionLevel="signature|privileged|vendorPrivileged|oem|verifier" /> + <!-- @SystemApi Must be required by activities that handle the intent action {@link Intent#ACTION_SEND_SHOW_SUSPENDED_APP_DETAILS}. This is for use by apps that hold {@link Manifest.permission#SUSPEND_APPS} to interact with the system. diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index c32e8dc94486..85b19e7de04b 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -303,6 +303,9 @@ <!-- Additional flag from base permission type: this permission will be granted to the retail demo app, as defined by the OEM. --> <flag name="retailDemo" value="0x1000000" /> + <!-- Additional flag from base permission type: this permission will be granted to the + recents app. --> + <flag name="recents" value="0x2000000" /> </attr> <!-- Flags indicating more context for a permission group. --> diff --git a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java index c66bac6cc335..82d066f6ab16 100644 --- a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java +++ b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java @@ -23,7 +23,6 @@ import static org.junit.Assert.assertTrue; import android.content.Context; import android.content.res.AssetManager; -import android.graphics.fonts.Font; import android.graphics.fonts.FontCustomizationParser; import android.graphics.fonts.FontFamily; import android.graphics.fonts.SystemFonts; @@ -49,7 +48,6 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.StandardCopyOption; -import java.util.ArrayList; import java.util.Locale; @SmallTest @@ -133,14 +131,14 @@ public class TypefaceSystemFallbackTest { private static void buildSystemFallback(String xml, FontCustomizationParser.Result oemCustomization, ArrayMap<String, Typeface> fontMap, ArrayMap<String, FontFamily[]> fallbackMap) { - final ArrayList<Font> availableFonts = new ArrayList<>(); try (FileOutputStream fos = new FileOutputStream(TEST_FONTS_XML)) { fos.write(xml.getBytes(Charset.forName("UTF-8"))); } catch (IOException e) { throw new RuntimeException(e); } + final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(TEST_FONTS_XML, - TEST_FONT_DIR, oemCustomization, fallbackMap, availableFonts); + TEST_FONT_DIR, oemCustomization, fallbackMap); Typeface.initSystemDefaultTypefaces(fontMap, fallbackMap, aliases); } @@ -156,12 +154,11 @@ public class TypefaceSystemFallbackTest { public void testBuildSystemFallback() { final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final ArrayList<Font> availableFonts = new ArrayList<>(); final FontCustomizationParser.Result oemCustomization = new FontCustomizationParser.Result(); final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(SYSTEM_FONTS_XML, - SYSTEM_FONT_DIR, oemCustomization, fallbackMap, availableFonts); + SYSTEM_FONT_DIR, oemCustomization, fallbackMap); assertNotNull(aliases); assertFalse(fallbackMap.isEmpty()); diff --git a/core/tests/coretests/src/android/graphics/TypefaceTest.java b/core/tests/coretests/src/android/graphics/TypefaceTest.java index 5fa8c4fbaa56..392c6b7199a5 100644 --- a/core/tests/coretests/src/android/graphics/TypefaceTest.java +++ b/core/tests/coretests/src/android/graphics/TypefaceTest.java @@ -23,8 +23,11 @@ import static org.junit.Assert.assertTrue; import android.content.Context; import android.content.res.AssetManager; import android.content.res.Resources; +import android.graphics.fonts.FontFamily; import android.graphics.fonts.SystemFonts; import android.os.SharedMemory; +import android.text.FontConfig; +import android.util.Pair; import androidx.test.InstrumentationRegistry; import androidx.test.filters.LargeTest; @@ -195,8 +198,9 @@ public class TypefaceTest { @Test public void testSerialize() throws Exception { HashMap<String, Typeface> systemFontMap = new HashMap<>(); - Typeface.initSystemDefaultTypefaces(systemFontMap, SystemFonts.getRawSystemFallbackMap(), - SystemFonts.getAliases()); + Pair<FontConfig.Alias[], Map<String, FontFamily[]>> res = + SystemFonts.initializePreinstalledFonts(); + Typeface.initSystemDefaultTypefaces(systemFontMap, res.second, res.first); SharedMemory sharedMemory = Typeface.serializeFontMap(systemFontMap); Map<String, Typeface> copiedFontMap = Typeface.deserializeFontMap(sharedMemory.mapReadOnly().order(ByteOrder.BIG_ENDIAN)); diff --git a/core/tests/coretests/src/android/text/FontFallbackSetup.java b/core/tests/coretests/src/android/text/FontFallbackSetup.java index cc51ec3da1fd..64e6f82d82af 100644 --- a/core/tests/coretests/src/android/text/FontFallbackSetup.java +++ b/core/tests/coretests/src/android/text/FontFallbackSetup.java @@ -20,7 +20,6 @@ import android.annotation.NonNull; import android.content.Context; import android.content.res.AssetManager; import android.graphics.Typeface; -import android.graphics.fonts.Font; import android.graphics.fonts.FontCustomizationParser; import android.graphics.fonts.FontFamily; import android.graphics.fonts.SystemFonts; @@ -35,7 +34,6 @@ import java.io.InputStream; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.StandardCopyOption; -import java.util.ArrayList; public class FontFallbackSetup implements AutoCloseable { private final String[] mTestFontFiles; @@ -78,11 +76,10 @@ public class FontFallbackSetup implements AutoCloseable { } final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); - final ArrayList<Font> availableFonts = new ArrayList<>(); final FontCustomizationParser.Result oemCustomization = new FontCustomizationParser.Result(); final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(testFontsXml, - mTestFontsDir, oemCustomization, fallbackMap, availableFonts); + mTestFontsDir, oemCustomization, fallbackMap); Typeface.initSystemDefaultTypefaces(mFontMap, fallbackMap, aliases); } diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java index 75dd7fb82f30..ff1d965c643f 100644 --- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java +++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java @@ -192,8 +192,8 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl { } @Override - public Future<?> scheduleCpuSyncDueToScreenStateChange( - boolean onBattery, boolean onBatteryScreenOff) { + public Future<?> scheduleSyncDueToScreenStateChange( + int flag, boolean onBattery, boolean onBatteryScreenOff, int screenState) { return null; } diff --git a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java new file mode 100644 index 000000000000..88295efa90b9 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java @@ -0,0 +1,278 @@ +/* + * 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.internal.power; + +import static com.android.internal.power.MeasuredEnergyArray.NUMBER_SUBSYSTEMS; +import static com.android.internal.power.MeasuredEnergyArray.SUBSYSTEM_DISPLAY; +import static com.android.internal.power.MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_DOZE; +import static com.android.internal.power.MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON; +import static com.android.internal.power.MeasuredEnergyStats.NUMBER_ENERGY_BUCKETS; + +import static org.junit.Assert.assertEquals; + +import android.os.Parcel; +import android.view.Display; + +import org.junit.Before; +import org.junit.Test; + +/** + * Test class for {@link MeasuredEnergyStats}. + * + * To run the tests, use + * atest FrameworksCoreTests:com.android.internal.power.MeasuredEnergyStatsTest + */ +public class MeasuredEnergyStatsTest { + private MeasuredEnergyStats mStats; + private int[] mAllSubsystems = new int[NUMBER_SUBSYSTEMS]; + private long[] mCurrentSubsystemEnergyUJ = new long[NUMBER_SUBSYSTEMS]; + + MeasuredEnergyArray mMeasuredEnergyArray = new MeasuredEnergyArray() { + @Override + public int getSubsystem(int index) { + return mAllSubsystems[index]; + } + + @Override + public long getEnergy(int index) { + return mCurrentSubsystemEnergyUJ[index]; + } + + @Override + public int size() { + return NUMBER_SUBSYSTEMS; + } + }; + + @Before + public void setUp() { + // Populate all supported subsystems indexes and arbitrary starting energy values. + mAllSubsystems[SUBSYSTEM_DISPLAY] = SUBSYSTEM_DISPLAY; + mCurrentSubsystemEnergyUJ[SUBSYSTEM_DISPLAY] = 111; + + mStats = new MeasuredEnergyStats(mMeasuredEnergyArray, Display.STATE_UNKNOWN); + } + + @Test + public void testReadWriteParcel() { + // update with some arbitrary data + mCurrentSubsystemEnergyUJ[SUBSYSTEM_DISPLAY] += 222; + mStats.update(mMeasuredEnergyArray, Display.STATE_ON, true); + mCurrentSubsystemEnergyUJ[SUBSYSTEM_DISPLAY] += 321; + mStats.update(mMeasuredEnergyArray, Display.STATE_DOZE, true); + mCurrentSubsystemEnergyUJ[SUBSYSTEM_DISPLAY] += 456; + mStats.update(mMeasuredEnergyArray, Display.STATE_OFF, true); + + final Parcel parcel = Parcel.obtain(); + mStats.writeToParcel(parcel); + + parcel.setDataPosition(0); + MeasuredEnergyStats stats = new MeasuredEnergyStats(parcel); + + for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { + assertEquals(mStats.getAccumulatedBucketEnergy(i), stats.getAccumulatedBucketEnergy(i)); + } + parcel.recycle(); + } + + @Test + public void testReadWriteSummaryParcel() { + // update with some arbitrary data + mCurrentSubsystemEnergyUJ[SUBSYSTEM_DISPLAY] += 222; + mStats.update(mMeasuredEnergyArray, Display.STATE_ON, true); + mCurrentSubsystemEnergyUJ[SUBSYSTEM_DISPLAY] += 321; + mStats.update(mMeasuredEnergyArray, Display.STATE_DOZE, true); + mCurrentSubsystemEnergyUJ[SUBSYSTEM_DISPLAY] += 456; + mStats.update(mMeasuredEnergyArray, Display.STATE_OFF, true); + + final Parcel parcel = Parcel.obtain(); + MeasuredEnergyStats.writeSummaryToParcel(mStats, parcel); + + parcel.setDataPosition(0); + MeasuredEnergyStats stats = new MeasuredEnergyStats(mMeasuredEnergyArray, + Display.STATE_UNKNOWN); + MeasuredEnergyStats.readSummaryFromParcel(stats, parcel); + + for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { + assertEquals(mStats.getAccumulatedBucketEnergy(i), stats.getAccumulatedBucketEnergy(i)); + } + parcel.recycle(); + } + + @Test + public void testDisplayStateEnergyAttribution() { + long expectedScreenOnEnergy = 0; + long expectedScreenDozeEnergy = 0; + + // Display energy should be attributed to the previous screen state. + mStats.update(mMeasuredEnergyArray, Display.STATE_UNKNOWN, true); + + incrementDisplayState(222, Display.STATE_ON, true, expectedScreenOnEnergy, + expectedScreenDozeEnergy); + + expectedScreenOnEnergy += 321; + incrementDisplayState(321, Display.STATE_DOZE, true, expectedScreenOnEnergy, + expectedScreenDozeEnergy); + + expectedScreenDozeEnergy += 456; + incrementDisplayState(456, Display.STATE_OFF, true, expectedScreenOnEnergy, + expectedScreenDozeEnergy); + + incrementDisplayState(1111, Display.STATE_DOZE_SUSPEND, true, expectedScreenOnEnergy, + expectedScreenDozeEnergy); + + expectedScreenDozeEnergy += 2345; + incrementDisplayState(2345, Display.STATE_ON_SUSPEND, true, expectedScreenOnEnergy, + expectedScreenDozeEnergy); + + expectedScreenOnEnergy += 767; + incrementDisplayState(767, Display.STATE_VR, true, expectedScreenOnEnergy, + expectedScreenDozeEnergy); + + expectedScreenOnEnergy += 999; + incrementDisplayState(999, Display.STATE_UNKNOWN, true, expectedScreenOnEnergy, + expectedScreenDozeEnergy); + } + + @Test + public void testDisplayStateEnergyAttribution_notRunning() { + long expectedScreenOnEnergy = 0; + long expectedScreenDozeEnergy = 0; + + // Display energy should be attributed to the previous screen state. + mStats.update(mMeasuredEnergyArray, Display.STATE_UNKNOWN, true); + + incrementDisplayState(222, Display.STATE_ON, true, expectedScreenOnEnergy, + expectedScreenDozeEnergy); + + expectedScreenOnEnergy += 321; + incrementDisplayState(321, Display.STATE_DOZE, true, expectedScreenOnEnergy, + expectedScreenDozeEnergy); + + // Updates after this point should not result in energy accumulation. + incrementDisplayState(456, Display.STATE_OFF, false, expectedScreenOnEnergy, + expectedScreenDozeEnergy); + + incrementDisplayState(1111, Display.STATE_DOZE_SUSPEND, false, expectedScreenOnEnergy, + expectedScreenDozeEnergy); + + incrementDisplayState(2345, Display.STATE_ON_SUSPEND, false, expectedScreenOnEnergy, + expectedScreenDozeEnergy); + + incrementDisplayState(767, Display.STATE_VR, false, expectedScreenOnEnergy, + expectedScreenDozeEnergy); + + // Resume energy accumulation. + expectedScreenOnEnergy += 999; + incrementDisplayState(999, Display.STATE_UNKNOWN, true, expectedScreenOnEnergy, + expectedScreenDozeEnergy); + } + + @Test + public void testReset() { + // update with some arbitrary data. + mCurrentSubsystemEnergyUJ[SUBSYSTEM_DISPLAY] += 222; + mStats.update(mMeasuredEnergyArray, Display.STATE_ON, true); + mCurrentSubsystemEnergyUJ[SUBSYSTEM_DISPLAY] += 321; + mStats.update(mMeasuredEnergyArray, Display.STATE_DOZE, true); + mCurrentSubsystemEnergyUJ[SUBSYSTEM_DISPLAY] += 456; + mStats.update(mMeasuredEnergyArray, Display.STATE_OFF, true); + + mStats.reset(); + // All energy should be reset to 0 + for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { + assertEquals(mStats.getAccumulatedBucketEnergy(i), 0); + } + + // Increment all subsystem energy by some arbitrary amount and update + for (int i = 0; i < NUMBER_SUBSYSTEMS; i++) { + mCurrentSubsystemEnergyUJ[i] += 100 * i; + } + mStats.update(mMeasuredEnergyArray, Display.STATE_OFF, true); + + // All energy should still be 0 after the first post-reset update. + for (int i = 0; i < NUMBER_ENERGY_BUCKETS; i++) { + assertEquals(mStats.getAccumulatedBucketEnergy(i), 0); + } + + // Energy accumulation should continue like normal. + long expectedScreenOnEnergy = 0; + long expectedScreenDozeEnergy = 0; + incrementDisplayState(222, Display.STATE_ON, true, expectedScreenOnEnergy, + expectedScreenDozeEnergy); + + expectedScreenOnEnergy += 321; + incrementDisplayState(321, Display.STATE_DOZE, true, expectedScreenOnEnergy, + expectedScreenDozeEnergy); + + expectedScreenDozeEnergy += 456; + incrementDisplayState(456, Display.STATE_OFF, true, expectedScreenOnEnergy, + expectedScreenDozeEnergy); + } + + @Test + public void testHasSubsystem() { + for (int i = 0; i < NUMBER_SUBSYSTEMS; i++) { + assertEquals(mStats.hasSubsystem(i), true); + } + } + + @Test + public void testHasSubsystem_unavailable() { + // Setup MeasuredEnergyStats with not available subsystems. + int[] subsystems = new int[0]; + long[] energies = new long[0]; + MeasuredEnergyArray measuredEnergyArray = new MeasuredEnergyArray() { + @Override + public int getSubsystem(int index) { + return subsystems[index]; + } + + @Override + public long getEnergy(int index) { + return energies[index]; + } + + @Override + public int size() { + return 0; + } + }; + MeasuredEnergyStats stats = new MeasuredEnergyStats(measuredEnergyArray, + Display.STATE_UNKNOWN); + + for (int i = 0; i < NUMBER_SUBSYSTEMS; i++) { + assertEquals(stats.hasSubsystem(i), false); + } + + stats.reset(); + // a reset should not change the state of an unavailable subsystem. + for (int i = 0; i < NUMBER_SUBSYSTEMS; i++) { + assertEquals(stats.hasSubsystem(i), false); + } + } + + private void incrementDisplayState(long deltaEnergy, int nextState, boolean accumulate, + long expectScreenEnergy, long expectedDozeEnergy) { + mCurrentSubsystemEnergyUJ[SUBSYSTEM_DISPLAY] += deltaEnergy; + mStats.update(mMeasuredEnergyArray, nextState, accumulate); + assertEquals(expectScreenEnergy, + mStats.getAccumulatedBucketEnergy(ENERGY_BUCKET_SCREEN_ON)); + assertEquals(expectedDozeEnergy, + mStats.getAccumulatedBucketEnergy(ENERGY_BUCKET_SCREEN_DOZE)); + } +} diff --git a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsData.java b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsData.java index 099ee22b12e4..7aeb86ae0cb1 100644 --- a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsData.java +++ b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsData.java @@ -17,6 +17,7 @@ package com.android.frameworks.core.powerstatsviewer; import android.content.Context; +import android.os.BatteryStats; import android.os.Process; import com.android.internal.os.BatterySipper; @@ -32,6 +33,15 @@ public class PowerStatsData { private static final String[] PACKAGES_SYSTEM = {PACKAGE_MEDIA_PROVIDER, PACKAGE_CALENDAR_PROVIDER, PACKAGE_SYSTEMUI}; + // Temporary placeholder voltage for converting energy to charge + // TODO: remove this when b/173765509 is resolved + private static final double MOCK_NOMINAL_VOLTAGE = 3.7; + + // Unit conversion: + // mAh = uWs * (1/1000)(milli/micro) * (1/Voltage) * (1/3600)(hours/second) + private static final double UJ_2_MAH = + (1.0 / 1000) * (1.0 / MOCK_NOMINAL_VOLTAGE) * (1.0 / 3600); + enum EntryType { POWER, DURATION, @@ -50,6 +60,7 @@ public class PowerStatsData { public PowerStatsData(Context context, BatteryStatsHelper batteryStatsHelper, String powerConsumerId) { List<BatterySipper> usageList = batteryStatsHelper.getUsageList(); + BatteryStats batteryStats = batteryStatsHelper.getStats(); double totalPowerMah = 0; double totalSmearedPowerMah = 0; @@ -125,6 +136,8 @@ public class PowerStatsData { totalVideoTimeMs += sipper.videoTimeMs; } + long totalScreenMeasuredEnergyUJ = batteryStats.getScreenOnEnergy(); + if (requestedPowerConsumer == null) { mPowerConsumerInfo = null; return; @@ -135,10 +148,18 @@ public class PowerStatsData { addEntry("Total power", EntryType.POWER, requestedPowerConsumer.totalSmearedPowerMah, totalSmearedPowerMah); + maybeAddMeasuredEnergyEntry(requestedPowerConsumer.drainType, batteryStats); + addEntry("... excluding system", EntryType.POWER, requestedPowerConsumer.totalSmearedPowerMah, totalPowerExcludeSystemMah); addEntry("Screen, smeared", EntryType.POWER, requestedPowerConsumer.screenPowerMah, totalScreenPower); + if (totalScreenMeasuredEnergyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE) { + final double measuredCharge = UJ_2_MAH * totalScreenMeasuredEnergyUJ; + final double ratio = measuredCharge / totalScreenPower; + addEntry("Screen, smeared (PowerStatsHal adjusted)", EntryType.POWER, + requestedPowerConsumer.screenPowerMah * ratio, measuredCharge); + } addEntry("Other, smeared", EntryType.POWER, requestedPowerConsumer.proportionalSmearMah, totalProportionalSmearMah); addEntry("Excluding smeared", EntryType.POWER, @@ -218,6 +239,28 @@ public class PowerStatsData { mEntries.add(entry); } + private void maybeAddMeasuredEnergyEntry(BatterySipper.DrainType drainType, + BatteryStats batteryStats) { + switch (drainType) { + case AMBIENT_DISPLAY: + final long totalDozeMeasuredEnergyUJ = batteryStats.getScreenDozeEnergy(); + if (totalDozeMeasuredEnergyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE) { + final double measuredCharge = UJ_2_MAH * totalDozeMeasuredEnergyUJ; + addEntry("Measured ambient display power", EntryType.POWER, measuredCharge, + measuredCharge); + } + break; + case SCREEN: + final long totalScreenMeasuredEnergyUJ = batteryStats.getScreenOnEnergy(); + if (totalScreenMeasuredEnergyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE) { + final double measuredCharge = UJ_2_MAH * totalScreenMeasuredEnergyUJ; + addEntry("Measured screen power", EntryType.POWER, measuredCharge, + measuredCharge); + } + break; + } + } + public PowerConsumerInfoHelper.PowerConsumerInfo getPowerConsumerInfo() { return mPowerConsumerInfo; } diff --git a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsViewerActivity.java b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsViewerActivity.java index 78f2b9135d0c..05679101f86a 100644 --- a/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsViewerActivity.java +++ b/core/tests/powertests/PowerStatsViewer/src/com/android/frameworks/core/powerstatsviewer/PowerStatsViewerActivity.java @@ -29,6 +29,7 @@ import android.widget.ImageView; import android.widget.TextView; import androidx.activity.ComponentActivity; +import androidx.activity.result.ActivityResultLauncher; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.loader.app.LoaderManager; @@ -60,6 +61,8 @@ public class PowerStatsViewerActivity extends ComponentActivity { private RecyclerView mPowerStatsDataView; private View mLoadingView; private View mEmptyView; + private ActivityResultLauncher<Void> mStartAppPicker = registerForActivityResult( + PowerConsumerPickerActivity.CONTRACT, this::onApplicationSelected); @Override protected void onCreate(@Nullable Bundle savedInstanceState) { @@ -105,8 +108,7 @@ public class PowerStatsViewerActivity extends ComponentActivity { } private void startAppPicker() { - registerForActivityResult(PowerConsumerPickerActivity.CONTRACT, this::onApplicationSelected) - .launch(null); + mStartAppPicker.launch(null); } private void onApplicationSelected(String powerConsumerId) { diff --git a/data/etc/com.android.provision.xml b/data/etc/com.android.provision.xml index 05404ef73732..d2ea0ec085d3 100644 --- a/data/etc/com.android.provision.xml +++ b/data/etc/com.android.provision.xml @@ -17,5 +17,7 @@ <permissions> <privapp-permissions package="com.android.provision"> <permission name="android.permission.WRITE_SECURE_SETTINGS"/> + <permissionn ame="android.permission.DISPATCH_PROVISIONING_MESSAGE"/> + <permission name="android.permission.MASTER_CLEAR"/> </privapp-permissions> </permissions> diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 52da707565d7..10310237f89b 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -961,6 +961,12 @@ "group": "WM_DEBUG_WINDOW_ORGANIZER", "at": "com\/android\/server\/wm\/TaskOrganizerController.java" }, + "-948446688": { + "message": "Create TaskDisplayArea uid=%d", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_ORGANIZER", + "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java" + }, "-937498525": { "message": "Executing finish of failed to pause activity: %s", "level": "VERBOSE", @@ -1273,6 +1279,12 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/Task.java" }, + "-597091183": { + "message": "Delete TaskDisplayArea uid=%d", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_ORGANIZER", + "at": "com\/android\/server\/wm\/DisplayAreaOrganizerController.java" + }, "-593535526": { "message": "Binding proc %s with config %s", "level": "VERBOSE", @@ -1495,12 +1507,6 @@ "group": "WM_DEBUG_APP_TRANSITIONS", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, - "-371630969": { - "message": "New wallpaper target=%s, oldWallpaper=%s, openingApps=%s, closingApps=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/AppTransitionController.java" - }, "-354571697": { "message": "Existence Changed in transition %d: %s", "level": "VERBOSE", @@ -2101,12 +2107,6 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "355940361": { - "message": "Config is destroying non-running %s", - "level": "VERBOSE", - "group": "WM_DEBUG_CONFIGURATION", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, "371173718": { "message": "finishSync cancel=%b for %s", "level": "VERBOSE", diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java index a7f2739153e1..cb4dd9e8cacd 100644 --- a/graphics/java/android/graphics/HardwareRenderer.java +++ b/graphics/java/android/graphics/HardwareRenderer.java @@ -1163,7 +1163,7 @@ public class HardwareRenderer { // heuristic we don't need to be always 100% correct. Mode activeMode = display.getMode(); nInitDisplayInfo(activeMode.getPhysicalWidth(), activeMode.getPhysicalHeight(), - activeMode.getRefreshRate(), maxRefreshRate, + display.getRefreshRate(), maxRefreshRate, wideColorDataspace.mNativeDataspace, display.getAppVsyncOffsetNanos(), display.getPresentationDeadlineNanos()); diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index 712349aaee57..36ef0a48fa20 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -44,6 +44,7 @@ import android.text.FontConfig; import android.util.Base64; import android.util.LongSparseArray; import android.util.LruCache; +import android.util.Pair; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; @@ -1321,8 +1322,9 @@ public class Typeface { /** @hide */ public static void loadPreinstalledSystemFontMap() { final HashMap<String, Typeface> systemFontMap = new HashMap<>(); - initSystemDefaultTypefaces(systemFontMap, SystemFonts.getRawSystemFallbackMap(), - SystemFonts.getAliases()); + Pair<FontConfig.Alias[], Map<String, FontFamily[]>> pair = + SystemFonts.initializePreinstalledFonts(); + initSystemDefaultTypefaces(systemFontMap, pair.second, pair.first); setSystemFontMap(systemFontMap); } diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java index 95c470634518..3635adc3ce39 100644 --- a/graphics/java/android/graphics/fonts/SystemFonts.java +++ b/graphics/java/android/graphics/fonts/SystemFonts.java @@ -22,7 +22,9 @@ import android.graphics.FontListParser; import android.text.FontConfig; import android.util.ArrayMap; import android.util.Log; +import android.util.Pair; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; @@ -51,9 +53,9 @@ public final class SystemFonts { private SystemFonts() {} // Do not instansiate. - private static final Map<String, FontFamily[]> sSystemFallbackMap; - private static final FontConfig.Alias[] sAliases; - private static final List<Font> sAvailableFonts; + private static final Object LOCK = new Object(); + private static @GuardedBy("sLock") Set<Font> sAvailableFonts; + private static @GuardedBy("sLock") Map<String, FontFamily[]> sFamilyMap; /** * Returns all available font files in the system. @@ -61,29 +63,24 @@ public final class SystemFonts { * @return a set of system fonts */ public static @NonNull Set<Font> getAvailableFonts() { - HashSet<Font> set = new HashSet<>(); - set.addAll(sAvailableFonts); - return set; - } + synchronized (LOCK) { + if (sAvailableFonts != null) { + return sAvailableFonts; + } - /** - * Returns raw system fallback map. - * - * This method is intended to be used only by Typeface static initializer. - * @hide - */ - public static @NonNull Map<String, FontFamily[]> getRawSystemFallbackMap() { - return sSystemFallbackMap; - } + Set<Font> set = new HashSet<>(); - /** - * Returns a list of aliases. - * - * This method is intended to be used only by Typeface static initializer. - * @hide - */ - public static @NonNull FontConfig.Alias[] getAliases() { - return sAliases; + for (FontFamily[] items : sFamilyMap.values()) { + for (FontFamily family : items) { + for (int i = 0; i < family.getSize(); ++i) { + set.add(family.getFont(i)); + } + } + } + + sAvailableFonts = Collections.unmodifiableSet(set); + return sAvailableFonts; + } } private static @Nullable ByteBuffer mmap(@NonNull String fullPath) { @@ -98,8 +95,7 @@ public final class SystemFonts { private static void pushFamilyToFallback(@NonNull FontConfig.Family xmlFamily, @NonNull ArrayMap<String, ArrayList<FontFamily>> fallbackMap, - @NonNull Map<String, ByteBuffer> cache, - @NonNull ArrayList<Font> availableFonts) { + @NonNull Map<String, ByteBuffer> cache) { final String languageTags = xmlFamily.getLanguages(); final int variant = xmlFamily.getVariant(); @@ -123,7 +119,7 @@ public final class SystemFonts { } final FontFamily defaultFamily = defaultFonts.isEmpty() ? null : createFontFamily( - xmlFamily.getName(), defaultFonts, languageTags, variant, cache, availableFonts); + xmlFamily.getName(), defaultFonts, languageTags, variant, cache); // Insert family into fallback map. for (int i = 0; i < fallbackMap.size(); i++) { @@ -135,8 +131,7 @@ public final class SystemFonts { } } else { final FontFamily family = createFontFamily( - xmlFamily.getName(), fallback, languageTags, variant, cache, - availableFonts); + xmlFamily.getName(), fallback, languageTags, variant, cache); if (family != null) { fallbackMap.valueAt(i).add(family); } else if (defaultFamily != null) { @@ -152,8 +147,7 @@ public final class SystemFonts { @NonNull List<FontConfig.Font> fonts, @NonNull String languageTags, @FontConfig.Family.Variant int variant, - @NonNull Map<String, ByteBuffer> cache, - @NonNull ArrayList<Font> availableFonts) { + @NonNull Map<String, ByteBuffer> cache) { if (fonts.size() == 0) { return null; } @@ -187,7 +181,6 @@ public final class SystemFonts { throw new RuntimeException(e); // Never reaches here } - availableFonts.add(font); if (b == null) { b = new FontFamily.Builder(font); } else { @@ -199,12 +192,11 @@ public final class SystemFonts { private static void appendNamedFamily(@NonNull FontConfig.Family xmlFamily, @NonNull HashMap<String, ByteBuffer> bufferCache, - @NonNull ArrayMap<String, ArrayList<FontFamily>> fallbackListMap, - @NonNull ArrayList<Font> availableFonts) { + @NonNull ArrayMap<String, ArrayList<FontFamily>> fallbackListMap) { final String familyName = xmlFamily.getName(); final FontFamily family = createFontFamily( familyName, Arrays.asList(xmlFamily.getFonts()), - xmlFamily.getLanguages(), xmlFamily.getVariant(), bufferCache, availableFonts); + xmlFamily.getLanguages(), xmlFamily.getVariant(), bufferCache); if (family == null) { return; } @@ -227,8 +219,7 @@ public final class SystemFonts { public static FontConfig.Alias[] buildSystemFallback(@NonNull String xmlPath, @NonNull String fontDir, @NonNull FontCustomizationParser.Result oemCustomization, - @NonNull ArrayMap<String, FontFamily[]> fallbackMap, - @NonNull ArrayList<Font> availableFonts) { + @NonNull Map<String, FontFamily[]> fallbackMap) { try { final FileInputStream fontsIn = new FileInputStream(xmlPath); final FontConfig fontConfig = FontListParser.parse(fontsIn, fontDir); @@ -243,12 +234,12 @@ public final class SystemFonts { if (familyName == null) { continue; } - appendNamedFamily(xmlFamily, bufferCache, fallbackListMap, availableFonts); + appendNamedFamily(xmlFamily, bufferCache, fallbackListMap); } for (int i = 0; i < oemCustomization.mAdditionalNamedFamilies.size(); ++i) { appendNamedFamily(oemCustomization.mAdditionalNamedFamilies.get(i), - bufferCache, fallbackListMap, availableFonts); + bufferCache, fallbackListMap); } // Then, add fallback fonts to the each fallback map. @@ -257,7 +248,7 @@ public final class SystemFonts { // The first family (usually the sans-serif family) is always placed immediately // after the primary family in the fallback. if (i == 0 || xmlFamily.getName() == null) { - pushFamilyToFallback(xmlFamily, fallbackListMap, bufferCache, availableFonts); + pushFamilyToFallback(xmlFamily, fallbackListMap, bufferCache); } } @@ -292,14 +283,17 @@ public final class SystemFonts { } } - static { - final ArrayMap<String, FontFamily[]> systemFallbackMap = new ArrayMap<>(); - final ArrayList<Font> availableFonts = new ArrayList<>(); + /** @hide */ + public static @NonNull Pair<FontConfig.Alias[], Map<String, FontFamily[]>> + initializePreinstalledFonts() { final FontCustomizationParser.Result oemCustomization = readFontCustomization("/product/etc/fonts_customization.xml", "/product/fonts/"); - sAliases = buildSystemFallback("/system/etc/fonts.xml", "/system/fonts/", - oemCustomization, systemFallbackMap, availableFonts); - sSystemFallbackMap = Collections.unmodifiableMap(systemFallbackMap); - sAvailableFonts = Collections.unmodifiableList(availableFonts); + Map<String, FontFamily[]> map = new ArrayMap<>(); + FontConfig.Alias[] aliases = buildSystemFallback("/system/etc/fonts.xml", "/system/fonts/", + oemCustomization, map); + synchronized (LOCK) { + sFamilyMap = map; + } + return new Pair(aliases, map); } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt index 3c222e7d8b56..bbf5afcff67a 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt @@ -31,6 +31,10 @@ val TEST_APP_PIP_ACTIVITY_COMPONENT_NAME: ComponentName = ComponentName.createRe TEST_APP_PACKAGE_NAME, ".PipActivity") const val TEST_APP_PIP_ACTIVITY_LABEL = "PipApp" const val TEST_APP_PIP_ACTIVITY_WINDOW_NAME = "PipActivity" +const val TEST_APP_PIP_MENU_ACTION_NO_OP = "No-Op" +const val TEST_APP_PIP_MENU_ACTION_ON = "On" +const val TEST_APP_PIP_MENU_ACTION_OFF = "Off" +const val TEST_APP_PIP_MENU_ACTION_CLEAR = "Clear" // Test App > Ime Activity val TEST_APP_IME_ACTIVITY_COMPONENT_NAME: ComponentName = ComponentName.createRelative( diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt index 532b3de6c99e..e85ba9ef6da2 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt @@ -70,6 +70,12 @@ class PipAppHelper( startButton.click() } + fun checkWithCustomActionsCheckbox() = uiDevice + .findObject(By.res(packageName, "with_custom_actions")) + ?.takeIf { it.isCheckable } + ?.apply { if (!isChecked) click() } + ?: error("'With custom actions' checkbox not found") + fun pauseMedia() = mediaController?.transportControls?.pause() ?: error("No active media session found") diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt index 871732cf7460..4cb6447f7d7e 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt @@ -20,7 +20,12 @@ import android.graphics.Rect import androidx.test.filters.RequiresDevice import androidx.test.uiautomator.UiObject2 import com.android.wm.shell.flicker.SYSTEM_UI_PACKAGE_NAME +import com.android.wm.shell.flicker.TEST_APP_PIP_MENU_ACTION_CLEAR +import com.android.wm.shell.flicker.TEST_APP_PIP_MENU_ACTION_NO_OP +import com.android.wm.shell.flicker.TEST_APP_PIP_MENU_ACTION_OFF +import com.android.wm.shell.flicker.TEST_APP_PIP_MENU_ACTION_ON import com.android.wm.shell.flicker.wait +import org.junit.Assert.assertNull import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test @@ -128,6 +133,7 @@ class TvPipMenuTests : TvPipTestBase() { testApp.clickStartMediaSessionButton() enterPip_openMenu_assertShown() + assertFullscreenAndCloseButtonsAreShown() // PiP menu should contain the Pause button val pauseButton = uiDevice.findTvPipMenuElementWithDescription(pauseButtonDescription) @@ -137,6 +143,7 @@ class TvPipMenuTests : TvPipTestBase() { // When we pause media, the button should change from Pause to Play pauseButton.click() + assertFullscreenAndCloseButtonsAreShown() // PiP menu should contain the Play button now uiDevice.waitForTvPipMenuElementWithDescription(playButtonDescription) ?: fail("\"Play\" button should be shown in Pip menu if there is an active " + @@ -145,10 +152,99 @@ class TvPipMenuTests : TvPipTestBase() { testApp.closePipWindow() } + @Test + fun pipMenu_withCustomActions() { + // Enter PiP with custom actions. + testApp.checkWithCustomActionsCheckbox() + enterPip_openMenu_assertShown() + + // PiP menu should contain "No-Op", "Off" and "Clear" buttons... + uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_NO_OP) + ?: fail("\"No-Op\" button should be shown in Pip menu") + val offButton = uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_OFF) + ?: fail("\"Off\" button should be shown in Pip menu") + uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_CLEAR) + ?: fail("\"Clear\" button should be shown in Pip menu") + // ... and should also contain the "Full screen" and "Close" buttons. + assertFullscreenAndCloseButtonsAreShown() + + offButton.click() + // Invoking the "Off" action should replace it with the "On" action/button and should + // remove the "No-Op" action/button. "Clear" action/button should remain in the menu ... + uiDevice.waitForTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_ON) + ?: fail("\"On\" button should be shown in Pip for a corresponding custom action") + assertNull("\"No-Op\" button should not be shown in Pip menu", + uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_NO_OP)) + val clearButton = + uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_CLEAR) + ?: fail("\"Clear\" button should be shown in Pip menu") + // ... as well as the "Full screen" and "Close" buttons. + assertFullscreenAndCloseButtonsAreShown() + + clearButton.click() + // Invoking the "Clear" action should remove all the custom actions and their corresponding + // buttons, ... + uiDevice.waitUntilTvPipMenuElementWithDescriptionIsGone(TEST_APP_PIP_MENU_ACTION_ON)?.also { + isGone -> if (!isGone) fail("\"On\" button should not be shown in Pip menu") + } + assertNull("\"Off\" button should not be shown in Pip menu", + uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_OFF)) + assertNull("\"Clear\" button should not be shown in Pip menu", + uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_CLEAR)) + assertNull("\"No-Op\" button should not be shown in Pip menu", + uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_NO_OP)) + // ... but the menu should still contain the "Full screen" and "Close" buttons. + assertFullscreenAndCloseButtonsAreShown() + + testApp.closePipWindow() + } + + @Test + fun pipMenu_customActions_override_mediaControls() { + // Start media session before entering PiP with custom actions. + testApp.clickStartMediaSessionButton() + testApp.checkWithCustomActionsCheckbox() + enterPip_openMenu_assertShown() + + // PiP menu should contain "No-Op", "Off" and "Clear" buttons for the custom actions... + uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_NO_OP) + ?: fail("\"No-Op\" button should be shown in Pip menu") + uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_OFF) + ?: fail("\"Off\" button should be shown in Pip menu") + val clearButton = + uiDevice.findTvPipMenuElementWithDescription(TEST_APP_PIP_MENU_ACTION_CLEAR) + ?: fail("\"Clear\" button should be shown in Pip menu") + // ... should also contain the "Full screen" and "Close" buttons, ... + assertFullscreenAndCloseButtonsAreShown() + // ... but should not contain media buttons. + assertNull("\"Play\" button should not be shown in menu when there are custom actions", + uiDevice.findTvPipMenuElementWithDescription(playButtonDescription)) + assertNull("\"Pause\" button should not be shown in menu when there are custom actions", + uiDevice.findTvPipMenuElementWithDescription(pauseButtonDescription)) + + clearButton.click() + // Invoking the "Clear" action should remove all the custom actions, which should bring up + // media buttons... + uiDevice.waitForTvPipMenuElementWithDescription(pauseButtonDescription) + ?: fail("\"Pause\" button should be shown in Pip menu if there is an active " + + "playing media session.") + // ... while the "Full screen" and "Close" buttons should remain in the menu. + assertFullscreenAndCloseButtonsAreShown() + + testApp.closePipWindow() + } + private fun enterPip_openMenu_assertShown(): UiObject2 { testApp.clickEnterPipButton() // Pressing the Window key should bring up Pip menu uiDevice.pressWindowKey() return uiDevice.waitForTvPipMenu() ?: fail("Pip menu should have been shown") } + + private fun assertFullscreenAndCloseButtonsAreShown() { + uiDevice.findTvPipMenuCloseButton() + ?: fail("\"Close PIP\" button should be shown in Pip menu") + uiDevice.findTvPipMenuFullscreenButton() + ?: fail("\"Full screen\" button should be shown in Pip menu") + } }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt index 8db8bc67da14..0732794903b7 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt @@ -58,6 +58,9 @@ fun UiDevice.waitForTvPipMenuElementWithDescription(desc: String): UiObject2? { ?.findObject(buttonSelector) } +fun UiDevice.waitUntilTvPipMenuElementWithDescriptionIsGone(desc: String): Boolean? = + wait(Until.gone(By.copy(tvPipMenuSelector).hasDescendant(By.desc(desc))), WAIT_TIME_MS) + fun UiObject2.isFullscreen(uiDevice: UiDevice): Boolean = visibleBounds.run { height() == uiDevice.displayHeight && width() == uiDevice.displayWidth }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml index b4a4c165cc7b..e5d2f82080a2 100644 --- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml +++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml @@ -28,6 +28,12 @@ android:text="Enter PIP" android:onClick="enterPip"/> + <CheckBox + android:id="@+id/with_custom_actions" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="With custom actions"/> + <RadioGroup android:layout_width="match_parent" android:layout_height="wrap_content" diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/PipActivity.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/PipActivity.java index d2fcd0d31558..909219583bf7 100644 --- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/PipActivity.java +++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/PipActivity.java @@ -25,7 +25,15 @@ import static android.media.session.PlaybackState.STATE_PLAYING; import static android.media.session.PlaybackState.STATE_STOPPED; import android.app.Activity; +import android.app.PendingIntent; import android.app.PictureInPictureParams; +import android.app.RemoteAction; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.res.Configuration; +import android.graphics.drawable.Icon; import android.media.MediaMetadata; import android.media.session.MediaSession; import android.media.session.PlaybackState; @@ -34,6 +42,12 @@ import android.util.Rational; import android.view.View; import android.view.Window; import android.view.WindowManager; +import android.widget.CheckBox; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; public class PipActivity extends Activity { /** @@ -52,7 +66,19 @@ public class PipActivity extends Activity { private static final Rational RATIO_WIDE = new Rational(2, 1); private static final Rational RATIO_TALL = new Rational(1, 2); - private PictureInPictureParams.Builder mPipParamsBuilder; + private static final String PIP_ACTION_NO_OP = "No-Op"; + private static final String PIP_ACTION_OFF = "Off"; + private static final String PIP_ACTION_ON = "On"; + private static final String PIP_ACTION_CLEAR = "Clear"; + private static final String ACTION_NO_OP = "com.android.wm.shell.flicker.testapp.NO_OP"; + private static final String ACTION_SWITCH_OFF = + "com.android.wm.shell.flicker.testapp.SWITCH_OFF"; + private static final String ACTION_SWITCH_ON = "com.android.wm.shell.flicker.testapp.SWITCH_ON"; + private static final String ACTION_CLEAR = "com.android.wm.shell.flicker.testapp.CLEAR"; + + private final PictureInPictureParams.Builder mPipParamsBuilder = + new PictureInPictureParams.Builder() + .setAspectRatio(RATIO_DEFAULT); private MediaSession mMediaSession; private final PlaybackState.Builder mPlaybackStateBuilder = new PlaybackState.Builder() .setActions(ACTION_PLAY | ACTION_PAUSE | ACTION_STOP) @@ -60,6 +86,30 @@ public class PipActivity extends Activity { private PlaybackState mPlaybackState = mPlaybackStateBuilder.build(); private final MediaMetadata.Builder mMediaMetadataBuilder = new MediaMetadata.Builder(); + private final List<RemoteAction> mSwitchOffActions = new ArrayList<>(); + private final List<RemoteAction> mSwitchOnActions = new ArrayList<>(); + private final BroadcastReceiver mCustomActionReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + switch (intent.getAction()) { + case ACTION_SWITCH_ON: + mPipParamsBuilder.setActions(mSwitchOnActions); + break; + case ACTION_SWITCH_OFF: + mPipParamsBuilder.setActions(mSwitchOffActions); + break; + case ACTION_CLEAR: + mPipParamsBuilder.setActions(Collections.emptyList()); + break; + case ACTION_NO_OP: + default: + return; + } + setPictureInPictureParams(mPipParamsBuilder.build()); + } + }; + private boolean mIsReceiverRegistered = false; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -72,9 +122,6 @@ public class PipActivity extends Activity { setContentView(R.layout.activity_pip); - mPipParamsBuilder = new PictureInPictureParams.Builder() - .setAspectRatio(RATIO_DEFAULT); - findViewById(R.id.media_session_start) .setOnClickListener(v -> updateMediaSessionState(STATE_PLAYING)); findViewById(R.id.media_session_stop) @@ -98,9 +145,63 @@ public class PipActivity extends Activity { updateMediaSessionState(STATE_STOPPED); } }); + + // Build two sets of the custom actions. We'll replace one with the other when 'On'/'Off' + // action is invoked. + // The first set consists of 3 actions: 1) Off; 2) No-Op; 3) Clear. + // The second set consists of 2 actions: 1) On; 2) Clear. + // Upon invocation 'Clear' action clear-off all the custom actions, including itself. + final Icon icon = Icon.createWithResource(this, android.R.drawable.ic_menu_help); + final RemoteAction noOpAction = buildRemoteAction(icon, PIP_ACTION_NO_OP, ACTION_NO_OP); + final RemoteAction switchOnAction = + buildRemoteAction(icon, PIP_ACTION_ON, ACTION_SWITCH_ON); + final RemoteAction switchOffAction = + buildRemoteAction(icon, PIP_ACTION_OFF, ACTION_SWITCH_OFF); + final RemoteAction clearAllAction = buildRemoteAction(icon, PIP_ACTION_CLEAR, ACTION_CLEAR); + mSwitchOffActions.addAll(Arrays.asList(switchOnAction, clearAllAction)); + mSwitchOnActions.addAll(Arrays.asList(noOpAction, switchOffAction, clearAllAction)); + } + + @Override + protected void onDestroy() { + if (mIsReceiverRegistered) { + unregisterReceiver(mCustomActionReceiver); + mIsReceiverRegistered = false; + } + super.onDestroy(); + } + + @Override + public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, + Configuration newConfig) { + if (isInPictureInPictureMode && !mIsReceiverRegistered) { + final IntentFilter filter = new IntentFilter(); + filter.addAction(ACTION_NO_OP); + filter.addAction(ACTION_SWITCH_ON); + filter.addAction(ACTION_SWITCH_OFF); + filter.addAction(ACTION_CLEAR); + registerReceiver(mCustomActionReceiver, filter); + + mIsReceiverRegistered = true; + } else if (!isInPictureInPictureMode && mIsReceiverRegistered) { + unregisterReceiver(mCustomActionReceiver); + + mIsReceiverRegistered = false; + } + } + + private RemoteAction buildRemoteAction(Icon icon, String label, String action) { + final Intent intent = new Intent(action); + final PendingIntent pendingIntent = + PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); + return new RemoteAction(icon, label, label, pendingIntent); } public void enterPip(View v) { + final boolean withCustomActions = + ((CheckBox) findViewById(R.id.with_custom_actions)).isChecked(); + mPipParamsBuilder.setActions( + withCustomActions ? mSwitchOnActions : Collections.emptyList()); enterPictureInPictureMode(mPipParamsBuilder.build()); } diff --git a/media/java/android/media/Rating.java b/media/java/android/media/Rating.java index be752583eae5..4da23a1319af 100644 --- a/media/java/android/media/Rating.java +++ b/media/java/android/media/Rating.java @@ -206,11 +206,12 @@ public final class Rating implements Parcelable { Log.e(TAG, "Invalid rating style (" + starRatingStyle + ") for a star rating"); return null; } - if ((starRating < 0.0f) || (starRating > maxRating)) { + if (starRating >= 0.0f && starRating <= maxRating) { + return new Rating(starRatingStyle, starRating); + } else { Log.e(TAG, "Trying to set out of range star-based rating"); return null; } - return new Rating(starRatingStyle, starRating); } /** @@ -221,11 +222,11 @@ public final class Rating implements Parcelable { * @return null if the rating is out of range, a new Rating instance otherwise. */ public static Rating newPercentageRating(float percent) { - if ((percent < 0.0f) || (percent > 100.0f)) { + if (percent >= 0.0f && percent <= 100.0f) { + return new Rating(RATING_PERCENTAGE, percent); + } else { Log.e(TAG, "Invalid percentage-based rating value"); return null; - } else { - return new Rating(RATING_PERCENTAGE, percent); } } diff --git a/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java b/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java index e7c0d9659d11..4505dad8ea12 100644 --- a/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java +++ b/packages/SettingsLib/Utils/src/com/android/settingslib/utils/applications/AppUtils.java @@ -52,6 +52,9 @@ public class AppUtils { public static String getAppContentDescription(Context context, String packageName, int userId) { final CharSequence appLabel = getApplicationLabel(context.getPackageManager(), packageName); + if (appLabel == null) { + return ""; + } return context.getSystemService(UserManager.class).isManagedProfile(userId) ? context.getString(R.string.accessibility_work_profile_app_description, appLabel) : appLabel.toString(); diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index a97af4bbe324..2a699ea45abe 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -146,6 +146,7 @@ <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" /> <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" /> <uses-permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" /> + <uses-permission android:name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND" /> <uses-permission android:name="android.permission.ACTIVITY_EMBEDDING" /> <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" /> <uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" /> diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index 014d73f281cc..2ea0c2294f76 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -33,6 +33,13 @@ java_library { srcs: ["src/com/android/systemui/EventLogTags.logtags"], } +java_library { + name: "SystemUI-sensors", + srcs: [ + "src/com/android/systemui/util/sensors/ThresholdSensor.java", + ] +} + android_library { name: "SystemUI-core", srcs: [ diff --git a/packages/SystemUI/plugin/Android.bp b/packages/SystemUI/plugin/Android.bp index df5561acbbc4..ab4f800d2586 100644 --- a/packages/SystemUI/plugin/Android.bp +++ b/packages/SystemUI/plugin/Android.bp @@ -19,7 +19,8 @@ java_library { srcs: ["src/**/*.java"], static_libs: [ - "PluginCoreLib" + "PluginCoreLib", + "SystemUI-sensors", ], } diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java index 0f94bca1d815..6e86f268a725 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java @@ -20,6 +20,7 @@ import android.net.Uri; import android.view.MotionEvent; import com.android.systemui.plugins.annotations.ProvidesInterface; +import com.android.systemui.util.sensors.ThresholdSensor; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -35,12 +36,6 @@ public interface FalsingManager { void onSuccessfulUnlock(); - void onNotificationActive(); - - void setShowingAod(boolean showingAod); - - void onNotificatonStartDraggingDown(); - boolean isUnlockingDisabled(); /** Returns true if the gesture should be rejected. */ @@ -82,66 +77,21 @@ public interface FalsingManager { */ boolean isFalseDoubleTap(); - void onNotificatonStopDraggingDown(); - - void setNotificationExpanded(); - boolean isClassifierEnabled(); - void onQsDown(); - - void setQsExpanded(boolean expanded); - boolean shouldEnforceBouncer(); - void onTrackingStarted(boolean secure); - - void onTrackingStopped(); - - void onLeftAffordanceOn(); - - void onCameraOn(); - - void onAffordanceSwipingStarted(boolean rightCorner); - - void onAffordanceSwipingAborted(); - - void onStartExpandingFromPulse(); - - void onExpansionFromPulseStopped(); - Uri reportRejectedTouch(); - void onScreenOnFromTouch(); - boolean isReportingEnabled(); - void onUnlockHintStarted(); - - void onCameraHintStarted(); - - void onLeftAffordanceHintStarted(); - - void onScreenTurningOn(); - - void onScreenOff(); - - void onNotificationStopDismissing(); - - void onNotificationDismissed(); - - void onNotificationStartDismissing(); - - void onNotificationDoubleTap(boolean accepted, float dx, float dy); - - void onBouncerShown(); - - void onBouncerHidden(); - void onTouchEvent(MotionEvent ev, int width, int height); /** From com.android.systemui.Dumpable. */ void dump(FileDescriptor fd, PrintWriter pw, String[] args); void cleanup(); + + /** Call to report a ProximityEvent to the FalsingManager. */ + void onProximityEvent(ThresholdSensor.ThresholdSensorEvent proximityEvent); } diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index ea1258f025f7..5b74687c3109 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -226,6 +226,8 @@ <string name="screenshot_saved_text">Tap to view your screenshot</string> <!-- Notification title displayed when we fail to take a screenshot. [CHAR LIMIT=50] --> <string name="screenshot_failed_title">Couldn\'t save screenshot</string> + <!-- Notification text displayed when we fail to save a screenshot due to locked storage. [CHAR LIMIT=100] --> + <string name="screenshot_failed_to_save_user_locked_text">Device must be unlocked before screenshot can be saved</string> <!-- Notification text displayed when we fail to save a screenshot for unknown reasons. [CHAR LIMIT=100] --> <string name="screenshot_failed_to_save_unknown_text">Try taking screenshot again</string> <!-- Notification text displayed when we fail to save a screenshot. [CHAR LIMIT=100] --> diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index 182b3e114887..935f89343754 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -281,7 +281,7 @@ public class AuthContainerView extends LinearLayout // Inflate biometric view only if necessary. if (Utils.isBiometricAllowed(mConfig.mPromptInfo)) { - if (config.mSensorIds.length == 1) { + if (config.mSensorIds.length == 1 || config.mSensorIds.length == 2) { final int singleSensorAuthId = config.mSensorIds[0]; if (Utils.containsSensorId(mFpProps, singleSensorAuthId)) { FingerprintSensorPropertiesInternal sensorProps = null; @@ -313,7 +313,6 @@ public class AuthContainerView extends LinearLayout return; } } else { - // The UI currently only supports authentication with a single sensor. Log.e(TAG, "Unsupported sensor array, length: " + config.mSensorIds.length); mBiometricView = null; mBackgroundView = null; diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java new file mode 100644 index 000000000000..c05ce93f0c13 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java @@ -0,0 +1,121 @@ +/* + * 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.classifier; + +import android.view.MotionEvent; + +/** + * Defines a class that can be used to ingest system events for later processing. + */ +public interface FalsingCollector { + /** */ + void onSuccessfulUnlock(); + + /** */ + void onNotificationActive(); + + /** */ + void setShowingAod(boolean showingAod); + + /** */ + void onNotificationStartDraggingDown(); + + /** */ + void onNotificationStopDraggingDown(); + + /** */ + void setNotificationExpanded(); + + /** */ + void onQsDown(); + + /** */ + void setQsExpanded(boolean expanded); + + /** */ + boolean shouldEnforceBouncer(); + + /** */ + void onTrackingStarted(boolean secure); + + /** */ + void onTrackingStopped(); + + /** */ + void onLeftAffordanceOn(); + + /** */ + void onCameraOn(); + + /** */ + void onAffordanceSwipingStarted(boolean rightCorner); + + /** */ + void onAffordanceSwipingAborted(); + + /** */ + void onStartExpandingFromPulse(); + + /** */ + void onExpansionFromPulseStopped(); + + /** */ + void onScreenOnFromTouch(); + + /** */ + boolean isReportingEnabled(); + + /** */ + void onUnlockHintStarted(); + + /** */ + void onCameraHintStarted(); + + /** */ + void onLeftAffordanceHintStarted(); + + /** */ + void onScreenTurningOn(); + + /** */ + void onScreenOff(); + + /** */ + void onNotificationStopDismissing(); + + /** */ + void onNotificationDismissed(); + + /** */ + void onNotificationStartDismissing(); + + /** */ + void onNotificationDoubleTap(boolean accepted, float dx, float dy); + + /** */ + void onBouncerShown(); + + /** */ + void onBouncerHidden(); + + /** */ + void onTouchEvent(MotionEvent ev, int width, int height); + + /** */ + void cleanup(); +} + diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java new file mode 100644 index 000000000000..a5691118261a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java @@ -0,0 +1,152 @@ +/* + * 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.classifier; + +import android.view.MotionEvent; + +/** */ +public class FalsingCollectorFake implements FalsingCollector { + @Override + public void onSuccessfulUnlock() { + } + + @Override + public void onNotificationActive() { + } + + @Override + public void setShowingAod(boolean showingAod) { + } + + @Override + public void onNotificationStartDraggingDown() { + } + + @Override + public void onNotificationStopDraggingDown() { + } + + @Override + public void setNotificationExpanded() { + } + + @Override + public void onQsDown() { + } + + @Override + public void setQsExpanded(boolean expanded) { + } + + @Override + public boolean shouldEnforceBouncer() { + return false; + } + + @Override + public void onTrackingStarted(boolean secure) { + } + + @Override + public void onTrackingStopped() { + } + + @Override + public void onLeftAffordanceOn() { + } + + @Override + public void onCameraOn() { + } + + @Override + public void onAffordanceSwipingStarted(boolean rightCorner) { + } + + @Override + public void onAffordanceSwipingAborted() { + } + + @Override + public void onStartExpandingFromPulse() { + } + + @Override + public void onExpansionFromPulseStopped() { + } + + @Override + public void onScreenOnFromTouch() { + } + + @Override + public boolean isReportingEnabled() { + return false; + } + + @Override + public void onUnlockHintStarted() { + } + + @Override + public void onCameraHintStarted() { + } + + @Override + public void onLeftAffordanceHintStarted() { + } + + @Override + public void onScreenTurningOn() { + } + + @Override + public void onScreenOff() { + } + + @Override + public void onNotificationStopDismissing() { + } + + @Override + public void onNotificationDismissed() { + } + + @Override + public void onNotificationStartDismissing() { + } + + @Override + public void onNotificationDoubleTap(boolean accepted, float dx, float dy) { + } + + @Override + public void onBouncerShown() { + } + + @Override + public void onBouncerHidden() { + } + + @Override + public void onTouchEvent(MotionEvent ev, int width, int height) { + } + + @Override + public void cleanup() { + } +} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java new file mode 100644 index 000000000000..3547392512a7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java @@ -0,0 +1,321 @@ +/* + * 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.classifier; + +import android.hardware.SensorManager; +import android.hardware.biometrics.BiometricSourceType; +import android.util.Log; +import android.view.MotionEvent; + +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.keyguard.KeyguardUpdateMonitorCallback; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.util.sensors.ProximitySensor; +import com.android.systemui.util.sensors.ThresholdSensor; + +import javax.inject.Inject; + +@SysUISingleton +class FalsingCollectorImpl implements FalsingCollector { + + private static final boolean DEBUG = false; + private static final String TAG = "FalsingManager"; + private static final String PROXIMITY_SENSOR_TAG = "FalsingManager"; + + private final FalsingDataProvider mFalsingDataProvider; + private final FalsingManager mFalsingManager; + private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; + private final ProximitySensor mProximitySensor; + private final StatusBarStateController mStatusBarStateController; + + private int mState; + private boolean mShowingAod; + private boolean mScreenOn; + private boolean mSessionStarted; + + private final ThresholdSensor.Listener mSensorEventListener = this::onProximityEvent; + + private final StatusBarStateController.StateListener mStatusBarStateListener = + new StatusBarStateController.StateListener() { + @Override + public void onStateChanged(int newState) { + logDebug("StatusBarState=" + StatusBarState.toShortString(newState)); + mState = newState; + updateSessionActive(); + } + }; + + + private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback = + new KeyguardUpdateMonitorCallback() { + @Override + public void onBiometricAuthenticated(int userId, + BiometricSourceType biometricSourceType, + boolean isStrongBiometric) { + if (userId == KeyguardUpdateMonitor.getCurrentUser() + && biometricSourceType == BiometricSourceType.FACE) { + mFalsingDataProvider.setJustUnlockedWithFace(true); + } + } + }; + + @Inject + FalsingCollectorImpl(FalsingDataProvider falsingDataProvider, FalsingManager falsingManager, + KeyguardUpdateMonitor keyguardUpdateMonitor, + ProximitySensor proximitySensor, StatusBarStateController statusBarStateController) { + mFalsingDataProvider = falsingDataProvider; + mFalsingManager = falsingManager; + mKeyguardUpdateMonitor = keyguardUpdateMonitor; + mProximitySensor = proximitySensor; + mStatusBarStateController = statusBarStateController; + + + mProximitySensor.setTag(PROXIMITY_SENSOR_TAG); + mProximitySensor.setDelay(SensorManager.SENSOR_DELAY_GAME); + + mStatusBarStateController.addCallback(mStatusBarStateListener); + mState = mStatusBarStateController.getState(); + + mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback); + } + + @Override + public void onSuccessfulUnlock() { + mFalsingManager.onSuccessfulUnlock(); + sessionEnd(); + } + + @Override + public void onNotificationActive() { + } + + @Override + public void setShowingAod(boolean showingAod) { + mShowingAod = showingAod; + updateSessionActive(); + } + + @Override + public void onNotificationStartDraggingDown() { + updateInteractionType(Classifier.NOTIFICATION_DRAG_DOWN); + } + + @Override + public void onNotificationStopDraggingDown() { + } + + @Override + public void setNotificationExpanded() { + } + + @Override + public void onQsDown() { + updateInteractionType(Classifier.QUICK_SETTINGS); + } + + @Override + public void setQsExpanded(boolean expanded) { + if (expanded) { + unregisterSensors(); + } else if (mSessionStarted) { + registerSensors(); + } + } + + @Override + public boolean shouldEnforceBouncer() { + return false; + } + + @Override + public void onTrackingStarted(boolean secure) { + updateInteractionType(secure ? Classifier.BOUNCER_UNLOCK : Classifier.UNLOCK); + } + + @Override + public void onTrackingStopped() { + } + + @Override + public void onLeftAffordanceOn() { + } + + @Override + public void onCameraOn() { + } + + @Override + public void onAffordanceSwipingStarted(boolean rightCorner) { + updateInteractionType( + rightCorner ? Classifier.RIGHT_AFFORDANCE : Classifier.LEFT_AFFORDANCE); + } + + @Override + public void onAffordanceSwipingAborted() { + } + + @Override + public void onStartExpandingFromPulse() { + updateInteractionType(Classifier.PULSE_EXPAND); + } + + @Override + public void onExpansionFromPulseStopped() { + } + + @Override + public void onScreenOnFromTouch() { + onScreenTurningOn(); + } + + @Override + public boolean isReportingEnabled() { + return false; + } + + @Override + public void onUnlockHintStarted() { + } + + @Override + public void onCameraHintStarted() { + } + + @Override + public void onLeftAffordanceHintStarted() { + } + + @Override + public void onScreenTurningOn() { + mScreenOn = true; + updateSessionActive(); + } + + @Override + public void onScreenOff() { + mScreenOn = false; + updateSessionActive(); + } + + @Override + public void onNotificationStopDismissing() { + } + + @Override + public void onNotificationDismissed() { + } + + @Override + public void onNotificationStartDismissing() { + updateInteractionType(Classifier.NOTIFICATION_DISMISS); + } + + @Override + public void onNotificationDoubleTap(boolean accepted, float dx, float dy) { + } + + @Override + public void onBouncerShown() { + unregisterSensors(); + } + + @Override + public void onBouncerHidden() { + if (mSessionStarted) { + registerSensors(); + } + } + + @Override + public void onTouchEvent(MotionEvent ev, int width, int height) { + mFalsingDataProvider.onMotionEvent(ev); + mFalsingManager.onTouchEvent(ev, width, height); + } + + @Override + public void cleanup() { + unregisterSensors(); + mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback); + mStatusBarStateController.removeCallback(mStatusBarStateListener); + } + + private void updateInteractionType(@Classifier.InteractionType int type) { + logDebug("InteractionType: " + type); + mFalsingDataProvider.setInteractionType(type); + } + + private boolean shouldSessionBeActive() { + return mScreenOn && (mState == StatusBarState.KEYGUARD) && !mShowingAod; + } + + private void updateSessionActive() { + if (shouldSessionBeActive()) { + sessionStart(); + } else { + sessionEnd(); + } + } + + private void sessionStart() { + if (!mSessionStarted && shouldSessionBeActive()) { + logDebug("Starting Session"); + mSessionStarted = true; + mFalsingDataProvider.setJustUnlockedWithFace(false); + registerSensors(); + mFalsingDataProvider.onSessionStarted(); + } + } + + private void sessionEnd() { + if (mSessionStarted) { + logDebug("Ending Session"); + mSessionStarted = false; + unregisterSensors(); + mFalsingDataProvider.onSessionEnd(); + } + } + + private void registerSensors() { + if (!mFalsingDataProvider.isWirelessCharging()) { + mProximitySensor.register(mSensorEventListener); + } + } + + private void unregisterSensors() { + mProximitySensor.unregister(mSensorEventListener); + } + + private void onProximityEvent(ThresholdSensor.ThresholdSensorEvent proximityEvent) { + // TODO: some of these classifiers might allow us to abort early, meaning we don't have to + // make these calls. + mFalsingManager.onProximityEvent(proximityEvent); + } + + + static void logDebug(String msg) { + logDebug(msg, null); + } + + static void logDebug(String msg, Throwable throwable) { + if (DEBUG) { + Log.d(TAG, msg, throwable); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java index 4681f9709a7d..b29871c1c3d0 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingDataProvider.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * 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. @@ -14,14 +14,17 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import android.util.DisplayMetrics; import android.view.MotionEvent; import android.view.MotionEvent.PointerCoords; import android.view.MotionEvent.PointerProperties; -import com.android.systemui.classifier.Classifier; +import com.android.systemui.classifier.brightline.BrightLineFalsingManager; +import com.android.systemui.classifier.brightline.FalsingClassifier; +import com.android.systemui.classifier.brightline.TimeLimitedMotionEventBuffer; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.util.time.SystemClock; @@ -36,6 +39,7 @@ import javax.inject.Inject; /** * Acts as a cache and utility class for FalsingClassifiers. */ +@SysUISingleton public class FalsingDataProvider { private static final long MOTION_EVENT_AGE_MS = 1000; @@ -48,6 +52,7 @@ public class FalsingDataProvider { private final SystemClock mSystemClock; private final float mXdpi; private final float mYdpi; + private final List<SessionListener> mSessionListeners = new ArrayList<>(); private @Classifier.InteractionType int mInteractionType; private final Deque<TimeLimitedMotionEventBuffer> mExtendedMotionEvents = new LinkedList<>(); @@ -57,9 +62,9 @@ public class FalsingDataProvider { private boolean mDirty = true; private float mAngle = 0; - private MotionEvent mFirstActualMotionEvent; private MotionEvent mFirstRecentMotionEvent; private MotionEvent mLastMotionEvent; + private boolean mJustUnlockedWithFace; @Inject public FalsingDataProvider(DisplayMetrics displayMetrics, BatteryController batteryController, @@ -76,10 +81,6 @@ public class FalsingDataProvider { } void onMotionEvent(MotionEvent motionEvent) { - if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) { - mFirstActualMotionEvent = motionEvent; - } - List<MotionEvent> motionEvents = unpackMotionEvent(motionEvent); FalsingClassifier.logDebug("Unpacked into: " + motionEvents.size()); if (BrightLineFalsingManager.DEBUG) { @@ -103,29 +104,29 @@ public class FalsingDataProvider { } /** Returns screen width in pixels. */ - int getWidthPixels() { + public int getWidthPixels() { return mWidthPixels; } /** Returns screen height in pixels. */ - int getHeightPixels() { + public int getHeightPixels() { return mHeightPixels; } - float getXdpi() { + public float getXdpi() { return mXdpi; } - float getYdpi() { + public float getYdpi() { return mYdpi; } - List<MotionEvent> getRecentMotionEvents() { + public List<MotionEvent> getRecentMotionEvents() { return mRecentMotionEvents; } /** Returns recent gestures, exclusive of the most recent gesture. Newer gestures come first. */ - Queue<? extends List<MotionEvent>> getHistoricalMotionEvents() { + public Queue<? extends List<MotionEvent>> getHistoricalMotionEvents() { long nowMs = mSystemClock.uptimeMillis(); mExtendedMotionEvents.removeIf( @@ -137,31 +138,38 @@ public class FalsingDataProvider { /** * interactionType is defined by {@link com.android.systemui.classifier.Classifier}. */ - final void setInteractionType(@Classifier.InteractionType int interactionType) { + public final void setInteractionType(@Classifier.InteractionType int interactionType) { if (mInteractionType != interactionType) { mInteractionType = interactionType; mDirty = true; } } + /** + * Returns true if new data has been supplied since the last time this class has been accessed. + */ public boolean isDirty() { return mDirty; } - final int getInteractionType() { + /** Return the interaction type that is being compared against for falsing. */ + public final int getInteractionType() { return mInteractionType; } - MotionEvent getFirstActualMotionEvent() { - return mFirstActualMotionEvent; - } - - MotionEvent getFirstRecentMotionEvent() { + /** + * Get the first recorded {@link MotionEvent} of the most recent gesture. + * + * Note that MotionEvents are not kept forever. As a gesture gets longer in duration, older + * MotionEvents may expire and be ejected. + */ + public MotionEvent getFirstRecentMotionEvent() { recalculateData(); return mFirstRecentMotionEvent; } - MotionEvent getLastMotionEvent() { + /** Get the last recorded {@link MotionEvent}. */ + public MotionEvent getLastMotionEvent() { recalculateData(); return mLastMotionEvent; } @@ -171,12 +179,13 @@ public class FalsingDataProvider { * * The angle will be in radians, always be between 0 and 2*PI, inclusive. */ - float getAngle() { + public float getAngle() { recalculateData(); return mAngle; } - boolean isHorizontal() { + /** Returns if the most recent gesture is more horizontal than vertical. */ + public boolean isHorizontal() { recalculateData(); if (mRecentMotionEvents.isEmpty()) { return false; @@ -186,7 +195,13 @@ public class FalsingDataProvider { .abs(mFirstRecentMotionEvent.getY() - mLastMotionEvent.getY()); } - boolean isRight() { + /** + * Is the most recent gesture more right than left. + * + * This does not mean the gesture is mostly horizontal. Simply that it ended at least one pixel + * to the right of where it started. See also {@link #isHorizontal()}. + */ + public boolean isRight() { recalculateData(); if (mRecentMotionEvents.isEmpty()) { return false; @@ -195,11 +210,18 @@ public class FalsingDataProvider { return mLastMotionEvent.getX() > mFirstRecentMotionEvent.getX(); } - boolean isVertical() { + /** Returns if the most recent gesture is more vertical than horizontal. */ + public boolean isVertical() { return !isHorizontal(); } - boolean isUp() { + /** + * Is the most recent gesture more up than down. + * + * This does not mean the gesture is mostly vertical. Simply that it ended at least one pixel + * higher than it started. See also {@link #isVertical()}. + */ + public boolean isUp() { recalculateData(); if (mRecentMotionEvents.isEmpty()) { return false; @@ -209,7 +231,7 @@ public class FalsingDataProvider { } /** Returns true if phone is being charged without a cable. */ - boolean isWirelessCharging() { + public boolean isWirelessCharging() { return mBatteryController.isWirelessCharging(); } @@ -292,9 +314,21 @@ public class FalsingDataProvider { return motionEvents; } - void onSessionEnd() { - mFirstActualMotionEvent = null; + /** Register a {@link SessionListener}. */ + public void addSessionListener(SessionListener listener) { + mSessionListeners.add(listener); + } + + /** Unregister a {@link SessionListener}. */ + public void removeSessionListener(SessionListener listener) { + mSessionListeners.remove(listener); + } + void onSessionStarted() { + mSessionListeners.forEach(SessionListener::onSessionStarted); + } + + void onSessionEnd() { for (MotionEvent ev : mRecentMotionEvents) { ev.recycle(); } @@ -302,5 +336,24 @@ public class FalsingDataProvider { mRecentMotionEvents.clear(); mDirty = true; + + mSessionListeners.forEach(SessionListener::onSessionEnded); + } + + public boolean isJustUnlockedWithFace() { + return mJustUnlockedWithFace; + } + + public void setJustUnlockedWithFace(boolean justUnlockedWithFace) { + mJustUnlockedWithFace = justUnlockedWithFace; + } + + /** Implement to be alerted abotu the beginning and ending of falsing tracking. */ + public interface SessionListener { + /** Called when the lock screen is shown and falsing-tracking begins. */ + void onSessionStarted(); + + /** Called when the lock screen exits and falsing-tracking ends. */ + void onSessionEnded(); } } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java index 121484303b99..32d27bcc01a6 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerFake.java @@ -21,6 +21,7 @@ import android.view.MotionEvent; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.util.sensors.ThresholdSensor; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -43,21 +44,6 @@ public class FalsingManagerFake implements FalsingManager { } - @Override - public void onNotificationActive() { - - } - - @Override - public void setShowingAod(boolean showingAod) { - - } - - @Override - public void onNotificatonStartDraggingDown() { - - } - @VisibleForTesting public void setIsUnlockingDisabled(boolean isUnlockingDisabled) { mIsUnlockingDisabled = isUnlockingDisabled; @@ -100,16 +86,6 @@ public class FalsingManagerFake implements FalsingManager { return mIsFalseDoubleTap; } - @Override - public void onNotificatonStopDraggingDown() { - - } - - @Override - public void setNotificationExpanded() { - - } - @VisibleForTesting public void setIsClassiferEnabled(boolean isClassiferEnabled) { mIsClassiferEnabled = isClassiferEnabled; @@ -121,76 +97,15 @@ public class FalsingManagerFake implements FalsingManager { } @Override - public void onQsDown() { - - } - - @Override - public void setQsExpanded(boolean expanded) { - - } - - @VisibleForTesting - public void setShouldEnforceBouncer(boolean shouldEnforceBouncer) { - mShouldEnforceBouncer = shouldEnforceBouncer; - } - - @Override public boolean shouldEnforceBouncer() { return mShouldEnforceBouncer; } @Override - public void onTrackingStarted(boolean secure) { - - } - - @Override - public void onTrackingStopped() { - - } - - @Override - public void onLeftAffordanceOn() { - - } - - @Override - public void onCameraOn() { - - } - - @Override - public void onAffordanceSwipingStarted(boolean rightCorner) { - - } - - @Override - public void onAffordanceSwipingAborted() { - - } - - @Override - public void onStartExpandingFromPulse() { - - } - - @Override - public void onExpansionFromPulseStopped() { - - } - - @Override public Uri reportRejectedTouch() { return null; } - @Override - public void onScreenOnFromTouch() { - - } - - @VisibleForTesting public void setIsReportingEnabled(boolean isReportingEnabled) { mIsReportingEnabled = isReportingEnabled; @@ -202,70 +117,20 @@ public class FalsingManagerFake implements FalsingManager { } @Override - public void onUnlockHintStarted() { - - } - - @Override - public void onCameraHintStarted() { - - } - - @Override - public void onLeftAffordanceHintStarted() { - - } - - @Override - public void onScreenTurningOn() { - - } - - @Override - public void onScreenOff() { - - } - - @Override - public void onNotificationStopDismissing() { - - } - - @Override - public void onNotificationDismissed() { - - } - - @Override - public void onNotificationStartDismissing() { - - } - - @Override - public void onNotificationDoubleTap(boolean accepted, float dx, float dy) { - - } - - @Override - public void onBouncerShown() { + public void onTouchEvent(MotionEvent ev, int width, int height) { } @Override - public void onBouncerHidden() { - + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { } @Override - public void onTouchEvent(MotionEvent ev, int width, int height) { - + public void cleanup() { } @Override - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - } + public void onProximityEvent(ThresholdSensor.ThresholdSensorEvent proximityEvent) { - @Override - public void cleanup() { } } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java index 814fff9c6d43..74629411c13d 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java @@ -17,37 +17,30 @@ package com.android.systemui.classifier; import android.content.Context; -import android.content.res.Resources; -import android.hardware.SensorManager; import android.net.Uri; import android.provider.DeviceConfig; import android.view.MotionEvent; -import android.view.ViewConfiguration; import androidx.annotation.NonNull; -import com.android.internal.annotations.VisibleForTesting; -import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.Dumpable; import com.android.systemui.classifier.brightline.BrightLineFalsingManager; -import com.android.systemui.classifier.brightline.FalsingDataProvider; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.dock.DockManager; import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.FalsingPlugin; import com.android.systemui.plugins.PluginListener; -import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.util.DeviceConfigProxy; -import com.android.systemui.util.sensors.ProximitySensor; +import com.android.systemui.util.sensors.ThresholdSensor; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.concurrent.Executor; import javax.inject.Inject; +import javax.inject.Provider; /** * Simple passthrough implementation of {@link FalsingManager} allowing plugins to swap in. @@ -57,21 +50,14 @@ import javax.inject.Inject; @SysUISingleton public class FalsingManagerProxy implements FalsingManager, Dumpable { - private static final String PROXIMITY_SENSOR_TAG = "FalsingManager"; private static final String DUMPABLE_TAG = "FalsingManager"; public static final String FALSING_REMAIN_LOCKED = "falsing_failure_after_attempts"; public static final String FALSING_SUCCESS = "falsing_success_after_attempts"; private final PluginManager mPluginManager; - private final ProximitySensor mProximitySensor; - private final Resources mResources; - private final ViewConfiguration mViewConfiguration; - private final FalsingDataProvider mFalsingDataProvider; private final DeviceConfigProxy mDeviceConfig; - private final DockManager mDockManager; - private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; + private final Provider<BrightLineFalsingManager> mBrightLineFalsingManagerProvider; private final DumpManager mDumpManager; - private final StatusBarStateController mStatusBarStateController; final PluginListener<FalsingPlugin> mPluginListener; private FalsingManager mInternalFalsingManager; @@ -81,31 +67,15 @@ public class FalsingManagerProxy implements FalsingManager, Dumpable { @Inject FalsingManagerProxy(PluginManager pluginManager, @Main Executor executor, - ProximitySensor proximitySensor, - DeviceConfigProxy deviceConfig, DockManager dockManager, - KeyguardUpdateMonitor keyguardUpdateMonitor, - DumpManager dumpManager, - StatusBarStateController statusBarStateController, - @Main Resources resources, - ViewConfiguration viewConfiguration, - FalsingDataProvider falsingDataProvider) { + DeviceConfigProxy deviceConfig, DumpManager dumpManager, + Provider<BrightLineFalsingManager> brightLineFalsingManagerProvider) { mPluginManager = pluginManager; - mProximitySensor = proximitySensor; - mDockManager = dockManager; - mKeyguardUpdateMonitor = keyguardUpdateMonitor; mDumpManager = dumpManager; - mStatusBarStateController = statusBarStateController; - mResources = resources; - mViewConfiguration = viewConfiguration; - mFalsingDataProvider = falsingDataProvider; - mProximitySensor.setTag(PROXIMITY_SENSOR_TAG); - mProximitySensor.setDelay(SensorManager.SENSOR_DELAY_GAME); mDeviceConfig = deviceConfig; + mBrightLineFalsingManagerProvider = brightLineFalsingManagerProvider; setupFalsingManager(); mDeviceConfig.addOnPropertiesChangedListener( - DeviceConfig.NAMESPACE_SYSTEMUI, - executor, - mDeviceConfigListener + DeviceConfig.NAMESPACE_SYSTEMUI, executor, mDeviceConfigListener ); mPluginListener = new PluginListener<FalsingPlugin>() { @@ -144,24 +114,7 @@ public class FalsingManagerProxy implements FalsingManager, Dumpable { if (mInternalFalsingManager != null) { mInternalFalsingManager.cleanup(); } - mInternalFalsingManager = new BrightLineFalsingManager( - mFalsingDataProvider, - mKeyguardUpdateMonitor, - mProximitySensor, - mDeviceConfig, - mResources, - mViewConfiguration, - mDockManager, - mStatusBarStateController - ); - } - - /** - * Returns the FalsingManager implementation in use. - */ - @VisibleForTesting - FalsingManager getInternalFalsingManager() { - return mInternalFalsingManager; + mInternalFalsingManager = mBrightLineFalsingManagerProvider.get(); } @Override @@ -170,21 +123,6 @@ public class FalsingManagerProxy implements FalsingManager, Dumpable { } @Override - public void onNotificationActive() { - mInternalFalsingManager.onNotificationActive(); - } - - @Override - public void setShowingAod(boolean showingAod) { - mInternalFalsingManager.setShowingAod(showingAod); - } - - @Override - public void onNotificatonStartDraggingDown() { - mInternalFalsingManager.onNotificatonStartDraggingDown(); - } - - @Override public boolean isUnlockingDisabled() { return mInternalFalsingManager.isUnlockingDisabled(); } @@ -194,7 +132,6 @@ public class FalsingManagerProxy implements FalsingManager, Dumpable { return mInternalFalsingManager.isFalseTouch(interactionType); } - @Override public boolean isFalseTap(boolean robustCheck) { return mInternalFalsingManager.isFalseTap(robustCheck); @@ -206,148 +143,33 @@ public class FalsingManagerProxy implements FalsingManager, Dumpable { } @Override - public void onNotificatonStopDraggingDown() { - mInternalFalsingManager.onNotificatonStartDraggingDown(); - } - - @Override - public void setNotificationExpanded() { - mInternalFalsingManager.setNotificationExpanded(); - } - - @Override public boolean isClassifierEnabled() { return mInternalFalsingManager.isClassifierEnabled(); } @Override - public void onQsDown() { - mInternalFalsingManager.onQsDown(); - } - - @Override - public void setQsExpanded(boolean expanded) { - mInternalFalsingManager.setQsExpanded(expanded); - } - - @Override public boolean shouldEnforceBouncer() { return mInternalFalsingManager.shouldEnforceBouncer(); } @Override - public void onTrackingStarted(boolean secure) { - mInternalFalsingManager.onTrackingStarted(secure); - } - - @Override - public void onTrackingStopped() { - mInternalFalsingManager.onTrackingStopped(); - } - - @Override - public void onLeftAffordanceOn() { - mInternalFalsingManager.onLeftAffordanceOn(); - } - - @Override - public void onCameraOn() { - mInternalFalsingManager.onCameraOn(); - } - - @Override - public void onAffordanceSwipingStarted(boolean rightCorner) { - mInternalFalsingManager.onAffordanceSwipingStarted(rightCorner); - } - - @Override - public void onAffordanceSwipingAborted() { - mInternalFalsingManager.onAffordanceSwipingAborted(); - } - - @Override - public void onStartExpandingFromPulse() { - mInternalFalsingManager.onStartExpandingFromPulse(); - } - - @Override - public void onExpansionFromPulseStopped() { - mInternalFalsingManager.onExpansionFromPulseStopped(); - } - - @Override public Uri reportRejectedTouch() { return mInternalFalsingManager.reportRejectedTouch(); } @Override - public void onScreenOnFromTouch() { - mInternalFalsingManager.onScreenOnFromTouch(); - } - - @Override public boolean isReportingEnabled() { return mInternalFalsingManager.isReportingEnabled(); } @Override - public void onUnlockHintStarted() { - mInternalFalsingManager.onUnlockHintStarted(); - } - - @Override - public void onCameraHintStarted() { - mInternalFalsingManager.onCameraHintStarted(); - } - - @Override - public void onLeftAffordanceHintStarted() { - mInternalFalsingManager.onLeftAffordanceHintStarted(); - } - - @Override - public void onScreenTurningOn() { - mInternalFalsingManager.onScreenTurningOn(); - } - - @Override - public void onScreenOff() { - mInternalFalsingManager.onScreenOff(); - } - - @Override - public void onNotificationStopDismissing() { - mInternalFalsingManager.onNotificationStopDismissing(); - } - - @Override - public void onNotificationDismissed() { - mInternalFalsingManager.onNotificationDismissed(); - } - - @Override - public void onNotificationStartDismissing() { - mInternalFalsingManager.onNotificationStartDismissing(); - } - - @Override - public void onNotificationDoubleTap(boolean accepted, float dx, float dy) { - mInternalFalsingManager.onNotificationDoubleTap(accepted, dx, dy); - } - - @Override - public void onBouncerShown() { - mInternalFalsingManager.onBouncerShown(); - } - - @Override - public void onBouncerHidden() { - mInternalFalsingManager.onBouncerHidden(); + public void onTouchEvent(MotionEvent ev, int width, int height) { + mInternalFalsingManager.onTouchEvent(ev, width, height); } @Override - public void onTouchEvent(MotionEvent ev, int width, int height) { - mInternalFalsingManager.onTouchEvent(ev, width, height); + public void onProximityEvent(ThresholdSensor.ThresholdSensorEvent proximityEvent) { + mInternalFalsingManager.onProximityEvent(proximityEvent); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java new file mode 100644 index 000000000000..937bcbaa6222 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingModule.java @@ -0,0 +1,31 @@ +/* + * 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.classifier; + +import com.android.systemui.dagger.SysUISingleton; + +import dagger.Binds; +import dagger.Module; + +/** Dagger Module for Falsing. */ +@Module +public interface FalsingModule { + /** */ + @Binds + @SysUISingleton + FalsingCollector bindsFalsingCollector(FalsingCollectorImpl impl); +} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java index 334102d68507..f6b8b4c92049 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java @@ -16,12 +16,10 @@ package com.android.systemui.classifier.brightline; -import static com.android.systemui.classifier.FalsingManagerProxy.FALSING_REMAIN_LOCKED; import static com.android.systemui.classifier.FalsingManagerProxy.FALSING_SUCCESS; import android.app.ActivityManager; import android.content.res.Resources; -import android.hardware.biometrics.BiometricSourceType; import android.net.Uri; import android.os.Build; import android.util.IndentingPrintWriter; @@ -32,18 +30,15 @@ import android.view.ViewConfiguration; import androidx.annotation.NonNull; import com.android.internal.logging.MetricsLogger; -import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.R; import com.android.systemui.classifier.Classifier; +import com.android.systemui.classifier.FalsingDataProvider; +import com.android.systemui.classifier.FalsingDataProvider.SessionListener; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dock.DockManager; import com.android.systemui.plugins.FalsingManager; -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.phone.NotificationTapHelper; import com.android.systemui.util.DeviceConfigProxy; -import com.android.systemui.util.sensors.ProximitySensor; import com.android.systemui.util.sensors.ThresholdSensor; import java.io.FileDescriptor; @@ -56,30 +51,25 @@ import java.util.Queue; import java.util.StringJoiner; import java.util.stream.Collectors; +import javax.inject.Inject; + /** * FalsingManager designed to make clear why a touch was rejected. */ public class BrightLineFalsingManager implements FalsingManager { private static final String TAG = "FalsingManager"; - static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final int RECENT_INFO_LOG_SIZE = 40; private static final int RECENT_SWIPE_LOG_SIZE = 20; private final FalsingDataProvider mDataProvider; - private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - private final ProximitySensor mProximitySensor; private final DockManager mDockManager; - private final StatusBarStateController mStatusBarStateController; private final SingleTapClassifier mSingleTapClassifier; private final DoubleTapClassifier mDoubleTapClassifier; - private boolean mSessionStarted; - private MetricsLogger mMetricsLogger; + private final MetricsLogger mMetricsLogger; private int mIsFalseTouchCalls; - private boolean mShowingAod; - private boolean mScreenOn; - private boolean mJustUnlockedWithFace; private static final Queue<String> RECENT_INFO_LOG = new ArrayDeque<>(RECENT_INFO_LOG_SIZE + 1); private static final Queue<DebugSwipeRecord> RECENT_SWIPES = @@ -87,46 +77,26 @@ public class BrightLineFalsingManager implements FalsingManager { private final List<FalsingClassifier> mClassifiers; - private ThresholdSensor.Listener mSensorEventListener = this::onProximityEvent; - - private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback = - new KeyguardUpdateMonitorCallback() { - @Override - public void onBiometricAuthenticated(int userId, - BiometricSourceType biometricSourceType, - boolean isStrongBiometric) { - if (userId == KeyguardUpdateMonitor.getCurrentUser() - && biometricSourceType == BiometricSourceType.FACE) { - mJustUnlockedWithFace = true; - } - } - }; - private boolean mPreviousResult = false; + private final SessionListener mSessionListener = new SessionListener() { + @Override + public void onSessionEnded() { + mClassifiers.forEach(FalsingClassifier::onSessionEnded); + } - private StatusBarStateController.StateListener mStatusBarStateListener = - new StatusBarStateController.StateListener() { @Override - public void onStateChanged(int newState) { - logDebug("StatusBarState=" + StatusBarState.toShortString(newState)); - mState = newState; - updateSessionActive(); + public void onSessionStarted() { + mClassifiers.forEach(FalsingClassifier::onSessionStarted); } }; - private int mState; + private boolean mPreviousResult = false; + + @Inject public BrightLineFalsingManager(FalsingDataProvider falsingDataProvider, - KeyguardUpdateMonitor keyguardUpdateMonitor, ProximitySensor proximitySensor, DeviceConfigProxy deviceConfigProxy, @Main Resources resources, - ViewConfiguration viewConfiguration, DockManager dockManager, - StatusBarStateController statusBarStateController) { - mKeyguardUpdateMonitor = keyguardUpdateMonitor; + ViewConfiguration viewConfiguration, DockManager dockManager) { mDataProvider = falsingDataProvider; - mProximitySensor = proximitySensor; mDockManager = dockManager; - mStatusBarStateController = statusBarStateController; - mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback); - mStatusBarStateController.addCallback(mStatusBarStateListener); - mState = mStatusBarStateController.getState(); mMetricsLogger = new MetricsLogger(); mClassifiers = new ArrayList<>(); @@ -146,58 +116,8 @@ public class BrightLineFalsingManager implements FalsingManager { mDoubleTapClassifier = new DoubleTapClassifier(mDataProvider, mSingleTapClassifier, resources.getDimension(R.dimen.double_tap_slop), NotificationTapHelper.DOUBLE_TAP_TIMEOUT_MS); - } - - private void registerSensors() { - if (!mDataProvider.isWirelessCharging()) { - mProximitySensor.register(mSensorEventListener); - } - } - - private void unregisterSensors() { - mProximitySensor.unregister(mSensorEventListener); - } - private void sessionStart() { - if (!mSessionStarted && shouldSessionBeActive()) { - logDebug("Starting Session"); - mSessionStarted = true; - mJustUnlockedWithFace = false; - registerSensors(); - mClassifiers.forEach(FalsingClassifier::onSessionStarted); - } - } - - private void sessionEnd() { - if (mSessionStarted) { - logDebug("Ending Session"); - mSessionStarted = false; - unregisterSensors(); - mDataProvider.onSessionEnd(); - mClassifiers.forEach(FalsingClassifier::onSessionEnded); - if (mIsFalseTouchCalls != 0) { - mMetricsLogger.histogram(FALSING_REMAIN_LOCKED, mIsFalseTouchCalls); - mIsFalseTouchCalls = 0; - } - } - } - - - private void updateSessionActive() { - if (shouldSessionBeActive()) { - sessionStart(); - } else { - sessionEnd(); - } - } - - private boolean shouldSessionBeActive() { - return mScreenOn && (mState == StatusBarState.KEYGUARD) && !mShowingAod; - } - - private void updateInteractionType(@Classifier.InteractionType int type) { - logDebug("InteractionType: " + type); - mDataProvider.setInteractionType(type); + mDataProvider.addSessionListener(mSessionListener); } @Override @@ -212,8 +132,9 @@ public class BrightLineFalsingManager implements FalsingManager { return mPreviousResult; } - mPreviousResult = !ActivityManager.isRunningInUserTestHarness() && !mJustUnlockedWithFace - && !mDockManager.isDocked() && mClassifiers.stream().anyMatch(falsingClassifier -> { + mPreviousResult = !ActivityManager.isRunningInUserTestHarness() + && !mDataProvider.isJustUnlockedWithFace() && !mDockManager.isDocked() + && mClassifiers.stream().anyMatch(falsingClassifier -> { boolean result = falsingClassifier.isFalseTouch(); if (result) { logInfo(String.format( @@ -245,9 +166,8 @@ public class BrightLineFalsingManager implements FalsingManager { (int) (motionEvent.getEventTime() - motionEvent.getDownTime()))) .collect(Collectors.toList()))); while (RECENT_SWIPES.size() > RECENT_INFO_LOG_SIZE) { - DebugSwipeRecord record = RECENT_SWIPES.remove(); + RECENT_SWIPES.remove(); } - } return mPreviousResult; @@ -287,11 +207,11 @@ public class BrightLineFalsingManager implements FalsingManager { public void onTouchEvent(MotionEvent motionEvent, int width, int height) { // TODO: some of these classifiers might allow us to abort early, meaning we don't have to // make these calls. - mDataProvider.onMotionEvent(motionEvent); mClassifiers.forEach((classifier) -> classifier.onTouchEvent(motionEvent)); } - private void onProximityEvent(ThresholdSensor.ThresholdSensorEvent proximityEvent) { + @Override + public void onProximityEvent(ThresholdSensor.ThresholdSensorEvent proximityEvent) { // TODO: some of these classifiers might allow us to abort early, meaning we don't have to // make these calls. mClassifiers.forEach((classifier) -> classifier.onProximityEvent(proximityEvent)); @@ -303,22 +223,6 @@ public class BrightLineFalsingManager implements FalsingManager { mMetricsLogger.histogram(FALSING_SUCCESS, mIsFalseTouchCalls); mIsFalseTouchCalls = 0; } - sessionEnd(); - } - - @Override - public void onNotificationActive() { - } - - @Override - public void setShowingAod(boolean showingAod) { - mShowingAod = showingAod; - updateSessionActive(); - } - - @Override - public void onNotificatonStartDraggingDown() { - updateInteractionType(Classifier.NOTIFICATION_DRAG_DOWN); } @Override @@ -326,147 +230,29 @@ public class BrightLineFalsingManager implements FalsingManager { return false; } - - @Override - public void onNotificatonStopDraggingDown() { - } - - @Override - public void setNotificationExpanded() { - } - - @Override - public void onQsDown() { - updateInteractionType(Classifier.QUICK_SETTINGS); - } - - @Override - public void setQsExpanded(boolean expanded) { - if (expanded) { - unregisterSensors(); - } else if (mSessionStarted) { - registerSensors(); - } - } - @Override public boolean shouldEnforceBouncer() { return false; } @Override - public void onTrackingStarted(boolean secure) { - updateInteractionType(secure ? Classifier.BOUNCER_UNLOCK : Classifier.UNLOCK); - } - - @Override - public void onTrackingStopped() { - } - - @Override - public void onLeftAffordanceOn() { - } - - @Override - public void onCameraOn() { - } - - @Override - public void onAffordanceSwipingStarted(boolean rightCorner) { - updateInteractionType( - rightCorner ? Classifier.RIGHT_AFFORDANCE : Classifier.LEFT_AFFORDANCE); - } - - @Override - public void onAffordanceSwipingAborted() { - } - - @Override - public void onStartExpandingFromPulse() { - updateInteractionType(Classifier.PULSE_EXPAND); - } - - @Override - public void onExpansionFromPulseStopped() { - } - - @Override public Uri reportRejectedTouch() { return null; } @Override - public void onScreenOnFromTouch() { - onScreenTurningOn(); - } - - @Override public boolean isReportingEnabled() { return false; } @Override - public void onUnlockHintStarted() { - } - - @Override - public void onCameraHintStarted() { - } - - @Override - public void onLeftAffordanceHintStarted() { - } - - @Override - public void onScreenTurningOn() { - mScreenOn = true; - updateSessionActive(); - } - - @Override - public void onScreenOff() { - mScreenOn = false; - updateSessionActive(); - } - - - @Override - public void onNotificationStopDismissing() { - } - - @Override - public void onNotificationDismissed() { - } - - @Override - public void onNotificationStartDismissing() { - updateInteractionType(Classifier.NOTIFICATION_DISMISS); - } - - @Override - public void onNotificationDoubleTap(boolean b, float v, float v1) { - } - - @Override - public void onBouncerShown() { - unregisterSensors(); - } - - @Override - public void onBouncerHidden() { - if (mSessionStarted) { - registerSensors(); - } - } - - @Override public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); ipw.println("BRIGHTLINE FALSING MANAGER"); ipw.print("classifierEnabled="); ipw.println(isClassifierEnabled() ? 1 : 0); ipw.print("mJustUnlockedWithFace="); - ipw.println(mJustUnlockedWithFace ? 1 : 0); + ipw.println(mDataProvider.isJustUnlockedWithFace() ? 1 : 0); ipw.print("isDocked="); ipw.println(mDockManager.isDocked() ? 1 : 0); ipw.print("width="); @@ -496,9 +282,7 @@ public class BrightLineFalsingManager implements FalsingManager { @Override public void cleanup() { - unregisterSensors(); - mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback); - mStatusBarStateController.removeCallback(mStatusBarStateListener); + mDataProvider.removeSessionListener(mSessionListener); } static void logDebug(String msg) { diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DiagonalClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DiagonalClassifier.java index 520e0a12dec4..a73ccf575249 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DiagonalClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DiagonalClassifier.java @@ -23,6 +23,7 @@ import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE; import android.provider.DeviceConfig; +import com.android.systemui.classifier.FalsingDataProvider; import com.android.systemui.util.DeviceConfigProxy; import java.util.Locale; diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java index 0329183e6048..893d43579ba5 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DistanceClassifier.java @@ -27,6 +27,7 @@ import android.provider.DeviceConfig; import android.view.MotionEvent; import android.view.VelocityTracker; +import com.android.systemui.classifier.FalsingDataProvider; import com.android.systemui.util.DeviceConfigProxy; import java.util.List; diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DoubleTapClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DoubleTapClassifier.java index d3af1c347048..a27ea6172414 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/DoubleTapClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/DoubleTapClassifier.java @@ -18,6 +18,8 @@ package com.android.systemui.classifier.brightline; import android.view.MotionEvent; +import com.android.systemui.classifier.FalsingDataProvider; + import java.util.List; import java.util.Queue; diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java index ed417b3d09ec..568dc432729f 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/FalsingClassifier.java @@ -19,6 +19,7 @@ package com.android.systemui.classifier.brightline; import android.view.MotionEvent; import com.android.systemui.classifier.Classifier; +import com.android.systemui.classifier.FalsingDataProvider; import com.android.systemui.util.sensors.ProximitySensor; import java.util.List; @@ -27,7 +28,7 @@ import java.util.Queue; /** * Base class for rules that determine False touches. */ -abstract class FalsingClassifier { +public abstract class FalsingClassifier { private final FalsingDataProvider mDataProvider; FalsingClassifier(FalsingDataProvider dataProvider) { @@ -126,15 +127,18 @@ abstract class FalsingClassifier { */ abstract String getReason(); - static void logDebug(String msg) { + /** */ + public static void logDebug(String msg) { BrightLineFalsingManager.logDebug(msg); } - static void logInfo(String msg) { + /** */ + public static void logInfo(String msg) { BrightLineFalsingManager.logInfo(msg); } - static void logError(String msg) { + /** */ + public static void logError(String msg) { BrightLineFalsingManager.logError(msg); } } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/PointerCountClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/PointerCountClassifier.java index b726c3e2783f..dd5d8a8f557b 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/PointerCountClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/PointerCountClassifier.java @@ -21,6 +21,8 @@ import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; import android.view.MotionEvent; +import com.android.systemui.classifier.FalsingDataProvider; + import java.util.Locale; /** diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java index b128678af5db..3551c241e71e 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ProximityClassifier.java @@ -22,6 +22,7 @@ import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; import android.provider.DeviceConfig; import android.view.MotionEvent; +import com.android.systemui.classifier.FalsingDataProvider; import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.sensors.ProximitySensor; diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/SingleTapClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/SingleTapClassifier.java index 47708f4dd789..8c7648149e44 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/SingleTapClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/SingleTapClassifier.java @@ -18,6 +18,8 @@ package com.android.systemui.classifier.brightline; import android.view.MotionEvent; +import com.android.systemui.classifier.FalsingDataProvider; + import java.util.List; /** diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/TimeLimitedMotionEventBuffer.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/TimeLimitedMotionEventBuffer.java index 92aa7c5b2f44..7430a1eda920 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/TimeLimitedMotionEventBuffer.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/TimeLimitedMotionEventBuffer.java @@ -36,7 +36,7 @@ public class TimeLimitedMotionEventBuffer implements List<MotionEvent> { private final LinkedList<MotionEvent> mMotionEvents; private final long mMaxAgeMs; - TimeLimitedMotionEventBuffer(long maxAgeMs) { + public TimeLimitedMotionEventBuffer(long maxAgeMs) { super(); mMaxAgeMs = maxAgeMs; mMotionEvents = new LinkedList<>(); diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/TypeClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/TypeClassifier.java index 5f1b37ae06d9..f62871f383b3 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/TypeClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/TypeClassifier.java @@ -26,6 +26,8 @@ import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS; import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE; import static com.android.systemui.classifier.Classifier.UNLOCK; +import com.android.systemui.classifier.FalsingDataProvider; + /** * Ensure that the swipe direction generally matches that of the interaction type. */ diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java index a796f3c6d547..9ca77d364bc4 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java @@ -25,6 +25,7 @@ import android.graphics.Point; import android.provider.DeviceConfig; import android.view.MotionEvent; +import com.android.systemui.classifier.FalsingDataProvider; import com.android.systemui.util.DeviceConfigProxy; import java.util.ArrayList; diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 780bb5b01103..4d697005be7b 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -27,6 +27,7 @@ import com.android.systemui.BootCompleteCache; import com.android.systemui.BootCompleteCacheImpl; import com.android.systemui.appops.dagger.AppOpsModule; import com.android.systemui.assist.AssistModule; +import com.android.systemui.classifier.FalsingModule; import com.android.systemui.controls.dagger.ControlsModule; import com.android.systemui.demomode.dagger.DemoModeModule; import com.android.systemui.doze.dagger.DozeComponent; @@ -89,6 +90,7 @@ import dagger.Provides; AssistModule.class, ControlsModule.class, DemoModeModule.class, + FalsingModule.class, LogModule.class, PeopleHubModule.class, PowerModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFalsingManagerAdapter.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFalsingManagerAdapter.java index 94b8ba305d0c..ef696a88548a 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeFalsingManagerAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFalsingManagerAdapter.java @@ -16,8 +16,8 @@ package com.android.systemui.doze; +import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.doze.dagger.DozeScope; -import com.android.systemui.plugins.FalsingManager; import javax.inject.Inject; @@ -27,16 +27,16 @@ import javax.inject.Inject; @DozeScope public class DozeFalsingManagerAdapter implements DozeMachine.Part { - private final FalsingManager mFalsingManager; + private final FalsingCollector mFalsingCollector; @Inject - public DozeFalsingManagerAdapter(FalsingManager falsingManager) { - mFalsingManager = falsingManager; + public DozeFalsingManagerAdapter(FalsingCollector falsingCollector) { + mFalsingCollector = falsingCollector; } @Override public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) { - mFalsingManager.setShowingAod(isAodMode(newState)); + mFalsingCollector.setShowingAod(isAodMode(newState)); } private boolean isAodMode(DozeMachine.State state) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index aea0dd0ee4b1..c6bfcbaae188 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -88,12 +88,12 @@ import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.SystemUI; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardService; import com.android.systemui.keyguard.dagger.KeyguardModule; import com.android.systemui.navigationbar.NavigationModeController; -import com.android.systemui.plugins.FalsingManager; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.phone.BiometricUnlockController; import com.android.systemui.statusbar.phone.KeyguardBypassController; @@ -223,7 +223,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable { private boolean mBootSendUserPresent; private boolean mShuttingDown; private boolean mDozing; - private final FalsingManager mFalsingManager; + private final FalsingCollector mFalsingCollector; /** High level access to the power manager for WakeLocks */ private final PowerManager mPM; @@ -715,7 +715,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable { */ public KeyguardViewMediator( Context context, - FalsingManager falsingManager, + FalsingCollector falsingCollector, LockPatternUtils lockPatternUtils, BroadcastDispatcher broadcastDispatcher, Lazy<KeyguardViewController> statusBarKeyguardViewManagerLazy, @@ -727,7 +727,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable { NavigationModeController navigationModeController, KeyguardDisplayManager keyguardDisplayManager) { super(context); - mFalsingManager = falsingManager; + mFalsingCollector = falsingCollector; mLockPatternUtils = lockPatternUtils; mBroadcastDispatcher = broadcastDispatcher; mKeyguardViewControllerLazy = statusBarKeyguardViewManagerLazy; @@ -1682,7 +1682,7 @@ public class KeyguardViewMediator extends SystemUI implements Dumpable { "KeyguardViewMediator#handleMessage START_KEYGUARD_EXIT_ANIM"); StartKeyguardExitAnimParams params = (StartKeyguardExitAnimParams) msg.obj; handleStartKeyguardExitAnimation(params.startTime, params.fadeoutDuration); - mFalsingManager.onSuccessfulUnlock(); + mFalsingCollector.onSuccessfulUnlock(); Trace.endSection(); break; case KEYGUARD_DONE_PENDING_TIMEOUT: diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java index e50fd6a96b5c..d7b5eea84c36 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java @@ -31,6 +31,8 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardViewController; import com.android.keyguard.dagger.KeyguardStatusViewComponent; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.classifier.FalsingCollector; +import com.android.systemui.classifier.FalsingModule; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.UiBackground; @@ -39,7 +41,6 @@ import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.keyguard.FaceAuthScreenBrightnessController; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.navigationbar.NavigationModeController; -import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.KeyguardLiftController; @@ -59,7 +60,8 @@ import dagger.Provides; /** * Dagger Module providing {@link StatusBar}. */ -@Module(subcomponents = {KeyguardStatusViewComponent.class}) +@Module(subcomponents = {KeyguardStatusViewComponent.class}, + includes = {FalsingModule.class}) public class KeyguardModule { /** * Provides our instance of KeyguardViewMediator which is considered optional. @@ -68,7 +70,7 @@ public class KeyguardModule { @SysUISingleton public static KeyguardViewMediator newKeyguardViewMediator( Context context, - FalsingManager falsingManager, + FalsingCollector falsingCollector, LockPatternUtils lockPatternUtils, BroadcastDispatcher broadcastDispatcher, Lazy<KeyguardViewController> statusBarKeyguardViewManagerLazy, @@ -83,7 +85,7 @@ public class KeyguardModule { KeyguardDisplayManager keyguardDisplayManager) { return new KeyguardViewMediator( context, - falsingManager, + falsingCollector, lockPatternUtils, broadcastDispatcher, statusBarKeyguardViewManagerLazy, diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt index 5eb6687f0b81..935352665314 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt @@ -12,6 +12,7 @@ import android.view.ViewGroup import android.widget.LinearLayout import androidx.annotation.VisibleForTesting import com.android.systemui.R +import com.android.systemui.classifier.FalsingCollector import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.ActivityStarter @@ -44,6 +45,7 @@ class MediaCarouselController @Inject constructor( @Main executor: DelayableExecutor, private val mediaManager: MediaDataManager, configurationController: ConfigurationController, + falsingCollector: FalsingCollector, falsingManager: FalsingManager ) { /** @@ -156,7 +158,7 @@ class MediaCarouselController @Inject constructor( pageIndicator = mediaFrame.requireViewById(R.id.media_page_indicator) mediaCarouselScrollHandler = MediaCarouselScrollHandler(mediaCarousel, pageIndicator, executor, mediaManager::onSwipeToDismiss, this::updatePageIndicatorLocation, - this::closeGuts, falsingManager) + this::closeGuts, falsingCollector, falsingManager) isRtl = context.resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL inflateSettingsButton() mediaContent = mediaCarousel.requireViewById(R.id.media_carousel) diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt index cb14f31abd16..bb6fbfab4c90 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt @@ -31,6 +31,7 @@ import com.android.systemui.Gefingerpoken import com.android.systemui.qs.PageIndicator import com.android.systemui.R import com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS +import com.android.systemui.classifier.FalsingCollector import com.android.systemui.plugins.FalsingManager import com.android.wm.shell.animation.PhysicsAnimator import com.android.systemui.util.concurrency.DelayableExecutor @@ -58,6 +59,7 @@ class MediaCarouselScrollHandler( private val dismissCallback: () -> Unit, private var translationChangedListener: () -> Unit, private val closeGuts: () -> Unit, + private val falsingCollector: FalsingCollector, private val falsingManager: FalsingManager ) { /** @@ -162,7 +164,7 @@ class MediaCarouselScrollHandler( override fun onDown(e: MotionEvent?): Boolean { if (falsingProtectionNeeded) { - falsingManager.onNotificationStartDismissing() + falsingCollector.onNotificationStartDismissing() } return false } @@ -258,7 +260,7 @@ class MediaCarouselScrollHandler( private fun onTouch(motionEvent: MotionEvent): Boolean { val isUp = motionEvent.action == MotionEvent.ACTION_UP if (isUp && falsingProtectionNeeded) { - falsingManager.onNotificationStopDismissing() + falsingCollector.onNotificationStopDismissing() } if (gestureDetector.onTouchEvent(motionEvent)) { if (isUp) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java index d7a2975a4b04..0f0a9a2766f1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java @@ -134,7 +134,7 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState> getHost().collapsePanels(); Intent intent = mController.getPromptIntent(); ActivityStarter.OnDismissAction dismissAction = () -> { - mContext.startActivity(intent); + mHost.getUserContext().startActivity(intent); return false; }; mKeyguardDismissUtil.executeWhenUnlocked(dismissAction, false); diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java index 10a44ddb17f2..7ca82774f737 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java @@ -54,14 +54,31 @@ public class RecordingController private CountDownTimer mCountDownTimer = null; private BroadcastDispatcher mBroadcastDispatcher; + protected static final String INTENT_UPDATE_STATE = + "com.android.systemui.screenrecord.UPDATE_STATE"; + protected static final String EXTRA_STATE = "extra_state"; + private ArrayList<RecordingStateChangeCallback> mListeners = new ArrayList<>(); @VisibleForTesting protected final BroadcastReceiver mUserChangeReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - if (mStopIntent != null) { - stopRecording(); + stopRecording(); + } + }; + + @VisibleForTesting + protected final BroadcastReceiver mStateChangeReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (intent != null && INTENT_UPDATE_STATE.equals(intent.getAction())) { + if (intent.hasExtra(EXTRA_STATE)) { + boolean state = intent.getBooleanExtra(EXTRA_STATE, false); + updateState(state); + } else { + Log.e(TAG, "Received update intent with no state"); + } } } }; @@ -118,6 +135,10 @@ public class RecordingController IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_SWITCHED); mBroadcastDispatcher.registerReceiver(mUserChangeReceiver, userFilter, null, UserHandle.ALL); + + IntentFilter stateFilter = new IntentFilter(INTENT_UPDATE_STATE); + mBroadcastDispatcher.registerReceiver(mStateChangeReceiver, stateFilter, null, + UserHandle.ALL); Log.d(TAG, "sent start intent"); } catch (PendingIntent.CanceledException e) { Log.e(TAG, "Pending intent was cancelled: " + e.getMessage()); @@ -174,7 +195,6 @@ public class RecordingController } catch (PendingIntent.CanceledException e) { Log.e(TAG, "Error stopping: " + e.getMessage()); } - mBroadcastDispatcher.unregisterReceiver(mUserChangeReceiver); } /** @@ -182,6 +202,11 @@ public class RecordingController * @param isRecording */ public synchronized void updateState(boolean isRecording) { + if (!isRecording && mIsRecording) { + // Unregister receivers if we have stopped recording + mBroadcastDispatcher.unregisterReceiver(mUserChangeReceiver); + mBroadcastDispatcher.unregisterReceiver(mStateChangeReceiver); + } mIsRecording = isRecording; for (RecordingStateChangeCallback cb : mListeners) { if (isRecording) { diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java index 3bf118d1f39f..197582104f8e 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java @@ -69,6 +69,7 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis private static final String ACTION_STOP_NOTIF = "com.android.systemui.screenrecord.STOP_FROM_NOTIF"; private static final String ACTION_SHARE = "com.android.systemui.screenrecord.SHARE"; + private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF"; private final RecordingController mController; private final KeyguardDismissUtil mKeyguardDismissUtil; @@ -120,8 +121,8 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis String action = intent.getAction(); Log.d(TAG, "onStartCommand " + action); - int mCurrentUserId = mUserContextTracker.getUserContext().getUserId(); - UserHandle currentUser = new UserHandle(mCurrentUserId); + int currentUserId = mUserContextTracker.getUserContext().getUserId(); + UserHandle currentUser = new UserHandle(currentUserId); switch (action) { case ACTION_START: mAudioSource = ScreenRecordingAudioSource @@ -137,11 +138,22 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis mRecorder = new ScreenMediaRecorder( mUserContextTracker.getUserContext(), - mCurrentUserId, + currentUserId, mAudioSource, this ); - startRecording(); + + if (startRecording()) { + updateState(true); + createRecordingNotification(); + mUiEventLogger.log(Events.ScreenRecordEvent.SCREEN_RECORD_START); + } else { + updateState(false); + createErrorNotification(); + stopForeground(true); + stopSelf(); + return Service.START_NOT_STICKY; + } break; case ACTION_STOP_NOTIF: @@ -201,22 +213,63 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis return mRecorder; } + private void updateState(boolean state) { + int userId = mUserContextTracker.getUserContext().getUserId(); + if (userId == UserHandle.USER_SYSTEM) { + // Main user has a reference to the correct controller, so no need to use a broadcast + mController.updateState(state); + } else { + Intent intent = new Intent(RecordingController.INTENT_UPDATE_STATE); + intent.putExtra(RecordingController.EXTRA_STATE, state); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + sendBroadcast(intent, PERMISSION_SELF); + } + } + /** * Begin the recording session + * @return true if successful, false if something went wrong */ - private void startRecording() { + private boolean startRecording() { try { getRecorder().start(); - mController.updateState(true); - createRecordingNotification(); - mUiEventLogger.log(Events.ScreenRecordEvent.SCREEN_RECORD_START); - } catch (IOException | RemoteException | IllegalStateException e) { - Toast.makeText(this, - R.string.screenrecord_start_error, Toast.LENGTH_LONG) - .show(); + return true; + } catch (IOException | RemoteException | RuntimeException e) { + showErrorToast(R.string.screenrecord_start_error); e.printStackTrace(); - mController.updateState(false); } + return false; + } + + /** + * Simple error notification, needed since startForeground must be called to avoid errors + */ + @VisibleForTesting + protected void createErrorNotification() { + Resources res = getResources(); + NotificationChannel channel = new NotificationChannel( + CHANNEL_ID, + getString(R.string.screenrecord_name), + NotificationManager.IMPORTANCE_DEFAULT); + channel.setDescription(getString(R.string.screenrecord_channel_description)); + channel.enableVibration(true); + mNotificationManager.createNotificationChannel(channel); + + Bundle extras = new Bundle(); + extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, + res.getString(R.string.screenrecord_name)); + String notificationTitle = res.getString(R.string.screenrecord_start_error); + + Notification.Builder builder = new Notification.Builder(this, CHANNEL_ID) + .setSmallIcon(R.drawable.ic_screenrecord) + .setContentTitle(notificationTitle) + .addExtras(extras); + startForeground(NOTIFICATION_RECORDING_ID, builder.build()); + } + + @VisibleForTesting + protected void showErrorToast(int stringId) { + Toast.makeText(this, stringId, Toast.LENGTH_LONG).show(); } @VisibleForTesting @@ -247,6 +300,7 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis .setColorized(true) .setColor(getResources().getColor(R.color.GM2_red_700)) .setOngoing(true) + .setShowForegroundImmediately(true) .setContentIntent( PendingIntent.getService(this, REQUEST_CODE, stopIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE)) @@ -326,7 +380,7 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis } else { Log.e(TAG, "stopRecording called, but recorder was null"); } - mController.updateState(false); + updateState(false); } private void saveRecording(int userId) { @@ -344,8 +398,7 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis } } catch (IOException e) { Log.e(TAG, "Error saving screen recording: " + e.getMessage()); - Toast.makeText(this, R.string.screenrecord_delete_error, Toast.LENGTH_LONG) - .show(); + showErrorToast(R.string.screenrecord_delete_error); } finally { mNotificationManager.cancelAsUser(null, NOTIFICATION_PROCESSING_ID, currentUser); } diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java index 45564b0bfa6b..9037192e2ddc 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java @@ -94,7 +94,7 @@ public class ScreenMediaRecorder { mAudioSource = audioSource; } - private void prepare() throws IOException, RemoteException { + private void prepare() throws IOException, RemoteException, RuntimeException { //Setup media projection IBinder b = ServiceManager.getService(MEDIA_PROJECTION_SERVICE); IMediaProjectionManager mediaService = @@ -257,7 +257,7 @@ public class ScreenMediaRecorder { /** * Start screen recording */ - void start() throws IOException, RemoteException, IllegalStateException { + void start() throws IOException, RemoteException, RuntimeException { Log.d(TAG, "start recording"); prepare(); mMediaRecorder.start(); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java index 672f82c2a583..2c82bcb24ad0 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java @@ -43,6 +43,7 @@ import android.view.WindowManager; import com.android.internal.logging.UiEventLogger; import com.android.internal.util.ScreenshotHelper; +import com.android.systemui.R; import com.android.systemui.shared.recents.utilities.BitmapUtil; import java.util.function.Consumer; @@ -55,6 +56,7 @@ public class TakeScreenshotService extends Service { private final ScreenshotController mScreenshot; private final UserManager mUserManager; private final UiEventLogger mUiEventLogger; + private final ScreenshotNotificationsController mNotificationsController; private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @@ -90,6 +92,8 @@ public class TakeScreenshotService extends Service { // animation and error notification. if (!mUserManager.isUserUnlocked()) { Log.w(TAG, "Skipping screenshot because storage is locked!"); + mNotificationsController.notifyScreenshotError( + R.string.screenshot_failed_to_save_user_locked_text); post(() -> uriConsumer.accept(null)); post(onComplete); return; @@ -125,11 +129,15 @@ public class TakeScreenshotService extends Service { }; @Inject - public TakeScreenshotService(ScreenshotController screenshotController, UserManager userManager, - UiEventLogger uiEventLogger) { + public TakeScreenshotService( + ScreenshotController screenshotController, + UserManager userManager, + UiEventLogger uiEventLogger, + ScreenshotNotificationsController notificationsController) { mScreenshot = screenshotController; mUserManager = userManager; mUiEventLogger = uiEventLogger; + mNotificationsController = notificationsController; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java index e61e05a7dc2f..7ef88bb0c2c8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/DragDownHelper.java @@ -31,6 +31,7 @@ import com.android.systemui.ExpandHelper; import com.android.systemui.Gefingerpoken; import com.android.systemui.Interpolators; import com.android.systemui.R; +import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.statusbar.notification.row.ExpandableView; @@ -46,6 +47,7 @@ public class DragDownHelper implements Gefingerpoken { private static final int SPRING_BACK_ANIMATION_LENGTH_MS = 375; private int mMinDragDistance; + private final FalsingManager mFalsingManager; private ExpandHelper.Callback mCallback; private float mInitialTouchX; private float mInitialTouchY; @@ -58,20 +60,21 @@ public class DragDownHelper implements Gefingerpoken { private boolean mDraggedFarEnough; private ExpandableView mStartingChild; private float mLastHeight; - private FalsingManager mFalsingManager; + private FalsingCollector mFalsingCollector; public DragDownHelper(Context context, View host, ExpandHelper.Callback callback, - DragDownCallback dragDownCallback, - FalsingManager falsingManager) { + DragDownCallback dragDownCallback, FalsingManager falsingManager, + FalsingCollector falsingCollector) { mMinDragDistance = context.getResources().getDimensionPixelSize( R.dimen.keyguard_drag_down_min_distance); + mFalsingManager = falsingManager; final ViewConfiguration configuration = ViewConfiguration.get(context); mTouchSlop = configuration.getScaledTouchSlop(); mSlopMultiplier = configuration.getScaledAmbiguousGestureMultiplier(); mCallback = callback; mDragDownCallback = dragDownCallback; mHost = host; - mFalsingManager = falsingManager; + mFalsingCollector = falsingCollector; } @Override @@ -96,7 +99,7 @@ public class DragDownHelper implements Gefingerpoken { ? mTouchSlop * mSlopMultiplier : mTouchSlop; if (h > touchSlop && h > Math.abs(x - mInitialTouchX)) { - mFalsingManager.onNotificatonStartDraggingDown(); + mFalsingCollector.onNotificationStartDraggingDown(); mDraggingDown = true; captureStartingChild(mInitialTouchX, mInitialTouchY); mInitialTouchY = y; @@ -229,7 +232,7 @@ public class DragDownHelper implements Gefingerpoken { } private void stopDragging() { - mFalsingManager.onNotificatonStopDraggingDown(); + mFalsingCollector.onNotificationStopDraggingDown(); if (mStartingChild != null) { cancelExpansion(mStartingChild); mStartingChild = null; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt index 6fa3633acc43..b7343f67a86d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt @@ -31,6 +31,7 @@ import com.android.systemui.Gefingerpoken import com.android.systemui.Interpolators import com.android.systemui.R import com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN +import com.android.systemui.classifier.FalsingCollector import com.android.systemui.dagger.SysUISingleton import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.statusbar.StatusBarStateController @@ -57,7 +58,8 @@ constructor( private val headsUpManager: HeadsUpManagerPhone, private val roundnessManager: NotificationRoundnessManager, private val statusBarStateController: StatusBarStateController, - private val falsingManager: FalsingManager + private val falsingManager: FalsingManager, + private val falsingCollector: FalsingCollector ) : Gefingerpoken { companion object { private val RUBBERBAND_FACTOR_STATIC = 0.25f @@ -148,7 +150,7 @@ constructor( MotionEvent.ACTION_MOVE -> { val h = y - mInitialTouchY if (h > mTouchSlop && h > Math.abs(x - mInitialTouchX)) { - falsingManager.onStartExpandingFromPulse() + falsingCollector.onStartExpandingFromPulse() isExpanding = true captureStartingChild(mInitialTouchX, mInitialTouchY) mInitialTouchY = y @@ -301,7 +303,7 @@ constructor( private fun cancelExpansion() { isExpanding = false - falsingManager.onExpansionFromPulseStopped() + falsingCollector.onExpansionFromPulseStopped() if (mStartingChild != null) { reset(mStartingChild!!) mStartingChild = null diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java index 0b79387fac77..50bbc38eddf3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java @@ -22,6 +22,7 @@ import android.view.View; import android.view.accessibility.AccessibilityManager; import com.android.systemui.Gefingerpoken; +import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.statusbar.phone.NotificationTapHelper; import com.android.systemui.util.ViewController; @@ -36,6 +37,7 @@ public class ActivatableNotificationViewController private final ExpandableOutlineViewController mExpandableOutlineViewController; private final AccessibilityManager mAccessibilityManager; private final FalsingManager mFalsingManager; + private final FalsingCollector mFalsingCollector; private final NotificationTapHelper mNotificationTapHelper; private final TouchHandler mTouchHandler = new TouchHandler(); @@ -45,17 +47,19 @@ public class ActivatableNotificationViewController public ActivatableNotificationViewController(ActivatableNotificationView view, NotificationTapHelper.Factory notificationTapHelpFactory, ExpandableOutlineViewController expandableOutlineViewController, - AccessibilityManager accessibilityManager, FalsingManager falsingManager) { + AccessibilityManager accessibilityManager, FalsingManager falsingManager, + FalsingCollector falsingCollector) { super(view); mExpandableOutlineViewController = expandableOutlineViewController; mAccessibilityManager = accessibilityManager; mFalsingManager = falsingManager; + mFalsingCollector = falsingCollector; mNotificationTapHelper = notificationTapHelpFactory.create( (active) -> { if (active) { mView.makeActive(); - mFalsingManager.onNotificationActive(); + mFalsingCollector.onNotificationActive(); } else { mView.makeInactive(true /* animate */); } @@ -64,7 +68,7 @@ public class ActivatableNotificationViewController mView.setOnActivatedListener(new ActivatableNotificationView.OnActivatedListener() { @Override public void onActivated(ActivatableNotificationView view) { - mFalsingManager.onNotificationActive(); + mFalsingCollector.onNotificationActive(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 5682c88edc63..5ed17f1ee07f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -43,7 +43,6 @@ import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; -import android.provider.Settings; import android.service.notification.StatusBarNotification; import android.util.ArraySet; import android.util.AttributeSet; @@ -74,7 +73,7 @@ import com.android.internal.widget.MessagingLayout; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; -import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem; @@ -213,7 +212,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private NotificationGuts mGuts; private NotificationEntry mEntry; private String mAppName; - private FalsingManager mFalsingManager; + private FalsingCollector mFalsingCollector; /** * Whether or not the notification is using the heads up view and should peek from the top. @@ -1584,7 +1583,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView OnExpandClickListener onExpandClickListener, NotificationMediaManager notificationMediaManager, CoordinateOnClickListener onFeedbackClickListener, - FalsingManager falsingManager, + FalsingCollector falsingCollector, StatusBarStateController statusBarStateController, PeopleNotificationIdentifier peopleNotificationIdentifier, OnUserInteractionCallback onUserInteractionCallback, @@ -1609,7 +1608,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mOnExpandClickListener = onExpandClickListener; mMediaManager = notificationMediaManager; setOnFeedbackClickListener(onFeedbackClickListener); - mFalsingManager = falsingManager; + mFalsingCollector = falsingCollector; mStatusBarStateController = statusBarStateController; mPeopleNotificationIdentifier = peopleNotificationIdentifier; @@ -2176,7 +2175,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView * @param allowChildExpansion whether a call to this method allows expanding children */ public void setUserExpanded(boolean userExpanded, boolean allowChildExpansion) { - mFalsingManager.setNotificationExpanded(); + mFalsingCollector.setNotificationExpanded(); if (mIsSummaryWithChildren && !shouldShowPublic() && allowChildExpansion && !mChildrenContainer.showingAsLowPriority()) { final boolean wasExpanded = mGroupExpansionManager.isGroupExpanded(mEntry); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java index cb2af54a25cb..0d0e97eae519 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java @@ -25,7 +25,7 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; -import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shared.plugins.PluginManager; @@ -78,7 +78,7 @@ public class ExpandableNotificationRowController implements NodeController { private final ExpandableNotificationRow.CoordinateOnClickListener mOnFeedbackClickListener; private final NotificationGutsManager mNotificationGutsManager; private final OnUserInteractionCallback mOnUserInteractionCallback; - private final FalsingManager mFalsingManager; + private final FalsingCollector mFalsingCollector; private final boolean mAllowLongPress; private final PeopleNotificationIdentifier mPeopleNotificationIdentifier; private final Optional<BubblesManager> mBubblesManagerOptional; @@ -104,7 +104,7 @@ public class ExpandableNotificationRowController implements NodeController { NotificationGutsManager notificationGutsManager, @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress, OnUserInteractionCallback onUserInteractionCallback, - FalsingManager falsingManager, + FalsingCollector falsingCollector, PeopleNotificationIdentifier peopleNotificationIdentifier, Optional<BubblesManager> bubblesManagerOptional) { mView = view; @@ -127,7 +127,7 @@ public class ExpandableNotificationRowController implements NodeController { mOnUserInteractionCallback = onUserInteractionCallback; mOnFeedbackClickListener = mNotificationGutsManager::openGuts; mAllowLongPress = allowLongPress; - mFalsingManager = falsingManager; + mFalsingCollector = falsingCollector; mPeopleNotificationIdentifier = peopleNotificationIdentifier; mBubblesManagerOptional = bubblesManagerOptional; } @@ -150,7 +150,7 @@ public class ExpandableNotificationRowController implements NodeController { mOnExpandClickListener, mMediaManager, mOnFeedbackClickListener, - mFalsingManager, + mFalsingCollector, mStatusBarStateController, mPeopleNotificationIdentifier, mOnUserInteractionCallback, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index 006d8da91941..9d4665a5eeca 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -60,10 +60,10 @@ import com.android.systemui.ExpandHelper; import com.android.systemui.Gefingerpoken; import com.android.systemui.R; import com.android.systemui.SwipeHelper; +import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.media.KeyguardMediaController; -import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; @@ -143,7 +143,7 @@ public class NotificationStackScrollLayoutController { private final ConfigurationController mConfigurationController; private final ZenModeController mZenModeController; private final MetricsLogger mMetricsLogger; - private final FalsingManager mFalsingManager; + private final FalsingCollector mFalsingCollector; private final Resources mResources; private final NotificationSwipeHelper.Builder mNotificationSwipeHelperBuilder; private final ScrimController mScrimController; @@ -361,7 +361,7 @@ public class NotificationStackScrollLayoutController { @Override public void onDragCancelled(View v) { - mFalsingManager.onNotificationStopDismissing(); + mFalsingCollector.onNotificationStopDismissing(); } /** @@ -405,8 +405,8 @@ public class NotificationStackScrollLayoutController { } mView.addSwipedOutView(view); - mFalsingManager.onNotificationDismissed(); - if (mFalsingManager.shouldEnforceBouncer()) { + mFalsingCollector.onNotificationDismissed(); + if (mFalsingCollector.shouldEnforceBouncer()) { mStatusBar.executeRunnableDismissingKeyguard( null, null /* cancelAction */, @@ -448,7 +448,7 @@ public class NotificationStackScrollLayoutController { @Override public void onBeginDrag(View v) { - mFalsingManager.onNotificationStartDismissing(); + mFalsingCollector.onNotificationStartDismissing(); mView.onSwipeBegin(); } @@ -550,7 +550,7 @@ public class NotificationStackScrollLayoutController { SysuiColorExtractor colorExtractor, NotificationLockscreenUserManager lockscreenUserManager, MetricsLogger metricsLogger, - FalsingManager falsingManager, + FalsingCollector falsingCollector, @Main Resources resources, NotificationSwipeHelper.Builder notificationSwipeHelperBuilder, StatusBar statusBar, @@ -584,7 +584,7 @@ public class NotificationStackScrollLayoutController { mColorExtractor = colorExtractor; mLockscreenUserManager = lockscreenUserManager; mMetricsLogger = metricsLogger; - mFalsingManager = falsingManager; + mFalsingCollector = falsingCollector; mResources = resources; mNotificationSwipeHelperBuilder = notificationSwipeHelperBuilder; mStatusBar = statusBar; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java index 80cb289a3c0d..9854f5450df1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -39,9 +39,9 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.keyguard.ViewMediatorCallback; import com.android.keyguard.dagger.KeyguardBouncerComponent; import com.android.systemui.DejankUtils; +import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.dagger.qualifiers.RootView; import com.android.systemui.keyguard.DismissCallbackRegistry; -import com.android.systemui.plugins.FalsingManager; import com.android.systemui.shared.system.SysUiStatsLog; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -65,7 +65,7 @@ public class KeyguardBouncer { protected final Context mContext; protected final ViewMediatorCallback mCallback; protected final ViewGroup mContainer; - private final FalsingManager mFalsingManager; + private final FalsingCollector mFalsingCollector; private final DismissCallbackRegistry mDismissCallbackRegistry; private final Handler mHandler; private final List<BouncerExpansionCallback> mExpansionCallbacks = new ArrayList<>(); @@ -100,7 +100,7 @@ public class KeyguardBouncer { private KeyguardBouncer(Context context, ViewMediatorCallback callback, ViewGroup container, - DismissCallbackRegistry dismissCallbackRegistry, FalsingManager falsingManager, + DismissCallbackRegistry dismissCallbackRegistry, FalsingCollector falsingCollector, BouncerExpansionCallback expansionCallback, KeyguardStateController keyguardStateController, KeyguardUpdateMonitor keyguardUpdateMonitor, @@ -111,7 +111,7 @@ public class KeyguardBouncer { mCallback = callback; mContainer = container; mKeyguardUpdateMonitor = keyguardUpdateMonitor; - mFalsingManager = falsingManager; + mFalsingCollector = falsingCollector; mDismissCallbackRegistry = dismissCallbackRegistry; mHandler = handler; mKeyguardStateController = keyguardStateController; @@ -203,7 +203,7 @@ public class KeyguardBouncer { * will never be notified and its internal state will be out of sync. */ private void onFullyShown() { - mFalsingManager.onBouncerShown(); + mFalsingCollector.onBouncerShown(); if (mKeyguardViewController == null) { Log.wtf(TAG, "onFullyShown when view was null"); } else { @@ -223,7 +223,7 @@ public class KeyguardBouncer { if (mRoot != null) { mRoot.setVisibility(View.INVISIBLE); } - mFalsingManager.onBouncerHidden(); + mFalsingCollector.onBouncerHidden(); DejankUtils.postAfterTraversal(mResetRunnable); } @@ -290,7 +290,7 @@ public class KeyguardBouncer { mDismissCallbackRegistry.notifyDismissCancelled(); } mIsScrimmed = false; - mFalsingManager.onBouncerHidden(); + mFalsingCollector.onBouncerHidden(); mCallback.onBouncerVisiblityChanged(false /* shown */); cancelShowRunnable(); if (mKeyguardViewController != null) { @@ -327,7 +327,7 @@ public class KeyguardBouncer { public void reset() { cancelShowRunnable(); inflateView(); - mFalsingManager.onBouncerHidden(); + mFalsingCollector.onBouncerHidden(); } public void onScreenTurnedOff() { @@ -541,7 +541,7 @@ public class KeyguardBouncer { private final Context mContext; private final ViewMediatorCallback mCallback; private final DismissCallbackRegistry mDismissCallbackRegistry; - private final FalsingManager mFalsingManager; + private final FalsingCollector mFalsingCollector; private final KeyguardStateController mKeyguardStateController; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final KeyguardBypassController mKeyguardBypassController; @@ -551,7 +551,7 @@ public class KeyguardBouncer { @Inject public Factory(Context context, ViewMediatorCallback callback, - DismissCallbackRegistry dismissCallbackRegistry, FalsingManager falsingManager, + DismissCallbackRegistry dismissCallbackRegistry, FalsingCollector falsingCollector, KeyguardStateController keyguardStateController, KeyguardUpdateMonitor keyguardUpdateMonitor, KeyguardBypassController keyguardBypassController, Handler handler, @@ -560,7 +560,7 @@ public class KeyguardBouncer { mContext = context; mCallback = callback; mDismissCallbackRegistry = dismissCallbackRegistry; - mFalsingManager = falsingManager; + mFalsingCollector = falsingCollector; mKeyguardStateController = keyguardStateController; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mKeyguardBypassController = keyguardBypassController; @@ -572,7 +572,7 @@ public class KeyguardBouncer { public KeyguardBouncer create(@RootView ViewGroup container, BouncerExpansionCallback expansionCallback) { return new KeyguardBouncer(mContext, mCallback, container, - mDismissCallbackRegistry, mFalsingManager, expansionCallback, + mDismissCallbackRegistry, mFalsingCollector, expansionCallback, mKeyguardStateController, mKeyguardUpdateMonitor, mKeyguardBypassController, mHandler, mKeyguardSecurityModel, mKeyguardBouncerComponentFactory); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index 6b8881168e4e..f7139aa2acce 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -79,6 +79,7 @@ import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.biometrics.AuthController; import com.android.systemui.classifier.Classifier; +import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.dagger.qualifiers.DisplayId; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.DozeLog; @@ -364,7 +365,8 @@ public class NotificationPanelViewController extends PanelViewController { private boolean mHeadsUpAnimatingAway; private boolean mLaunchingAffordance; private boolean mAffordanceHasPreview; - private FalsingManager mFalsingManager; + private final FalsingManager mFalsingManager; + private final FalsingCollector mFalsingCollector; private String mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE; private Runnable mHeadsUpExistenceChangedRunnable = () -> { @@ -502,7 +504,7 @@ public class NotificationPanelViewController extends PanelViewController { NotificationWakeUpCoordinator coordinator, PulseExpansionHandler pulseExpansionHandler, DynamicPrivacyController dynamicPrivacyController, KeyguardBypassController bypassController, FalsingManager falsingManager, - ShadeController shadeController, + FalsingCollector falsingCollector, ShadeController shadeController, NotificationLockscreenUserManager notificationLockscreenUserManager, NotificationEntryManager notificationEntryManager, KeyguardStateController keyguardStateController, @@ -543,6 +545,7 @@ public class NotificationPanelViewController extends PanelViewController { mView.setWillNotDraw(!DEBUG); mInjectionInflationController = injectionInflationController; mFalsingManager = falsingManager; + mFalsingCollector = falsingCollector; mPowerManager = powerManager; mWakeUpCoordinator = coordinator; mAccessibilityManager = accessibilityManager; @@ -1426,7 +1429,7 @@ public class NotificationPanelViewController extends PanelViewController { private void handleQsDown(MotionEvent event) { if (event.getActionMasked() == MotionEvent.ACTION_DOWN && shouldQuickSettingsIntercept( event.getX(), event.getY(), -1)) { - mFalsingManager.onQsDown(); + mFalsingCollector.onQsDown(); mQsTracking = true; onQsExpansionStarted(); mInitialHeightOnTouch = mQsExpansionHeight; @@ -1606,7 +1609,7 @@ public class NotificationPanelViewController extends PanelViewController { mQsExpanded = expanded; updateQsState(); requestPanelHeightUpdate(); - mFalsingManager.setQsExpanded(expanded); + mFalsingCollector.setQsExpanded(expanded); mStatusBar.setQsExpanded(expanded); mNotificationContainerParent.setQsExpanded(expanded); mPulseExpansionHandler.setQsExpanded(expanded); @@ -1743,7 +1746,7 @@ public class NotificationPanelViewController extends PanelViewController { } if (!mFalsingManager.isUnlockingDisabled() && mQsFullyExpanded - && mFalsingManager.shouldEnforceBouncer()) { + && mFalsingCollector.shouldEnforceBouncer()) { mStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */, false /* dismissShade */, true /* afterKeyguardGone */, false /* deferred */); } @@ -2404,7 +2407,7 @@ public class NotificationPanelViewController extends PanelViewController { @Override protected void onTrackingStarted() { - mFalsingManager.onTrackingStarted(!mKeyguardStateController.canDismissLockScreen()); + mFalsingCollector.onTrackingStarted(!mKeyguardStateController.canDismissLockScreen()); super.onTrackingStarted(); if (mQsFullyExpanded) { mQsExpandImmediate = true; @@ -2418,7 +2421,7 @@ public class NotificationPanelViewController extends PanelViewController { @Override protected void onTrackingStopped(boolean expand) { - mFalsingManager.onTrackingStopped(); + mFalsingCollector.onTrackingStopped(); super.onTrackingStopped(expand); if (expand) { mNotificationStackScrollLayoutController.setOverScrolledPixels(0.0f, true /* onTop */, @@ -3365,8 +3368,8 @@ public class NotificationPanelViewController extends PanelViewController { if (start) { mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_DIALER, lengthDp, velocityDp); mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_DIALER); - mFalsingManager.onLeftAffordanceOn(); - if (mFalsingManager.shouldEnforceBouncer()) { + mFalsingCollector.onLeftAffordanceOn(); + if (mFalsingCollector.shouldEnforceBouncer()) { mStatusBar.executeRunnableDismissingKeyguard( () -> mKeyguardBottomArea.launchLeftAffordance(), null, true /* dismissShade */, false /* afterKeyguardGone */, @@ -3381,8 +3384,8 @@ public class NotificationPanelViewController extends PanelViewController { MetricsEvent.ACTION_LS_CAMERA, lengthDp, velocityDp); mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_CAMERA); } - mFalsingManager.onCameraOn(); - if (mFalsingManager.shouldEnforceBouncer()) { + mFalsingCollector.onCameraOn(); + if (mFalsingCollector.shouldEnforceBouncer()) { mStatusBar.executeRunnableDismissingKeyguard( () -> mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource), null, true /* dismissShade */, false /* afterKeyguardGone */, @@ -3413,7 +3416,7 @@ public class NotificationPanelViewController extends PanelViewController { @Override public void onSwipingStarted(boolean rightIcon) { - mFalsingManager.onAffordanceSwipingStarted(rightIcon); + mFalsingCollector.onAffordanceSwipingStarted(rightIcon); boolean camera = mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? !rightIcon @@ -3428,7 +3431,7 @@ public class NotificationPanelViewController extends PanelViewController { @Override public void onSwipingAborted() { - mFalsingManager.onAffordanceSwipingAborted(); + mFalsingCollector.onAffordanceSwipingAborted(); mKeyguardBottomArea.unbindCameraPrewarmService(false /* launched */); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java index 3db3ab5a56c8..ba4fbb871b97 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java @@ -36,6 +36,7 @@ import android.view.ViewGroup; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.ExpandHelper; import com.android.systemui.R; +import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.dock.DockManager; import com.android.systemui.doze.DozeLog; import com.android.systemui.plugins.FalsingManager; @@ -73,6 +74,7 @@ public class NotificationShadeWindowViewController { private final KeyguardBypassController mBypassController; private final PluginManager mPluginManager; private final FalsingManager mFalsingManager; + private final FalsingCollector mFalsingCollector; private final TunerService mTunerService; private final NotificationLockscreenUserManager mNotificationLockscreenUserManager; private final NotificationEntryManager mNotificationEntryManager; @@ -118,6 +120,7 @@ public class NotificationShadeWindowViewController { DynamicPrivacyController dynamicPrivacyController, KeyguardBypassController bypassController, FalsingManager falsingManager, + FalsingCollector falsingCollector, PluginManager pluginManager, TunerService tunerService, NotificationLockscreenUserManager notificationLockscreenUserManager, @@ -140,6 +143,7 @@ public class NotificationShadeWindowViewController { mDynamicPrivacyController = dynamicPrivacyController; mBypassController = bypassController; mFalsingManager = falsingManager; + mFalsingCollector = falsingCollector; mPluginManager = pluginManager; mTunerService = tunerService; mNotificationLockscreenUserManager = notificationLockscreenUserManager; @@ -234,7 +238,7 @@ public class NotificationShadeWindowViewController { if (mTouchCancelled || mExpandAnimationRunning || mExpandAnimationPending) { return false; } - mFalsingManager.onTouchEvent(ev, mView.getWidth(), mView.getHeight()); + mFalsingCollector.onTouchEvent(ev, mView.getWidth(), mView.getHeight()); mGestureDetector.onTouchEvent(ev); if (mBrightnessMirror != null && mBrightnessMirror.getVisibility() == View.VISIBLE) { @@ -394,7 +398,7 @@ public class NotificationShadeWindowViewController { setDragDownHelper( new DragDownHelper( mView.getContext(), mView, expandHelperCallback, - dragDownCallback, mFalsingManager)); + dragDownCallback, mFalsingManager, mFalsingCollector)); mDepthController.setRoot(mView); mNotificationPanelViewController.addExpansionListener(mDepthController); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index e6ac7dcee8a7..c8c5a6331a3b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -147,6 +147,7 @@ import com.android.systemui.SystemUI; import com.android.systemui.assist.AssistManager; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.charging.WirelessChargingAnimation; +import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.demomode.DemoMode; @@ -384,6 +385,7 @@ public class StatusBar extends SystemUI implements DemoMode, private final StatusBarTouchableRegionManager mStatusBarTouchableRegionManager; private final DynamicPrivacyController mDynamicPrivacyController; private final BypassHeadsUpNotifier mBypassHeadsUpNotifier; + private final FalsingCollector mFalsingCollector; private final FalsingManager mFalsingManager; private final BroadcastDispatcher mBroadcastDispatcher; private final ConfigurationController mConfigurationController; @@ -692,6 +694,7 @@ public class StatusBar extends SystemUI implements DemoMode, DynamicPrivacyController dynamicPrivacyController, BypassHeadsUpNotifier bypassHeadsUpNotifier, FalsingManager falsingManager, + FalsingCollector falsingCollector, BroadcastDispatcher broadcastDispatcher, RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler, NotificationGutsManager notificationGutsManager, @@ -772,6 +775,7 @@ public class StatusBar extends SystemUI implements DemoMode, mStatusBarTouchableRegionManager = statusBarTouchableRegionManager; mDynamicPrivacyController = dynamicPrivacyController; mBypassHeadsUpNotifier = bypassHeadsUpNotifier; + mFalsingCollector = falsingCollector; mFalsingManager = falsingManager; mBroadcastDispatcher = broadcastDispatcher; mRemoteInputQuickSettingsDisabler = remoteInputQuickSettingsDisabler; @@ -1389,7 +1393,7 @@ public class StatusBar extends SystemUI implements DemoMode, where.getLocationInWindow(mTmpInt2); mWakeUpTouchLocation = new PointF(mTmpInt2[0] + where.getWidth() / 2, mTmpInt2[1] + where.getHeight() / 2); - mFalsingManager.onScreenOnFromTouch(); + mFalsingCollector.onScreenOnFromTouch(); } } @@ -1648,7 +1652,7 @@ public class StatusBar extends SystemUI implements DemoMode, return; } mReportRejectedTouch.setVisibility(mState == StatusBarState.KEYGUARD && !mDozing - && mFalsingManager.isReportingEnabled() ? View.VISIBLE : View.INVISIBLE); + && mFalsingCollector.isReportingEnabled() ? View.VISIBLE : View.INVISIBLE); } /** @@ -3685,7 +3689,7 @@ public class StatusBar extends SystemUI implements DemoMode, } public void onUnlockHintStarted() { - mFalsingManager.onUnlockHintStarted(); + mFalsingCollector.onUnlockHintStarted(); mKeyguardIndicationController.showTransientIndication(R.string.keyguard_unlock); } @@ -3695,17 +3699,17 @@ public class StatusBar extends SystemUI implements DemoMode, } public void onCameraHintStarted() { - mFalsingManager.onCameraHintStarted(); + mFalsingCollector.onCameraHintStarted(); mKeyguardIndicationController.showTransientIndication(R.string.camera_hint); } public void onVoiceAssistHintStarted() { - mFalsingManager.onLeftAffordanceHintStarted(); + mFalsingCollector.onLeftAffordanceHintStarted(); mKeyguardIndicationController.showTransientIndication(R.string.voice_hint); } public void onPhoneHintStarted() { - mFalsingManager.onLeftAffordanceHintStarted(); + mFalsingCollector.onLeftAffordanceHintStarted(); mKeyguardIndicationController.showTransientIndication(R.string.phone_hint); } @@ -3756,7 +3760,7 @@ public class StatusBar extends SystemUI implements DemoMode, boolean fullShadeNeedsBouncer = !mLockscreenUserManager. userAllowsPrivateNotificationsInPublic(mLockscreenUserManager.getCurrentUserId()) || !mLockscreenUserManager.shouldShowLockscreenNotifications() - || mFalsingManager.shouldEnforceBouncer(); + || mFalsingCollector.shouldEnforceBouncer(); if (mKeyguardBypassController.getBypassEnabled()) { fullShadeNeedsBouncer = false; } @@ -3888,7 +3892,7 @@ public class StatusBar extends SystemUI implements DemoMode, final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() { @Override public void onScreenTurningOn() { - mFalsingManager.onScreenTurningOn(); + mFalsingCollector.onScreenTurningOn(); mNotificationPanelViewController.onScreenTurningOn(); } @@ -3899,7 +3903,7 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void onScreenTurnedOff() { - mFalsingManager.onScreenOff(); + mFalsingCollector.onScreenOff(); mScrimController.onScreenTurnedOff(); updateIsKeyguard(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java index f6631ce1414c..2aa3f37ccf11 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java @@ -31,6 +31,7 @@ import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.InitController; import com.android.systemui.assist.AssistManager; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.UiBackground; @@ -136,6 +137,7 @@ public interface StatusBarPhoneModule { DynamicPrivacyController dynamicPrivacyController, BypassHeadsUpNotifier bypassHeadsUpNotifier, FalsingManager falsingManager, + FalsingCollector falsingCollector, BroadcastDispatcher broadcastDispatcher, RemoteInputQuickSettingsDisabler remoteInputQuickSettingsDisabler, NotificationGutsManager notificationGutsManager, @@ -216,6 +218,7 @@ public interface StatusBarPhoneModule { dynamicPrivacyController, bypassHeadsUpNotifier, falsingManager, + falsingCollector, broadcastDispatcher, remoteInputQuickSettingsDisabler, notificationGutsManager, diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/ClassifierTest.java index 69d39fa10b72..472ed7a22fe3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/ClassifierTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static com.android.systemui.classifier.Classifier.UNLOCK; @@ -55,7 +55,7 @@ public class ClassifierTest extends LeakCheckedTest { resetDataProvider(); } - FalsingDataProvider getDataProvider() { + protected FalsingDataProvider getDataProvider() { return mDataProvider; } @@ -63,15 +63,15 @@ public class ClassifierTest extends LeakCheckedTest { return mFakeBatteryController; } - void setOffsetX(float offsetX) { + protected void setOffsetX(float offsetX) { mOffsetX = offsetX; } - void setOffsetY(float offsetY) { + protected void setOffsetY(float offsetY) { mOffsetY = offsetY; } - void resetDataProvider() { + protected void resetDataProvider() { for (MotionEvent motionEvent : mMotionEvents) { motionEvent.recycle(); } @@ -81,28 +81,28 @@ public class ClassifierTest extends LeakCheckedTest { mDataProvider.onSessionEnd(); } - MotionEvent appendDownEvent(float x, float y) { + protected MotionEvent appendDownEvent(float x, float y) { return appendMotionEvent(MotionEvent.ACTION_DOWN, x, y); } - MotionEvent appendDownEvent(float x, float y, long eventTime) { + protected MotionEvent appendDownEvent(float x, float y, long eventTime) { return appendMotionEvent(MotionEvent.ACTION_DOWN, x, y, eventTime); } - MotionEvent appendMoveEvent(float x, float y) { + protected MotionEvent appendMoveEvent(float x, float y) { return appendMotionEvent(MotionEvent.ACTION_MOVE, x, y); } - MotionEvent appendMoveEvent(float x, float y, long eventTime) { + protected MotionEvent appendMoveEvent(float x, float y, long eventTime) { return appendMotionEvent(MotionEvent.ACTION_MOVE, x, y, eventTime); } - MotionEvent appendUpEvent(float x, float y) { + protected MotionEvent appendUpEvent(float x, float y) { return appendMotionEvent(MotionEvent.ACTION_UP, x, y); } - MotionEvent appendUpEvent(float x, float y, long eventTime) { + protected MotionEvent appendUpEvent(float x, float y, long eventTime) { return appendMotionEvent(MotionEvent.ACTION_UP, x, y, eventTime); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java index 30dddc02be90..af5e789a0c41 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/BrightLineFalsingManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.never; @@ -22,123 +22,102 @@ import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.content.res.Resources; -import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; -import android.util.DisplayMetrics; -import android.view.ViewConfiguration; -import com.android.internal.logging.testing.UiEventLoggerFake; +import androidx.test.filters.SmallTest; + import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.systemui.R; -import com.android.systemui.dock.DockManager; -import com.android.systemui.dock.DockManagerFake; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.StatusBarStateControllerImpl; import com.android.systemui.statusbar.SysuiStatusBarStateController; -import com.android.systemui.util.DeviceConfigProxy; -import com.android.systemui.util.DeviceConfigProxyFake; import com.android.systemui.util.sensors.ProximitySensor; import com.android.systemui.util.sensors.ThresholdSensor; -import com.android.systemui.util.time.FakeSystemClock; -import com.android.systemui.utils.leaks.FakeBatteryController; -import com.android.systemui.utils.leaks.LeakCheckedTest; 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) -@TestableLooper.RunWithLooper -public class BrightLineFalsingManagerTest extends LeakCheckedTest { - +public class FalsingCollectorImplTest extends SysuiTestCase { + private FalsingCollectorImpl mFalsingCollector; + @Mock + private FalsingDataProvider mFalsingDataProvider; + private final FalsingManagerFake mFalsingManager = new FalsingManagerFake(); @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor; @Mock private ProximitySensor mProximitySensor; @Mock - private Resources mResources; - @Mock - private ViewConfiguration mViewConfiguration; private SysuiStatusBarStateController mStatusBarStateController; - private FalsingDataProvider mFalsingDataProvider; - private FakeBatteryController mFakeBatteryController; - - private BrightLineFalsingManager mFalsingManager; @Before - public void setup() { + public void setUp() { MockitoAnnotations.initMocks(this); - mFakeBatteryController = new FakeBatteryController(getLeakCheck()); - DisplayMetrics dm = new DisplayMetrics(); - dm.xdpi = 100; - dm.ydpi = 100; - dm.widthPixels = 100; - dm.heightPixels = 100; - mFalsingDataProvider = new FalsingDataProvider(dm, mFakeBatteryController, - new FakeSystemClock()); - DeviceConfigProxy deviceConfigProxy = new DeviceConfigProxyFake(); - DockManager dockManager = new DockManagerFake(); - mStatusBarStateController = new StatusBarStateControllerImpl(new UiEventLoggerFake()); - mStatusBarStateController.setState(StatusBarState.KEYGUARD); - when(mResources.getDimension(R.dimen.double_tap_slop)).thenReturn(1f); - when(mViewConfiguration.getScaledTouchSlop()).thenReturn(1); - mFalsingManager = new BrightLineFalsingManager(mFalsingDataProvider, - mKeyguardUpdateMonitor, mProximitySensor, deviceConfigProxy, mResources, - mViewConfiguration, dockManager, mStatusBarStateController); + + when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD); + + mFalsingCollector = new FalsingCollectorImpl(mFalsingDataProvider, mFalsingManager, + mKeyguardUpdateMonitor, mProximitySensor, mStatusBarStateController); } + @Test public void testRegisterSensor() { - mFalsingManager.onScreenTurningOn(); + mFalsingCollector.onScreenTurningOn(); verify(mProximitySensor).register(any(ThresholdSensor.Listener.class)); } @Test public void testNoProximityWhenWirelessCharging() { - mFakeBatteryController.setWirelessCharging(true); - mFalsingManager.onScreenTurningOn(); + when(mFalsingDataProvider.isWirelessCharging()).thenReturn(true); + mFalsingCollector.onScreenTurningOn(); verify(mProximitySensor, never()).register(any(ThresholdSensor.Listener.class)); } @Test public void testUnregisterSensor() { - mFalsingManager.onScreenTurningOn(); + mFalsingCollector.onScreenTurningOn(); reset(mProximitySensor); - mFalsingManager.onScreenOff(); + mFalsingCollector.onScreenOff(); verify(mProximitySensor).unregister(any(ThresholdSensor.Listener.class)); } @Test public void testUnregisterSensor_QS() { - mFalsingManager.onScreenTurningOn(); + mFalsingCollector.onScreenTurningOn(); reset(mProximitySensor); - mFalsingManager.setQsExpanded(true); + mFalsingCollector.setQsExpanded(true); verify(mProximitySensor).unregister(any(ThresholdSensor.Listener.class)); - mFalsingManager.setQsExpanded(false); + mFalsingCollector.setQsExpanded(false); verify(mProximitySensor).register(any(ThresholdSensor.Listener.class)); } @Test public void testUnregisterSensor_Bouncer() { - mFalsingManager.onScreenTurningOn(); + mFalsingCollector.onScreenTurningOn(); reset(mProximitySensor); - mFalsingManager.onBouncerShown(); + mFalsingCollector.onBouncerShown(); verify(mProximitySensor).unregister(any(ThresholdSensor.Listener.class)); - mFalsingManager.onBouncerHidden(); + mFalsingCollector.onBouncerHidden(); verify(mProximitySensor).register(any(ThresholdSensor.Listener.class)); } @Test public void testUnregisterSensor_StateTransition() { - mFalsingManager.onScreenTurningOn(); + + ArgumentCaptor<StatusBarStateController.StateListener> stateListenerArgumentCaptor = + ArgumentCaptor.forClass(StatusBarStateController.StateListener.class); + verify(mStatusBarStateController).addCallback(stateListenerArgumentCaptor.capture()); + + mFalsingCollector.onScreenTurningOn(); reset(mProximitySensor); - mStatusBarStateController.setState(StatusBarState.SHADE); + stateListenerArgumentCaptor.getValue().onStateChanged(StatusBarState.SHADE); verify(mProximitySensor).unregister(any(ThresholdSensor.Listener.class)); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/FalsingDataProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java index be38f4419372..be0cc97a2f0f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/FalsingDataProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * 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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.classifier.brightline; +package com.android.systemui.classifier; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.Matchers.closeTo; @@ -112,13 +112,6 @@ public class FalsingDataProviderTest extends ClassifierTest { assertThat(motionEventList.get(1).getX(), is(6f)); assertThat(motionEventList.get(0).getY(), is(7f)); assertThat(motionEventList.get(1).getY(), is(5f)); - - // The first, real event should still be a, however. - MotionEvent firstRealMotionEvent = mDataProvider.getFirstActualMotionEvent(); - assertThat(firstRealMotionEvent.getActionMasked(), is(MotionEvent.ACTION_DOWN)); - assertThat(firstRealMotionEvent.getEventTime(), is(1L)); - assertThat(firstRealMotionEvent.getX(), is(2f)); - assertThat(firstRealMotionEvent.getY(), is(9f)); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DiagonalClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DiagonalClassifierTest.java index e88ff2df60ca..714d6581ff00 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DiagonalClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DiagonalClassifierTest.java @@ -27,6 +27,8 @@ import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; +import com.android.systemui.classifier.ClassifierTest; +import com.android.systemui.classifier.FalsingDataProvider; import com.android.systemui.util.DeviceConfigProxyFake; import org.junit.After; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java index e5ab9be288ef..d66c7a9d43a5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DistanceClassifierTest.java @@ -23,6 +23,8 @@ import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; +import com.android.systemui.classifier.ClassifierTest; +import com.android.systemui.classifier.FalsingDataProvider; import com.android.systemui.util.DeviceConfigProxyFake; import org.junit.After; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DoubleTapClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DoubleTapClassifierTest.java index 9f3a1e4821c9..288ab0ad6596 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DoubleTapClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/DoubleTapClassifierTest.java @@ -27,6 +27,9 @@ import android.view.MotionEvent; import androidx.test.filters.SmallTest; +import com.android.systemui.classifier.ClassifierTest; +import com.android.systemui.classifier.FalsingDataProvider; + import org.junit.After; import org.junit.Before; import org.junit.Test; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/PointerCountClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/PointerCountClassifierTest.java index 4f8e7c801d04..b512f0d6ef32 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/PointerCountClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/PointerCountClassifierTest.java @@ -26,6 +26,8 @@ import android.view.MotionEvent; import androidx.test.filters.SmallTest; +import com.android.systemui.classifier.ClassifierTest; + import org.junit.After; import org.junit.Before; import org.junit.Test; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java index 3cebf0d6af57..c2e290f166a3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ProximityClassifierTest.java @@ -28,6 +28,8 @@ import android.view.MotionEvent; import androidx.test.filters.SmallTest; +import com.android.systemui.classifier.ClassifierTest; +import com.android.systemui.classifier.FalsingDataProvider; import com.android.systemui.util.DeviceConfigProxyFake; import com.android.systemui.util.sensors.ProximitySensor; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/SingleTapClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/SingleTapClassifierTest.java index 642b077f0dcf..d67f2b833deb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/SingleTapClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/SingleTapClassifierTest.java @@ -25,6 +25,9 @@ import android.view.MotionEvent; import androidx.test.filters.SmallTest; +import com.android.systemui.classifier.ClassifierTest; +import com.android.systemui.classifier.FalsingDataProvider; + import org.junit.After; import org.junit.Before; import org.junit.Test; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TypeClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TypeClassifierTest.java index 4346e7df088f..5f3b84c2f7ae 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TypeClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/TypeClassifierTest.java @@ -33,6 +33,9 @@ import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; +import com.android.systemui.classifier.ClassifierTest; +import com.android.systemui.classifier.FalsingDataProvider; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java index a8cce00ba996..e49262f5099f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java @@ -23,6 +23,7 @@ import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; +import com.android.systemui.classifier.ClassifierTest; import com.android.systemui.util.DeviceConfigProxyFake; import org.junit.After; diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java index f6d6f562e3fa..67d0295c82d3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -41,7 +41,7 @@ import com.android.keyguard.KeyguardDisplayManager; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.SysuiTestCase; import com.android.systemui.broadcast.BroadcastDispatcher; -import com.android.systemui.classifier.FalsingManagerFake; +import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.dump.DumpManager; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; @@ -76,18 +76,18 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake(); private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); - private FalsingManagerFake mFalsingManager; + private FalsingCollectorFake mFalsingCollector; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mFalsingManager = new FalsingManagerFake(); + mFalsingCollector = new FalsingCollectorFake(); when(mLockPatternUtils.getDevicePolicyManager()).thenReturn(mDevicePolicyManager); when(mPowerManager.newWakeLock(anyInt(), any())).thenReturn(mock(WakeLock.class)); mViewMediator = new KeyguardViewMediator( - mContext, mFalsingManager, mLockPatternUtils, mBroadcastDispatcher, + mContext, mFalsingCollector, mLockPatternUtils, mBroadcastDispatcher, () -> mStatusBarKeyguardViewManager, mDismissCallbackRegistry, mUpdateMonitor, mDumpManager, mUiBgExecutor, mPowerManager, mTrustManager, mDeviceConfig, mNavigationModeController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java index 11ef3e33f9d0..b7cc651dc24b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingControllerTest.java @@ -19,6 +19,8 @@ package com.android.systemui.screenrecord; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import android.app.PendingIntent; @@ -128,6 +130,35 @@ public class RecordingControllerTest extends SysuiTestCase { verify(mCallback).onRecordingEnd(); } + // Test that broadcast will update state + @Test + public void testUpdateStateBroadcast() { + if (Looper.myLooper() == null) { + Looper.prepare(); + } + + // When a recording has started + PendingIntent startIntent = Mockito.mock(PendingIntent.class); + mController.startCountdown(0, 0, startIntent, null); + verify(mCallback).onCountdownEnd(); + + // then the receiver was registered + verify(mBroadcastDispatcher).registerReceiver(eq(mController.mStateChangeReceiver), + any(), any(), any()); + + // When the receiver gets an update + Intent intent = new Intent(RecordingController.INTENT_UPDATE_STATE); + intent.putExtra(RecordingController.EXTRA_STATE, false); + mController.mStateChangeReceiver.onReceive(mContext, intent); + + // then the state is updated + assertFalse(mController.isRecording()); + verify(mCallback).onRecordingEnd(); + + // and the receiver is unregistered + verify(mBroadcastDispatcher).unregisterReceiver(eq(mController.mStateChangeReceiver)); + } + // Test that switching users will stop an ongoing recording @Test public void testUserChange() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java index 3e37fde84544..91cafead596c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java @@ -17,15 +17,18 @@ package com.android.systemui.screenrecord; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.app.Notification; import android.app.NotificationManager; import android.content.Intent; +import android.os.RemoteException; import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; @@ -43,6 +46,7 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import java.io.IOException; import java.util.concurrent.Executor; @RunWith(AndroidTestingRunner.class) @@ -88,6 +92,9 @@ public class RecordingServiceTest extends SysuiTestCase { doNothing().when(mRecordingService).createRecordingNotification(); doReturn(mNotification).when(mRecordingService).createProcessingNotification(); doReturn(mNotification).when(mRecordingService).createSaveNotification(any()); + doNothing().when(mRecordingService).createErrorNotification(); + doNothing().when(mRecordingService).showErrorToast(anyInt()); + doNothing().when(mRecordingService).stopForeground(anyBoolean()); doNothing().when(mRecordingService).startForeground(anyInt(), any()); doReturn(mScreenMediaRecorder).when(mRecordingService).getRecorder(); @@ -124,4 +131,16 @@ public class RecordingServiceTest extends SysuiTestCase { .log(Events.ScreenRecordEvent.SCREEN_RECORD_END_NOTIFICATION); verify(mUiEventLogger, times(0)).log(Events.ScreenRecordEvent.SCREEN_RECORD_END_QS_TILE); } + + @Test + public void testErrorUpdatesState() throws IOException, RemoteException { + // When the screen recording does not start properly + doThrow(new RuntimeException("fail")).when(mScreenMediaRecorder).start(); + + Intent startIntent = RecordingService.getStartIntent(mContext, 0, 0, false); + mRecordingService.onStartCommand(startIntent, 0, 0); + + // Then the state is set to not recording + verify(mController).updateState(false); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java index dbaf5c467c45..ababebdd807c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java @@ -44,9 +44,9 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.NotificationMessagingUtil; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.media.MediaFeatureFlag; import com.android.systemui.media.dialog.MediaOutputDialogFactory; -import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.statusbar.FeatureFlags; @@ -129,7 +129,6 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { @Mock(answer = Answers.RETURNS_SELF) private ExpandableNotificationRowComponent.Builder mExpandableNotificationRowComponentBuilder; @Mock private ExpandableNotificationRowComponent mExpandableNotificationRowComponent; - @Mock private FalsingManager mFalsingManager; @Mock private KeyguardBypassController mKeyguardBypassController; @Mock private StatusBarStateController mStatusBarStateController; @@ -244,7 +243,7 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { mGutsManager, true, null, - mFalsingManager, + new FalsingCollectorFake(), mPeopleNotificationIdentifier, Optional.of(mock(BubblesManager.class)) )); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index baae8fda18f9..7470a13ae39e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -45,9 +45,9 @@ import android.widget.RemoteViews; import com.android.systemui.R; import com.android.systemui.TestableDependency; +import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.media.MediaFeatureFlag; import com.android.systemui.media.dialog.MediaOutputDialogFactory; -import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationRemoteInputManager; @@ -428,7 +428,7 @@ public class NotificationTestHelper { mock(OnExpandClickListener.class), mock(NotificationMediaManager.class), mock(ExpandableNotificationRow.CoordinateOnClickListener.class), - mock(FalsingManager.class), + new FalsingCollectorFake(), mStatusBarStateController, mPeopleNotificationIdentifier, mock(OnUserInteractionCallback.class), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java index 01d49c269fb2..3c4fde8f6106 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollerControllerTest.java @@ -42,9 +42,9 @@ import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.nano.MetricsProto; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.SysuiTestCase; +import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.media.KeyguardMediaController; -import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -106,7 +106,6 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase { @Mock private SysuiColorExtractor mColorExtractor; @Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager; @Mock private MetricsLogger mMetricsLogger; - @Mock private FalsingManager mFalsingManager; @Mock private Resources mResources; @Mock(answer = Answers.RETURNS_SELF) private NotificationSwipeHelper.Builder mNotificationSwipeHelperBuilder; @@ -160,7 +159,7 @@ public class NotificationStackScrollerControllerTest extends SysuiTestCase { mColorExtractor, mNotificationLockscreenUserManager, mMetricsLogger, - mFalsingManager, + new FalsingCollectorFake(), mResources, mNotificationSwipeHelperBuilder, mStatusBar, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java index 1b05ad7f8b5b..bc014ec16103 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java @@ -50,9 +50,9 @@ import com.android.keyguard.ViewMediatorCallback; import com.android.keyguard.dagger.KeyguardBouncerComponent; import com.android.systemui.DejankUtils; import com.android.systemui.SysuiTestCase; +import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; -import com.android.systemui.plugins.FalsingManager; import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Assert; @@ -72,7 +72,7 @@ import org.mockito.stubbing.Answer; public class KeyguardBouncerTest extends SysuiTestCase { @Mock - private FalsingManager mFalsingManager; + private FalsingCollector mFalsingCollector; @Mock private ViewMediatorCallback mViewMediatorCallback; @Mock @@ -128,7 +128,7 @@ public class KeyguardBouncerTest extends SysuiTestCase { final ViewGroup container = new FrameLayout(getContext()); mBouncer = new KeyguardBouncer.Factory(getContext(), mViewMediatorCallback, - mDismissCallbackRegistry, mFalsingManager, + mDismissCallbackRegistry, mFalsingCollector, mKeyguardStateController, mKeyguardUpdateMonitor, mKeyguardBypassController, mHandler, mKeyguardSecurityModel, mKeyguardBouncerComponentFactory) @@ -143,10 +143,10 @@ public class KeyguardBouncerTest extends SysuiTestCase { @Test public void testShow_notifiesFalsingManager() { mBouncer.show(true); - verify(mFalsingManager).onBouncerShown(); + verify(mFalsingCollector).onBouncerShown(); mBouncer.show(true, false); - verifyNoMoreInteractions(mFalsingManager); + verifyNoMoreInteractions(mFalsingCollector); } /** @@ -212,11 +212,11 @@ public class KeyguardBouncerTest extends SysuiTestCase { mBouncer.setExpansion(0.5f); mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN); - verify(mFalsingManager).onBouncerHidden(); + verify(mFalsingCollector).onBouncerHidden(); verify(mExpansionCallback).onFullyHidden(); mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE); - verify(mFalsingManager).onBouncerShown(); + verify(mFalsingCollector).onBouncerShown(); verify(mExpansionCallback).onFullyShown(); verify(mExpansionCallback, never()).onStartingToHide(); @@ -239,7 +239,7 @@ public class KeyguardBouncerTest extends SysuiTestCase { @Test public void testHide_notifiesFalsingManager() { mBouncer.hide(false); - verify(mFalsingManager).onBouncerHidden(); + verify(mFalsingCollector).onBouncerHidden(); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java index 4841b3bf951f..3d582e74ea4e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java @@ -60,10 +60,10 @@ import com.android.keyguard.dagger.KeyguardStatusViewComponent; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.AuthController; +import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.doze.DozeLog; import com.android.systemui.media.MediaHierarchyManager; -import com.android.systemui.plugins.FalsingManager; import com.android.systemui.qs.QSDetailDisplayer; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.KeyguardAffordanceView; @@ -132,8 +132,6 @@ public class NotificationPanelViewTest extends SysuiTestCase { @Mock private KeyguardUpdateMonitor mUpdateMonitor; @Mock - private FalsingManager mFalsingManager; - @Mock private KeyguardBypassController mKeyguardBypassController; @Mock private DozeParameters mDozeParameters; @@ -252,7 +250,7 @@ public class NotificationPanelViewTest extends SysuiTestCase { mKeyguardBypassController, mHeadsUpManager, mock(NotificationRoundnessManager.class), mStatusBarStateController, - new FalsingManagerFake()); + new FalsingManagerFake(), new FalsingCollectorFake()); when(mKeyguardStatusViewComponentFactory.build(any())) .thenReturn(mKeyguardStatusViewComponent); when(mKeyguardStatusViewComponent.getKeyguardClockSwitchController()) @@ -263,7 +261,7 @@ public class NotificationPanelViewTest extends SysuiTestCase { mResources, mInjectionInflationController, coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController, - mFalsingManager, mShadeController, + new FalsingManagerFake(), new FalsingCollectorFake(), mShadeController, mNotificationLockscreenUserManager, mNotificationEntryManager, mKeyguardStateController, mStatusBarStateController, mDozeLog, mDozeParameters, mCommandQueue, mVibratorHelper, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java index 25af584ed3f8..e0fa9babbb48 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java @@ -30,6 +30,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.R; import com.android.systemui.SystemUIFactory; import com.android.systemui.SysuiTestCase; +import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.dock.DockManager; import com.android.systemui.doze.DozeLog; @@ -111,6 +112,7 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { mDynamicPrivacyController, mBypassController, new FalsingManagerFake(), + new FalsingCollectorFake(), mPluginManager, mTunerService, mNotificationLockScreenUserManager, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index 710122d83b22..5416f75dffff 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -83,6 +83,7 @@ import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.assist.AssistManager; import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.demomode.DemoModeController; @@ -356,6 +357,7 @@ public class StatusBarTest extends SysuiTestCase { mDynamicPrivacyController, mBypassHeadsUpNotifier, new FalsingManagerFake(), + new FalsingCollectorFake(), mBroadcastDispatcher, new RemoteInputQuickSettingsDisabler( mContext, diff --git a/packages/services/PacProcessor/AndroidManifest.xml b/packages/services/PacProcessor/AndroidManifest.xml index ad1326194a4d..533098cbdc75 100644 --- a/packages/services/PacProcessor/AndroidManifest.xml +++ b/packages/services/PacProcessor/AndroidManifest.xml @@ -3,6 +3,7 @@ package="com.android.pacprocessor"> <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <application android:label="@string/app_name" diff --git a/services/companion/java/com/android/server/companion/OWNERS b/services/companion/java/com/android/server/companion/OWNERS new file mode 100644 index 000000000000..da723b3b67da --- /dev/null +++ b/services/companion/java/com/android/server/companion/OWNERS @@ -0,0 +1 @@ +eugenesusla@google.com
\ No newline at end of file diff --git a/services/core/java/android/app/usage/UsageStatsManagerInternal.java b/services/core/java/android/app/usage/UsageStatsManagerInternal.java index fa84427ac064..b2226d1e0fa3 100644 --- a/services/core/java/android/app/usage/UsageStatsManagerInternal.java +++ b/services/core/java/android/app/usage/UsageStatsManagerInternal.java @@ -326,4 +326,18 @@ public abstract class UsageStatsManagerInternal { * @return {@code true} if the updating was successful, {@code false} otherwise */ public abstract boolean updatePackageMappingsData(); + + /** + * Listener interface for usage events. + */ + public interface UsageEventListener { + /** Callback to inform listeners of a new usage event. */ + void onUsageEvent(@UserIdInt int userId, @NonNull UsageEvents.Event event); + } + + /** Register a listener that will be notified of every new usage event. */ + public abstract void registerListener(@NonNull UsageEventListener listener); + + /** Unregister a listener from being notified of every new usage event. */ + public abstract void unregisterListener(@NonNull UsageEventListener listener); } diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index f20566280bc8..6989e320f465 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -77,6 +77,7 @@ public abstract class PackageManagerInternal { PACKAGE_WIFI, PACKAGE_COMPANION, PACKAGE_RETAIL_DEMO, + PACKAGE_RECENTS, }) @Retention(RetentionPolicy.SOURCE) public @interface KnownPackage {} @@ -97,9 +98,10 @@ public abstract class PackageManagerInternal { public static final int PACKAGE_WIFI = 13; public static final int PACKAGE_COMPANION = 14; public static final int PACKAGE_RETAIL_DEMO = 15; + public static final int PACKAGE_RECENTS = 16; // Integer value of the last known package ID. Increases as new ID is added to KnownPackage. // Please note the numbers should be continuous. - public static final int LAST_KNOWN_PACKAGE = PACKAGE_RETAIL_DEMO; + public static final int LAST_KNOWN_PACKAGE = PACKAGE_RECENTS; @IntDef(flag = true, prefix = "RESOLVE_", value = { RESOLVE_NON_BROWSER_ONLY, @@ -1060,6 +1062,8 @@ public abstract class PackageManagerInternal { return "Retail Demo"; case PACKAGE_OVERLAY_CONFIG_SIGNATURE: return "Overlay Config Signature"; + case PACKAGE_RECENTS: + return "Recents"; } return "Unknown"; } diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java index 735d248b12e4..f3c5fd8d1064 100644 --- a/services/core/java/com/android/server/PackageWatchdog.java +++ b/services/core/java/com/android/server/PackageWatchdog.java @@ -47,7 +47,6 @@ import android.util.Xml; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BackgroundThread; -import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.XmlUtils; @@ -64,7 +63,6 @@ import java.io.IOException; import java.io.InputStream; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; @@ -129,9 +127,17 @@ public class PackageWatchdog { @VisibleForTesting static final int DEFAULT_BOOT_LOOP_TRIGGER_COUNT = 5; static final long DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS = TimeUnit.MINUTES.toMillis(10); + + // These properties track individual system server boot events, and are reset once the boot + // threshold is met, or the boot loop trigger window is exceeded between boot events. private static final String PROP_RESCUE_BOOT_COUNT = "sys.rescue_boot_count"; private static final String PROP_RESCUE_BOOT_START = "sys.rescue_boot_start"; + // These properties track multiple calls made to observers tracking boot loops. They are reset + // when the de-escalation window is exceeded between boot events. + private static final String PROP_BOOT_MITIGATION_WINDOW_START = "sys.boot_mitigation_start"; + private static final String PROP_BOOT_MITIGATION_COUNT = "sys.boot_mitigation_count"; + private long mNumberOfNativeCrashPollsRemaining; private static final int DB_VERSION = 1; @@ -191,7 +197,6 @@ public class PackageWatchdog { @FunctionalInterface @VisibleForTesting interface SystemClock { - // TODO: Add elapsedRealtime to this interface long uptimeMillis(); } @@ -471,13 +476,14 @@ public class PackageWatchdog { synchronized (mLock) { if (mBootThreshold.incrementAndTest()) { mBootThreshold.reset(); + int mitigationCount = mBootThreshold.getMitigationCount() + 1; PackageHealthObserver currentObserverToNotify = null; int currentObserverImpact = Integer.MAX_VALUE; for (int i = 0; i < mAllObservers.size(); i++) { final ObserverInternal observer = mAllObservers.valueAt(i); PackageHealthObserver registeredObserver = observer.registeredObserver; if (registeredObserver != null) { - int impact = registeredObserver.onBootLoop(); + int impact = registeredObserver.onBootLoop(mitigationCount); if (impact != PackageHealthObserverImpact.USER_IMPACT_NONE && impact < currentObserverImpact) { currentObserverToNotify = registeredObserver; @@ -486,7 +492,8 @@ public class PackageWatchdog { } } if (currentObserverToNotify != null) { - currentObserverToNotify.executeBootLoopMitigation(); + mBootThreshold.setMitigationCount(mitigationCount); + currentObserverToNotify.executeBootLoopMitigation(mitigationCount); } } } @@ -609,15 +616,20 @@ public class PackageWatchdog { /** * Called when the system server has booted several times within a window of time, defined * by {@link #mBootThreshold} + * + * @param mitigationCount the number of times mitigation has been attempted for this + * boot loop (including this time). */ - default @PackageHealthObserverImpact int onBootLoop() { + default @PackageHealthObserverImpact int onBootLoop(int mitigationCount) { return PackageHealthObserverImpact.USER_IMPACT_NONE; } /** * Executes mitigation for {@link #onBootLoop} + * @param mitigationCount the number of times mitigation has been attempted for this + * boot loop (including this time). */ - default boolean executeBootLoopMitigation() { + default boolean executeBootLoopMitigation(int mitigationCount) { return false; } @@ -1577,7 +1589,7 @@ public class PackageWatchdog { /** * Handles the thresholding logic for system server boots. */ - static class BootThreshold { + class BootThreshold { private final int mBootTriggerCount; private final long mTriggerWindow; @@ -1604,18 +1616,44 @@ public class PackageWatchdog { return SystemProperties.getLong(PROP_RESCUE_BOOT_START, 0); } + public int getMitigationCount() { + return SystemProperties.getInt(PROP_BOOT_MITIGATION_COUNT, 0); + } + public void setStart(long start) { - final long now = android.os.SystemClock.elapsedRealtime(); + setPropertyStart(PROP_RESCUE_BOOT_START, start); + } + + public void setMitigationStart(long start) { + setPropertyStart(PROP_BOOT_MITIGATION_WINDOW_START, start); + } + + public long getMitigationStart() { + return SystemProperties.getLong(PROP_BOOT_MITIGATION_WINDOW_START, 0); + } + + public void setMitigationCount(int count) { + SystemProperties.set(PROP_BOOT_MITIGATION_COUNT, Integer.toString(count)); + } + + public void setPropertyStart(String property, long start) { + final long now = mSystemClock.uptimeMillis(); final long newStart = MathUtils.constrain(start, 0, now); - SystemProperties.set(PROP_RESCUE_BOOT_START, Long.toString(newStart)); + SystemProperties.set(property, Long.toString(newStart)); } + /** Increments the boot counter, and returns whether the device is bootlooping. */ public boolean incrementAndTest() { - final long now = android.os.SystemClock.elapsedRealtime(); + final long now = mSystemClock.uptimeMillis(); if (now - getStart() < 0) { Slog.e(TAG, "Window was less than zero. Resetting start to current time."); setStart(now); + setMitigationStart(now); + } + if (now - getMitigationStart() > DEFAULT_DEESCALATION_WINDOW_MS) { + setMitigationCount(0); + setMitigationStart(now); } final long window = now - getStart(); if (window >= mTriggerWindow) { diff --git a/services/core/java/com/android/server/RescueParty.java b/services/core/java/com/android/server/RescueParty.java index d04949ac54db..e8e1a16d116b 100644 --- a/services/core/java/com/android/server/RescueParty.java +++ b/services/core/java/com/android/server/RescueParty.java @@ -29,7 +29,6 @@ import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.FileUtils; -import android.os.Process; import android.os.RecoverySystem; import android.os.RemoteCallback; import android.os.SystemClock; @@ -40,7 +39,6 @@ import android.provider.Settings; import android.util.ArraySet; import android.util.ExceptionUtils; import android.util.Log; -import android.util.MathUtils; import android.util.Slog; import com.android.internal.annotations.GuardedBy; @@ -75,8 +73,6 @@ import java.util.concurrent.TimeUnit; public class RescueParty { @VisibleForTesting static final String PROP_ENABLE_RESCUE = "persist.sys.enable_rescue"; - @VisibleForTesting - static final String PROP_RESCUE_LEVEL = "sys.rescue_level"; static final String PROP_ATTEMPTING_FACTORY_RESET = "sys.attempting_factory_reset"; static final String PROP_MAX_RESCUE_LEVEL_ATTEMPTED = "sys.max_rescue_level_attempted"; @VisibleForTesting @@ -168,7 +164,6 @@ public class RescueParty { */ public static void onSettingsProviderPublished(Context context) { handleNativeRescuePartyResets(); - executeRescueLevel(context, /*failedPackage=*/ null); ContentResolver contentResolver = context.getContentResolver(); Settings.Config.registerMonitorCallback(contentResolver, new RemoteCallback(result -> { handleMonitorCallback(context, result); @@ -260,33 +255,6 @@ public class RescueParty { } } - /** - * Get the next rescue level. This indicates the next level of mitigation that may be taken. - */ - private static int getNextRescueLevel() { - return MathUtils.constrain(SystemProperties.getInt(PROP_RESCUE_LEVEL, LEVEL_NONE) + 1, - LEVEL_NONE, getMaxRescueLevel()); - } - - /** - * Escalate to the next rescue level. After incrementing the level you'll - * probably want to call {@link #executeRescueLevel(Context, String)}. - */ - private static void incrementRescueLevel(int triggerUid) { - final int level = getNextRescueLevel(); - SystemProperties.set(PROP_RESCUE_LEVEL, Integer.toString(level)); - - EventLogTags.writeRescueLevel(level, triggerUid); - logCriticalInfo(Log.WARN, "Incremented rescue level to " - + levelToString(level) + " triggered by UID " + triggerUid); - } - - private static void executeRescueLevel(Context context, @Nullable String failedPackage) { - final int level = SystemProperties.getInt(PROP_RESCUE_LEVEL, LEVEL_NONE); - if (level == LEVEL_NONE) return; - executeRescueLevel(context, failedPackage, level); - } - private static void executeRescueLevel(Context context, @Nullable String failedPackage, int level) { Slog.w(TAG, "Attempting rescue level " + levelToString(level)); @@ -561,20 +529,19 @@ public class RescueParty { } @Override - public int onBootLoop() { + public int onBootLoop(int mitigationCount) { if (isDisabled()) { return PackageHealthObserverImpact.USER_IMPACT_NONE; } - return mapRescueLevelToUserImpact(getNextRescueLevel()); + return mapRescueLevelToUserImpact(getRescueLevel(mitigationCount)); } @Override - public boolean executeBootLoopMitigation() { + public boolean executeBootLoopMitigation(int mitigationCount) { if (isDisabled()) { return false; } - incrementRescueLevel(Process.ROOT_UID); - executeRescueLevel(mContext, /*failedPackage=*/ null); + executeRescueLevel(mContext, /*failedPackage=*/ null, getRescueLevel(mitigationCount)); return true; } diff --git a/services/core/java/com/android/server/am/ActiveInstrumentation.java b/services/core/java/com/android/server/am/ActiveInstrumentation.java index 43474d5f22d4..61ccf11ff73e 100644 --- a/services/core/java/com/android/server/am/ActiveInstrumentation.java +++ b/services/core/java/com/android/server/am/ActiveInstrumentation.java @@ -52,6 +52,9 @@ class ActiveInstrumentation { // Whether the caller holds START_ACTIVITIES_FROM_BACKGROUND permission boolean mHasBackgroundActivityStartsPermission; + // Whether the caller holds START_FOREGROUND_SERVICES_FROM_BACKGROUND permission + boolean mHasBackgroundForegroundServiceStartsPermission; + // As given to us Bundle mArguments; @@ -128,6 +131,8 @@ class ActiveInstrumentation { } pw.print("mHasBackgroundActivityStartsPermission="); pw.println(mHasBackgroundActivityStartsPermission); + pw.print("mHasBackgroundForegroundServiceStartsPermission="); + pw.println(mHasBackgroundForegroundServiceStartsPermission); pw.print(prefix); pw.print("mArguments="); pw.println(mArguments); } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index d6f72990e4ac..872902626fb4 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -17,6 +17,8 @@ package com.android.server.am; import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND; +import static android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND; +import static android.Manifest.permission.SYSTEM_ALERT_WINDOW; import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MANIFEST; @@ -56,9 +58,11 @@ import android.app.PendingIntent; import android.app.Service; import android.app.ServiceStartArgs; import android.app.admin.DevicePolicyEventLogger; +import android.app.compat.CompatChanges; import android.appwidget.AppWidgetManagerInternal; import android.compat.annotation.ChangeId; import android.compat.annotation.Disabled; +import android.compat.annotation.EnabledSince; import android.content.ComponentName; import android.content.ComponentName.WithComponentName; import android.content.Context; @@ -104,7 +108,6 @@ import android.webkit.WebViewZygote; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.procstats.ServiceState; -import com.android.internal.compat.IPlatformCompat; import com.android.internal.messages.nano.SystemMessageProto; import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.BatteryStatsImpl; @@ -147,30 +150,40 @@ public final class ActiveServices { public static final int FGS_FEATURE_DENIED = 0; public static final int FGS_FEATURE_ALLOWED_BY_UID_STATE = 1; - public static final int FGS_FEATURE_ALLOWED_BY_UID_VISIBLE = 2; - public static final int FGS_FEATURE_ALLOWED_BY_FLAG = 3; - public static final int FGS_FEATURE_ALLOWED_BY_SYSTEM_UID = 4; - public static final int FGS_FEATURE_ALLOWED_BY_INSTR_PERMISSION = 5; - public static final int FGS_FEATURE_ALLOWED_BY_TOKEN = 6; - public static final int FGS_FEATURE_ALLOWED_BY_PERMISSION = 7; - public static final int FGS_FEATURE_ALLOWED_BY_WHITELIST = 8; - public static final int FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER = 9; - public static final int FGS_FEATURE_ALLOWED_BY_PROC_STATE = 10; - public static final int FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST = 11; + public static final int FGS_FEATURE_ALLOWED_BY_PROC_STATE = 2; + public static final int FGS_FEATURE_ALLOWED_BY_UID_VISIBLE = 3; + public static final int FGS_FEATURE_ALLOWED_BY_FLAG = 4; + public static final int FGS_FEATURE_ALLOWED_BY_SYSTEM_UID = 5; + public static final int FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_ACTIVITY_PERMISSION = 6; + public static final int FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_FGS_PERMISSION = 7; + public static final int FGS_FEATURE_ALLOWED_BY_ACTIVITY_TOKEN = 8; + public static final int FGS_FEATURE_ALLOWED_BY_FGS_TOKEN = 9; + public static final int FGS_FEATURE_ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION = 10; + public static final int FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION = 12; + public static final int FGS_FEATURE_ALLOWED_BY_ALLOWLIST = 13; + public static final int FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER = 14; + public static final int FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST = 15; + public static final int FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION = 16; + public static final int FGS_FEATURE_ALLOWED_BY_FGS_BINDING = 17; @IntDef(flag = true, prefix = { "FGS_FEATURE_" }, value = { FGS_FEATURE_DENIED, FGS_FEATURE_ALLOWED_BY_UID_STATE, + FGS_FEATURE_ALLOWED_BY_PROC_STATE, FGS_FEATURE_ALLOWED_BY_UID_VISIBLE, FGS_FEATURE_ALLOWED_BY_FLAG, FGS_FEATURE_ALLOWED_BY_SYSTEM_UID, - FGS_FEATURE_ALLOWED_BY_INSTR_PERMISSION, - FGS_FEATURE_ALLOWED_BY_TOKEN, - FGS_FEATURE_ALLOWED_BY_PERMISSION, - FGS_FEATURE_ALLOWED_BY_WHITELIST, + FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_ACTIVITY_PERMISSION, + FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_FGS_PERMISSION, + FGS_FEATURE_ALLOWED_BY_ACTIVITY_TOKEN, + FGS_FEATURE_ALLOWED_BY_FGS_TOKEN, + FGS_FEATURE_ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION, + FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION, + FGS_FEATURE_ALLOWED_BY_ALLOWLIST, FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER, - FGS_FEATURE_ALLOWED_BY_PROC_STATE, - FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST + FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST, + FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION, + FGS_FEATURE_ALLOWED_BY_FGS_BINDING }) @Retention(RetentionPolicy.SOURCE) public @interface FgsFeatureRetCode {} @@ -242,14 +255,12 @@ public final class ActiveServices { AppWidgetManagerInternal mAppWidgetManagerInternal; // white listed packageName. - ArraySet<String> mWhiteListAllowWhileInUsePermissionInFgs = new ArraySet<>(); + ArraySet<String> mAllowListWhileInUsePermissionInFgs = new ArraySet<>(); // TODO: remove this after feature development is done private static final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - private final IPlatformCompat mPlatformCompat; - /** * The BG-launch FGS restriction feature is going to be allowed only for apps targetSdkVersion * is higher than R. @@ -258,6 +269,14 @@ public final class ActiveServices { @Disabled static final long FGS_BG_START_RESTRICTION_CHANGE_ID = 170668199L; + /** + * If a service can not become foreground service due to BG-FGS-launch restriction or other + * reasons, throws an IllegalStateException. + */ + @ChangeId + @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.S) + static final long FGS_START_EXCEPTION_CHANGE_ID = 174041399L; + final Runnable mLastAnrDumpClearer = new Runnable() { @Override public void run() { synchronized (mAm) { @@ -456,26 +475,25 @@ public final class ActiveServices { ? maxBg : ActivityManager.isLowRamDeviceStatic() ? 1 : 8; final IBinder b = ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE); - mPlatformCompat = IPlatformCompat.Stub.asInterface(b); } void systemServicesReady() { AppStateTracker ast = LocalServices.getService(AppStateTracker.class); ast.addServiceStateListener(new ForcedStandbyListener()); mAppWidgetManagerInternal = LocalServices.getService(AppWidgetManagerInternal.class); - setWhiteListAllowWhileInUsePermissionInFgs(); + setAllowListWhileInUsePermissionInFgs(); } - private void setWhiteListAllowWhileInUsePermissionInFgs() { + private void setAllowListWhileInUsePermissionInFgs() { final String attentionServicePackageName = mAm.mContext.getPackageManager().getAttentionServicePackageName(); if (!TextUtils.isEmpty(attentionServicePackageName)) { - mWhiteListAllowWhileInUsePermissionInFgs.add(attentionServicePackageName); + mAllowListWhileInUsePermissionInFgs.add(attentionServicePackageName); } final String systemCaptionsServicePackageName = mAm.mContext.getPackageManager().getSystemCaptionsServicePackageName(); if (!TextUtils.isEmpty(systemCaptionsServicePackageName)) { - mWhiteListAllowWhileInUsePermissionInFgs.add(systemCaptionsServicePackageName); + mAllowListWhileInUsePermissionInFgs.add(systemCaptionsServicePackageName); } } @@ -583,13 +601,27 @@ public final class ActiveServices { Slog.wtf(TAG, "Background started FGS " + r.mInfoAllowStartForeground); r.mLoggedInfoAllowStartForeground = true; } - if (r.mAllowStartForeground == FGS_FEATURE_DENIED - && (mAm.mConstants.mFlagFgsStartRestrictionEnabled - || isChangeEnabled(FGS_BG_START_RESTRICTION_CHANGE_ID, r))) { - Slog.w(TAG, "startForegroundService() not allowed due to " + if (r.mAllowStartForeground == FGS_FEATURE_DENIED && isBgFgsRestrictionEnabled(r)) { + String msg = "startForegroundService() not allowed due to " + "mAllowStartForeground false: service " - + r.shortInstanceName); + + r.shortInstanceName; + Slog.w(TAG, msg); showFgsBgRestrictedNotificationLocked(r); + ApplicationInfo aInfo = null; + try { + aInfo = AppGlobals.getPackageManager().getApplicationInfo( + callingPackage, ActivityManagerService.STOCK_PM_FLAGS, + userId); + } catch (android.os.RemoteException e) { + // pm is in same process, this will never happen. + } + if (aInfo == null) { + throw new SecurityException("startServiceLocked failed, " + + "could not resolve client package " + callingPackage); + } + if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID, aInfo.uid)) { + throw new IllegalStateException(msg); + } return null; } } @@ -1449,7 +1481,7 @@ public final class ActiveServices { } try { - boolean ignoreForeground = false; + String ignoreForeground = null; final int mode = mAm.getAppOpsManager().checkOpNoThrow( AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName); switch (mode) { @@ -1459,9 +1491,9 @@ public final class ActiveServices { break; case AppOpsManager.MODE_IGNORED: // Whoops, silently ignore this. - Slog.w(TAG, "Service.startForeground() not allowed due to app op: service " - + r.shortInstanceName); - ignoreForeground = true; + ignoreForeground = "Service.startForeground() not allowed due to app op: " + + "service " + r.shortInstanceName; + Slog.w(TAG, ignoreForeground); break; default: throw new SecurityException("Foreground not allowed as per app op"); @@ -1469,19 +1501,18 @@ public final class ActiveServices { // Apps that are TOP or effectively similar may call startForeground() on // their services even if they are restricted from doing that while in bg. - if (!ignoreForeground + if (ignoreForeground == null && !appIsTopLocked(r.appInfo.uid) && appRestrictedAnyInBackground(r.appInfo.uid, r.packageName)) { - Slog.w(TAG, - "Service.startForeground() not allowed due to bg restriction: service " - + r.shortInstanceName); + ignoreForeground = "Service.startForeground() not allowed due to bg restriction" + + ":service " + r.shortInstanceName; + Slog.w(TAG, ignoreForeground); // Back off of any foreground expectations around this service, since we've // just turned down its fg request. updateServiceForegroundLocked(r.app, false); - ignoreForeground = true; } - if (!ignoreForeground) { + if (ignoreForeground == null) { if (isFgsBgStart(r.mAllowStartForeground)) { if (!r.mLoggedInfoAllowStartForeground) { Slog.wtf(TAG, "Background started FGS " @@ -1489,14 +1520,13 @@ public final class ActiveServices { r.mLoggedInfoAllowStartForeground = true; } if (r.mAllowStartForeground == FGS_FEATURE_DENIED - && (mAm.mConstants.mFlagFgsStartRestrictionEnabled - || isChangeEnabled(FGS_BG_START_RESTRICTION_CHANGE_ID, r))) { - Slog.w(TAG, "Service.startForeground() not allowed due to " - + "mAllowStartForeground false: service " - + r.shortInstanceName); + && isBgFgsRestrictionEnabled(r)) { + ignoreForeground = "Service.startForeground() not allowed due to " + + "mAllowStartForeground false: service " + + r.shortInstanceName; + Slog.w(TAG, ignoreForeground); showFgsBgRestrictedNotificationLocked(r); updateServiceForegroundLocked(r.app, true); - ignoreForeground = true; } } } @@ -1505,7 +1535,7 @@ public final class ActiveServices { // services, so now that we've enforced the startForegroundService() contract // we only do the machinery of making the service foreground when the app // is not restricted. - if (!ignoreForeground) { + if (ignoreForeground == null) { if (r.foregroundId != id) { cancelForegroundNotificationLocked(r); r.foregroundId = id; @@ -1567,6 +1597,10 @@ public final class ActiveServices { if (DEBUG_FOREGROUND_SERVICE) { Slog.d(TAG, "Suppressing startForeground() for FAS " + r); } + if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID, r.appInfo.uid) + && isBgFgsRestrictionEnabled(r)) { + throw new IllegalStateException(ignoreForeground); + } } } finally { if (stopProcStatsOp) { @@ -2094,6 +2128,12 @@ public final class ActiveServices { "BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS"); } + if ((flags & Context.BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND) != 0) { + mAm.enforceCallingPermission( + android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND, + "BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND"); + } + final boolean callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND; final boolean isBindExternal = (flags & Context.BIND_EXTERNAL_SERVICE) != 0; final boolean allowInstant = (flags & Context.BIND_ALLOW_INSTANT) != 0; @@ -2239,6 +2279,11 @@ public final class ActiveServices { if ((flags & Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS) != 0) { s.setAllowedBgActivityStartsByBinding(true); } + + if ((flags & Context.BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND) != 0) { + s.setAllowedBgFgsStartsByBinding(true); + } + if (s.app != null) { updateServiceClientActivitiesLocked(s.app, c, true); } @@ -2256,7 +2301,6 @@ public final class ActiveServices { return 0; } } - setFgsRestrictionLocked(callingPackage, callingPid, callingUid, service, s, false); if (s.app != null) { @@ -3647,6 +3691,9 @@ public final class ActiveServices { if ((c.flags & Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS) != 0) { s.updateIsAllowedBgActivityStartsByBinding(); } + if ((c.flags & Context.BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND) != 0) { + s.updateIsAllowedBgFgsStartsByBinding(); + } if (s.app != null) { updateServiceClientActivitiesLocked(s.app, c, true); } @@ -5113,13 +5160,22 @@ public final class ActiveServices { } if (ret == FGS_FEATURE_DENIED) { + for (int i = mAm.mProcessList.mLruProcesses.size() - 1; i >= 0; i--) { + final ProcessRecord pr = mAm.mProcessList.mLruProcesses.get(i); + if (pr.uid == callingUid) { + if (pr.areBackgroundActivityStartsAllowedByToken()) { + ret = FGS_FEATURE_ALLOWED_BY_ACTIVITY_TOKEN; + break; + } + } + } + } + + if (ret == FGS_FEATURE_DENIED) { if (r.app != null) { ActiveInstrumentation instr = r.app.getActiveInstrumentation(); if (instr != null && instr.mHasBackgroundActivityStartsPermission) { - ret = FGS_FEATURE_ALLOWED_BY_INSTR_PERMISSION; - } - if (r.app.areBackgroundActivityStartsAllowedByToken()) { - ret = FGS_FEATURE_ALLOWED_BY_TOKEN; + ret = FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_ACTIVITY_PERMISSION; } } } @@ -5127,15 +5183,15 @@ public final class ActiveServices { if (ret == FGS_FEATURE_DENIED) { if (mAm.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid) == PERMISSION_GRANTED) { - ret = FGS_FEATURE_ALLOWED_BY_PERMISSION; + ret = FGS_FEATURE_ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION; } } if (ret == FGS_FEATURE_DENIED) { - final boolean isWhiteListedPackage = - mWhiteListAllowWhileInUsePermissionInFgs.contains(callingPackage); - if (isWhiteListedPackage) { - ret = FGS_FEATURE_ALLOWED_BY_WHITELIST; + final boolean isAllowedPackage = + mAllowListWhileInUsePermissionInFgs.contains(callingPackage); + if (isAllowedPackage) { + ret = FGS_FEATURE_ALLOWED_BY_ALLOWLIST; } } @@ -5187,6 +5243,39 @@ public final class ActiveServices { } if (ret == FGS_FEATURE_DENIED) { + for (int i = mAm.mProcessList.mLruProcesses.size() - 1; i >= 0; i--) { + final ProcessRecord pr = mAm.mProcessList.mLruProcesses.get(i); + if (pr.uid == callingUid) { + if (pr.areBackgroundFgsStartsAllowedByToken()) { + ret = FGS_FEATURE_ALLOWED_BY_FGS_BINDING; + break; + } else { + final ActiveInstrumentation instr = pr.getActiveInstrumentation(); + if (instr != null + && instr.mHasBackgroundForegroundServiceStartsPermission) { + ret = FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_FGS_PERMISSION; + break; + } + } + } + } + } + + if (ret == FGS_FEATURE_DENIED) { + if (mAm.checkPermission(START_FOREGROUND_SERVICES_FROM_BACKGROUND, callingPid, + callingUid) == PERMISSION_GRANTED) { + ret = FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION; + } + } + + if (ret == FGS_FEATURE_DENIED) { + if (mAm.checkPermission(SYSTEM_ALERT_WINDOW, callingPid, + callingUid) == PERMISSION_GRANTED) { + ret = FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION; + } + } + + if (ret == FGS_FEATURE_DENIED) { if (mAm.mConstants.mFlagFgsStartTempAllowListEnabled && mAm.isOnDeviceIdleWhitelistLocked(r.appInfo.uid, false)) { // uid is on DeviceIdleController's allowlist. @@ -5217,26 +5306,36 @@ public final class ActiveServices { return "DENIED"; case FGS_FEATURE_ALLOWED_BY_UID_STATE: return "ALLOWED_BY_UID_STATE"; + case FGS_FEATURE_ALLOWED_BY_PROC_STATE: + return "ALLOWED_BY_PROC_STATE"; case FGS_FEATURE_ALLOWED_BY_UID_VISIBLE: return "ALLOWED_BY_UID_VISIBLE"; case FGS_FEATURE_ALLOWED_BY_FLAG: return "ALLOWED_BY_FLAG"; case FGS_FEATURE_ALLOWED_BY_SYSTEM_UID: return "ALLOWED_BY_SYSTEM_UID"; - case FGS_FEATURE_ALLOWED_BY_INSTR_PERMISSION: - return "ALLOWED_BY_INSTR_PERMISSION"; - case FGS_FEATURE_ALLOWED_BY_TOKEN: - return "ALLOWED_BY_TOKEN"; - case FGS_FEATURE_ALLOWED_BY_PERMISSION: - return "ALLOWED_BY_PERMISSION"; - case FGS_FEATURE_ALLOWED_BY_WHITELIST: + case FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_ACTIVITY_PERMISSION: + return "ALLOWED_BY_INSTR_BACKGROUND_ACTIVITY_PERMISSION"; + case FGS_FEATURE_ALLOWED_BY_INSTR_BACKGROUND_FGS_PERMISSION: + return "ALLOWED_BY_INSTR_BACKGROUND_FGS_PERMISSION"; + case FGS_FEATURE_ALLOWED_BY_ACTIVITY_TOKEN: + return "ALLOWED_BY_ACTIVITY_TOKEN"; + case FGS_FEATURE_ALLOWED_BY_FGS_TOKEN: + return "ALLOWED_BY_FGS_TOKEN"; + case FGS_FEATURE_ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION: + return "ALLOWED_BY_BACKGROUND_ACTIVITY_PERMISSION"; + case FGS_FEATURE_ALLOWED_BY_BACKGROUND_FGS_PERMISSION: + return "ALLOWED_BY_BACKGROUND_FGS_PERMISSION"; + case FGS_FEATURE_ALLOWED_BY_ALLOWLIST: return "ALLOWED_BY_WHITELIST"; case FGS_FEATURE_ALLOWED_BY_DEVICE_OWNER: return "ALLOWED_BY_DEVICE_OWNER"; - case FGS_FEATURE_ALLOWED_BY_PROC_STATE: - return "ALLOWED_BY_PROC_STATE"; case FGS_FEATURE_ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST: return "ALLOWED_BY_DEVICE_IDLE_ALLOW_LIST"; + case FGS_FEATURE_ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION: + return "ALLOWED_BY_SYSTEM_ALERT_WINDOW_PERMISSION"; + case FGS_FEATURE_ALLOWED_BY_FGS_BINDING: + return "ALLOWED_BY_FGS_BINDING"; default: return ""; } @@ -5271,11 +5370,10 @@ public final class ActiveServices { NOTE_FOREGROUND_SERVICE_BG_LAUNCH, n.build(), UserHandle.ALL); } - private boolean isChangeEnabled(long changeId, ServiceRecord r) { - boolean enabled = false; - try { - enabled = mPlatformCompat.isChangeEnabled(changeId, r.appInfo); - } catch (RemoteException e) { } - return enabled; + private boolean isBgFgsRestrictionEnabled(ServiceRecord r) { + if (mAm.mConstants.mFlagFgsStartRestrictionEnabled) { + return true; + } + return CompatChanges.isChangeEnabled(FGS_BG_START_RESTRICTION_CHANGE_ID, r.appInfo.uid); } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 9eca15b241f4..75e8b13ccc0d 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -22,6 +22,7 @@ import static android.Manifest.permission.FILTER_EVENTS; import static android.Manifest.permission.INTERACT_ACROSS_USERS; import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND; +import static android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND; import static android.app.ActivityManager.INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS; import static android.app.ActivityManager.INSTR_FLAG_DISABLE_ISOLATED_STORAGE; import static android.app.ActivityManager.INSTR_FLAG_DISABLE_TEST_API_CHECKS; @@ -13321,15 +13322,22 @@ public class ActivityManagerService extends IActivityManager.Stub // See if the caller is allowed to do this. Note we are checking against // the actual real caller (not whoever provided the operation as say a // PendingIntent), because that who is actually supplied the arguments. - if (checkComponentPermission( - android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST, + if (checkComponentPermission(CHANGE_DEVICE_IDLE_TEMP_WHITELIST, + realCallingPid, realCallingUid, -1, true) + != PackageManager.PERMISSION_GRANTED + && checkComponentPermission(START_ACTIVITIES_FROM_BACKGROUND, + realCallingPid, realCallingUid, -1, true) + != PackageManager.PERMISSION_GRANTED + && checkComponentPermission(START_FOREGROUND_SERVICES_FROM_BACKGROUND, realCallingPid, realCallingUid, -1, true) != PackageManager.PERMISSION_GRANTED) { String msg = "Permission Denial: " + intent.getAction() + " broadcast from " + callerPackage + " (pid=" + callingPid + ", uid=" + callingUid + ")" + " requires " - + android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST; + + CHANGE_DEVICE_IDLE_TEMP_WHITELIST + " or " + + START_ACTIVITIES_FROM_BACKGROUND + " or " + + START_FOREGROUND_SERVICES_FROM_BACKGROUND; Slog.w(TAG, msg); throw new SecurityException(msg); } @@ -14289,8 +14297,10 @@ public class ActivityManagerService extends IActivityManager.Stub activeInstr.mHasBackgroundActivityStartsPermission = checkPermission( START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid) == PackageManager.PERMISSION_GRANTED; + activeInstr.mHasBackgroundForegroundServiceStartsPermission = checkPermission( + START_FOREGROUND_SERVICES_FROM_BACKGROUND, callingPid, callingUid) + == PackageManager.PERMISSION_GRANTED; activeInstr.mNoRestart = noRestart; - boolean disableHiddenApiChecks = ai.usesNonSdkApi() || (flags & INSTR_FLAG_DISABLE_HIDDEN_API_CHECKS) != 0; boolean disableTestApiChecks = disableHiddenApiChecks diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java index 93dd1aa37a11..fdc0f5949da8 100644 --- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java +++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java @@ -37,13 +37,14 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.BatteryStatsImpl; +import com.android.internal.power.MeasuredEnergyArray; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.function.pooled.PooledLambda; -import java.util.concurrent.ExecutionException; import libcore.util.EmptyArray; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.Future; @@ -103,6 +104,9 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { private boolean mOnBatteryScreenOff; @GuardedBy("this") + private int mScreenState; + + @GuardedBy("this") private boolean mUseLatestStates = true; @GuardedBy("this") @@ -194,15 +198,17 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { } @Override - public Future<?> scheduleCpuSyncDueToScreenStateChange( - boolean onBattery, boolean onBatteryScreenOff) { + public Future<?> scheduleSyncDueToScreenStateChange( + int flags, boolean onBattery, boolean onBatteryScreenOff, int screenState) { synchronized (BatteryExternalStatsWorker.this) { if (mCurrentFuture == null || (mUpdateFlags & UPDATE_CPU) == 0) { mOnBattery = onBattery; mOnBatteryScreenOff = onBatteryScreenOff; mUseLatestStates = false; } - return scheduleSyncLocked("screen-state", UPDATE_CPU); + // always update screen state + mScreenState = screenState; + return scheduleSyncLocked("screen-state", flags); } } @@ -332,6 +338,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { final int[] uidsToRemove; final boolean onBattery; final boolean onBatteryScreenOff; + final int screenState; final boolean useLatestStates; synchronized (BatteryExternalStatsWorker.this) { updateFlags = mUpdateFlags; @@ -339,6 +346,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { uidsToRemove = mUidsToRemove.size() > 0 ? mUidsToRemove.toArray() : EmptyArray.INT; onBattery = mOnBattery; onBatteryScreenOff = mOnBatteryScreenOff; + screenState = mScreenState; useLatestStates = mUseLatestStates; mUpdateFlags = 0; mCurrentReason = null; @@ -360,7 +368,7 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { } try { updateExternalStatsLocked(reason, updateFlags, onBattery, - onBatteryScreenOff, useLatestStates); + onBatteryScreenOff, screenState, useLatestStates); } finally { if (DEBUG) { Slog.d(TAG, "end updateExternalStatsSync"); @@ -402,13 +410,14 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { }; @GuardedBy("mWorkerLock") - private void updateExternalStatsLocked(final String reason, int updateFlags, - boolean onBattery, boolean onBatteryScreenOff, boolean useLatestStates) { + private void updateExternalStatsLocked(final String reason, int updateFlags, boolean onBattery, + boolean onBatteryScreenOff, int screenState, boolean useLatestStates) { // We will request data from external processes asynchronously, and wait on a timeout. SynchronousResultReceiver wifiReceiver = null; SynchronousResultReceiver bluetoothReceiver = null; CompletableFuture<ModemActivityInfo> modemFuture = CompletableFuture.completedFuture(null); boolean railUpdated = false; + MeasuredEnergyArray energyArray = null; if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI) != 0) { // We were asked to fetch WiFi data. @@ -486,6 +495,13 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { } } + if ((updateFlags & UPDATE_ENERGY) != 0) { + synchronized (mStats) { + // TODO(b/172934873) evaluate a safe way to query the HAL without holding mStats + energyArray = mStats.getEnergyConsumptionDataLocked(); + } + } + final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver); final BluetoothActivityEnergyInfo bluetoothInfo = awaitControllerInfo(bluetoothReceiver); ModemActivityInfo modemInfo = null; @@ -533,6 +549,11 @@ class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStatsSync { Slog.w(TAG, "bluetooth info is invalid: " + bluetoothInfo); } } + + if ((updateFlags & UPDATE_ENERGY) != 0 && energyArray != null) { + // Always use what BatteryExternalStatsWorker thinks screenState is. + mStats.updateMeasuredEnergyStatsLocked(energyArray, screenState); + } } // WiFi and Modem state are updated without the mStats lock held, because they diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index ed2faf992e0e..46e16bcbb1b6 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -21,6 +21,8 @@ import android.content.ContentResolver; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.hardware.power.stats.EnergyConsumerId; +import android.hardware.power.stats.EnergyConsumerResult; import android.os.BatteryStats; import android.os.BatteryStatsInternal; import android.os.Binder; @@ -59,6 +61,7 @@ import com.android.internal.os.BinderCallsStats; import com.android.internal.os.PowerProfile; import com.android.internal.os.RailStats; import com.android.internal.os.RpmStats; +import com.android.internal.power.MeasuredEnergyArray; import com.android.internal.util.DumpUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.ParseUtils; @@ -66,6 +69,7 @@ import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.LocalServices; import com.android.server.Watchdog; import com.android.server.pm.UserManagerInternal; +import com.android.server.powerstats.PowerStatsHALWrapper; import java.io.File; import java.io.FileDescriptor; @@ -90,7 +94,7 @@ import java.util.concurrent.Future; public final class BatteryStatsService extends IBatteryStats.Stub implements PowerManagerInternal.LowPowerModeListener, BatteryStatsImpl.PlatformIdleStateCallback, - BatteryStatsImpl.RailEnergyDataCallback, + BatteryStatsImpl.MeasuredEnergyRetriever, Watchdog.Monitor { static final String TAG = "BatteryStatsService"; static final boolean DBG = false; @@ -115,6 +119,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub private CharBuffer mUtf16BufferStat = CharBuffer.allocate(MAX_LOW_POWER_STATS_SIZE); private static final int MAX_LOW_POWER_STATS_SIZE = 4096; + private final PowerStatsHALWrapper.IPowerStatsHALWrapper mPowerStatsHALWrapper; + private final HandlerThread mHandlerThread; private final Handler mHandler; private final Object mLock = new Object(); @@ -186,6 +192,43 @@ public final class BatteryStatsService extends IBatteryStats.Stub } } + @Override + public MeasuredEnergyArray getEnergyConsumptionData() { + final EnergyConsumerResult[] results = mPowerStatsHALWrapper.getEnergyConsumed(new int[0]); + if (results == null) return null; + final int size = results.length; + final int[] subsystems = new int[size]; + final long[] energyUJ = new long[size]; + + for (int i = 0; i < size; i++) { + final EnergyConsumerResult consumer = results[i]; + final int subsystem; + switch (consumer.energyConsumerId) { + case EnergyConsumerId.DISPLAY: + subsystem = MeasuredEnergyArray.SUBSYSTEM_DISPLAY; + break; + default: + continue; + } + subsystems[i] = subsystem; + energyUJ[i] = consumer.energyUWs; + } + return new MeasuredEnergyArray() { + @Override + public int getSubsystem(int index) { + return subsystems[index]; + } + @Override + public long getEnergy(int index) { + return energyUJ[index]; + } + @Override + public int size() { + return size; + } + }; + } + BatteryStatsService(Context context, File systemDir, Handler handler) { // BatteryStatsImpl expects the ActivityManagerService handler, so pass that one through. mContext = context; @@ -202,6 +245,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub mHandlerThread = new HandlerThread("batterystats-handler"); mHandlerThread.start(); mHandler = new Handler(mHandlerThread.getLooper()); + + // TODO(b/173077356): Replace directly calling the HAL with PowerStatsService queries + // Make sure to init Hal Wrapper before creating BatteryStatsImpl. + mPowerStatsHALWrapper = new PowerStatsHALWrapper.PowerStatsHALWrapperImpl(); + mPowerStatsHALWrapper.initialize(); + mStats = new BatteryStatsImpl(systemDir, handler, this, this, mUserManagerUserInfoProvider); mWorker = new BatteryExternalStatsWorker(context, mStats); @@ -1963,6 +2012,15 @@ public final class BatteryStatsService extends IBatteryStats.Stub } } + private void dumpMeasuredEnergyStats(PrintWriter pw) { + // Wait for the completion of pending works if there is any + awaitCompletion(); + syncStats("dump", BatteryExternalStatsWorker.UPDATE_ENERGY); + synchronized (mStats) { + mStats.dumpMeasuredEnergyStatsLocked(pw); + } + } + private int doEnableOrDisable(PrintWriter pw, int i, String[] args, boolean enable) { i++; if (i >= args.length) { @@ -2103,6 +2161,9 @@ public final class BatteryStatsService extends IBatteryStats.Stub } else if ("--cpu".equals(arg)) { dumpCpuStats(pw); return; + } else if ("--measured-energy".equals(arg)) { + dumpMeasuredEnergyStats(pw); + return; } else if ("-a".equals(arg)) { flags |= BatteryStats.DUMP_VERBOSE; } else if (arg.length() > 0 && arg.charAt(0) == '-'){ diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 1b06dd90df83..cf4adc65a7fc 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -359,6 +359,8 @@ class ProcessRecord implements WindowProcessListener { // It must obtain the proc state from a persistent/top process or FGS, not transitive. int mAllowStartFgsState = PROCESS_STATE_NONEXISTENT; + private final ArraySet<Binder> mBackgroundFgsStartTokens = new ArraySet<>(); + void setStartParams(int startUid, HostingRecord hostingRecord, String seInfo, long startTime) { this.startUid = startUid; @@ -1965,6 +1967,18 @@ class ProcessRecord implements WindowProcessListener { } } + public void addAllowBackgroundFgsStartsToken(Binder entity) { + mBackgroundFgsStartTokens.add(entity); + } + + public void removeAllowBackgroundFgsStartsToken(Binder entity) { + mBackgroundFgsStartTokens.remove(entity); + } + + public boolean areBackgroundFgsStartsAllowedByToken() { + return !mBackgroundFgsStartTokens.isEmpty(); + } + ErrorDialogController getDialogController() { return mDialogController; } diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 364ad21b93cf..e129561b8bc5 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -147,6 +147,10 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN @GuardedBy("ams") private List<IBinder> mBgActivityStartsByStartOriginatingTokens = new ArrayList<>(); + // any current binding to this service has BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND + // flag? if true, the process can start FGS from background. + boolean mIsAllowedBgFgsStartsByBinding; + // allow while-in-use permissions in foreground service or not. // while-in-use permissions in FGS started from background might be restricted. boolean mAllowWhileInUsePermissionInFgs; @@ -418,6 +422,10 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN pw.print(prefix); pw.print("mIsAllowedBgActivityStartsByStart="); pw.println(mIsAllowedBgActivityStartsByStart); } + if (mIsAllowedBgFgsStartsByBinding) { + pw.print(prefix); pw.print("mIsAllowedBgFgsStartsByBinding="); + pw.println(mIsAllowedBgFgsStartsByBinding); + } pw.print(prefix); pw.print("allowWhileInUsePermissionInFgs="); pw.println(mAllowWhileInUsePermissionInFgs); pw.print(prefix); pw.print("recentCallingPackage="); @@ -600,6 +608,11 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN } else { _proc.removeAllowBackgroundActivityStartsToken(this); } + if (mIsAllowedBgFgsStartsByBinding) { + _proc.addAllowBackgroundFgsStartsToken(this); + } else { + _proc.removeAllowBackgroundFgsStartsToken(this); + } } if (app != null && app != _proc) { // If the old app is allowed to start bg activities because of a service start, leave it @@ -686,11 +699,34 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN setAllowedBgActivityStartsByBinding(isAllowedByBinding); } + void updateIsAllowedBgFgsStartsByBinding() { + boolean isAllowedByBinding = false; + for (int conni = connections.size() - 1; conni >= 0; conni--) { + ArrayList<ConnectionRecord> cr = connections.valueAt(conni); + for (int i = 0; i < cr.size(); i++) { + if ((cr.get(i).flags + & Context.BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND) != 0) { + isAllowedByBinding = true; + break; + } + } + if (isAllowedByBinding) { + break; + } + } + setAllowedBgFgsStartsByBinding(isAllowedByBinding); + } + void setAllowedBgActivityStartsByBinding(boolean newValue) { mIsAllowedBgActivityStartsByBinding = newValue; updateParentProcessBgActivityStartsToken(); } + void setAllowedBgFgsStartsByBinding(boolean newValue) { + mIsAllowedBgFgsStartsByBinding = newValue; + updateParentProcessBgFgsStartsToken(); + } + /** * Called when the service is started with allowBackgroundActivityStarts set. We allow * it for background activity starts, setting up a callback to remove this ability after a @@ -777,6 +813,17 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN } } + private void updateParentProcessBgFgsStartsToken() { + if (app == null) { + return; + } + if (mIsAllowedBgFgsStartsByBinding) { + app.addAllowBackgroundFgsStartsToken(this); + } else { + app.removeAllowBackgroundFgsStartsToken(this); + } + } + /** * Returns the originating token if that's the only reason background activity starts are * allowed. In order for that to happen the service has to be allowed only due to starts, since diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java index a471664b19b7..8494e515a406 100644 --- a/services/core/java/com/android/server/biometrics/BiometricService.java +++ b/services/core/java/com/android/server/biometrics/BiometricService.java @@ -500,7 +500,11 @@ public class BiometricService extends SystemService { final List<SensorPropertiesInternal> sensors = new ArrayList<>(); for (BiometricSensor sensor : mSensors) { - sensors.add(sensor.impl.getSensorProperties(opPackageName)); + // Explicitly re-create as the super class, since AIDL doesn't play nicely with + // "List<? extends SensorPropertiesInternal> ... + final SensorPropertiesInternal prop = SensorPropertiesInternal + .from(sensor.impl.getSensorProperties(opPackageName)); + sensors.add(prop); } return sensors; diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java index 36796b842410..cc9298603a3e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java @@ -65,7 +65,6 @@ import java.util.List; * Provider for a single instance of the {@link IFace} HAL. */ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { - private static final String TAG = "FaceProvider"; private static final int ENROLL_TIMEOUT_SEC = 75; private boolean mTestHalEnabled; @@ -88,7 +87,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { public void onTaskStackChanged() { mHandler.post(() -> { for (int i = 0; i < mSensors.size(); i++) { - final ClientMonitor<?> client = mSensors.get(i).getScheduler() + final ClientMonitor<?> client = mSensors.valueAt(i).getScheduler() .getCurrentClient(); if (!(client instanceof AuthenticationClient)) { Slog.e(getTag(), "Task stack changed for client: " + client); @@ -108,7 +107,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { && !client.isAlreadyDone()) { Slog.e(getTag(), "Stopping background authentication, top: " + topPackage + " currentClient: " + client); - mSensors.get(i).getScheduler() + mSensors.valueAt(i).getScheduler() .cancelAuthentication(client.getToken()); } } @@ -550,12 +549,13 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { JSONObject dump = new JSONObject(); try { - dump.put("service", "Face Manager"); + dump.put("service", getTag()); JSONArray sets = new JSONArray(); for (UserInfo user : UserManager.get(mContext).getUsers()) { final int userId = user.getUserHandle().getIdentifier(); - final int c = FaceUtils.getInstance().getBiometricsForUser(mContext, userId).size(); + final int c = FaceUtils.getInstance(sensorId) + .getBiometricsForUser(mContext, userId).size(); JSONObject set = new JSONObject(); set.put("id", userId); set.put("count", c); @@ -574,7 +574,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { dump.put("prints", sets); } catch (JSONException e) { - Slog.e(TAG, "dump formatting failure", e); + Slog.e(getTag(), "dump formatting failure", e); } pw.println(dump); pw.println("HAL deaths since last reboot: " + performanceTracker.getHALDeathCount()); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestSession.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestSession.java index 9707eddfee74..fbc26c6498b3 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestSession.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/TestSession.java @@ -24,7 +24,6 @@ import android.hardware.common.NativeHandle; import android.hardware.keymaster.HardwareAuthToken; import android.os.Binder; import android.os.IBinder; -import android.os.RemoteException; /** * Test session that provides mostly no-ops. @@ -59,7 +58,7 @@ public class TestSession extends ISession.Stub { public ICancellationSignal authenticate(int cookie, long operationId) { return new ICancellationSignal() { @Override - public void cancel() throws RemoteException { + public void cancel() { mHalSessionCallback.onError(Error.CANCELED, 0 /* vendorCode */); } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java index c57ab505ca2a..d384bc645d61 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java @@ -796,7 +796,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { JSONObject dump = new JSONObject(); try { - dump.put("service", "Face Manager"); + dump.put("service", TAG); JSONArray sets = new JSONArray(); for (UserInfo user : UserManager.get(mContext).getUsers()) { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index db34d1444650..98c32cbcfc4a 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -53,6 +53,10 @@ import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDisp import com.android.server.biometrics.sensors.fingerprint.ServiceProvider; import com.android.server.biometrics.sensors.fingerprint.Udfps; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; @@ -84,7 +88,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi public void onTaskStackChanged() { mHandler.post(() -> { for (int i = 0; i < mSensors.size(); i++) { - final ClientMonitor<?> client = mSensors.get(i).getScheduler() + final ClientMonitor<?> client = mSensors.valueAt(i).getScheduler() .getCurrentClient(); if (!(client instanceof AuthenticationClient)) { Slog.e(getTag(), "Task stack changed for client: " + client); @@ -104,7 +108,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi && !client.isAlreadyDone()) { Slog.e(getTag(), "Stopping background authentication, top: " + topPackage + " currentClient: " + client); - mSensors.get(i).getScheduler() + mSensors.valueAt(i).getScheduler() .cancelAuthentication(client.getToken()); } } @@ -593,7 +597,42 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @Override public void dumpInternal(int sensorId, @NonNull PrintWriter pw) { + PerformanceTracker performanceTracker = + PerformanceTracker.getInstanceForSensorId(sensorId); + + JSONObject dump = new JSONObject(); + try { + dump.put("service", getTag()); + + JSONArray sets = new JSONArray(); + for (UserInfo user : UserManager.get(mContext).getUsers()) { + final int userId = user.getUserHandle().getIdentifier(); + final int c = FingerprintUtils.getInstance(sensorId) + .getBiometricsForUser(mContext, userId).size(); + JSONObject set = new JSONObject(); + set.put("id", userId); + set.put("count", c); + 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 fingerprint 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(getTag(), "dump formatting failure", e); + } + pw.println(dump); + pw.println("HAL deaths since last reboot: " + performanceTracker.getHALDeathCount()); + mSensors.get(sensorId).getScheduler().dump(pw); } @NonNull diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestSession.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestSession.java index d6378780594f..ddae1107ff77 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestSession.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/TestSession.java @@ -18,8 +18,11 @@ package com.android.server.biometrics.sensors.fingerprint.aidl; import android.annotation.NonNull; import android.hardware.biometrics.common.ICancellationSignal; +import android.hardware.biometrics.face.Error; import android.hardware.biometrics.fingerprint.ISession; import android.hardware.keymaster.HardwareAuthToken; +import android.os.Binder; +import android.os.IBinder; import android.util.Slog; /** @@ -37,24 +40,32 @@ class TestSession extends ISession.Stub { @Override public void generateChallenge(int cookie, int timeoutSec) { - + mHalSessionCallback.onChallengeGenerated(0 /* challenge */); } @Override public void revokeChallenge(int cookie, long challenge) { - + mHalSessionCallback.onChallengeRevoked(challenge); } @Override public ICancellationSignal enroll(int cookie, HardwareAuthToken hat) { - Slog.d(TAG, "enroll"); return null; } @Override public ICancellationSignal authenticate(int cookie, long operationId) { - Slog.d(TAG, "authenticate"); - return null; + return new ICancellationSignal() { + @Override + public void cancel() { + mHalSessionCallback.onError(Error.CANCELED, 0 /* vendorCode */); + } + + @Override + public IBinder asBinder() { + return new Binder(); + } + }; } @Override @@ -86,7 +97,7 @@ class TestSession extends ISession.Stub { @Override public void resetLockout(int cookie, HardwareAuthToken hat) { - + mHalSessionCallback.onLockoutCleared(); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java index 11372a30599d..f38dd092007a 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java @@ -776,7 +776,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider JSONObject dump = new JSONObject(); try { - dump.put("service", "Fingerprint Manager"); + dump.put("service", TAG); JSONArray sets = new JSONArray(); for (UserInfo user : UserManager.get(mContext).getUsers()) { diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java index fe6500e8942c..468d8259db18 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java +++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java @@ -22,6 +22,7 @@ import android.util.DisplayMetrics; import android.view.Display; import android.view.DisplayAddress; import android.view.DisplayCutout; +import android.view.DisplayEventReceiver; import android.view.Surface; import java.util.Arrays; @@ -333,6 +334,9 @@ final class DisplayDeviceInfo { */ public String ownerPackageName; + public DisplayEventReceiver.FrameRateOverride[] frameRateOverrides = + new DisplayEventReceiver.FrameRateOverride[0]; + public void setAssumedDensityForExternalDisplay(int width, int height) { densityDpi = Math.min(width, height) * DisplayMetrics.DENSITY_XHIGH / 1080; // Technically, these values should be smaller than the apparent density @@ -386,7 +390,8 @@ final class DisplayDeviceInfo { || !Objects.equals(address, other.address) || !Objects.equals(deviceProductInfo, other.deviceProductInfo) || ownerUid != other.ownerUid - || !Objects.equals(ownerPackageName, other.ownerPackageName)) { + || !Objects.equals(ownerPackageName, other.ownerPackageName) + || !Objects.equals(frameRateOverrides, other.frameRateOverrides)) { diff |= DIFF_OTHER; } return diff; @@ -425,6 +430,7 @@ final class DisplayDeviceInfo { state = other.state; ownerUid = other.ownerUid; ownerPackageName = other.ownerPackageName; + frameRateOverrides = other.frameRateOverrides; } // For debugging purposes @@ -461,6 +467,10 @@ final class DisplayDeviceInfo { sb.append(", owner ").append(ownerPackageName); sb.append(" (uid ").append(ownerUid).append(")"); } + sb.append(", frameRateOverride "); + for (DisplayEventReceiver.FrameRateOverride frameRateOverride : frameRateOverrides) { + sb.append(frameRateOverride).append(" "); + } sb.append(flagsToString(flags)); sb.append("}"); return sb.toString(); diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 4e60f1ff929e..29b413d921e1 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -37,6 +37,9 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.AppOpsManager; +import android.app.compat.CompatChanges; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledSince; import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.ParceledListSlice; @@ -83,7 +86,9 @@ import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; +import android.sysprop.DisplayProperties; import android.text.TextUtils; +import android.util.ArraySet; import android.util.EventLog; import android.util.IntArray; import android.util.Pair; @@ -92,6 +97,7 @@ import android.util.SparseArray; import android.util.SparseIntArray; import android.util.Spline; import android.view.Display; +import android.view.DisplayEventReceiver; import android.view.DisplayInfo; import android.view.IDisplayFoldListener; import android.view.Surface; @@ -114,6 +120,7 @@ import com.android.server.wm.WindowManagerInternal; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Arrays; import java.util.Optional; import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.Consumer; @@ -181,6 +188,7 @@ public final class DisplayManagerService extends SystemService { private static final int MSG_REQUEST_TRAVERSAL = 4; private static final int MSG_UPDATE_VIEWPORT = 5; private static final int MSG_LOAD_BRIGHTNESS_CONFIGURATION = 6; + private static final int MSG_DELIVER_DISPLAY_EVENT_FRAME_RATE_OVERRIDE = 7; private final Context mContext; private final DisplayManagerHandler mHandler; @@ -357,6 +365,30 @@ public final class DisplayManagerService extends SystemService { // Received notifications of the display-fold action private DisplayFoldListener mDisplayFoldListener; + private final boolean mAllowNonNativeRefreshRateOverride; + + private static final float THRESHOLD_FOR_REFRESH_RATES_DIVIDERS = 0.1f; + + /** + * Applications use {@link android.view.Display#getRefreshRate} and + * {@link android.view.Display.Mode#getRefreshRate} to know what is the display refresh rate. + * Starting with Android S, the platform might throttle down applications frame rate to a + * divisor of the refresh rate if it is more preferable (for example if the application called + * to {@link android.view.Surface#setFrameRate}). + * Applications will experience {@link android.view.Choreographer#postFrameCallback} callbacks + * and backpressure at the throttled frame rate. + * + * {@link android.view.Display#getRefreshRate} will always return the application frame rate + * and not the physical display refresh rate to allow applications to do frame pacing correctly. + * + * {@link android.view.Display.Mode#getRefreshRate} will return the application frame rate if + * compiled to a previous release and starting with Android S it will return the physical + * display refresh rate. + */ + @ChangeId + @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.S) + static final long DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE = 170503758L; + public DisplayManagerService(Context context) { this(context, new Injector()); } @@ -389,6 +421,7 @@ public final class DisplayManagerService extends SystemService { mCurrentUserId = UserHandle.USER_SYSTEM; ColorSpace[] colorSpaces = SurfaceControl.getCompositionColorSpaces(); mWideColorSpace = colorSpaces[1]; + mAllowNonNativeRefreshRateOverride = mInjector.getAllowNonNativeRefreshRateOverride(); mSystemReady = false; } @@ -677,11 +710,82 @@ public final class DisplayManagerService extends SystemService { Settings.Secure.MINIMAL_POST_PROCESSING_ALLOWED, 1, UserHandle.USER_CURRENT) != 0; } + private DisplayInfo getDisplayInfoForFrameRateOverride(DisplayEventReceiver.FrameRateOverride[] + frameRateOverrides, DisplayInfo info, int callingUid) { + float frameRateHz = 0; + for (DisplayEventReceiver.FrameRateOverride frameRateOverride : frameRateOverrides) { + if (frameRateOverride.uid == callingUid) { + frameRateHz = frameRateOverride.frameRateHz; + break; + } + } + if (frameRateHz == 0) { + return info; + } + + // Override the refresh rate only if it is a divider of the current + // refresh rate. This calculation needs to be in sync with the native code + // in RefreshRateConfigs::getRefreshRateDividerForUid + Display.Mode currentMode = info.getMode(); + float numPeriods = currentMode.getRefreshRate() / frameRateHz; + float numPeriodsRound = Math.round(numPeriods); + if (Math.abs(numPeriods - numPeriodsRound) > THRESHOLD_FOR_REFRESH_RATES_DIVIDERS) { + return info; + } + frameRateHz = currentMode.getRefreshRate() / numPeriodsRound; + + DisplayInfo overriddenInfo = new DisplayInfo(); + overriddenInfo.copyFrom(info); + for (Display.Mode mode : info.supportedModes) { + if (!mode.equalsExceptRefreshRate(currentMode)) { + continue; + } + + if (mode.getRefreshRate() >= frameRateHz - THRESHOLD_FOR_REFRESH_RATES_DIVIDERS + && mode.getRefreshRate() + <= frameRateHz + THRESHOLD_FOR_REFRESH_RATES_DIVIDERS) { + if (DEBUG) { + Slog.d(TAG, "found matching modeId " + mode.getModeId()); + } + overriddenInfo.refreshRateOverride = mode.getRefreshRate(); + + if (!CompatChanges.isChangeEnabled(DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE, + callingUid)) { + overriddenInfo.modeId = mode.getModeId(); + } + return overriddenInfo; + } + } + + if (mAllowNonNativeRefreshRateOverride) { + overriddenInfo.refreshRateOverride = frameRateHz; + if (!CompatChanges.isChangeEnabled(DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE, + callingUid)) { + overriddenInfo.supportedModes = Arrays.copyOf(info.supportedModes, + info.supportedModes.length + 1); + overriddenInfo.supportedModes[overriddenInfo.supportedModes.length - 1] = + new Display.Mode(Display.DISPLAY_MODE_ID_FOR_FRAME_RATE_OVERRIDE, + currentMode.getPhysicalWidth(), currentMode.getPhysicalHeight(), + overriddenInfo.refreshRateOverride); + overriddenInfo.modeId = + overriddenInfo.supportedModes[overriddenInfo.supportedModes.length - 1] + .getModeId(); + } + return overriddenInfo; + } + + + + return info; + } + private DisplayInfo getDisplayInfoInternal(int displayId, int callingUid) { synchronized (mSyncRoot) { LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId); if (display != null) { - DisplayInfo info = display.getDisplayInfoLocked(); + DisplayInfo info = + getDisplayInfoForFrameRateOverride(display.getFrameRateOverrides(), + display.getDisplayInfoLocked(), callingUid); if (info.hasAccess(callingUid) || isUidPresentOnDisplayInternal(callingUid, displayId)) { return info; @@ -691,14 +795,15 @@ public final class DisplayManagerService extends SystemService { } } - private void registerCallbackInternal(IDisplayManagerCallback callback, int callingPid) { + private void registerCallbackInternal(IDisplayManagerCallback callback, int callingPid, + int callingUid) { synchronized (mSyncRoot) { if (mCallbacks.get(callingPid) != null) { throw new SecurityException("The calling process has already " + "registered an IDisplayManagerCallback."); } - CallbackRecord record = new CallbackRecord(callingPid, callback); + CallbackRecord record = new CallbackRecord(callingPid, callingUid, callback); try { IBinder binder = callback.asBinder(); binder.linkToDeath(record, 0); @@ -1034,6 +1139,16 @@ public final class DisplayManagerService extends SystemService { scheduleTraversalLocked(false); } + private void handleLogicalDisplayFrameRateOverridesChangedLocked( + @NonNull LogicalDisplay display) { + final int displayId = display.getDisplayIdLocked(); + // We don't bother invalidating the display info caches here because any changes to the + // display info will trigger a cache invalidation inside of LogicalDisplay before we hit + // this point. + sendDisplayEventFrameRateOverrideLocked(displayId); + scheduleTraversalLocked(false); + } + private void handleLogicalDisplayRemovedLocked(@NonNull LogicalDisplay display) { final int displayId = display.getDisplayIdLocked(); mDisplayPowerControllers.delete(displayId); @@ -1545,6 +1660,12 @@ public final class DisplayManagerService extends SystemService { mHandler.sendMessage(msg); } + private void sendDisplayEventFrameRateOverrideLocked(int displayId) { + Message msg = mHandler.obtainMessage(MSG_DELIVER_DISPLAY_EVENT_FRAME_RATE_OVERRIDE, + displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED); + mHandler.sendMessage(msg); + } + // Requests that performTraversals be called at a // later time to apply changes to surfaces and displays. private void scheduleTraversalLocked(boolean inTraversal) { @@ -1558,7 +1679,7 @@ public final class DisplayManagerService extends SystemService { // Runs on Handler thread. // Delivers display event notifications to callbacks. - private void deliverDisplayEvent(int displayId, int event) { + private void deliverDisplayEvent(int displayId, ArraySet<Integer> uids, int event) { if (DEBUG) { Slog.d(TAG, "Delivering display event: displayId=" + displayId + ", event=" + event); @@ -1570,12 +1691,14 @@ public final class DisplayManagerService extends SystemService { count = mCallbacks.size(); mTempCallbacks.clear(); for (int i = 0; i < count; i++) { - mTempCallbacks.add(mCallbacks.valueAt(i)); + if (uids == null || uids.contains(mCallbacks.valueAt(i).mUid)) { + mTempCallbacks.add(mCallbacks.valueAt(i)); + } } } // After releasing the lock, send the notifications out. - for (int i = 0; i < count; i++) { + for (int i = 0; i < mTempCallbacks.size(); i++) { mTempCallbacks.get(i).notifyDisplayEventAsync(displayId, event); } mTempCallbacks.clear(); @@ -1691,6 +1814,11 @@ public final class DisplayManagerService extends SystemService { long getDefaultDisplayDelayTimeout() { return WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT; } + + boolean getAllowNonNativeRefreshRateOverride() { + return DisplayProperties + .debug_allow_non_native_refresh_rate_override().orElse(false); + } } @VisibleForTesting @@ -1760,7 +1888,7 @@ public final class DisplayManagerService extends SystemService { break; case MSG_DELIVER_DISPLAY_EVENT: - deliverDisplayEvent(msg.arg1, msg.arg2); + deliverDisplayEvent(msg.arg1, null, msg.arg2); break; case MSG_REQUEST_TRAVERSAL: @@ -1787,6 +1915,17 @@ public final class DisplayManagerService extends SystemService { case MSG_LOAD_BRIGHTNESS_CONFIGURATION: loadBrightnessConfiguration(); break; + + case MSG_DELIVER_DISPLAY_EVENT_FRAME_RATE_OVERRIDE: + ArraySet<Integer> uids; + synchronized (mSyncRoot) { + int displayId = msg.arg1; + LogicalDisplay display = mLogicalDisplayMapper.getLocked(displayId); + uids = display.getPendingFrameRateOverrideUids(); + display.clearPendingFrameRateOverrideUids(); + } + deliverDisplayEvent(msg.arg1, uids, msg.arg2); + break; } } } @@ -1810,6 +1949,10 @@ public final class DisplayManagerService extends SystemService { case LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_SWAPPED: handleLogicalDisplaySwappedLocked(display); break; + + case LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED: + handleLogicalDisplayFrameRateOverridesChangedLocked(display); + break; } } @@ -1823,12 +1966,14 @@ public final class DisplayManagerService extends SystemService { private final class CallbackRecord implements DeathRecipient { public final int mPid; + public final int mUid; private final IDisplayManagerCallback mCallback; public boolean mWifiDisplayScanRequested; - public CallbackRecord(int pid, IDisplayManagerCallback callback) { + CallbackRecord(int pid, int uid, IDisplayManagerCallback callback) { mPid = pid; + mUid = uid; mCallback = callback; } @@ -1918,9 +2063,10 @@ public final class DisplayManagerService extends SystemService { } final int callingPid = Binder.getCallingPid(); + final int callingUid = Binder.getCallingUid(); final long token = Binder.clearCallingIdentity(); try { - registerCallbackInternal(callback, callingPid); + registerCallbackInternal(callback, callingPid, callingUid); } finally { Binder.restoreCallingIdentity(token); } diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index f6578584cb18..74ea2d7114fc 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -208,6 +208,9 @@ final class LocalDisplayAdapter extends DisplayAdapter { private DisplayDeviceConfig mDisplayDeviceConfig; + private DisplayEventReceiver.FrameRateOverride[] mFrameRateOverrides = + new DisplayEventReceiver.FrameRateOverride[0]; + LocalDisplayDevice(IBinder displayToken, long physicalDisplayId, SurfaceControl.DisplayInfo info, SurfaceControl.DisplayConfig[] configs, int activeConfigId, SurfaceControl.DesiredDisplayConfigSpecs configSpecs, @@ -625,6 +628,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { mInfo.name = getContext().getResources().getString( com.android.internal.R.string.display_manager_hdmi_display_name); } + mInfo.frameRateOverrides = mFrameRateOverrides; + // The display is trusted since it is created by system. mInfo.flags |= DisplayDeviceInfo.FLAG_TRUSTED; } @@ -882,6 +887,13 @@ final class LocalDisplayAdapter extends DisplayAdapter { } } + public void onFrameRateOverridesChanged( + DisplayEventReceiver.FrameRateOverride[] overrides) { + if (updateFrameRateOverridesLocked(overrides)) { + updateDeviceInfoLocked(); + } + } + public boolean updateActiveModeLocked(int activeConfigId) { if (mActiveConfigId == activeConfigId) { return false; @@ -895,6 +907,16 @@ final class LocalDisplayAdapter extends DisplayAdapter { return true; } + public boolean updateFrameRateOverridesLocked( + DisplayEventReceiver.FrameRateOverride[] overrides) { + if (overrides.equals(mFrameRateOverrides)) { + return false; + } + + mFrameRateOverrides = overrides; + return true; + } + public void requestColorModeLocked(int colorMode) { if (mActiveColorMode == colorMode) { return; @@ -1102,23 +1124,39 @@ final class LocalDisplayAdapter extends DisplayAdapter { public interface DisplayEventListener { void onHotplug(long timestampNanos, long physicalDisplayId, boolean connected); void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId); + void onFrameRateOverridesChanged(long timestampNanos, long physicalDisplayId, + DisplayEventReceiver.FrameRateOverride[] overrides); + } public static final class ProxyDisplayEventReceiver extends DisplayEventReceiver { private final DisplayEventListener mListener; ProxyDisplayEventReceiver(Looper looper, DisplayEventListener listener) { - super(looper, VSYNC_SOURCE_APP, CONFIG_CHANGED_EVENT_DISPATCH); + super(looper, VSYNC_SOURCE_APP, + EVENT_REGISTRATION_CONFIG_CHANGED_FLAG + | EVENT_REGISTRATION_FRAME_RATE_OVERRIDE_FLAG); mListener = listener; } + + @Override public void onHotplug(long timestampNanos, long physicalDisplayId, boolean connected) { mListener.onHotplug(timestampNanos, physicalDisplayId, connected); } + + @Override public void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId) { mListener.onConfigChanged(timestampNanos, physicalDisplayId, configId); } + + @Override + public void onFrameRateOverridesChanged(long timestampNanos, long physicalDisplayId, + DisplayEventReceiver.FrameRateOverride[] overrides) { + mListener.onFrameRateOverridesChanged(timestampNanos, physicalDisplayId, overrides); + } } private final class LocalDisplayEventListener implements DisplayEventListener { + @Override public void onHotplug(long timestampNanos, long physicalDisplayId, boolean connected) { synchronized (getSyncRoot()) { if (connected) { @@ -1128,6 +1166,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { } } } + + @Override public void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId) { if (DEBUG) { Slog.d(TAG, "onConfigChanged(" @@ -1147,5 +1187,26 @@ final class LocalDisplayAdapter extends DisplayAdapter { device.onActiveDisplayConfigChangedLocked(configId); } } + + @Override + public void onFrameRateOverridesChanged(long timestampNanos, long physicalDisplayId, + DisplayEventReceiver.FrameRateOverride[] overrides) { + if (DEBUG) { + Slog.d(TAG, "onFrameRateOverrideChanged(timestampNanos=" + timestampNanos + + ", physicalDisplayId=" + physicalDisplayId + " overrides=" + + Arrays.toString(overrides) + ")"); + } + synchronized (getSyncRoot()) { + LocalDisplayDevice device = mDevices.get(physicalDisplayId); + if (device == null) { + if (DEBUG) { + Slog.d(TAG, "Received frame rate override event for unhandled physical" + + " display: physicalDisplayId=" + physicalDisplayId); + } + return; + } + device.onFrameRateOverridesChanged(overrides); + } + } } } diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java index 979c3b871284..d80e1687f67f 100644 --- a/services/core/java/com/android/server/display/LogicalDisplay.java +++ b/services/core/java/com/android/server/display/LogicalDisplay.java @@ -20,8 +20,11 @@ import android.annotation.NonNull; import android.graphics.Point; import android.graphics.Rect; import android.hardware.display.DisplayManagerInternal; +import android.util.ArraySet; import android.util.Slog; +import android.util.SparseArray; import android.view.Display; +import android.view.DisplayEventReceiver; import android.view.DisplayInfo; import android.view.Surface; import android.view.SurfaceControl; @@ -123,10 +126,27 @@ final class LogicalDisplay { */ private boolean mIsEnabled = true; + /** + * The UID mappings for refresh rate override + */ + private DisplayEventReceiver.FrameRateOverride[] mFrameRateOverrides; + + /** + * Holds a set of UIDs that their frame rate override changed and needs to be notified + */ + private ArraySet<Integer> mPendingFrameRateOverrideUids; + + /** + * Temporary frame rate override list, used when needed. + */ + private final SparseArray<Float> mTempFrameRateOverride; + public LogicalDisplay(int displayId, int layerStack, DisplayDevice primaryDisplayDevice) { mDisplayId = displayId; mLayerStack = layerStack; mPrimaryDisplayDevice = primaryDisplayDevice; + mPendingFrameRateOverrideUids = new ArraySet<>(); + mTempFrameRateOverride = new SparseArray<>(); } /** @@ -179,6 +199,27 @@ final class LogicalDisplay { } /** + * Returns the frame rate overrides list + */ + public DisplayEventReceiver.FrameRateOverride[] getFrameRateOverrides() { + return mFrameRateOverrides; + } + + /** + * Returns the list of uids that needs to be updated about their frame rate override + */ + public ArraySet<Integer> getPendingFrameRateOverrideUids() { + return mPendingFrameRateOverrideUids; + } + + /** + * Clears the list of uids that needs to be updated about their frame rate override + */ + public void clearPendingFrameRateOverrideUids() { + mPendingFrameRateOverrideUids = new ArraySet<>(); + } + + /** * @see DisplayManagerInternal#getNonOverrideDisplayInfo(int, DisplayInfo) */ void getNonOverrideDisplayInfoLocked(DisplayInfo outInfo) { @@ -324,12 +365,40 @@ final class LogicalDisplay { (deviceInfo.flags & DisplayDeviceInfo.FLAG_MASK_DISPLAY_CUTOUT) != 0; mBaseDisplayInfo.displayCutout = maskCutout ? null : deviceInfo.displayCutout; mBaseDisplayInfo.displayId = mDisplayId; + updateFrameRateOverrides(deviceInfo); mPrimaryDisplayDeviceInfo = deviceInfo; mInfo.set(null); } } + private void updateFrameRateOverrides(DisplayDeviceInfo deviceInfo) { + mTempFrameRateOverride.clear(); + if (mFrameRateOverrides != null) { + for (DisplayEventReceiver.FrameRateOverride frameRateOverride + : mFrameRateOverrides) { + mTempFrameRateOverride.put(frameRateOverride.uid, + frameRateOverride.frameRateHz); + } + } + mFrameRateOverrides = deviceInfo.frameRateOverrides; + if (mFrameRateOverrides != null) { + for (DisplayEventReceiver.FrameRateOverride frameRateOverride + : mFrameRateOverrides) { + float refreshRate = mTempFrameRateOverride.get(frameRateOverride.uid, 0f); + if (refreshRate == 0 || frameRateOverride.frameRateHz != refreshRate) { + mTempFrameRateOverride.put(frameRateOverride.uid, + frameRateOverride.frameRateHz); + } else { + mTempFrameRateOverride.delete(frameRateOverride.uid); + } + } + } + for (int i = 0; i < mTempFrameRateOverride.size(); i++) { + mPendingFrameRateOverrideUids.add(mTempFrameRateOverride.keyAt(i)); + } + } + /** * Return the insets currently applied to the display. * @@ -638,5 +707,7 @@ final class LogicalDisplay { pw.println("mBaseDisplayInfo=" + mBaseDisplayInfo); pw.println("mOverrideDisplayInfo=" + mOverrideDisplayInfo); pw.println("mRequestedMinimalPostProcessing=" + mRequestedMinimalPostProcessing); + pw.println("mFrameRateOverrides=" + Arrays.toString(mFrameRateOverrides)); + pw.println("mPendingFrameRateOverrideUids=" + mPendingFrameRateOverrideUids); } } diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java index 6b741709321c..cdcbb4f123a1 100644 --- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java +++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java @@ -23,6 +23,7 @@ import android.text.TextUtils; import android.util.Slog; import android.util.SparseArray; import android.view.Display; +import android.view.DisplayEventReceiver; import android.view.DisplayInfo; import com.android.internal.util.IndentingPrintWriter; @@ -52,6 +53,7 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { public static final int LOGICAL_DISPLAY_EVENT_CHANGED = 2; public static final int LOGICAL_DISPLAY_EVENT_REMOVED = 3; public static final int LOGICAL_DISPLAY_EVENT_SWAPPED = 4; + public static final int LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED = 5; /** * Temporary display info, used for comparing display configurations. @@ -342,6 +344,8 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { mTempDisplayInfo.copyFrom(display.getDisplayInfoLocked()); display.getNonOverrideDisplayInfoLocked(mTempNonOverrideDisplayInfo); + DisplayEventReceiver.FrameRateOverride[] frameRatesOverrides = + display.getFrameRateOverrides(); display.updateLocked(mDisplayDeviceRepo); if (!display.isValidLocked()) { mLogicalDisplays.removeAt(i); @@ -376,6 +380,9 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { final int eventMsg = TextUtils.equals(oldUniqueId, newUniqueId) ? LOGICAL_DISPLAY_EVENT_CHANGED : LOGICAL_DISPLAY_EVENT_SWAPPED; mListener.onLogicalDisplayEventLocked(display, eventMsg); + } else if (!display.getPendingFrameRateOverrideUids().isEmpty()) { + mListener.onLogicalDisplayEventLocked(display, + LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED); } else { // While applications shouldn't know nor care about the non-overridden info, we // still need to let WindowManager know so it can update its own internal state for diff --git a/services/core/java/com/android/server/display/OverlayDisplayWindow.java b/services/core/java/com/android/server/display/OverlayDisplayWindow.java index 49f0d35d2e78..cd3a453d5e64 100644 --- a/services/core/java/com/android/server/display/OverlayDisplayWindow.java +++ b/services/core/java/com/android/server/display/OverlayDisplayWindow.java @@ -16,8 +16,6 @@ package com.android.server.display; -import com.android.internal.util.DumpUtils; - import android.content.Context; import android.graphics.SurfaceTexture; import android.hardware.display.DisplayManager; @@ -30,12 +28,14 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.ScaleGestureDetector; import android.view.TextureView; +import android.view.TextureView.SurfaceTextureListener; import android.view.ThreadedRenderer; import android.view.View; import android.view.WindowManager; -import android.view.TextureView.SurfaceTextureListener; import android.widget.TextView; +import com.android.internal.util.DumpUtils; + import java.io.PrintWriter; /** @@ -319,7 +319,7 @@ final class OverlayDisplayWindow implements DumpUtils.Dump { public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) { mListener.onWindowCreated(surfaceTexture, - mDefaultDisplayInfo.getMode().getRefreshRate(), + mDefaultDisplayInfo.getRefreshRate(), mDefaultDisplayInfo.presentationDeadlineNanos, mDefaultDisplayInfo.state); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 38807112576a..f76dec319379 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -21,7 +21,6 @@ import static android.Manifest.permission.MANAGE_DEVICE_ADMINS; import static android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS; import static android.Manifest.permission.REQUEST_DELETE_PACKAGES; import static android.Manifest.permission.SET_HARMFUL_APP_WARNINGS; -import static android.app.AppOpsManager.MODE_ALLOWED; import static android.app.AppOpsManager.MODE_DEFAULT; import static android.app.AppOpsManager.MODE_IGNORED; import static android.content.Intent.ACTION_MAIN; @@ -44,7 +43,6 @@ import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT; import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED; import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED; import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET; -import static android.content.pm.PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER; import static android.content.pm.PackageManager.INSTALL_FAILED_ALREADY_EXISTS; import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_PERMISSION_GROUP; import static android.content.pm.PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE; @@ -141,6 +139,7 @@ import android.annotation.AppIdInt; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.StringRes; import android.annotation.UserIdInt; import android.annotation.WorkerThread; import android.app.ActivityManager; @@ -1203,6 +1202,7 @@ public class PackageManagerService extends IPackageManager.Stub public ViewCompiler viewCompiler; public @Nullable String wellbeingPackage; public @Nullable String retailDemoPackage; + public @Nullable String recentsPackage; public ComponentName resolveComponentName; public ArrayMap<String, AndroidPackage> packages; public boolean enableFreeCacheV2; @@ -1737,6 +1737,7 @@ public class PackageManagerService extends IPackageManager.Stub final @Nullable String mSharedSystemSharedLibraryPackageName; final @Nullable String mRetailDemoPackage; final @Nullable String mOverlayConfigSignaturePackage; + final @Nullable String mRecentsPackage; private final PackageUsage mPackageUsage = new PackageUsage(); private final CompilerStats mCompilerStats = new CompilerStats(); @@ -2169,7 +2170,7 @@ public class PackageManagerService extends IPackageManager.Stub private void handlePackagePostInstall(PackageInstalledInfo res, boolean grantPermissions, boolean killApp, boolean virtualPreload, - String[] grantedPermissions, List<String> whitelistedRestrictedPermissions, + String[] grantedPermissions, List<String> allowlistedRestrictedPermissions, int autoRevokePermissionsMode, boolean launchedForRestore, String installerPackage, IPackageInstallObserver2 installObserver, int dataLoaderType) { @@ -2202,32 +2203,21 @@ public class PackageManagerService extends IPackageManager.Stub res.removedInfo.sendPackageRemovedBroadcasts(killApp, false /*removedBySystem*/); } - // Allowlist any restricted permissions first as some may be runtime - // that the installer requested to be granted at install time. - if (whitelistedRestrictedPermissions != null - && !whitelistedRestrictedPermissions.isEmpty()) { - mPermissionManager.setAllowlistedRestrictedPermissions(res.pkg, - whitelistedRestrictedPermissions, FLAG_PERMISSION_WHITELIST_INSTALLER, - res.newUsers); - } - - if (autoRevokePermissionsMode == MODE_ALLOWED - || autoRevokePermissionsMode == MODE_IGNORED) { - mPermissionManager.setAutoRevokeExempted(res.pkg, - autoRevokePermissionsMode == MODE_IGNORED, res.newUsers); - } - - // Now that we successfully installed the package, grant runtime - // permissions if requested before broadcasting the install. Also - // for legacy apps in permission review mode we clear the permission - // review flag which is used to emulate runtime permissions for - // legacy apps. + final List<String> grantedPermissionsList; if (grantPermissions) { - final int callingUid = Binder.getCallingUid(); - mPermissionManager.grantRequestedRuntimePermissions(res.pkg, - grantedPermissions != null ? Arrays.asList(grantedPermissions) : null, - res.newUsers); + if (grantedPermissions != null) { + grantedPermissionsList = Arrays.asList(grantedPermissions); + } else { + grantedPermissionsList = res.pkg.getRequestedPermissions(); + } + } else { + grantedPermissionsList = Collections.emptyList(); + } + if (allowlistedRestrictedPermissions == null) { + allowlistedRestrictedPermissions = Collections.emptyList(); } + mPermissionManager.onPackageInstalled(res.pkg, grantedPermissionsList, + allowlistedRestrictedPermissions, autoRevokePermissionsMode, res.newUsers); final String installerPackageName = res.installerPackageName != null @@ -2980,6 +2970,7 @@ public class PackageManagerService extends IPackageManager.Stub mSystemTextClassifierPackageName = testParams.systemTextClassifierPackage; mWellbeingPackage = testParams.wellbeingPackage; mRetailDemoPackage = testParams.retailDemoPackage; + mRecentsPackage = testParams.recentsPackage; mDocumenterPackage = testParams.documenterPackage; mConfiguratorPackage = testParams.configuratorPackage; mAppPredictionServicePackage = testParams.appPredictionServicePackage; @@ -3564,6 +3555,7 @@ public class PackageManagerService extends IPackageManager.Stub mIncidentReportApproverPackage = getIncidentReportApproverPackageName(); mRetailDemoPackage = getRetailDemoPackageName(); mOverlayConfigSignaturePackage = getOverlayConfigSignaturePackageName(); + mRecentsPackage = getRecentsPackageName(); // Now that we know all of the shared libraries, update all clients to have // the correct library paths. @@ -13676,9 +13668,8 @@ public class PackageManagerService extends IPackageManager.Stub != 0) { whiteListedPermissions = pkgSetting.pkg.getRequestedPermissions(); } - mPermissionManager.setAllowlistedRestrictedPermissions(pkgSetting.pkg, - whiteListedPermissions, FLAG_PERMISSION_WHITELIST_INSTALLER, - new int[] { userId }); + mPermissionManager.onPackageInstalled(pkgSetting.pkg, Collections.emptyList(), + whiteListedPermissions, MODE_DEFAULT, new int[] { userId }); } if (pkgSetting.pkg != null) { @@ -21329,15 +21320,8 @@ public class PackageManagerService extends IPackageManager.Stub @Override public @Nullable String getAttentionServicePackageName() { - final String flattenedComponentName = - mContext.getString(R.string.config_defaultAttentionService); - if (flattenedComponentName != null) { - ComponentName componentName = ComponentName.unflattenFromString(flattenedComponentName); - if (componentName != null && componentName.getPackageName() != null) { - return ensureSystemPackageName(componentName.getPackageName()); - } - } - return null; + return ensureSystemPackageName( + getPackageFromComponentString(R.string.config_defaultAttentionService)); } private @Nullable String getDocumenterPackageName() { @@ -21372,17 +21356,8 @@ public class PackageManagerService extends IPackageManager.Stub @Override public String getAppPredictionServicePackageName() { - String flattenedAppPredictionServiceComponentName = - mContext.getString(R.string.config_defaultAppPredictionService); - if (flattenedAppPredictionServiceComponentName == null) { - return null; - } - ComponentName appPredictionServiceComponentName = - ComponentName.unflattenFromString(flattenedAppPredictionServiceComponentName); - if (appPredictionServiceComponentName == null) { - return null; - } - return ensureSystemPackageName(appPredictionServiceComponentName.getPackageName()); + return ensureSystemPackageName( + getPackageFromComponentString(R.string.config_defaultAppPredictionService)); } private @NonNull String[] dropNonSystemPackages(@NonNull String[] pkgNames) { @@ -21399,19 +21374,8 @@ public class PackageManagerService extends IPackageManager.Stub @Override public String getSystemCaptionsServicePackageName() { - String flattenedSystemCaptionsServiceComponentName = - mContext.getString(R.string.config_defaultSystemCaptionsService); - - if (TextUtils.isEmpty(flattenedSystemCaptionsServiceComponentName)) { - return null; - } - - ComponentName systemCaptionsServiceComponentName = - ComponentName.unflattenFromString(flattenedSystemCaptionsServiceComponentName); - if (systemCaptionsServiceComponentName == null) { - return null; - } - return ensureSystemPackageName(systemCaptionsServiceComponentName.getPackageName()); + return ensureSystemPackageName( + getPackageFromComponentString(R.string.config_defaultSystemCaptionsService)); } @Override @@ -21429,19 +21393,8 @@ public class PackageManagerService extends IPackageManager.Stub @Override public String getContentCaptureServicePackageName() { - final String flattenedContentCaptureService = - mContext.getString(R.string.config_defaultContentCaptureService); - - if (TextUtils.isEmpty(flattenedContentCaptureService)) { - return null; - } - - final ComponentName contentCaptureServiceComponentName = - ComponentName.unflattenFromString(flattenedContentCaptureService); - if (contentCaptureServiceComponentName == null) { - return null; - } - return ensureSystemPackageName(contentCaptureServiceComponentName.getPackageName()); + return ensureSystemPackageName( + getPackageFromComponentString(R.string.config_defaultContentCaptureService)); } public String getOverlayConfigSignaturePackageName() { @@ -21485,6 +21438,26 @@ public class PackageManagerService extends IPackageManager.Stub } @Nullable + private String getRecentsPackageName() { + return ensureSystemPackageName( + getPackageFromComponentString(R.string.config_recentsComponentName)); + + } + + @Nullable + private String getPackageFromComponentString(@StringRes int stringResId) { + final String componentString = mContext.getString(stringResId); + if (TextUtils.isEmpty(componentString)) { + return null; + } + final ComponentName component = ComponentName.unflattenFromString(componentString); + if (component == null) { + return null; + } + return component.getPackageName(); + } + + @Nullable private String ensureSystemPackageName(@Nullable String packageName) { if (packageName == null) { return null; @@ -25092,6 +25065,8 @@ public class PackageManagerService extends IPackageManager.Stub : new String[] {mRetailDemoPackage}; case PackageManagerInternal.PACKAGE_OVERLAY_CONFIG_SIGNATURE: return filterOnlySystemPackages(getOverlayConfigSignaturePackageName()); + case PackageManagerInternal.PACKAGE_RECENTS: + return filterOnlySystemPackages(mRecentsPackage); default: return ArrayUtils.emptyArray(String.class); } diff --git a/services/core/java/com/android/server/pm/permission/Permission.java b/services/core/java/com/android/server/pm/permission/Permission.java index 687e96c3c9dd..995b59e97e91 100644 --- a/services/core/java/com/android/server/pm/permission/Permission.java +++ b/services/core/java/com/android/server/pm/permission/Permission.java @@ -331,6 +331,10 @@ public final class Permission { return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO) != 0; } + public boolean isRecents() { + return (mPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_RECENTS) != 0; + } + public void transfer(@NonNull String oldPackageName, @NonNull String newPackageName) { if (!oldPackageName.equals(mPermissionInfo.packageName)) { return; 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 1b35d29f0d6b..311ba68b69ae 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -3616,6 +3616,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { // Special permission granted only to the OEM specified retail demo app allowed = true; } + if (!allowed && bp.isRecents() + && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames( + PackageManagerInternal.PACKAGE_RECENTS, UserHandle.USER_SYSTEM), + pkg.getPackageName())) { + // Special permission for the recents app. + allowed = true; + } return allowed; } @@ -4907,6 +4914,20 @@ public class PermissionManagerService extends IPermissionManager.Stub { return true; } + private void onPackageInstalledInternal(@NonNull AndroidPackage pkg, + @NonNull List<String> grantedPermissions, + @NonNull List<String> allowlistedRestrictedPermissions, int autoRevokePermissionsMode, + @NonNull int[] userIds) { + setAllowlistedRestrictedPermissionsInternal(pkg, allowlistedRestrictedPermissions, + FLAG_PERMISSION_WHITELIST_INSTALLER, userIds); + if (autoRevokePermissionsMode == AppOpsManager.MODE_ALLOWED + || autoRevokePermissionsMode == AppOpsManager.MODE_IGNORED) { + setAutoRevokeExemptedInternal(pkg, + autoRevokePermissionsMode == AppOpsManager.MODE_IGNORED, userIds); + } + grantRequestedRuntimePermissionsInternal(pkg, grantedPermissions, userIds); + } + private void onPackageRemovedInternal(@NonNull AndroidPackage pkg) { removeAllPermissionsInternal(pkg); } @@ -5073,28 +5094,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { return PermissionManagerService.this.getAppOpPermissionPackagesInternal(permissionName); } @Override - public void grantRequestedRuntimePermissions(@NonNull AndroidPackage pkg, - @Nullable List<String> permissions, @NonNull int[] userIds) { - Objects.requireNonNull(pkg, "pkg"); - Objects.requireNonNull(userIds, "userIds"); - grantRequestedRuntimePermissionsInternal(pkg, permissions, userIds); - } - @Override - public void setAllowlistedRestrictedPermissions(@NonNull AndroidPackage pkg, - @Nullable List<String> permissions, @PermissionWhitelistFlags int allowlistFlags, - @NonNull int[] userIds) { - Objects.requireNonNull(pkg, "pkg"); - Objects.requireNonNull(userIds, "userIds"); - setAllowlistedRestrictedPermissionsInternal(pkg, permissions, allowlistFlags, userIds); - } - @Override - public void setAutoRevokeExempted(@NonNull AndroidPackage pkg, boolean exempted, - @NonNull int[] userIds) { - Objects.requireNonNull(pkg, "pkg"); - Objects.requireNonNull(userIds, "userIds"); - setAutoRevokeExemptedInternal(pkg, exempted, userIds); - } - @Override public void updatePermissions(@NonNull String packageName, @Nullable AndroidPackage pkg) { PermissionManagerService.this .updatePermissions(packageName, pkg, mDefaultPermissionCallback); @@ -5365,6 +5364,20 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @Override + public void onPackageInstalled(@NonNull AndroidPackage pkg, + @NonNull List<String> grantedPermissions, + @NonNull List<String> allowlistedRestrictedPermissions, + int autoRevokePermissionsMode, @NonNull int[] userIds) { + Objects.requireNonNull(pkg, "pkg"); + Objects.requireNonNull(grantedPermissions, "grantedPermissions"); + Objects.requireNonNull(allowlistedRestrictedPermissions, + "allowlistedRestrictedPermissions"); + Objects.requireNonNull(userIds, "userIds"); + onPackageInstalledInternal(pkg, grantedPermissions, allowlistedRestrictedPermissions, + autoRevokePermissionsMode, userIds); + } + + @Override public void onPackageRemoved(@NonNull AndroidPackage pkg) { Objects.requireNonNull(pkg); onPackageRemovedInternal(pkg); diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java index 1becbedc29fb..873bf87e5ab4 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java @@ -20,7 +20,6 @@ import android.annotation.AppIdInt; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; -import android.content.pm.PackageManager; import android.content.pm.PermissionInfo; import android.permission.PermissionManagerInternal; @@ -190,42 +189,6 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager @UserIdInt int userId); /** - * Grant the requested runtime permissions for a package, or an explicit subset of them. - * - * @param pkg the package - * @param permissions the names of the subset of permissions to be granted, or {@code null} for - * granting all the requested permissions - * @param userIds the user IDs - */ - //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) - public abstract void grantRequestedRuntimePermissions(@NonNull AndroidPackage pkg, - @Nullable List<String> permissions, @NonNull int[] userIds); - - /** - * Set the allowlisted restricted permissions for a package, or an explicit subset of them. - * - * @param pkg the package - * @param permissions the names of the subset of permissions to be allowlisted, or {@code null} - * for allowlisting all the requested restricted permissions - * @param userIds the user IDs - */ - //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) - public abstract void setAllowlistedRestrictedPermissions( - @NonNull AndroidPackage pkg, @Nullable List<String> permissions, - @PackageManager.PermissionWhitelistFlags int allowlistFlags, @NonNull int[] userIds); - - /** - * Set whether a package is exempted from auto revoke. - * - * @param pkg the package - * @param exempted whether the package is exempted from auto revoke - * @param userIds the user IDs - */ - //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) - public abstract void setAutoRevokeExempted(@NonNull AndroidPackage pkg, boolean exempted, - @NonNull int[] userIds); - - /** * Update permissions when a package changed. * * <p><ol> @@ -526,6 +489,21 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager @Nullable AndroidPackage oldPkg); /** + * Callback when a package has been installed for certain users. + * + * @param pkg the installed package + * @param grantedPermissions the permissions to be granted + * @param allowlistedRestrictedPermissions the restricted permissions to be allowlisted + * @param autoRevokePermissionsMode the auto revoke permissions mode for this package + * @param userIds the user IDs this package is installed for + */ + //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) + public abstract void onPackageInstalled(@NonNull AndroidPackage pkg, + @NonNull List<String> grantedPermissions, + @NonNull List<String> allowlistedRestrictedPermissions, + int autoRevokePermissionsMode, @NonNull int[] userIds); + + /** * Callback when a package has been removed. * * @param pkg the removed package diff --git a/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java b/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java index 18646b9cc06c..88e5f69e02c4 100644 --- a/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java +++ b/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java @@ -38,11 +38,41 @@ public final class PowerStatsHALWrapper { */ public interface IPowerStatsHALWrapper { /** + * Returns information related to all supported PowerEntity(s) for which state residency + * data is available. + * + * A PowerEntity is defined as a platform subsystem, peripheral, or power domain that + * impacts the total device power consumption. + * + * @return List of information on each PowerEntity. + */ + android.hardware.power.stats.PowerEntityInfo[] getPowerEntityInfo(); + + /** + * Reports the accumulated state residency for each requested PowerEntity. + * + * Each PowerEntity may reside in one of multiple states. It may also transition from one + * state to another. StateResidency is defined as an accumulation of time that a + * PowerEntity resided in each of its possible states, the number of times that each state + * was entered, and a timestamp corresponding to the last time that state was entered. + * + * Data is accumulated starting at device boot. + * + * @param powerEntityIds List of IDs of PowerEntities for which data is requested. Passing + * an empty list will return state residency for all available + * PowerEntities. ID of each PowerEntity is contained in + * PowerEntityInfo. + * + * @return StateResidency since boot for each requested PowerEntity + */ + android.hardware.power.stats.StateResidencyResult[] getStateResidency(int[] powerEntityIds); + + /** * Returns the energy consumer IDs for all available energy consumers (power models) on the - * device. Examples of subsystems for which energy consumer results (power models) - * may be available are GPS, display, wifi, etc. The default list of energy - * consumers can be found in the PowerStats HAL definition (EnergyConsumerId.aidl). - * The availability of energy consumer IDs is hardware dependent. + * device. Examples of subsystems for which energy consumer results (power models) may be + * available are GPS, display, wifi, etc. The default list of energy consumers can be + * found in the PowerStats HAL definition (EnergyConsumerId.aidl). The availability of + * energy consumer IDs is hardware dependent. * * @return List of EnergyConsumerIds all available energy consumers. */ @@ -50,14 +80,19 @@ public final class PowerStatsHALWrapper { /** * Returns the energy consumer result for all available energy consumers (power models). - * Available consumers can be retrieved by calling getEnergyConsumerInfo(). The - * subsystem corresponding to the energy consumer result is defined by the energy - * consumer ID. + * Available consumers can be retrieved by calling getEnergyConsumerInfo(). The subsystem + * corresponding to the energy consumer result is defined by the energy consumer ID. + * + * @param energyConsumerIds Array of energy consumer IDs for which energy consumed is being + * requested. Energy consumers available on the device can be + * queried by calling getEnergyConsumerInfo(). Passing an empty + * array will return results for all energy consumers. * * @return List of EnergyConsumerResult objects containing energy consumer results for all * available energy consumers (power models). */ - android.hardware.power.stats.EnergyConsumerResult[] getEnergyConsumed(); + android.hardware.power.stats.EnergyConsumerResult[] getEnergyConsumed( + int[] energyConsumerIds); /** * Returns channel info for all available energy meters. @@ -69,17 +104,21 @@ public final class PowerStatsHALWrapper { /** * Returns energy measurements for all available energy meters. Available channels can be - * retrieved by calling getEnergyMeterInfo(). Energy measurements and channel info - * can be linked through the channelId field. + * retrieved by calling getEnergyMeterInfo(). Energy measurements and channel info can be + * linked through the channelId field. + * + * @param channelIds Array of channel IDs for which energy measurements are being requested. + * Channel IDs available on the device can be queried by calling + * getEnergyMeterInfo(). Passing an empty array will return energy + * measurements for all channels. * * @return List of EnergyMeasurement objects containing energy measurements for all * available energy meters. */ - android.hardware.power.stats.EnergyMeasurement[] readEnergyMeters(); + android.hardware.power.stats.EnergyMeasurement[] readEnergyMeters(int[] channelIds); /** - * Returns boolean indicating if connection to power stats HAL was - * established. + * Returns boolean indicating if connection to power stats HAL was established. * * @return true if connection to power stats HAL was correctly established. */ @@ -95,6 +134,38 @@ public final class PowerStatsHALWrapper { private static Supplier<IPowerStats> sVintfPowerStats; @Override + public android.hardware.power.stats.PowerEntityInfo[] getPowerEntityInfo() { + android.hardware.power.stats.PowerEntityInfo[] powerEntityInfoHAL = null; + + if (sVintfPowerStats != null) { + try { + powerEntityInfoHAL = sVintfPowerStats.get().getPowerEntityInfo(); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to get power entity info from PowerStats HAL"); + } + } + + return powerEntityInfoHAL; + } + + @Override + public android.hardware.power.stats.StateResidencyResult[] getStateResidency( + int[] powerEntityIds) { + android.hardware.power.stats.StateResidencyResult[] stateResidencyResultHAL = null; + + if (sVintfPowerStats != null) { + try { + stateResidencyResultHAL = + sVintfPowerStats.get().getStateResidency(powerEntityIds); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to get state residency from PowerStats HAL"); + } + } + + return stateResidencyResultHAL; + } + + @Override public int[] getEnergyConsumerInfo() { int[] energyConsumerInfoHAL = null; @@ -110,13 +181,14 @@ public final class PowerStatsHALWrapper { } @Override - public android.hardware.power.stats.EnergyConsumerResult[] getEnergyConsumed() { + public android.hardware.power.stats.EnergyConsumerResult[] getEnergyConsumed( + int[] energyConsumerIds) { android.hardware.power.stats.EnergyConsumerResult[] energyConsumedHAL = null; if (sVintfPowerStats != null) { try { energyConsumedHAL = - sVintfPowerStats.get().getEnergyConsumed(new int[0]); + sVintfPowerStats.get().getEnergyConsumed(energyConsumerIds); } catch (RemoteException e) { Slog.e(TAG, "Failed to get energy consumer results from PowerStats HAL"); } @@ -141,13 +213,13 @@ public final class PowerStatsHALWrapper { } @Override - public android.hardware.power.stats.EnergyMeasurement[] readEnergyMeters() { + public android.hardware.power.stats.EnergyMeasurement[] readEnergyMeters(int[] channelIds) { android.hardware.power.stats.EnergyMeasurement[] energyMeasurementHAL = null; if (sVintfPowerStats != null) { try { energyMeasurementHAL = - sVintfPowerStats.get().readEnergyMeters(new int[0]); + sVintfPowerStats.get().readEnergyMeters(channelIds); } catch (RemoteException e) { Slog.e(TAG, "Failed to get energy measurements from PowerStats HAL"); } diff --git a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java index f5131c4b371d..409cd826b6bc 100644 --- a/services/core/java/com/android/server/powerstats/PowerStatsLogger.java +++ b/services/core/java/com/android/server/powerstats/PowerStatsLogger.java @@ -61,14 +61,15 @@ public final class PowerStatsLogger extends Handler { if (DEBUG) Slog.d(TAG, "Logging to data storage"); // Log power meter data. - EnergyMeasurement[] energyMeasurements = mPowerStatsHALWrapper.readEnergyMeters(); + EnergyMeasurement[] energyMeasurements = + mPowerStatsHALWrapper.readEnergyMeters(new int[0]); mPowerStatsMeterStorage.write( EnergyMeasurementUtils.getProtoBytes(energyMeasurements)); if (DEBUG) EnergyMeasurementUtils.print(energyMeasurements); // Log power model data. EnergyConsumerResult[] energyConsumerResults = - mPowerStatsHALWrapper.getEnergyConsumed(); + mPowerStatsHALWrapper.getEnergyConsumed(new int[0]); mPowerStatsModelStorage.write( EnergyConsumerResultUtils.getProtoBytes(energyConsumerResults)); if (DEBUG) EnergyConsumerResultUtils.print(energyConsumerResults); diff --git a/services/core/java/com/android/server/powerstats/PowerStatsService.java b/services/core/java/com/android/server/powerstats/PowerStatsService.java index bf3919e7344f..1150d4bbe770 100644 --- a/services/core/java/com/android/server/powerstats/PowerStatsService.java +++ b/services/core/java/com/android/server/powerstats/PowerStatsService.java @@ -19,6 +19,7 @@ package com.android.server.powerstats; import android.annotation.Nullable; import android.content.Context; import android.hardware.power.stats.ChannelInfo; +import android.hardware.power.stats.PowerEntityInfo; import android.os.Binder; import android.os.Environment; import android.os.UserHandle; @@ -31,6 +32,7 @@ import com.android.server.powerstats.PowerStatsHALWrapper.IPowerStatsHALWrapper; import com.android.server.powerstats.PowerStatsHALWrapper.PowerStatsHALWrapperImpl; import com.android.server.powerstats.ProtoStreamUtils.ChannelInfoUtils; import com.android.server.powerstats.ProtoStreamUtils.EnergyConsumerIdUtils; +import com.android.server.powerstats.ProtoStreamUtils.PowerEntityInfoUtils; import java.io.File; import java.io.FileDescriptor; @@ -110,6 +112,10 @@ public class PowerStatsService extends SystemService { mPowerStatsLogger.writeMeterDataToFile(fd); } } else if (args.length == 0) { + pw.println("PowerStatsService dumpsys: available PowerEntityInfos"); + PowerEntityInfo[] powerEntityInfo = mPowerStatsHALWrapper.getPowerEntityInfo(); + PowerEntityInfoUtils.dumpsys(powerEntityInfo, pw); + pw.println("PowerStatsService dumpsys: available ChannelInfos"); ChannelInfo[] channelInfo = mPowerStatsHALWrapper.getEnergyMeterInfo(); ChannelInfoUtils.dumpsys(channelInfo, pw); diff --git a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java index 43afeed8b925..5a4256ac0264 100644 --- a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java +++ b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java @@ -19,6 +19,8 @@ package com.android.server.powerstats; import android.hardware.power.stats.ChannelInfo; import android.hardware.power.stats.EnergyConsumerResult; import android.hardware.power.stats.EnergyMeasurement; +import android.hardware.power.stats.PowerEntityInfo; +import android.hardware.power.stats.StateResidencyResult; import android.util.Slog; import android.util.proto.ProtoInputStream; import android.util.proto.ProtoOutputStream; @@ -42,6 +44,48 @@ import java.util.List; public class ProtoStreamUtils { private static final String TAG = ProtoStreamUtils.class.getSimpleName(); + static class PowerEntityInfoUtils { + public static void print(PowerEntityInfo[] powerEntityInfo) { + for (int i = 0; i < powerEntityInfo.length; i++) { + Slog.d(TAG, "PowerEntityId: " + powerEntityInfo[i].powerEntityId + + ", PowerEntityName: " + powerEntityInfo[i].powerEntityName); + for (int j = 0; j < powerEntityInfo[i].states.length; j++) { + Slog.d(TAG, " StateId: " + powerEntityInfo[i].states[j].stateId + + ", StateName: " + powerEntityInfo[i].states[j].stateName); + } + } + } + + public static void dumpsys(PowerEntityInfo[] powerEntityInfo, PrintWriter pw) { + for (int i = 0; i < powerEntityInfo.length; i++) { + pw.println("PowerEntityId: " + powerEntityInfo[i].powerEntityId + + ", PowerEntityName: " + powerEntityInfo[i].powerEntityName); + for (int j = 0; j < powerEntityInfo[i].states.length; j++) { + pw.println(" StateId: " + powerEntityInfo[i].states[j].stateId + + ", StateName: " + powerEntityInfo[i].states[j].stateName); + } + } + } + } + + static class StateResidencyResultUtils { + public static void print(StateResidencyResult[] stateResidencyResult) { + for (int i = 0; i < stateResidencyResult.length; i++) { + Slog.d(TAG, "PowerEntityId: " + stateResidencyResult[i].powerEntityId); + for (int j = 0; j < stateResidencyResult[i].stateResidencyData.length; j++) { + Slog.d(TAG, " StateId: " + + stateResidencyResult[i].stateResidencyData[j].stateId + + ", TotalTimeInStateMs: " + + stateResidencyResult[i].stateResidencyData[j].totalTimeInStateMs + + ", TotalStateEntryCount: " + + stateResidencyResult[i].stateResidencyData[j].totalStateEntryCount + + ", LastEntryTimestampMs: " + + stateResidencyResult[i].stateResidencyData[j].lastEntryTimestampMs); + } + } + } + } + static class ChannelInfoUtils { public static void packProtoMessage(ChannelInfo[] channelInfo, ProtoOutputStream pos) { long token; @@ -57,15 +101,15 @@ public class ProtoStreamUtils { public static void print(ChannelInfo[] channelInfo) { for (int i = 0; i < channelInfo.length; i++) { - Slog.d(TAG, "ChannelId = " + channelInfo[i].channelId - + ", ChannelName = " + channelInfo[i].channelName); + Slog.d(TAG, "ChannelId: " + channelInfo[i].channelId + + ", ChannelName: " + channelInfo[i].channelName); } } public static void dumpsys(ChannelInfo[] channelInfo, PrintWriter pw) { for (int i = 0; i < channelInfo.length; i++) { - pw.println("ChannelId = " + channelInfo[i].channelId - + ", ChannelName = " + channelInfo[i].channelName); + pw.println("ChannelId: " + channelInfo[i].channelId + + ", ChannelName: " + channelInfo[i].channelName); } } } @@ -157,9 +201,9 @@ public class ProtoStreamUtils { public static void print(EnergyMeasurement[] energyMeasurement) { for (int i = 0; i < energyMeasurement.length; i++) { - Slog.d(TAG, "ChannelId = " + energyMeasurement[i].channelId - + ", Timestamp (ms) = " + energyMeasurement[i].timestampMs - + ", Energy (uWs) = " + energyMeasurement[i].energyUWs); + Slog.d(TAG, "ChannelId: " + energyMeasurement[i].channelId + + ", Timestamp (ms): " + energyMeasurement[i].timestampMs + + ", Energy (uWs): " + energyMeasurement[i].energyUWs); } } } @@ -177,13 +221,13 @@ public class ProtoStreamUtils { public static void print(int[] energyConsumerId) { for (int i = 0; i < energyConsumerId.length; i++) { - Slog.d(TAG, "EnergyConsumerId = " + energyConsumerId[i]); + Slog.d(TAG, "EnergyConsumerId: " + energyConsumerId[i]); } } public static void dumpsys(int[] energyConsumerId, PrintWriter pw) { for (int i = 0; i < energyConsumerId.length; i++) { - pw.println("EnergyConsumerId = " + energyConsumerId[i]); + pw.println("EnergyConsumerId: " + energyConsumerId[i]); } } } @@ -278,9 +322,9 @@ public class ProtoStreamUtils { public static void print(EnergyConsumerResult[] energyConsumerResult) { for (int i = 0; i < energyConsumerResult.length; i++) { - Slog.d(TAG, "EnergyConsumerId = " + energyConsumerResult[i].energyConsumerId - + ", Timestamp (ms) = " + energyConsumerResult[i].timestampMs - + ", Energy (uWs) = " + energyConsumerResult[i].energyUWs); + Slog.d(TAG, "EnergyConsumerId: " + energyConsumerResult[i].energyConsumerId + + ", Timestamp (ms): " + energyConsumerResult[i].timestampMs + + ", Energy (uWs): " + energyConsumerResult[i].energyUWs); } } } diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java index 8ad2958f9a76..48e030050b07 100644 --- a/services/core/java/com/android/server/wm/DisplayArea.java +++ b/services/core/java/com/android/server/wm/DisplayArea.java @@ -382,6 +382,13 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { @Nullable @Override + <R> R getItemFromDisplayAreas(Function<DisplayArea, R> callback) { + final R item = super.getItemFromDisplayAreas(callback); + return item != null ? item : callback.apply(this); + } + + @Nullable + @Override <R> R getItemFromTaskDisplayAreas(Function<TaskDisplayArea, R> callback, boolean traverseTopToBottom) { // Only DisplayArea of Type.ANY may contain TaskDisplayArea as children. diff --git a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java index 43b9a218d072..c475da354dda 100644 --- a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java +++ b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java @@ -16,7 +16,10 @@ package com.android.server.wm; +import static android.window.DisplayAreaOrganizer.FEATURE_RUNTIME_TASK_CONTAINER_FIRST; + import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER; +import static com.android.server.wm.DisplayArea.Type.ANY; import android.content.pm.ParceledListSlice; import android.os.Binder; @@ -26,6 +29,7 @@ import android.view.SurfaceControl; import android.window.DisplayAreaAppearedInfo; import android.window.IDisplayAreaOrganizer; import android.window.IDisplayAreaOrganizerController; +import android.window.WindowContainerToken; import com.android.internal.protolog.common.ProtoLog; @@ -36,6 +40,12 @@ import java.util.List; public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerController.Stub { private static final String TAG = "DisplayAreaOrganizerController"; + /** + * Next available feature id for a runtime task display area. + * @see #createTaskDisplayArea(IDisplayAreaOrganizer organizer, int, int, String) + */ + private int mNextTaskDisplayAreaFeatureId = FEATURE_RUNTIME_TASK_CONTAINER_FIRST; + final ActivityTaskManagerService mService; private final WindowManagerGlobalLock mGlobalLock; private final HashMap<Integer, IDisplayAreaOrganizer> mOrganizersByFeatureIds = new HashMap(); @@ -92,10 +102,8 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl final List<DisplayAreaAppearedInfo> displayAreaInfos = new ArrayList<>(); mService.mRootWindowContainer.forAllDisplayAreas((da) -> { if (da.mFeatureId != feature) return; - da.setOrganizer(organizer, true /* skipDisplayAreaAppeared */); - displayAreaInfos.add(new DisplayAreaAppearedInfo(da.getDisplayAreaInfo(), - new SurfaceControl(da.getSurfaceControl(), - "DisplayAreaOrganizerController.registerOrganizer"))); + displayAreaInfos.add(organizeDisplayArea(organizer, da, + "DisplayAreaOrganizerController.registerOrganizer")); }); mOrganizersByFeatureIds.put(feature, organizer); @@ -124,6 +132,77 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl } } + @Override + public DisplayAreaAppearedInfo createTaskDisplayArea(IDisplayAreaOrganizer organizer, + int displayId, int rootFeatureId, String name) { + enforceTaskPermission("createTaskDisplayArea()"); + final long uid = Binder.getCallingUid(); + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Create TaskDisplayArea uid=%d", uid); + + final DisplayContent display = + mService.mRootWindowContainer.getDisplayContent(displayId); + if (display == null) { + throw new IllegalArgumentException("createTaskDisplayArea unknown displayId=" + + displayId); + } + + final DisplayArea root = display.getItemFromDisplayAreas(da -> + da.asRootDisplayArea() != null && da.mFeatureId == rootFeatureId + ? da + : null); + if (root == null) { + throw new IllegalArgumentException("Can't find RootDisplayArea with featureId=" + + rootFeatureId); + } + + final int taskDisplayAreaFeatureId = mNextTaskDisplayAreaFeatureId++; + final DeathRecipient dr = new DeathRecipient(organizer, taskDisplayAreaFeatureId); + try { + organizer.asBinder().linkToDeath(dr, 0); + } catch (RemoteException e) { + // Oh well... + } + + final TaskDisplayArea tda = createTaskDisplayArea(root.asRootDisplayArea(), name, + taskDisplayAreaFeatureId); + return organizeDisplayArea(organizer, tda, + "DisplayAreaOrganizerController.createTaskDisplayArea"); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + @Override + public void deleteTaskDisplayArea(WindowContainerToken token) { + enforceTaskPermission("deleteTaskDisplayArea()"); + final long uid = Binder.getCallingUid(); + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Delete TaskDisplayArea uid=%d", uid); + + final WindowContainer wc = WindowContainer.fromBinder(token.asBinder()); + if (wc == null || wc.asTaskDisplayArea() == null) { + throw new IllegalArgumentException("Can't resolve TaskDisplayArea from token"); + } + final TaskDisplayArea taskDisplayArea = wc.asTaskDisplayArea(); + if (!taskDisplayArea.mCreatedByOrganizer) { + throw new IllegalArgumentException( + "Attempt to delete TaskDisplayArea not created by organizer " + + "TaskDisplayArea=" + taskDisplayArea); + } + + deleteTaskDisplayArea(taskDisplayArea); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + void onDisplayAreaAppeared(IDisplayAreaOrganizer organizer, DisplayArea da) { ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "DisplayArea appeared name=%s", da.getName()); try { @@ -157,8 +236,71 @@ public class DisplayAreaOrganizerController extends IDisplayAreaOrganizerControl IBinder organizerBinder = organizer.asBinder(); mService.mRootWindowContainer.forAllDisplayAreas((da) -> { if (da.mOrganizer != null && da.mOrganizer.asBinder().equals(organizerBinder)) { - da.setOrganizer(null); + if (da.isTaskDisplayArea() && da.asTaskDisplayArea().mCreatedByOrganizer) { + // Delete the organizer created TDA when unregister. + deleteTaskDisplayArea(da.asTaskDisplayArea()); + } else { + da.setOrganizer(null); + } + } + }); + } + + private DisplayAreaAppearedInfo organizeDisplayArea(IDisplayAreaOrganizer organizer, + DisplayArea displayArea, String callsite) { + displayArea.setOrganizer(organizer, true /* skipDisplayAreaAppeared */); + return new DisplayAreaAppearedInfo(displayArea.getDisplayAreaInfo(), + new SurfaceControl(displayArea.getSurfaceControl(), callsite)); + } + + private TaskDisplayArea createTaskDisplayArea(RootDisplayArea root, String name, + int taskDisplayAreaFeatureId) { + final TaskDisplayArea taskDisplayArea = new TaskDisplayArea(root.mDisplayContent, + root.mWmService, name, taskDisplayAreaFeatureId, true /* createdByOrganizer */); + + // Find the top most DA that can contain Task (either a TDA or a DisplayAreaGroup). + final DisplayArea topTaskContainer = root.getItemFromDisplayAreas(da -> { + if (da.mType != ANY) { + return null; + } + + final RootDisplayArea rootDA = da.getRootDisplayArea(); + if (rootDA == root || rootDA == da) { + // Either it is the top TDA below the root or it is a DisplayAreaGroup. + return da; } + return null; }); + if (topTaskContainer == null) { + throw new IllegalStateException("Root must either contain TDA or DAG root=" + root); + } + + // Insert the TaskDisplayArea as the top Task container. + final WindowContainer parent = topTaskContainer.getParent(); + final int index = parent.mChildren.indexOf(topTaskContainer) + 1; + parent.addChild(taskDisplayArea, index); + + return taskDisplayArea; + } + + private void deleteTaskDisplayArea(TaskDisplayArea taskDisplayArea) { + taskDisplayArea.setOrganizer(null); + mService.mRootWindowContainer.mTaskSupervisor.beginDeferResume(); + + // TaskDisplayArea#remove() move the stacks to the default TaskDisplayArea. + Task lastReparentedStack; + try { + lastReparentedStack = taskDisplayArea.remove(); + } finally { + mService.mRootWindowContainer.mTaskSupervisor.endDeferResume(); + } + + taskDisplayArea.removeImmediately(); + + // Only update focus/visibility for the last one because there may be many stacks are + // reparented and the intermediate states are unnecessary. + if (lastReparentedStack != null) { + lastReparentedStack.postReparent(); + } } } diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java index 80ec722bc274..6a420874b924 100644 --- a/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java +++ b/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java @@ -172,10 +172,16 @@ class DisplayAreaPolicyBuilder { throw new IllegalStateException("Root must be set for the display area policy."); } + final Set<Integer> rootIdSet = new ArraySet<>(); + rootIdSet.add(mRootHierarchyBuilder.mRoot.mFeatureId); boolean containsImeContainer = mRootHierarchyBuilder.mImeContainer != null; boolean containsDefaultTda = containsDefaultTaskDisplayArea(mRootHierarchyBuilder); for (int i = 0; i < mDisplayAreaGroupHierarchyBuilders.size(); i++) { HierarchyBuilder hierarchyBuilder = mDisplayAreaGroupHierarchyBuilders.get(i); + if (!rootIdSet.add(hierarchyBuilder.mRoot.mFeatureId)) { + throw new IllegalStateException("There should not be two RootDisplayAreas with id " + + hierarchyBuilder.mRoot.mFeatureId); + } if (hierarchyBuilder.mTaskDisplayAreas.isEmpty()) { throw new IllegalStateException( "DisplayAreaGroup must contain at least one TaskDisplayArea."); diff --git a/services/core/java/com/android/server/wm/RootDisplayArea.java b/services/core/java/com/android/server/wm/RootDisplayArea.java index 1e5d045e8680..da04f438a496 100644 --- a/services/core/java/com/android/server/wm/RootDisplayArea.java +++ b/services/core/java/com/android/server/wm/RootDisplayArea.java @@ -57,6 +57,11 @@ class RootDisplayArea extends DisplayArea<DisplayArea> { return this; } + @Override + RootDisplayArea asRootDisplayArea() { + return this; + } + /** Whether the orientation (based on dimensions) of this root is different from the Display. */ boolean isOrientationDifferentFromDisplay() { return false; diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index c02e7ad5b7c6..81b8200aa2b4 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -157,12 +157,24 @@ final class TaskDisplayArea extends DisplayArea<Task> { */ private int mLastLeafTaskToFrontId; + /** + * Whether this TaskDisplayArea was created by a {@link android.window.DisplayAreaOrganizer}. + * If {@code true}, this will be removed when the organizer is unregistered. + */ + final boolean mCreatedByOrganizer; + TaskDisplayArea(DisplayContent displayContent, WindowManagerService service, String name, int displayAreaFeature) { + this(displayContent, service, name, displayAreaFeature, false /* createdByOrganizer */); + } + + TaskDisplayArea(DisplayContent displayContent, WindowManagerService service, String name, + int displayAreaFeature, boolean createdByOrganizer) { super(service, Type.ANY, name, displayAreaFeature); mDisplayContent = displayContent; mRootWindowContainer = service.mRoot; mAtmService = service.mAtmService; + mCreatedByOrganizer = createdByOrganizer; } /** @@ -1914,6 +1926,11 @@ final class TaskDisplayArea extends DisplayArea<Task> { } @Override + TaskDisplayArea asTaskDisplayArea() { + return this; + } + + @Override void dump(PrintWriter pw, String prefix, boolean dumpAll) { pw.println(prefix + "TaskDisplayArea " + getName()); final String doublePrefix = prefix + " "; diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 4574be70674f..06449c69f1be 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -1814,7 +1814,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< /** * For all {@link TaskDisplayArea} at or below this container call the callback. * @param callback Applies on each {@link TaskDisplayArea} found and stops the search if it - * returns {@code true}. + * returns {@code true}. * @param traverseTopToBottom If {@code true}, traverses the hierarchy from top-to-bottom in * terms of z-order, else from bottom-to-top. * @return {@code true} if the search ended before we reached the end of the hierarchy due to @@ -1837,7 +1837,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< * For all {@link TaskDisplayArea} at or below this container call the callback. Traverses from * top to bottom in terms of z-order. * @param callback Applies on each {@link TaskDisplayArea} found and stops the search if it - * returns {@code true}. + * returns {@code true}. * @return {@code true} if the search ended before we reached the end of the hierarchy due to * callback returning {@code true}. */ @@ -1873,7 +1873,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< * Performs a reduction on all {@link TaskDisplayArea} at or below this container, using the * provided initial value and an accumulation function, and returns the reduced value. * @param accumulator Applies on each {@link TaskDisplayArea} found with the accumulative result - * from the previous call. + * from the previous call. * @param initValue The initial value to pass to the accumulating function with the first * {@link TaskDisplayArea}. * @param traverseTopToBottom If {@code true}, traverses the hierarchy from top-to-bottom in @@ -1899,7 +1899,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< * provided initial value and an accumulation function, and returns the reduced value. Traverses * from top to bottom in terms of z-order. * @param accumulator Applies on each {@link TaskDisplayArea} found with the accumulative result - * from the previous call. + * from the previous call. * @param initValue The initial value to pass to the accumulating function with the first * {@link TaskDisplayArea}. * @return the accumulative result. @@ -1912,9 +1912,29 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< /** * Finds the first non {@code null} return value from calling the callback on all + * {@link DisplayArea} at or below this container. Traverses from top to bottom in terms of + * z-order. + * @param callback Applies on each {@link DisplayArea} found and stops the search if it + * returns non {@code null}. + * @return the first returned object that is not {@code null}. Returns {@code null} if not + * found. + */ + @Nullable + <R> R getItemFromDisplayAreas(Function<DisplayArea, R> callback) { + for (int i = mChildren.size() - 1; i >= 0; --i) { + R result = (R) mChildren.get(i).getItemFromDisplayAreas(callback); + if (result != null) { + return result; + } + } + return null; + } + + /** + * Finds the first non {@code null} return value from calling the callback on all * {@link TaskDisplayArea} at or below this container. * @param callback Applies on each {@link TaskDisplayArea} found and stops the search if it - * returns non {@code null}. + * returns non {@code null}. * @param traverseTopToBottom If {@code true}, traverses the hierarchy from top-to-bottom in * terms of z-order, else from bottom-to-top. * @return the first returned object that is not {@code null}. Returns {@code null} if not @@ -1941,7 +1961,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< * {@link TaskDisplayArea} at or below this container. Traverses from top to bottom in terms of * z-order. * @param callback Applies on each {@link TaskDisplayArea} found and stops the search if it - * returns non {@code null}. + * returns non {@code null}. * @return the first returned object that is not {@code null}. Returns {@code null} if not * found. */ @@ -2884,6 +2904,16 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< return null; } + /** Cheap way of doing cast and instanceof. */ + RootDisplayArea asRootDisplayArea() { + return null; + } + + /** Cheap way of doing cast and instanceof. */ + TaskDisplayArea asTaskDisplayArea() { + return null; + } + /** * @return {@code true} if window container is manage by a * {@link android.window.WindowOrganizer} diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index a6466821ff38..78d4f2fcc563 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -3192,6 +3192,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } + private void checkAllUsersAreAffiliatedWithDevice() { + Preconditions.checkCallAuthorization(areAllUsersAffiliatedWithDeviceLocked(), + "operation not allowed when device has unaffiliated users"); + } + @Override public boolean isAdminActive(ComponentName adminReceiver, int userHandle) { if (!mHasFeature) { @@ -7034,7 +7039,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // next boot? Might not be needed given that this still requires user consent. final CallerIdentity caller = getCallerIdentity(who); Preconditions.checkCallAuthorization(isDeviceOwner(caller)); - Preconditions.checkCallAuthorization(areAllUsersAffiliatedWithDeviceLocked()); + checkAllUsersAreAffiliatedWithDevice(); if (mBugreportCollectionManager.requestBugreport()) { DevicePolicyEventLogger @@ -8643,6 +8648,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { pw.println(policy.mUserControlDisabledPackages); pw.print("mAppsSuspended="); pw.println(policy.mAppsSuspended); pw.print("mUserSetupComplete="); pw.println(policy.mUserSetupComplete); + pw.print("mAffiliationIds="); pw.println(policy.mAffiliationIds); pw.decreaseIndent(); } } @@ -12925,13 +12931,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { }); } - private boolean canStartSecurityLogging() { - synchronized (getLockObject()) { - return isOrganizationOwnedDeviceWithManagedProfile() - || areAllUsersAffiliatedWithDeviceLocked(); - } - } - private @UserIdInt int getSecurityLoggingEnabledUser() { synchronized (getLockObject()) { if (mOwners.hasDeviceOwner()) { @@ -13651,7 +13650,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final CallerIdentity caller = getCallerIdentity(admin, packageName); Preconditions.checkCallAuthorization((caller.hasAdminComponent() && isDeviceOwner(caller)) || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_NETWORK_LOGGING))); - Preconditions.checkCallAuthorization(areAllUsersAffiliatedWithDeviceLocked()); + checkAllUsersAreAffiliatedWithDevice(); synchronized (getLockObject()) { if (mNetworkLogger == null || !isNetworkLoggingEnabledInternalLocked()) { diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp index a2e6698963a4..8fc5c085999e 100644 --- a/services/tests/mockingservicestests/Android.bp +++ b/services/tests/mockingservicestests/Android.bp @@ -20,6 +20,7 @@ android_test { static_libs: [ "services.core", "services.net", + "services.usage", "service-jobscheduler", "service-permission.impl", "service-blobstore", diff --git a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java index ebd4a4c5378f..9c8f733730a7 100644 --- a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java @@ -174,8 +174,6 @@ public class RescuePartyTest { doReturn(CURRENT_NETWORK_TIME_MILLIS).when(() -> RescueParty.getElapsedRealtime()); - SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, - Integer.toString(RescueParty.LEVEL_NONE)); SystemProperties.set(RescueParty.PROP_RESCUE_BOOT_COUNT, Integer.toString(0)); SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true)); SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(false)); @@ -193,12 +191,10 @@ public class RescuePartyTest { mMonitorCallbackCaptor.capture())); HashMap<String, Integer> verifiedTimesMap = new HashMap<String, Integer>(); - noteBoot(); + noteBoot(1); verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, /*resetNamespaces=*/ null, verifiedTimesMap); - assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS, - SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE)); // Record DeviceConfig accesses RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext); @@ -208,24 +204,19 @@ public class RescuePartyTest { final String[] expectedAllResetNamespaces = new String[]{NAMESPACE1, NAMESPACE2}; - noteBoot(); + noteBoot(2); verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_CHANGES, expectedAllResetNamespaces, verifiedTimesMap); - assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES, - SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE)); - noteBoot(); + noteBoot(3); verifySettingsResets(Settings.RESET_MODE_TRUSTED_DEFAULTS, expectedAllResetNamespaces, verifiedTimesMap); - assertEquals(RescueParty.LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS, - SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE)); - noteBoot(); + noteBoot(4); - assertEquals(LEVEL_FACTORY_RESET, - SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE)); + assertTrue(RescueParty.isAttemptingFactoryReset()); } @Test @@ -364,24 +355,12 @@ public class RescuePartyTest { @Test public void testIsAttemptingFactoryReset() { for (int i = 0; i < LEVEL_FACTORY_RESET; i++) { - noteBoot(); + noteBoot(i + 1); } assertTrue(RescueParty.isAttemptingFactoryReset()); } @Test - public void testOnSettingsProviderPublishedExecutesRescueLevels() { - SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString(1)); - - RescueParty.onSettingsProviderPublished(mMockContext); - - verifySettingsResets(Settings.RESET_MODE_UNTRUSTED_DEFAULTS, /*resetNamespaces=*/ null, - /*configResetVerifiedTimesMap=*/ null); - assertEquals(RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS, - SystemProperties.getInt(RescueParty.PROP_RESCUE_LEVEL, RescueParty.LEVEL_NONE)); - } - - @Test public void testNativeRescuePartyResets() { doReturn(true).when(() -> SettingsToPropertiesMapper.isNativeFlagsResetPerformed()); doReturn(FAKE_RESET_NATIVE_NAMESPACES).when( @@ -425,7 +404,7 @@ public class RescuePartyTest { SystemProperties.set(PROP_DISABLE_FACTORY_RESET_FLAG, Boolean.toString(true)); for (int i = 0; i < LEVEL_FACTORY_RESET; i++) { - noteBoot(); + noteBoot(i + 1); } assertFalse(RescueParty.isAttemptingFactoryReset()); @@ -463,29 +442,12 @@ public class RescuePartyTest { public void testBootLoopLevels() { RescuePartyObserver observer = RescuePartyObserver.getInstance(mMockContext); - /* - Ensure that the returned user impact corresponds with the user impact of the next available - rescue level, not the current one. - */ - SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString( - RescueParty.LEVEL_NONE)); - assertEquals(observer.onBootLoop(), PackageHealthObserverImpact.USER_IMPACT_LOW); - - SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString( - RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS)); - assertEquals(observer.onBootLoop(), PackageHealthObserverImpact.USER_IMPACT_LOW); - - SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString( - RescueParty.LEVEL_RESET_SETTINGS_UNTRUSTED_CHANGES)); - assertEquals(observer.onBootLoop(), PackageHealthObserverImpact.USER_IMPACT_HIGH); - - SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString( - RescueParty.LEVEL_RESET_SETTINGS_TRUSTED_DEFAULTS)); - assertEquals(observer.onBootLoop(), PackageHealthObserverImpact.USER_IMPACT_HIGH); - - SystemProperties.set(RescueParty.PROP_RESCUE_LEVEL, Integer.toString( - LEVEL_FACTORY_RESET)); - assertEquals(observer.onBootLoop(), PackageHealthObserverImpact.USER_IMPACT_HIGH); + assertEquals(observer.onBootLoop(0), PackageHealthObserverImpact.USER_IMPACT_NONE); + assertEquals(observer.onBootLoop(1), PackageHealthObserverImpact.USER_IMPACT_LOW); + assertEquals(observer.onBootLoop(2), PackageHealthObserverImpact.USER_IMPACT_LOW); + assertEquals(observer.onBootLoop(3), PackageHealthObserverImpact.USER_IMPACT_HIGH); + assertEquals(observer.onBootLoop(4), PackageHealthObserverImpact.USER_IMPACT_HIGH); + assertEquals(observer.onBootLoop(5), PackageHealthObserverImpact.USER_IMPACT_HIGH); } private void verifySettingsResets(int resetMode, String[] resetNamespaces, @@ -513,8 +475,8 @@ public class RescuePartyTest { } } - private void noteBoot() { - RescuePartyObserver.getInstance(mMockContext).executeBootLoopMitigation(); + private void noteBoot(int mitigationCount) { + RescuePartyObserver.getInstance(mMockContext).executeBootLoopMitigation(mitigationCount); } private void notePersistentAppCrash(int mitigationCount) { diff --git a/services/tests/mockingservicestests/src/com/android/server/usage/UsageStatsServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/usage/UsageStatsServiceTest.java new file mode 100644 index 000000000000..c9fcd0233bef --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/usage/UsageStatsServiceTest.java @@ -0,0 +1,134 @@ +/* + * 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.usage; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; + +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; + +import android.app.ActivityManager; +import android.app.IActivityManager; +import android.app.usage.UsageEvents; +import android.app.usage.UsageStatsManagerInternal; +import android.content.Context; +import android.os.RemoteException; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.server.LocalServices; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoSession; +import org.mockito.quality.Strictness; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +@RunWith(AndroidJUnit4.class) +public class UsageStatsServiceTest { + private static final long TIMEOUT = 5000; + + private UsageStatsService mService; + + private MockitoSession mMockingSession; + @Mock + private Context mContext; + + private static class TestInjector extends UsageStatsService.Injector { + AppStandbyInternal getAppStandbyController(Context context) { + return mock(AppStandbyInternal.class); + } + } + + @Before + public void setUp() { + mMockingSession = mockitoSession() + .initMocks(this) + .strictness(Strictness.LENIENT) + .startMocking(); + IActivityManager activityManager = ActivityManager.getService(); + spyOn(activityManager); + try { + doNothing().when(activityManager).registerUidObserver(any(), anyInt(), anyInt(), any()); + } catch (RemoteException e) { + fail("registerUidObserver threw exception: " + e.getMessage()); + } + mService = new UsageStatsService(mContext, new TestInjector()); + spyOn(mService); + doNothing().when(mService).publishBinderServices(); + mService.onStart(); + } + + @Test + public void testUsageEventListener() throws Exception { + TestUsageEventListener listener = new TestUsageEventListener(); + UsageStatsManagerInternal usmi = LocalServices.getService(UsageStatsManagerInternal.class); + usmi.registerListener(listener); + + UsageEvents.Event event = new UsageEvents.Event(UsageEvents.Event.CONFIGURATION_CHANGE, 10); + usmi.reportEvent("com.android.test", 10, event.getEventType()); + listener.setExpectation(10, event); + listener.mCountDownLatch.await(TIMEOUT, TimeUnit.MILLISECONDS); + + usmi.unregisterListener(listener); + listener.reset(); + + usmi.reportEvent("com.android.test", 0, UsageEvents.Event.CHOOSER_ACTION); + Thread.sleep(TIMEOUT); + assertNull(listener.mLastReceivedEvent); + } + + private static class TestUsageEventListener implements + UsageStatsManagerInternal.UsageEventListener { + UsageEvents.Event mLastReceivedEvent; + int mLastReceivedUserId; + UsageEvents.Event mExpectedEvent; + int mExpectedUserId; + CountDownLatch mCountDownLatch; + + @Override + public void onUsageEvent(int userId, UsageEvents.Event event) { + mLastReceivedUserId = userId; + mLastReceivedEvent = event; + if (mCountDownLatch != null && userId == mExpectedUserId + && event.getEventType() == mExpectedEvent.getEventType()) { + mCountDownLatch.countDown(); + } + } + + private void setExpectation(int userId, UsageEvents.Event event) { + mExpectedUserId = userId; + mExpectedEvent = event; + mCountDownLatch = new CountDownLatch(1); + } + + private void reset() { + mLastReceivedUserId = mExpectedUserId = -1; + mLastReceivedEvent = mExpectedEvent = null; + mCountDownLatch = null; + } + } +} diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index f2bb91c66ac3..0c2fab83ee66 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -49,6 +49,8 @@ android_test { // TODO: remove once Android migrates to JUnit 4.12, // which provides assertThrows "testng", + "junit", + "platform-compat-test-rules", ], diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java index 026db42d4d7a..640d6e599736 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -28,6 +28,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.PropertyInvalidatedCache; +import android.compat.testing.PlatformCompatChangeRule; import android.content.Context; import android.graphics.Insets; import android.graphics.Rect; @@ -44,8 +45,10 @@ import android.hardware.display.VirtualDisplayConfig; import android.hardware.input.InputManagerInternal; import android.os.Handler; import android.os.IBinder; +import android.os.Process; import android.view.Display; import android.view.DisplayCutout; +import android.view.DisplayEventReceiver; import android.view.DisplayInfo; import android.view.Surface; import android.view.SurfaceControl; @@ -57,13 +60,17 @@ import androidx.test.runner.AndroidJUnit4; import com.android.server.LocalServices; import com.android.server.SystemService; -import com.android.server.display.DisplayDeviceInfo; import com.android.server.display.DisplayManagerService.SyncRoot; import com.android.server.lights.LightsManager; import com.android.server.wm.WindowManagerInternal; +import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; +import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; + import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TestRule; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; @@ -80,6 +87,9 @@ public class DisplayManagerServiceTest { private static final String VIRTUAL_DISPLAY_NAME = "Test Virtual Display"; private static final String PACKAGE_NAME = "com.android.frameworks.servicestests"; + @Rule + public TestRule compatChangeRule = new PlatformCompatChangeRule(); + private Context mContext; private final DisplayManagerService.Injector mShortMockedInjector = @@ -95,15 +105,31 @@ public class DisplayManagerServiceTest { return SHORT_DEFAULT_DISPLAY_TIMEOUT_MILLIS; } }; - private final DisplayManagerService.Injector mBasicInjector = - new DisplayManagerService.Injector() { + + class BasicInjector extends DisplayManagerService.Injector { + @Override + VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot, Context context, + Handler handler, DisplayAdapter.Listener displayAdapterListener) { + return new VirtualDisplayAdapter(syncRoot, context, handler, displayAdapterListener, + (String name, boolean secure) -> mMockDisplayToken); + } + } + + private final DisplayManagerService.Injector mBasicInjector = new BasicInjector(); + + private final DisplayManagerService.Injector mAllowNonNativeRefreshRateOverrideInjector = + new BasicInjector() { @Override - VirtualDisplayAdapter getVirtualDisplayAdapter(SyncRoot syncRoot, - Context context, Handler handler, - DisplayAdapter.Listener displayAdapterListener) { - return new VirtualDisplayAdapter(syncRoot, context, handler, - displayAdapterListener, - (String name, boolean secure) -> mMockDisplayToken); + boolean getAllowNonNativeRefreshRateOverride() { + return true; + } + }; + + private final DisplayManagerService.Injector mDenyNonNativeRefreshRateOverrideInjector = + new BasicInjector() { + @Override + boolean getAllowNonNativeRefreshRateOverride() { + return false; } }; @@ -575,6 +601,337 @@ public class DisplayManagerServiceTest { assertEquals(displayManager.getVirtualDisplaySurfaceInternal(mMockAppToken), surface); } + /** + * Tests that there should be a display change notification if the frame rate overrides + * list is updated. + */ + @Test + public void testShouldNotifyChangeWhenDisplayInfoFrameRateOverrideChanged() throws Exception { + DisplayManagerService displayManager = + new DisplayManagerService(mContext, mShortMockedInjector); + DisplayManagerService.BinderService displayManagerBinderService = + displayManager.new BinderService(); + registerDefaultDisplays(displayManager); + displayManager.onBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY); + + FakeDisplayDevice displayDevice = createFakeDisplayDevice(displayManager, new float[]{60f}); + FakeDisplayManagerCallback callback = registerDisplayListenerCallback(displayManager, + displayManagerBinderService, displayDevice); + + int myUid = Process.myUid(); + updateFrameRateOverride(displayManager, displayDevice, + new DisplayEventReceiver.FrameRateOverride[]{ + new DisplayEventReceiver.FrameRateOverride(myUid, 30f), + }); + assertTrue(callback.mCalled); + callback.clear(); + + updateFrameRateOverride(displayManager, displayDevice, + new DisplayEventReceiver.FrameRateOverride[]{ + new DisplayEventReceiver.FrameRateOverride(myUid, 30f), + new DisplayEventReceiver.FrameRateOverride(1234, 30f), + }); + assertFalse(callback.mCalled); + + updateFrameRateOverride(displayManager, displayDevice, + new DisplayEventReceiver.FrameRateOverride[]{ + new DisplayEventReceiver.FrameRateOverride(myUid, 20f), + new DisplayEventReceiver.FrameRateOverride(1234, 30f), + new DisplayEventReceiver.FrameRateOverride(5678, 30f), + }); + assertTrue(callback.mCalled); + callback.clear(); + + updateFrameRateOverride(displayManager, displayDevice, + new DisplayEventReceiver.FrameRateOverride[]{ + new DisplayEventReceiver.FrameRateOverride(1234, 30f), + new DisplayEventReceiver.FrameRateOverride(5678, 30f), + }); + assertTrue(callback.mCalled); + callback.clear(); + + updateFrameRateOverride(displayManager, displayDevice, + new DisplayEventReceiver.FrameRateOverride[]{ + new DisplayEventReceiver.FrameRateOverride(5678, 30f), + }); + assertFalse(callback.mCalled); + } + + /** + * Tests that the DisplayInfo is updated correctly with a frame rate override + */ + @Test + public void testDisplayInfoFrameRateOverride() throws Exception { + DisplayManagerService displayManager = + new DisplayManagerService(mContext, mShortMockedInjector); + DisplayManagerService.BinderService displayManagerBinderService = + displayManager.new BinderService(); + registerDefaultDisplays(displayManager); + displayManager.onBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY); + + FakeDisplayDevice displayDevice = createFakeDisplayDevice(displayManager, + new float[]{60f, 30f, 20f}); + int displayId = getDisplayIdForDisplayDevice(displayManager, displayManagerBinderService, + displayDevice); + DisplayInfo displayInfo = displayManagerBinderService.getDisplayInfo(displayId); + assertEquals(60f, displayInfo.getRefreshRate(), 0.01f); + + updateFrameRateOverride(displayManager, displayDevice, + new DisplayEventReceiver.FrameRateOverride[]{ + new DisplayEventReceiver.FrameRateOverride( + Process.myUid(), 20f), + new DisplayEventReceiver.FrameRateOverride( + Process.myUid() + 1, 30f) + }); + displayInfo = displayManagerBinderService.getDisplayInfo(displayId); + assertEquals(20f, displayInfo.getRefreshRate(), 0.01f); + + // Changing the mode to 30Hz should not override the refresh rate to 20Hz anymore + // as 20 is not a divider of 30. + updateModeId(displayManager, displayDevice, 2); + displayInfo = displayManagerBinderService.getDisplayInfo(displayId); + assertEquals(30f, displayInfo.getRefreshRate(), 0.01f); + } + + /** + * Tests that the frame rate override is updated accordingly to the + * allowNonNativeRefreshRateOverride policy. + */ + @Test + public void testDisplayInfoNonNativeFrameRateOverride() throws Exception { + testDisplayInfoNonNativeFrameRateOverride(mDenyNonNativeRefreshRateOverrideInjector); + testDisplayInfoNonNativeFrameRateOverride(mAllowNonNativeRefreshRateOverrideInjector); + } + + /** + * Tests that the mode reflects the frame rate override is in compat mode + */ + @Test + @DisableCompatChanges({DisplayManagerService.DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE}) + public void testDisplayInfoFrameRateOverrideModeCompat() throws Exception { + testDisplayInfoFrameRateOverrideModeCompat(/*compatChangeEnabled*/ false); + } + + /** + * Tests that the mode reflects the physical display refresh rate when not in compat mode. + */ + @Test + @EnableCompatChanges({DisplayManagerService.DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE}) + public void testDisplayInfoFrameRateOverrideMode() throws Exception { + testDisplayInfoFrameRateOverrideModeCompat(/*compatChangeEnabled*/ true); + } + + /** + * Tests that the mode reflects the frame rate override is in compat mode and accordingly to the + * allowNonNativeRefreshRateOverride policy. + */ + @Test + @DisableCompatChanges({DisplayManagerService.DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE}) + public void testDisplayInfoNonNativeFrameRateOverrideModeCompat() throws Exception { + testDisplayInfoNonNativeFrameRateOverrideMode(mDenyNonNativeRefreshRateOverrideInjector, + /*compatChangeEnabled*/ false); + testDisplayInfoNonNativeFrameRateOverrideMode(mAllowNonNativeRefreshRateOverrideInjector, + /*compatChangeEnabled*/ false); + } + + /** + * Tests that the mode reflects the physical display refresh rate when not in compat mode. + */ + @Test + @EnableCompatChanges({DisplayManagerService.DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE}) + public void testDisplayInfoNonNativeFrameRateOverrideMode() throws Exception { + testDisplayInfoNonNativeFrameRateOverrideMode(mDenyNonNativeRefreshRateOverrideInjector, + /*compatChangeEnabled*/ true); + testDisplayInfoNonNativeFrameRateOverrideMode(mAllowNonNativeRefreshRateOverrideInjector, + /*compatChangeEnabled*/ true); + } + + private void testDisplayInfoFrameRateOverrideModeCompat(boolean compatChangeEnabled) + throws Exception { + DisplayManagerService displayManager = + new DisplayManagerService(mContext, mShortMockedInjector); + DisplayManagerService.BinderService displayManagerBinderService = + displayManager.new BinderService(); + registerDefaultDisplays(displayManager); + displayManager.onBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY); + + FakeDisplayDevice displayDevice = createFakeDisplayDevice(displayManager, + new float[]{60f, 30f, 20f}); + int displayId = getDisplayIdForDisplayDevice(displayManager, displayManagerBinderService, + displayDevice); + DisplayInfo displayInfo = displayManagerBinderService.getDisplayInfo(displayId); + assertEquals(60f, displayInfo.getRefreshRate(), 0.01f); + + updateFrameRateOverride(displayManager, displayDevice, + new DisplayEventReceiver.FrameRateOverride[]{ + new DisplayEventReceiver.FrameRateOverride( + Process.myUid(), 20f), + new DisplayEventReceiver.FrameRateOverride( + Process.myUid() + 1, 30f) + }); + displayInfo = displayManagerBinderService.getDisplayInfo(displayId); + assertEquals(20f, displayInfo.getRefreshRate(), 0.01f); + Display.Mode expectedMode; + if (compatChangeEnabled) { + expectedMode = new Display.Mode(1, 100, 200, 60f); + } else { + expectedMode = new Display.Mode(3, 100, 200, 20f); + } + assertEquals(expectedMode, displayInfo.getMode()); + } + + private void testDisplayInfoNonNativeFrameRateOverrideMode( + DisplayManagerService.Injector injector, boolean compatChangeEnabled) { + DisplayManagerService displayManager = + new DisplayManagerService(mContext, injector); + DisplayManagerService.BinderService displayManagerBinderService = + displayManager.new BinderService(); + registerDefaultDisplays(displayManager); + displayManager.onBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY); + + FakeDisplayDevice displayDevice = createFakeDisplayDevice(displayManager, + new float[]{60f}); + int displayId = getDisplayIdForDisplayDevice(displayManager, displayManagerBinderService, + displayDevice); + DisplayInfo displayInfo = displayManagerBinderService.getDisplayInfo(displayId); + assertEquals(60f, displayInfo.getRefreshRate(), 0.01f); + + updateFrameRateOverride(displayManager, displayDevice, + new DisplayEventReceiver.FrameRateOverride[]{ + new DisplayEventReceiver.FrameRateOverride( + Process.myUid(), 20f) + }); + displayInfo = displayManagerBinderService.getDisplayInfo(displayId); + Display.Mode expectedMode; + if (compatChangeEnabled) { + expectedMode = new Display.Mode(1, 100, 200, 60f); + } else if (injector.getAllowNonNativeRefreshRateOverride()) { + expectedMode = new Display.Mode(255, 100, 200, 20f); + } else { + expectedMode = new Display.Mode(1, 100, 200, 60f); + } + assertEquals(expectedMode, displayInfo.getMode()); + } + + private void testDisplayInfoNonNativeFrameRateOverride( + DisplayManagerService.Injector injector) { + DisplayManagerService displayManager = + new DisplayManagerService(mContext, injector); + DisplayManagerService.BinderService displayManagerBinderService = + displayManager.new BinderService(); + registerDefaultDisplays(displayManager); + displayManager.onBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY); + + FakeDisplayDevice displayDevice = createFakeDisplayDevice(displayManager, + new float[]{60f}); + int displayId = getDisplayIdForDisplayDevice(displayManager, displayManagerBinderService, + displayDevice); + DisplayInfo displayInfo = displayManagerBinderService.getDisplayInfo(displayId); + assertEquals(60f, displayInfo.getRefreshRate(), 0.01f); + + updateFrameRateOverride(displayManager, displayDevice, + new DisplayEventReceiver.FrameRateOverride[]{ + new DisplayEventReceiver.FrameRateOverride( + Process.myUid(), 20f) + }); + displayInfo = displayManagerBinderService.getDisplayInfo(displayId); + float expectedRefreshRate = injector.getAllowNonNativeRefreshRateOverride() ? 20f : 60f; + assertEquals(expectedRefreshRate, displayInfo.getRefreshRate(), 0.01f); + } + + private int getDisplayIdForDisplayDevice( + DisplayManagerService displayManager, + DisplayManagerService.BinderService displayManagerBinderService, + FakeDisplayDevice displayDevice) { + + final int[] displayIds = displayManagerBinderService.getDisplayIds(); + assertTrue(displayIds.length > 0); + int displayId = Display.INVALID_DISPLAY; + for (int i = 0; i < displayIds.length; i++) { + DisplayDeviceInfo ddi = displayManager.getDisplayDeviceInfoInternal(displayIds[i]); + if (displayDevice.getDisplayDeviceInfoLocked().equals(ddi)) { + displayId = displayIds[i]; + break; + } + } + assertFalse(displayId == Display.INVALID_DISPLAY); + return displayId; + } + + private void updateDisplayDeviceInfo(DisplayManagerService displayManager, + FakeDisplayDevice displayDevice, + DisplayDeviceInfo displayDeviceInfo) { + displayDevice.setDisplayDeviceInfo(displayDeviceInfo); + displayManager.getDisplayDeviceRepository() + .onDisplayDeviceEvent(displayDevice, DisplayAdapter.DISPLAY_DEVICE_EVENT_CHANGED); + Handler handler = displayManager.getDisplayHandler(); + handler.runWithScissors(() -> { + }, 0 /* now */); + } + + private void updateFrameRateOverride(DisplayManagerService displayManager, + FakeDisplayDevice displayDevice, + DisplayEventReceiver.FrameRateOverride[] frameRateOverrides) { + DisplayDeviceInfo displayDeviceInfo = new DisplayDeviceInfo(); + displayDeviceInfo.copyFrom(displayDevice.getDisplayDeviceInfoLocked()); + displayDeviceInfo.frameRateOverrides = frameRateOverrides; + updateDisplayDeviceInfo(displayManager, displayDevice, displayDeviceInfo); + } + + private void updateModeId(DisplayManagerService displayManager, + FakeDisplayDevice displayDevice, + int modeId) { + DisplayDeviceInfo displayDeviceInfo = new DisplayDeviceInfo(); + displayDeviceInfo.copyFrom(displayDevice.getDisplayDeviceInfoLocked()); + displayDeviceInfo.modeId = modeId; + updateDisplayDeviceInfo(displayManager, displayDevice, displayDeviceInfo); + } + + private FakeDisplayManagerCallback registerDisplayListenerCallback( + DisplayManagerService displayManager, + DisplayManagerService.BinderService displayManagerBinderService, + FakeDisplayDevice displayDevice) { + // Find the display id of the added FakeDisplayDevice + DisplayDeviceInfo displayDeviceInfo = displayDevice.getDisplayDeviceInfoLocked(); + + int displayId = getDisplayIdForDisplayDevice(displayManager, displayManagerBinderService, + displayDevice); + + Handler handler = displayManager.getDisplayHandler(); + handler.runWithScissors(() -> { + }, 0 /* now */); + + // register display listener callback + FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback(displayId); + displayManagerBinderService.registerCallback(callback); + return callback; + } + + private FakeDisplayDevice createFakeDisplayDevice(DisplayManagerService displayManager, + float[] refreshRates) { + FakeDisplayDevice displayDevice = new FakeDisplayDevice(); + DisplayDeviceInfo displayDeviceInfo = new DisplayDeviceInfo(); + int width = 100; + int height = 200; + displayDeviceInfo.supportedModes = new Display.Mode[refreshRates.length]; + for (int i = 0; i < refreshRates.length; i++) { + displayDeviceInfo.supportedModes[i] = + new Display.Mode(i + 1, width, height, refreshRates[i]); + } + displayDeviceInfo.modeId = 1; + displayDeviceInfo.width = width; + displayDeviceInfo.height = height; + final Rect zeroRect = new Rect(); + displayDeviceInfo.displayCutout = new DisplayCutout( + Insets.of(0, 10, 0, 0), + zeroRect, new Rect(0, 0, 10, 10), zeroRect, zeroRect); + displayDeviceInfo.flags = DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY; + displayDevice.setDisplayDeviceInfo(displayDeviceInfo); + displayManager.getDisplayDeviceRepository() + .onDisplayDeviceEvent(displayDevice, DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED); + return displayDevice; + } + private void registerDefaultDisplays(DisplayManagerService displayManager) { Handler handler = displayManager.getDisplayHandler(); // Would prefer to call displayManager.onStart() directly here but it performs binderService @@ -598,6 +955,10 @@ public class DisplayManagerServiceTest { mCalled = true; } } + + public void clear() { + mCalled = false; + } } private class FakeDisplayDevice extends DisplayDevice { diff --git a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java index 59aff8d43755..b26d1efef2a8 100644 --- a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java @@ -23,6 +23,10 @@ import android.content.Context; import android.hardware.power.stats.ChannelInfo; import android.hardware.power.stats.EnergyConsumerResult; import android.hardware.power.stats.EnergyMeasurement; +import android.hardware.power.stats.PowerEntityInfo; +import android.hardware.power.stats.StateInfo; +import android.hardware.power.stats.StateResidency; +import android.hardware.power.stats.StateResidencyResult; import androidx.test.InstrumentationRegistry; @@ -56,8 +60,13 @@ public class PowerStatsServiceTest { private static final String MODEL_FILENAME = "modeltest"; private static final String PROTO_OUTPUT_FILENAME = "powerstats.proto"; private static final String CHANNEL_NAME = "channelname"; + private static final String POWER_ENTITY_NAME = "powerentityinfo"; + private static final String STATE_NAME = "stateinfo"; private static final int ENERGY_METER_COUNT = 8; private static final int ENERGY_CONSUMER_COUNT = 2; + private static final int POWER_ENTITY_COUNT = 3; + private static final int STATE_INFO_COUNT = 5; + private static final int STATE_RESIDENCY_COUNT = 4; private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext(); private PowerStatsService mService; @@ -118,6 +127,43 @@ public class PowerStatsServiceTest { public static final class TestPowerStatsHALWrapper implements IPowerStatsHALWrapper { @Override + public PowerEntityInfo[] getPowerEntityInfo() { + PowerEntityInfo[] powerEntityInfoList = new PowerEntityInfo[POWER_ENTITY_COUNT]; + for (int i = 0; i < powerEntityInfoList.length; i++) { + powerEntityInfoList[i] = new PowerEntityInfo(); + powerEntityInfoList[i].powerEntityId = i; + powerEntityInfoList[i].powerEntityName = new String(POWER_ENTITY_NAME + i); + powerEntityInfoList[i].states = new StateInfo[STATE_INFO_COUNT]; + for (int j = 0; j < powerEntityInfoList[i].states.length; j++) { + powerEntityInfoList[i].states[j] = new StateInfo(); + powerEntityInfoList[i].states[j].stateId = j; + powerEntityInfoList[i].states[j].stateName = new String(STATE_NAME + i); + } + } + return powerEntityInfoList; + } + + @Override + public StateResidencyResult[] getStateResidency(int[] powerEntityIds) { + StateResidencyResult[] stateResidencyResultList = + new StateResidencyResult[POWER_ENTITY_COUNT]; + for (int i = 0; i < stateResidencyResultList.length; i++) { + stateResidencyResultList[i] = new StateResidencyResult(); + stateResidencyResultList[i].powerEntityId = i; + stateResidencyResultList[i].stateResidencyData = + new StateResidency[STATE_RESIDENCY_COUNT]; + for (int j = 0; j < stateResidencyResultList[i].stateResidencyData.length; j++) { + stateResidencyResultList[i].stateResidencyData[j] = new StateResidency(); + stateResidencyResultList[i].stateResidencyData[j].totalTimeInStateMs = j; + stateResidencyResultList[i].stateResidencyData[j].totalStateEntryCount = j; + stateResidencyResultList[i].stateResidencyData[j].lastEntryTimestampMs = j; + } + } + + return stateResidencyResultList; + } + + @Override public int[] getEnergyConsumerInfo() { int[] energyConsumerInfoList = new int[ENERGY_CONSUMER_COUNT]; for (int i = 0; i < energyConsumerInfoList.length; i++) { @@ -127,7 +173,7 @@ public class PowerStatsServiceTest { } @Override - public EnergyConsumerResult[] getEnergyConsumed() { + public EnergyConsumerResult[] getEnergyConsumed(int[] energyConsumerIds) { EnergyConsumerResult[] energyConsumedList = new EnergyConsumerResult[ENERGY_CONSUMER_COUNT]; for (int i = 0; i < energyConsumedList.length; i++) { @@ -151,7 +197,7 @@ public class PowerStatsServiceTest { } @Override - public EnergyMeasurement[] readEnergyMeters() { + public EnergyMeasurement[] readEnergyMeters(int[] channelIds) { EnergyMeasurement[] energyMeasurementList = new EnergyMeasurement[ENERGY_METER_COUNT]; for (int i = 0; i < energyMeasurementList.length; i++) { energyMeasurementList[i] = new EnergyMeasurement(); diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index 0ad669f32060..11fb0021be62 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -65,6 +65,7 @@ import android.annotation.NonNull; import android.app.ActivityManager; import android.app.usage.AppStandbyInfo; import android.app.usage.UsageEvents; +import android.app.usage.UsageStatsManagerInternal; import android.appwidget.AppWidgetManager; import android.content.Context; import android.content.ContextWrapper; @@ -86,9 +87,11 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener; +import org.junit.After; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; @@ -425,11 +428,18 @@ public class AppStandbyControllerTests { @Before public void setUp() throws Exception { + LocalServices.addService( + UsageStatsManagerInternal.class, mock(UsageStatsManagerInternal.class)); MyContextWrapper myContext = new MyContextWrapper(InstrumentationRegistry.getContext()); mInjector = new MyInjector(myContext, Looper.getMainLooper()); mController = setupController(); } + @After + public void tearDown() { + LocalServices.removeServiceForTest(UsageStatsManagerInternal.class); + } + @Test public void testBoundWidgetPackageExempt() throws Exception { assumeTrue(mInjector.getContext().getSystemService(AppWidgetManager.class) != null); @@ -562,7 +572,7 @@ public class AppStandbyControllerTests { UsageEvents.Event ev = new UsageEvents.Event(); ev.mPackage = packageName; ev.mEventType = eventType; - controller.reportEvent(ev, USER_ID); + controller.onUsageEvent(USER_ID, ev); } private int getStandbyBucket(AppStandbyController controller, String packageName) { diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java index 3220d1d6a990..1198ee2ba5f4 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaOrganizerTest.java @@ -16,8 +16,13 @@ package com.android.server.wm; +import static android.view.Display.DEFAULT_DISPLAY; +import static android.window.DisplayAreaOrganizer.FEATURE_ROOT; +import static android.window.DisplayAreaOrganizer.FEATURE_RUNTIME_TASK_CONTAINER_FIRST; import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -25,6 +30,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.testng.Assert.assertThrows; import android.content.res.Configuration; import android.graphics.Rect; @@ -55,9 +61,12 @@ import java.util.List; public class DisplayAreaOrganizerTest extends WindowTestsBase { private DisplayArea mTestDisplayArea; + private DisplayAreaOrganizerController mOrganizerController; @Before public void setUp() { + mOrganizerController = + mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController; WindowContainer parentWindow = mDisplayContent.getDefaultTaskDisplayArea().getParent(); mTestDisplayArea = new DisplayArea(mWm, DisplayArea.Type.ANY, "TestDisplayArea", FEATURE_VENDOR_FIRST); @@ -76,8 +85,7 @@ public class DisplayAreaOrganizerTest extends WindowTestsBase { private IDisplayAreaOrganizer registerMockOrganizer(int feature, Binder binder) { final IDisplayAreaOrganizer organizer = createMockOrganizer(binder); - mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController - .registerOrganizer(organizer, feature); + mOrganizerController.registerOrganizer(organizer, feature); return organizer; } @@ -87,16 +95,10 @@ public class DisplayAreaOrganizerTest extends WindowTestsBase { return organizer; } - private void unregisterMockOrganizer(IDisplayAreaOrganizer organizer) { - mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController - .unregisterOrganizer(organizer); - } - @Test public void testRegisterOrganizer() throws RemoteException { - IDisplayAreaOrganizer organizer = createMockOrganizer(new Binder()); - List<DisplayAreaAppearedInfo> infos = mWm.mAtmService.mWindowOrganizerController - .mDisplayAreaOrganizerController + final IDisplayAreaOrganizer organizer = createMockOrganizer(new Binder()); + List<DisplayAreaAppearedInfo> infos = mOrganizerController .registerOrganizer(organizer, FEATURE_VENDOR_FIRST).getList(); // Return a list contains the DA, and no onDisplayAreaAppeared triggered. @@ -108,16 +110,135 @@ public class DisplayAreaOrganizerTest extends WindowTestsBase { } @Test + public void testRegisterOrganizer_alreadyRegisteredFeature() { + registerMockOrganizer(FEATURE_VENDOR_FIRST); + assertThrows(IllegalStateException.class, + () -> registerMockOrganizer(FEATURE_VENDOR_FIRST)); + } + + @Test + public void testCreateTaskDisplayArea() { + final String newTdaName = "testTda"; + final IDisplayAreaOrganizer organizer = createMockOrganizer(new Binder()); + final DisplayAreaAppearedInfo tdaInfo = mOrganizerController.createTaskDisplayArea( + organizer, DEFAULT_DISPLAY, FEATURE_ROOT, newTdaName); + + final int newTdaIndex = + mTestDisplayArea.getParent().mChildren.indexOf(mTestDisplayArea) + 1; + final WindowContainer wc = mTestDisplayArea.getParent().getChildAt(newTdaIndex); + + // A new TaskDisplayArea is created on the top. + assertThat(wc).isInstanceOf(TaskDisplayArea.class); + assertThat(tdaInfo.getDisplayAreaInfo().displayId).isEqualTo(DEFAULT_DISPLAY); + assertThat(tdaInfo.getDisplayAreaInfo().token) + .isEqualTo(wc.mRemoteToken.toWindowContainerToken()); + + final TaskDisplayArea tda = wc.asTaskDisplayArea(); + + assertThat(tda.getName()).isEqualTo(newTdaName); + assertThat(tda.mFeatureId).isEqualTo(tdaInfo.getDisplayAreaInfo().featureId); + assertThat(tda.mCreatedByOrganizer).isTrue(); + assertThat(tda.mOrganizer).isEqualTo(organizer); + } + + @Test + public void testCreateTaskDisplayArea_incrementalTdaFeatureId() { + final String newTdaName = "testTda"; + final IDisplayAreaOrganizer organizer = createMockOrganizer(new Binder()); + final DisplayAreaAppearedInfo tdaInfo1 = mOrganizerController.createTaskDisplayArea( + organizer, DEFAULT_DISPLAY, FEATURE_ROOT, newTdaName); + final DisplayAreaAppearedInfo tdaInfo2 = mOrganizerController.createTaskDisplayArea( + organizer, DEFAULT_DISPLAY, FEATURE_ROOT, newTdaName); + + // New created TDA has unique feature id starting from FEATURE_RUNTIME_TASK_CONTAINER_FIRST. + assertThat(tdaInfo1.getDisplayAreaInfo().featureId).isEqualTo( + FEATURE_RUNTIME_TASK_CONTAINER_FIRST); + assertThat(tdaInfo2.getDisplayAreaInfo().featureId).isEqualTo( + FEATURE_RUNTIME_TASK_CONTAINER_FIRST + 1); + } + + + @Test + public void testCreateTaskDisplayArea_invalidDisplayAndRoot() { + final IDisplayAreaOrganizer organizer = createMockOrganizer(new Binder()); + assertThrows(IllegalArgumentException.class, () -> + mOrganizerController.createTaskDisplayArea( + organizer, SystemServicesTestRule.sNextDisplayId + 1, FEATURE_ROOT, + "testTda")); + assertThrows(IllegalArgumentException.class, () -> + mOrganizerController.createTaskDisplayArea( + organizer, DEFAULT_DISPLAY, FEATURE_ROOT - 1, "testTda")); + } + + @Test + public void testDeleteTaskDisplayArea() { + final String newTdaName = "testTda"; + final IDisplayAreaOrganizer organizer = createMockOrganizer(new Binder()); + final DisplayAreaAppearedInfo tdaInfo = mOrganizerController.createTaskDisplayArea( + organizer, DEFAULT_DISPLAY, FEATURE_ROOT, newTdaName); + final int tdaFeatureId = tdaInfo.getDisplayAreaInfo().featureId; + + final TaskDisplayArea newTda = mDisplayContent.getItemFromDisplayAreas( + da -> da.mFeatureId == tdaFeatureId ? da.asTaskDisplayArea() : null); + spyOn(newTda); + + mOrganizerController.deleteTaskDisplayArea(newTda.mRemoteToken.toWindowContainerToken()); + + verify(newTda).remove(); + verify(newTda).removeImmediately(); + assertThat(newTda.mOrganizer).isNull(); + assertThat(newTda.isRemoved()).isTrue(); + + final TaskDisplayArea curTda = mDisplayContent.getItemFromDisplayAreas( + da -> da.mFeatureId == tdaFeatureId ? da.asTaskDisplayArea() : null); + + assertThat(curTda).isNull(); + } + + @Test + public void testUnregisterOrganizer_deleteNewCreatedTaskDisplayArea() { + final String newTdaName = "testTda"; + final IDisplayAreaOrganizer organizer = createMockOrganizer(new Binder()); + final DisplayAreaAppearedInfo tdaInfo = mOrganizerController.createTaskDisplayArea( + organizer, DEFAULT_DISPLAY, FEATURE_ROOT, newTdaName); + final int tdaFeatureId = tdaInfo.getDisplayAreaInfo().featureId; + + final TaskDisplayArea newTda = mDisplayContent.getItemFromDisplayAreas( + da -> da.mFeatureId == tdaFeatureId ? da.asTaskDisplayArea() : null); + spyOn(newTda); + + mOrganizerController.unregisterOrganizer(organizer); + + verify(newTda).remove(); + verify(newTda).removeImmediately(); + assertThat(newTda.mOrganizer).isNull(); + assertThat(newTda.isRemoved()).isTrue(); + + final TaskDisplayArea curTda = mDisplayContent.getItemFromDisplayAreas( + da -> da.mFeatureId == tdaFeatureId ? da.asTaskDisplayArea() : null); + + assertThat(curTda).isNull(); + } + + @Test + public void testDeleteTaskDisplayArea_invalidTaskDisplayArea() { + final TaskDisplayArea tda = mDisplayContent.getDefaultTaskDisplayArea(); + assertThrows(IllegalArgumentException.class, () -> + mOrganizerController.deleteTaskDisplayArea( + tda.mRemoteToken.toWindowContainerToken())); + } + + @Test public void testAppearedVanished() throws RemoteException { - IDisplayAreaOrganizer organizer = registerMockOrganizer(FEATURE_VENDOR_FIRST); - unregisterMockOrganizer(organizer); + final IDisplayAreaOrganizer organizer = registerMockOrganizer(FEATURE_VENDOR_FIRST); + mOrganizerController.unregisterOrganizer(organizer); verify(organizer).onDisplayAreaVanished(any()); } @Test public void testChanged() throws RemoteException { - IDisplayAreaOrganizer organizer = registerMockOrganizer(FEATURE_VENDOR_FIRST); + final IDisplayAreaOrganizer organizer = registerMockOrganizer(FEATURE_VENDOR_FIRST); mDisplayContent.setBounds(new Rect(0, 0, 1000, 1000)); verify(organizer).onDisplayAreaInfoChanged(any()); @@ -137,7 +258,7 @@ public class DisplayAreaOrganizerTest extends WindowTestsBase { assertThat(mTestDisplayArea.mOrganizer).isNotNull(); - unregisterMockOrganizer(createMockOrganizer(binder)); + mOrganizerController.unregisterOrganizer(createMockOrganizer(binder)); assertThat(mTestDisplayArea.mOrganizer).isNull(); } diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 8e56e5bb2d85..aa36e47a359b 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -82,6 +82,7 @@ import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.SparseIntArray; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.content.PackageMonitor; import com.android.internal.os.BackgroundThread; import com.android.internal.util.CollectionUtils; @@ -90,7 +91,6 @@ import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.IndentingPrintWriter; import com.android.server.LocalServices; import com.android.server.SystemService; -import com.android.server.SystemService.TargetUser; import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener; import java.io.BufferedReader; @@ -178,6 +178,8 @@ public class UsageStatsService extends SystemService implements private final SparseArray<LinkedList<Event>> mReportedEvents = new SparseArray<>(); final SparseArray<ArraySet<String>> mUsageReporters = new SparseArray(); final SparseArray<ActivityData> mVisibleActivities = new SparseArray(); + private final ArraySet<UsageStatsManagerInternal.UsageEventListener> mUsageEventListeners = + new ArraySet<>(); private static class ActivityData { private final String mTaskRootPackage; @@ -202,8 +204,24 @@ public class UsageStatsService extends SystemService implements } }; + @VisibleForTesting + static class Injector { + AppStandbyInternal getAppStandbyController(Context context) { + return AppStandbyInternal.newAppStandbyController( + UsageStatsService.class.getClassLoader(), context); + } + } + + private final Injector mInjector; + public UsageStatsService(Context context) { + this(context, new Injector()); + } + + @VisibleForTesting + UsageStatsService(Context context, Injector injector) { super(context); + mInjector = injector; } @Override @@ -214,8 +232,7 @@ public class UsageStatsService extends SystemService implements mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); mHandler = new H(BackgroundThread.get().getLooper()); - mAppStandby = AppStandbyInternal.newAppStandbyController( - UsageStatsService.class.getClassLoader(), getContext()); + mAppStandby = mInjector.getAppStandbyController(getContext()); mAppTimeLimit = new AppTimeLimitController( new AppTimeLimitController.TimeLimitCallbackListener() { @@ -262,6 +279,11 @@ public class UsageStatsService extends SystemService implements publishLocalService(UsageStatsManagerInternal.class, new LocalService()); publishLocalService(AppStandbyInternal.class, mAppStandby); + publishBinderServices(); + } + + @VisibleForTesting + void publishBinderServices() { publishBinderService(Context.USAGE_STATS_SERVICE, new BinderService()); } @@ -928,7 +950,10 @@ public class UsageStatsService extends SystemService implements service.reportEvent(event); } - mAppStandby.reportEvent(event, userId); + final int size = mUsageEventListeners.size(); + for (int i = 0; i < size; ++i) { + mUsageEventListeners.valueAt(i).onUsageEvent(userId, event); + } } /** @@ -1151,6 +1176,25 @@ public class UsageStatsService extends SystemService implements } } + /** + * Called via the local interface. + */ + private void registerListener(@NonNull UsageStatsManagerInternal.UsageEventListener listener) { + synchronized (mLock) { + mUsageEventListeners.add(listener); + } + } + + /** + * Called via the local interface. + */ + private void unregisterListener( + @NonNull UsageStatsManagerInternal.UsageEventListener listener) { + synchronized (mLock) { + mUsageEventListeners.remove(listener); + } + } + private String buildFullToken(String packageName, String token) { final StringBuilder sb = new StringBuilder(packageName.length() + token.length() + 1); sb.append(packageName); @@ -2317,6 +2361,22 @@ public class UsageStatsService extends SystemService implements public boolean updatePackageMappingsData() { return UsageStatsService.this.updatePackageMappingsData(); } + + /** + * Register a listener that will be notified of every new usage event. + */ + @Override + public void registerListener(@NonNull UsageEventListener listener) { + UsageStatsService.this.registerListener(listener); + } + + /** + * Unregister a listener from being notified of every new usage event. + */ + @Override + public void unregisterListener(@NonNull UsageEventListener listener) { + UsageStatsService.this.unregisterListener(listener); + } } private class MyPackageMonitor extends PackageMonitor { diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java index fa0574a503f1..9738e58543e1 100644 --- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java +++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java @@ -1064,6 +1064,31 @@ public class PackageWatchdogTest { } /** + * Ensure that the correct mitigation counts are sent to the boot loop observer. + */ + @Test + public void testMultipleBootLoopMitigation() { + PackageWatchdog watchdog = createWatchdog(); + TestObserver bootObserver = new TestObserver(OBSERVER_NAME_1); + watchdog.registerHealthObserver(bootObserver); + for (int i = 0; i < 4; i++) { + for (int j = 0; j < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; j++) { + watchdog.noteBoot(); + } + } + + moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_DEESCALATION_WINDOW_MS + 1); + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT; j++) { + watchdog.noteBoot(); + } + } + + assertThat(bootObserver.mBootMitigationCounts).isEqualTo(List.of(1, 2, 3, 4, 1, 2, 3, 4)); + } + + /** * Ensure that passing a null list of failed packages does not cause any mitigation logic to * execute. */ @@ -1267,6 +1292,7 @@ public class PackageWatchdogTest { final List<String> mHealthCheckFailedPackages = new ArrayList<>(); final List<String> mMitigatedPackages = new ArrayList<>(); final List<Integer> mMitigationCounts = new ArrayList<>(); + final List<Integer> mBootMitigationCounts = new ArrayList<>(); TestObserver(String name) { mName = name; @@ -1304,12 +1330,13 @@ public class PackageWatchdogTest { return mMayObservePackages; } - public int onBootLoop() { + public int onBootLoop(int level) { return mImpact; } - public boolean executeBootLoopMitigation() { + public boolean executeBootLoopMitigation(int level) { mMitigatedBootLoop = true; + mBootMitigationCounts.add(level); return true; } diff --git a/tests/net/common/java/android/net/MatchAllNetworkSpecifierTest.kt b/tests/net/common/java/android/net/MatchAllNetworkSpecifierTest.kt index a67156a74d18..a5e44d59fcab 100644 --- a/tests/net/common/java/android/net/MatchAllNetworkSpecifierTest.kt +++ b/tests/net/common/java/android/net/MatchAllNetworkSpecifierTest.kt @@ -53,8 +53,13 @@ class MatchAllNetworkSpecifierTest { assertParcelSane(MatchAllNetworkSpecifier(), 0) } - @Test @IgnoreAfter(Build.VERSION_CODES.R) - fun testCanBeSatisfiedBy_BeforeS() { + @Test + @IgnoreUpTo(Build.VERSION_CODES.Q) + @IgnoreAfter(Build.VERSION_CODES.R) + // Only run this test on Android R. + // The method - satisfiedBy() has changed to canBeSatisfiedBy() starting from Android R, so the + // method - canBeSatisfiedBy() cannot be found when running this test on Android Q. + fun testCanBeSatisfiedBy_OnlyForR() { // MatchAllNetworkSpecifier didn't follow its parent class to change the satisfiedBy() to // canBeSatisfiedBy(), so if a caller calls MatchAllNetworkSpecifier#canBeSatisfiedBy(), the // NetworkSpecifier#canBeSatisfiedBy() will be called actually, and false will be returned. diff --git a/tools/codegen/src/com/android/codegen/Generators.kt b/tools/codegen/src/com/android/codegen/Generators.kt index 6c6d011cfede..5fc800b09ee9 100644 --- a/tools/codegen/src/com/android/codegen/Generators.kt +++ b/tools/codegen/src/com/android/codegen/Generators.kt @@ -5,8 +5,6 @@ import com.github.javaparser.ast.body.MethodDeclaration import com.github.javaparser.ast.body.VariableDeclarator import com.github.javaparser.ast.expr.AnnotationExpr import com.github.javaparser.ast.expr.ArrayInitializerExpr -import com.github.javaparser.ast.expr.LiteralExpr -import com.github.javaparser.ast.expr.UnaryExpr import java.io.File @@ -163,7 +161,12 @@ fun ClassPrinter.generateCopyConstructor() { return } - +"/** Copy constructor */" + +"/**" + +" * Copy constructor" + if (FeatureFlag.COPY_CONSTRUCTOR.hidden) { + +" * @hide" + } + +" */" +GENERATED_MEMBER_HEADER "public $ClassName(@$NonNull $ClassName orig)" { fields.forEachApply { diff --git a/tools/codegen/src/com/android/codegen/SharedConstants.kt b/tools/codegen/src/com/android/codegen/SharedConstants.kt index 2e176c3d3bec..6a635d0e6181 100644 --- a/tools/codegen/src/com/android/codegen/SharedConstants.kt +++ b/tools/codegen/src/com/android/codegen/SharedConstants.kt @@ -1,7 +1,7 @@ package com.android.codegen const val CODEGEN_NAME = "codegen" -const val CODEGEN_VERSION = "1.0.20" +const val CODEGEN_VERSION = "1.0.21" const val CANONICAL_BUILDER_CLASS = "Builder" const val BASE_BUILDER_CLASS = "BaseBuilder" diff --git a/tools/hiddenapi/exclude.sh b/tools/hiddenapi/exclude.sh index 18c40546fd02..73eacc0641e5 100755 --- a/tools/hiddenapi/exclude.sh +++ b/tools/hiddenapi/exclude.sh @@ -35,7 +35,7 @@ TEAMS=LIBCORE PACKAGES=$(for t in $TEAMS; do echo $(eval echo \${${t}_PACKAGES}); done) RE=$(echo ${PACKAGES} | sed "s/ /|/g") git show --name-only --pretty=format: $1 | grep "config/hiddenapi-.*txt" | while read file; do - ENTRIES=$(grep -E "^L(${RE})/" <(git show $1:$file)) + ENTRIES=$(grep -E "^L(${RE})/" || true <(git show $1:$file)) if [[ -n "${ENTRIES}" ]]; then echo -e "\e[1m\e[31m$file $1 contains the following entries\e[0m" echo -e "\e[1m\e[31mfor packages that are handled using UnsupportedAppUsage. Please remove\e[0m" diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java index 9f9d7f3c3051..fddc8899a0c8 100644 --- a/wifi/java/android/net/wifi/SoftApConfiguration.java +++ b/wifi/java/android/net/wifi/SoftApConfiguration.java @@ -538,6 +538,15 @@ public final class SoftApConfiguration implements Parcelable { if (!SdkLevel.isAtLeastS()) { throw new UnsupportedOperationException(); } + return getChannelsInternal(); + } + + /** + * Internal version bypassing SdkLevel checks + * TODO(b/173791707): find a better way to allow Wifi to call its own new S APIs. + * @hide + */ + public @NonNull SparseIntArray getChannelsInternal() { return mChannels.clone(); } diff --git a/wifi/java/android/net/wifi/SoftApInfo.java b/wifi/java/android/net/wifi/SoftApInfo.java index 9a16facfec26..55c2f1759952 100644 --- a/wifi/java/android/net/wifi/SoftApInfo.java +++ b/wifi/java/android/net/wifi/SoftApInfo.java @@ -183,6 +183,15 @@ public final class SoftApInfo implements Parcelable { if (!SdkLevel.isAtLeastS()) { throw new UnsupportedOperationException(); } + return getWifiStandardInternal(); + } + + /** + * Internal version bypassing SdkLevel checks + * TODO(b/173791707): find a better way to allow Wifi to call its own new S APIs. + * @hide + */ + public @WifiAnnotations.WifiStandard int getWifiStandardInternal() { return mWifiStandard; } |