diff options
107 files changed, 2551 insertions, 970 deletions
diff --git a/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java b/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java index 6c8af39015f5..ae98fe14fbe6 100644 --- a/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java +++ b/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java @@ -77,6 +77,12 @@ public interface JobSchedulerInternal { @NonNull String notificationChannel, int userId, @NonNull String packageName); /** + * @return {@code true} if the given package holds the + * {@link android.Manifest.permission.RUN_BACKUP_JOBS} permission. + */ + boolean hasRunBackupJobsPermission(@NonNull String packageName, int packageUid); + + /** * Report a snapshot of sync-related jobs back to the sync manager */ JobStorePersistStats getPersistStats(); diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java index fc193d8147b5..57467e3cc83d 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -4197,6 +4197,11 @@ public class JobSchedulerService extends com.android.server.SystemService } @Override + public boolean hasRunBackupJobsPermission(@NonNull String packageName, int packageUid) { + return JobSchedulerService.this.hasRunBackupJobsPermission(packageName, packageUid); + } + + @Override public JobStorePersistStats getPersistStats() { synchronized (mLock) { return new JobStorePersistStats(mJobs.getPersistStats()); @@ -4359,6 +4364,22 @@ public class JobSchedulerService extends com.android.server.SystemService } /** + * Returns whether the app holds the {@link Manifest.permission.RUN_BACKUP_JOBS} permission. + */ + private boolean hasRunBackupJobsPermission(@NonNull String packageName, int packageUid) { + if (packageName == null) { + Slog.wtfStack(TAG, + "Expected a non-null package name when calling hasRunBackupJobsPermission"); + return false; + } + + return PermissionChecker.checkPermissionForPreflight(getTestableContext(), + android.Manifest.permission.RUN_BACKUP_JOBS, + PermissionChecker.PID_UNKNOWN, packageUid, packageName) + == PermissionChecker.PERMISSION_GRANTED; + } + + /** * Binder stub trampoline implementation */ final class JobSchedulerStub extends IJobScheduler.Stub { diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java index a4df5d829281..2ea980d40287 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java @@ -1222,21 +1222,25 @@ public final class JobStatus { return ACTIVE_INDEX; } - final int bucketWithMediaExemption; - if (actualBucket != RESTRICTED_INDEX && actualBucket != NEVER_INDEX - && mHasMediaBackupExemption) { + final boolean isEligibleAsBackupJob = job.getTriggerContentUris() != null + && job.getRequiredNetwork() != null + && !job.hasLateConstraint() + && mJobSchedulerInternal.hasRunBackupJobsPermission(sourcePackageName, sourceUid); + final boolean isBackupExempt = mHasMediaBackupExemption || isEligibleAsBackupJob; + final int bucketWithBackupExemption; + if (actualBucket != RESTRICTED_INDEX && actualBucket != NEVER_INDEX && isBackupExempt) { // Treat it as if it's at most WORKING_INDEX (lower index grants higher quota) since // media backup jobs are important to the user, and the source package may not have // been used directly in a while. - bucketWithMediaExemption = Math.min(WORKING_INDEX, actualBucket); + bucketWithBackupExemption = Math.min(WORKING_INDEX, actualBucket); } else { - bucketWithMediaExemption = actualBucket; + bucketWithBackupExemption = actualBucket; } // If the app is considered buggy, but hasn't yet been put in the RESTRICTED bucket // (potentially because it's used frequently by the user), limit its effective bucket // so that it doesn't get to run as much as a normal ACTIVE app. - if (isBuggy && bucketWithMediaExemption < WORKING_INDEX) { + if (isBuggy && bucketWithBackupExemption < WORKING_INDEX) { if (!mIsDowngradedDueToBuggyApp) { // Safety check to avoid logging multiple times for the same job. Counter.logIncrementWithUid( @@ -1246,7 +1250,7 @@ public final class JobStatus { } return WORKING_INDEX; } - return bucketWithMediaExemption; + return bucketWithBackupExemption; } /** Returns the real standby bucket of the job. */ diff --git a/core/api/current.txt b/core/api/current.txt index cb8db9ea69a0..55ea2f4da526 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -147,6 +147,7 @@ package android { field public static final String MANAGE_DEVICE_POLICY_CAMERA = "android.permission.MANAGE_DEVICE_POLICY_CAMERA"; field public static final String MANAGE_DEVICE_POLICY_CERTIFICATES = "android.permission.MANAGE_DEVICE_POLICY_CERTIFICATES"; field public static final String MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE = "android.permission.MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE"; + field @FlaggedApi("android.view.contentprotection.flags.manage_device_policy_enabled") public static final String MANAGE_DEVICE_POLICY_CONTENT_PROTECTION = "android.permission.MANAGE_DEVICE_POLICY_CONTENT_PROTECTION"; field public static final String MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES = "android.permission.MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES"; field public static final String MANAGE_DEVICE_POLICY_DEFAULT_SMS = "android.permission.MANAGE_DEVICE_POLICY_DEFAULT_SMS"; field public static final String MANAGE_DEVICE_POLICY_DEVICE_IDENTIFIERS = "android.permission.MANAGE_DEVICE_POLICY_DEVICE_IDENTIFIERS"; @@ -8194,6 +8195,9 @@ package android.app.admin { field public static final String ACTION_SET_NEW_PASSWORD = "android.app.action.SET_NEW_PASSWORD"; field public static final String ACTION_START_ENCRYPTION = "android.app.action.START_ENCRYPTION"; field public static final String ACTION_SYSTEM_UPDATE_POLICY_CHANGED = "android.app.action.SYSTEM_UPDATE_POLICY_CHANGED"; + field @FlaggedApi("android.view.contentprotection.flags.manage_device_policy_enabled") public static final int CONTENT_PROTECTION_DISABLED = 1; // 0x1 + field @FlaggedApi("android.view.contentprotection.flags.manage_device_policy_enabled") public static final int CONTENT_PROTECTION_ENABLED = 2; // 0x2 + field @FlaggedApi("android.view.contentprotection.flags.manage_device_policy_enabled") public static final int CONTENT_PROTECTION_NOT_CONTROLLED_BY_POLICY = 0; // 0x0 field public static final String DELEGATION_APP_RESTRICTIONS = "delegation-app-restrictions"; field public static final String DELEGATION_BLOCK_UNINSTALL = "delegation-block-uninstall"; field public static final String DELEGATION_CERT_INSTALL = "delegation-cert-install"; diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 42cf08ff9a66..1fa2b5c33652 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -195,8 +195,11 @@ package android.app { method public void setTaskOverlay(boolean, boolean); } - public static final class ActivityOptions.LaunchCookie { + public static final class ActivityOptions.LaunchCookie implements android.os.Parcelable { ctor public ActivityOptions.LaunchCookie(); + method public int describeContents(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.app.ActivityOptions.LaunchCookie> CREATOR; } public static interface ActivityOptions.OnAnimationFinishedListener { @@ -2108,6 +2111,14 @@ package android.media.metrics { } +package android.media.projection { + + public final class MediaProjectionManager { + method @NonNull public android.content.Intent createScreenCaptureIntent(@Nullable android.app.ActivityOptions.LaunchCookie); + } + +} + package android.media.soundtrigger { public final class SoundTriggerInstrumentation { diff --git a/core/java/android/app/ActivityOptions.aidl b/core/java/android/app/ActivityOptions.aidl index bd5cd88959a3..2d4a85f4f6f4 100644 --- a/core/java/android/app/ActivityOptions.aidl +++ b/core/java/android/app/ActivityOptions.aidl @@ -17,4 +17,7 @@ package android.app; /** @hide */ -parcelable ActivityOptions.SceneTransitionInfo;
\ No newline at end of file +parcelable ActivityOptions.SceneTransitionInfo; + +/** @hide */ +parcelable ActivityOptions.LaunchCookie;
\ No newline at end of file diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index 4a566db3afb3..111895e3053b 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -1958,14 +1958,87 @@ public class ActivityOptions extends ComponentOptions { */ @SuppressLint("UnflaggedApi") @TestApi - public static final class LaunchCookie { + public static final class LaunchCookie implements Parcelable { /** @hide */ - public final IBinder binder = new Binder(); + public final IBinder binder; /** @hide */ @SuppressLint("UnflaggedApi") @TestApi - public LaunchCookie() {} + public LaunchCookie() { + binder = new Binder(); + } + + /** @hide */ + public LaunchCookie(@Nullable String descriptor) { + binder = new Binder(descriptor); + } + + private LaunchCookie(Parcel in) { + this.binder = in.readStrongBinder(); + } + + /** @hide */ + @SuppressLint("UnflaggedApi") + @TestApi + @Override + public int describeContents() { + return 0; + } + + /** @hide */ + @SuppressLint("UnflaggedApi") + @TestApi + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeStrongBinder(binder); + } + + /** @hide */ + public static LaunchCookie readFromParcel(@NonNull Parcel in) { + return new LaunchCookie(in); + } + + /** @hide */ + public static void writeToParcel(@Nullable LaunchCookie launchCookie, Parcel out) { + if (launchCookie != null) { + launchCookie.writeToParcel(out, 0); + } else { + out.writeStrongBinder(null); + } + } + + /** @hide */ + @SuppressLint("UnflaggedApi") + @TestApi + @NonNull + public static final Parcelable.Creator<LaunchCookie> CREATOR = + new Parcelable.Creator<LaunchCookie>() { + + @Override + public LaunchCookie createFromParcel(Parcel source) { + return new LaunchCookie(source); + } + + @Override + public LaunchCookie[] newArray(int size) { + return new LaunchCookie[size]; + } + }; + + @Override + public boolean equals(@Nullable Object obj) { + if (obj instanceof LaunchCookie) { + LaunchCookie other = (LaunchCookie) obj; + return binder == other.binder; + } + return false; + } + + @Override + public int hashCode() { + return binder.hashCode(); + } } /** diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index 2162e3a77f15..68512b8bd771 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -79,6 +79,7 @@ import java.util.concurrent.TimeoutException; * implementation is described to the system through an AndroidManifest.xml's * <instrumentation> tag. */ +@android.ravenwood.annotation.RavenwoodKeepPartialClass public class Instrumentation { /** @@ -132,6 +133,7 @@ public class Instrumentation { private UiAutomation mUiAutomation; private final Object mAnimationCompleteLock = new Object(); + @android.ravenwood.annotation.RavenwoodKeep public Instrumentation() { } @@ -142,6 +144,7 @@ public class Instrumentation { * reflection, but it will serve as noticeable discouragement from * doing such a thing. */ + @android.ravenwood.annotation.RavenwoodReplace private void checkInstrumenting(String method) { // Check if we have an instrumentation context, as init should only get called by // the system in startup processes that are being instrumented. @@ -151,6 +154,11 @@ public class Instrumentation { } } + private void checkInstrumenting$ravenwood(String method) { + // At the moment, Ravenwood doesn't attach a Context, but we're only ever + // running code as part of tests, so we continue quietly + } + /** * Returns if it is being called in an instrumentation environment. * @@ -2504,6 +2512,7 @@ public class Instrumentation { * Takes control of the execution of messages on the specified looper until * {@link TestLooperManager#release} is called. */ + @android.ravenwood.annotation.RavenwoodKeep public TestLooperManager acquireLooperManager(Looper looper) { checkInstrumenting("acquireLooperManager"); return new TestLooperManager(looper); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 5c42b0ed975a..86d0125fd7a2 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -53,6 +53,7 @@ import static android.app.admin.flags.Flags.onboardingBugreportV2Enabled; import static android.content.Intent.LOCAL_FLAG_FROM_SYSTEM; import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1; import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE; +import static android.view.contentprotection.flags.Flags.FLAG_MANAGE_DEVICE_POLICY_ENABLED; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; @@ -61,6 +62,7 @@ import android.accounts.Account; import android.annotation.BroadcastBehavior; import android.annotation.CallbackExecutor; import android.annotation.ColorInt; +import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -4092,6 +4094,29 @@ public class DevicePolicyManager { return MTE_NOT_CONTROLLED_BY_POLICY; } + /** Indicates that content protection is not controlled by policy, allowing user to choose. */ + @FlaggedApi(FLAG_MANAGE_DEVICE_POLICY_ENABLED) + public static final int CONTENT_PROTECTION_NOT_CONTROLLED_BY_POLICY = 0; + + /** Indicates that content protection is controlled and disabled by a policy. */ + @FlaggedApi(FLAG_MANAGE_DEVICE_POLICY_ENABLED) + public static final int CONTENT_PROTECTION_DISABLED = 1; + + /** Indicates that content protection is controlled and enabled by a policy. */ + @FlaggedApi(FLAG_MANAGE_DEVICE_POLICY_ENABLED) + public static final int CONTENT_PROTECTION_ENABLED = 2; + + /** @hide */ + @IntDef( + prefix = {"CONTENT_PROTECTION_"}, + value = { + CONTENT_PROTECTION_NOT_CONTROLLED_BY_POLICY, + CONTENT_PROTECTION_DISABLED, + CONTENT_PROTECTION_ENABLED, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ContentProtectionPolicy {} + /** * This object is a single place to tack on invalidation and disable calls. All * binder caches in this class derive from this Config, so all can be invalidated or diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index ec181dac6b36..1f19f817a0b3 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -907,7 +907,10 @@ public class AppWidgetHostView extends FrameLayout implements AppWidgetHost.AppW private InteractionHandler getHandler(InteractionHandler handler) { return (view, pendingIntent, response) -> { - AppWidgetManager.getInstance(mContext).noteAppWidgetTapped(mAppWidgetId); + AppWidgetManager manager = AppWidgetManager.getInstance(mContext); + if (manager != null) { + manager.noteAppWidgetTapped(mAppWidgetId); + } if (handler != null) { return handler.onInteraction(view, pendingIntent, response); } else { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index b1173a25e95f..b8d754348211 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3657,8 +3657,8 @@ public abstract class Context { * On Android {@link android.os.Build.VERSION_CODES#S} and later, * if the application is in a state where the service * can not be started (such as not in the foreground in a state when services are allowed), - * {@link android.app.BackgroundServiceStartNotAllowedException} is thrown - * This excemption extends {@link IllegalStateException}, so apps can + * {@link android.app.BackgroundServiceStartNotAllowedException} is thrown. + * This exception extends {@link IllegalStateException}, so apps can * use {@code catch (IllegalStateException)} to catch both. * * @see #startForegroundService(Intent) diff --git a/core/java/android/content/res/Element.java b/core/java/android/content/res/Element.java index 89f4985461b7..6ff96f42e433 100644 --- a/core/java/android/content/res/Element.java +++ b/core/java/android/content/res/Element.java @@ -93,6 +93,7 @@ public class Element { protected static final String TAG_SUPPORTS_GL_TEXTURE = "supports-gl-texture"; protected static final String TAG_SUPPORTS_INPUT = "supports-input"; protected static final String TAG_SUPPORTS_SCREENS = "supports-screens"; + protected static final String TAG_URI_RELATIVE_FILTER_GROUP = "uri-relative-filter-group"; protected static final String TAG_USES_CONFIGURATION = "uses-configuration"; protected static final String TAG_USES_FEATURE = "uses-feature"; protected static final String TAG_USES_GL_TEXTURE = "uses-gl-texture"; @@ -106,6 +107,11 @@ public class Element { protected static final String TAG_ATTR_BACKUP_AGENT = "backupAgent"; protected static final String TAG_ATTR_CATEGORY = "category"; + protected static final String TAG_ATTR_FRAGMENT = "fragment"; + protected static final String TAG_ATTR_FRAGMENT_ADVANCED_PATTERN = "fragmentAdvancedPattern"; + protected static final String TAG_ATTR_FRAGMENT_PATTERN = "fragmentPattern"; + protected static final String TAG_ATTR_FRAGMENT_PREFIX = "fragmentPrefix"; + protected static final String TAG_ATTR_FRAGMENT_SUFFIX = "fragmentSuffix"; protected static final String TAG_ATTR_HOST = "host"; protected static final String TAG_ATTR_MANAGE_SPACE_ACTIVITY = "manageSpaceActivity"; protected static final String TAG_ATTR_MIMETYPE = "mimeType"; @@ -122,6 +128,11 @@ public class Element { protected static final String TAG_ATTR_PERMISSION_GROUP = "permissionGroup"; protected static final String TAG_ATTR_PORT = "port"; protected static final String TAG_ATTR_PROCESS = "process"; + protected static final String TAG_ATTR_QUERY = "query"; + protected static final String TAG_ATTR_QUERY_ADVANCED_PATTERN = "queryAdvancedPattern"; + protected static final String TAG_ATTR_QUERY_PATTERN = "queryPattern"; + protected static final String TAG_ATTR_QUERY_PREFIX = "queryPrefix"; + protected static final String TAG_ATTR_QUERY_SUFFIX = "querySuffix"; protected static final String TAG_ATTR_READ_PERMISSION = "readPermission"; protected static final String TAG_ATTR_REQUIRED_ACCOUNT_TYPE = "requiredAccountType"; protected static final String TAG_ATTR_REQUIRED_SYSTEM_PROPERTY_NAME = @@ -143,7 +154,7 @@ public class Element { // The length of mTagCounters corresponds to the number of tags defined in getCounterIdx. If new // tags are added then the size here should be increased to match. - private final TagCounter[] mTagCounters = new TagCounter[34]; + private final TagCounter[] mTagCounters = new TagCounter[35]; String mTag; @@ -238,9 +249,11 @@ public class Element { return 31; case TAG_INTENT: return 32; + case TAG_URI_RELATIVE_FILTER_GROUP: + return 33; default: // The size of the mTagCounters array should be equal to this value+1 - return 33; + return 34; } } @@ -276,6 +289,7 @@ public class Element { case TAG_SERVICE: case TAG_SUPPORTS_GL_TEXTURE: case TAG_SUPPORTS_SCREENS: + case TAG_URI_RELATIVE_FILTER_GROUP: case TAG_USES_CONFIGURATION: case TAG_USES_FEATURE: case TAG_USES_LIBRARY: @@ -322,6 +336,7 @@ public class Element { break; case TAG_INTENT: case TAG_INTENT_FILTER: + initializeCounter(TAG_URI_RELATIVE_FILTER_GROUP, 100); initializeCounter(TAG_ACTION, 20000); initializeCounter(TAG_CATEGORY, 40000); initializeCounter(TAG_DATA, 40000); @@ -354,6 +369,9 @@ public class Element { initializeCounter(TAG_INTENT, 2000); initializeCounter(TAG_PROVIDER, 8000); break; + case TAG_URI_RELATIVE_FILTER_GROUP: + initializeCounter(TAG_DATA, 100); + break; } } @@ -391,11 +409,21 @@ public class Element { case TAG_ATTR_VERSION_NAME: case TAG_ATTR_ZYGOTE_PRELOAD_NAME: return MAX_ATTR_LEN_NAME; + case TAG_ATTR_FRAGMENT: + case TAG_ATTR_FRAGMENT_ADVANCED_PATTERN: + case TAG_ATTR_FRAGMENT_PATTERN: + case TAG_ATTR_FRAGMENT_PREFIX: + case TAG_ATTR_FRAGMENT_SUFFIX: case TAG_ATTR_PATH: case TAG_ATTR_PATH_ADVANCED_PATTERN: case TAG_ATTR_PATH_PATTERN: case TAG_ATTR_PATH_PREFIX: case TAG_ATTR_PATH_SUFFIX: + case TAG_ATTR_QUERY: + case TAG_ATTR_QUERY_ADVANCED_PATTERN: + case TAG_ATTR_QUERY_PATTERN: + case TAG_ATTR_QUERY_PREFIX: + case TAG_ATTR_QUERY_SUFFIX: return MAX_ATTR_LEN_PATH; case TAG_ATTR_VALUE: return MAX_ATTR_LEN_VALUE; @@ -535,6 +563,16 @@ public class Element { case R.styleable.AndroidManifestData_pathPrefix: case R.styleable.AndroidManifestData_pathSuffix: case R.styleable.AndroidManifestData_pathAdvancedPattern: + case R.styleable.AndroidManifestData_query: + case R.styleable.AndroidManifestData_queryPattern: + case R.styleable.AndroidManifestData_queryPrefix: + case R.styleable.AndroidManifestData_querySuffix: + case R.styleable.AndroidManifestData_queryAdvancedPattern: + case R.styleable.AndroidManifestData_fragment: + case R.styleable.AndroidManifestData_fragmentPattern: + case R.styleable.AndroidManifestData_fragmentPrefix: + case R.styleable.AndroidManifestData_fragmentSuffix: + case R.styleable.AndroidManifestData_fragmentAdvancedPattern: return MAX_ATTR_LEN_PATH; default: return DEFAULT_MAX_STRING_ATTR_LENGTH; diff --git a/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl b/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl index 7c54a9b01dde..509bcb8e3d23 100644 --- a/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl +++ b/core/java/android/hardware/camera2/extension/CameraOutputConfig.aidl @@ -26,6 +26,7 @@ parcelable CameraOutputConfig Surface surface; int imageFormat; int capacity; + long usage; const int TYPE_SURFACE = 0; const int TYPE_IMAGEREADER = 1; diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java index 98bc31161591..f6c8f36a1b01 100644 --- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java @@ -1182,7 +1182,8 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes return null; } ImageReader reader = ImageReader.newInstance(output.size.width, - output.size.height, output.imageFormat, output.capacity); + output.size.height, output.imageFormat, output.capacity, + output.usage); mReaderMap.put(output.outputId.id, reader); return reader.getSurface(); case CameraOutputConfig.TYPE_MULTIRES_IMAGEREADER: diff --git a/core/java/android/os/TestLooperManager.java b/core/java/android/os/TestLooperManager.java index 5e7549fa67d8..4b16c1dce463 100644 --- a/core/java/android/os/TestLooperManager.java +++ b/core/java/android/os/TestLooperManager.java @@ -28,6 +28,7 @@ import java.util.concurrent.LinkedBlockingQueue; * The test code may use {@link #next()} to acquire messages that have been queued to this * {@link Looper}/{@link MessageQueue} and then {@link #execute} to run any that desires. */ +@android.ravenwood.annotation.RavenwoodKeepWholeClass public class TestLooperManager { private static final ArraySet<Looper> sHeldLoopers = new ArraySet<>(); diff --git a/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig b/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig index 2a3008a53635..5d3153c00e8a 100644 --- a/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig +++ b/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig @@ -34,3 +34,10 @@ flag { description: "If true, an appop is logged when a notification is rapidly cleared by a notification listener." bug: "289080543" } + +flag { + name: "manage_device_policy_enabled" + namespace: "content_protection" + description: "If true, the APIs to manage content protection device policy will be enabled." + bug: "319477846" +} diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java index 7a79e0f7cfea..aa60cc9e672c 100644 --- a/core/java/com/android/internal/os/BatteryStatsHistory.java +++ b/core/java/com/android/internal/os/BatteryStatsHistory.java @@ -490,7 +490,7 @@ public class BatteryStatsHistory { * Returns true if this instance only supports reading history. */ public boolean isReadOnly() { - return mActiveFile == null || mHistoryDir == null; + return !mMutable || mActiveFile == null || mHistoryDir == null; } /** @@ -508,6 +508,13 @@ public class BatteryStatsHistory { * create next history file. */ public void startNextFile(long elapsedRealtimeMs) { + synchronized (this) { + startNextFileLocked(elapsedRealtimeMs); + } + } + + @GuardedBy("this") + private void startNextFileLocked(long elapsedRealtimeMs) { if (mMaxHistoryFiles == 0) { Slog.wtf(TAG, "mMaxHistoryFiles should not be zero when writing history"); return; @@ -548,10 +555,7 @@ public class BatteryStatsHistory { } mWrittenPowerStatsDescriptors.clear(); - - synchronized (this) { - cleanupLocked(); - } + cleanupLocked(); } @GuardedBy("this") @@ -599,27 +603,31 @@ public class BatteryStatsHistory { * number 0 again. */ public void reset() { - if (DEBUG) Slog.i(TAG, "********** CLEARING HISTORY!"); - for (BatteryHistoryFile file : mHistoryFiles) { - file.atomicFile.delete(); - } - mHistoryFiles.clear(); + synchronized (this) { + if (DEBUG) Slog.i(TAG, "********** CLEARING HISTORY!"); + for (BatteryHistoryFile file : mHistoryFiles) { + file.atomicFile.delete(); + } + mHistoryFiles.clear(); - BatteryHistoryFile name = makeBatteryHistoryFile(); - mHistoryFiles.add(name); - setActiveFile(name); + BatteryHistoryFile name = makeBatteryHistoryFile(); + mHistoryFiles.add(name); + setActiveFile(name); - initHistoryBuffer(); + initHistoryBuffer(); + } } /** * Returns the monotonic clock time when the available battery history collection started. */ public long getStartTime() { - if (!mHistoryFiles.isEmpty()) { - return mHistoryFiles.get(0).monotonicTimeMs; - } else { - return mHistoryBufferStartTime; + synchronized (this) { + if (!mHistoryFiles.isEmpty()) { + return mHistoryFiles.get(0).monotonicTimeMs; + } else { + return mHistoryBufferStartTime; + } } } @@ -633,11 +641,14 @@ public class BatteryStatsHistory { */ @NonNull public BatteryStatsHistoryIterator iterate(long startTimeMs, long endTimeMs) { + if (mMutable) { + return copy().iterate(startTimeMs, endTimeMs); + } + mCurrentFileIndex = 0; mCurrentParcel = null; mCurrentParcelEnd = 0; mParcelIndex = 0; - mMutable = false; if (mWritableHistory != null) { synchronized (mWritableHistory) { mWritableHistory.setCleanupEnabledLocked(false); @@ -650,14 +661,11 @@ public class BatteryStatsHistory { * Finish iterating history files and history buffer. */ void iteratorFinished() { - // setDataPosition so mHistoryBuffer Parcel can be written. mHistoryBuffer.setDataPosition(mHistoryBuffer.dataSize()); if (mWritableHistory != null) { synchronized (mWritableHistory) { mWritableHistory.setCleanupEnabledLocked(true); } - } else { - mMutable = true; } } @@ -671,6 +679,8 @@ public class BatteryStatsHistory { */ @Nullable public Parcel getNextParcel(long startTimeMs, long endTimeMs) { + checkImmutable(); + // First iterate through all records in current parcel. if (mCurrentParcel != null) { if (mCurrentParcel.dataPosition() < mCurrentParcelEnd) { @@ -754,6 +764,12 @@ public class BatteryStatsHistory { return mCurrentParcel; } + private void checkImmutable() { + if (mMutable) { + throw new IllegalStateException("Iterating over a mutable battery history"); + } + } + /** * Read history file into a parcel. * @@ -863,8 +879,10 @@ public class BatteryStatsHistory { * @param out the output parcel */ public void writeToParcel(Parcel out) { - writeHistoryBuffer(out); - writeToParcel(out, false /* useBlobs */); + synchronized (this) { + writeHistoryBuffer(out); + writeToParcel(out, false /* useBlobs */); + } } /** @@ -874,8 +892,10 @@ public class BatteryStatsHistory { * @param out the output parcel */ public void writeToBatteryUsageStatsParcel(Parcel out) { - out.writeBlob(mHistoryBuffer.marshall()); - writeToParcel(out, true /* useBlobs */); + synchronized (this) { + out.writeBlob(mHistoryBuffer.marshall()); + writeToParcel(out, true /* useBlobs */); + } } private void writeToParcel(Parcel out, boolean useBlobs) { @@ -1022,14 +1042,18 @@ public class BatteryStatsHistory { * Enables/disables recording of history. When disabled, all "record*" calls are a no-op. */ public void setHistoryRecordingEnabled(boolean enabled) { - mRecordingHistory = enabled; + synchronized (this) { + mRecordingHistory = enabled; + } } /** * Returns true if history recording is enabled. */ public boolean isRecordingHistory() { - return mRecordingHistory; + synchronized (this) { + return mRecordingHistory; + } } /** @@ -1037,8 +1061,10 @@ public class BatteryStatsHistory { */ @VisibleForTesting public void forceRecordAllHistory() { - mHaveBatteryLevel = true; - mRecordingHistory = true; + synchronized (this) { + mHaveBatteryLevel = true; + mRecordingHistory = true; + } } /** @@ -1046,37 +1072,43 @@ public class BatteryStatsHistory { */ public void startRecordingHistory(final long elapsedRealtimeMs, final long uptimeMs, boolean reset) { - mRecordingHistory = true; - mHistoryCur.currentTime = mClock.currentTimeMillis(); - writeHistoryItem(elapsedRealtimeMs, uptimeMs, mHistoryCur, - reset ? HistoryItem.CMD_RESET : HistoryItem.CMD_CURRENT_TIME); - mHistoryCur.currentTime = 0; + synchronized (this) { + mRecordingHistory = true; + mHistoryCur.currentTime = mClock.currentTimeMillis(); + writeHistoryItem(elapsedRealtimeMs, uptimeMs, mHistoryCur, + reset ? HistoryItem.CMD_RESET : HistoryItem.CMD_CURRENT_TIME); + mHistoryCur.currentTime = 0; + } } /** * Prepares to continue recording after restoring previous history from persistent storage. */ public void continueRecordingHistory() { - if (mHistoryBuffer.dataPosition() <= 0 && mHistoryFiles.size() <= 1) { - return; - } + synchronized (this) { + if (mHistoryBuffer.dataPosition() <= 0 && mHistoryFiles.size() <= 1) { + return; + } - mRecordingHistory = true; - final long elapsedRealtimeMs = mClock.elapsedRealtime(); - final long uptimeMs = mClock.uptimeMillis(); - writeHistoryItem(elapsedRealtimeMs, uptimeMs, mHistoryCur, HistoryItem.CMD_START); - startRecordingHistory(elapsedRealtimeMs, uptimeMs, false); + mRecordingHistory = true; + final long elapsedRealtimeMs = mClock.elapsedRealtime(); + final long uptimeMs = mClock.uptimeMillis(); + writeHistoryItem(elapsedRealtimeMs, uptimeMs, mHistoryCur, HistoryItem.CMD_START); + startRecordingHistory(elapsedRealtimeMs, uptimeMs, false); + } } /** * Notes the current battery state to be reflected in the next written history item. */ public void setBatteryState(boolean charging, int status, int level, int chargeUah) { - mHaveBatteryLevel = true; - setChargingState(charging); - mHistoryCur.batteryStatus = (byte) status; - mHistoryCur.batteryLevel = (byte) level; - mHistoryCur.batteryChargeUah = chargeUah; + synchronized (this) { + mHaveBatteryLevel = true; + setChargingState(charging); + mHistoryCur.batteryStatus = (byte) status; + mHistoryCur.batteryLevel = (byte) level; + mHistoryCur.batteryChargeUah = chargeUah; + } } /** @@ -1084,24 +1116,28 @@ public class BatteryStatsHistory { */ public void setBatteryState(int status, int level, int health, int plugType, int temperature, int voltageMv, int chargeUah) { - mHaveBatteryLevel = true; - mHistoryCur.batteryStatus = (byte) status; - mHistoryCur.batteryLevel = (byte) level; - mHistoryCur.batteryHealth = (byte) health; - mHistoryCur.batteryPlugType = (byte) plugType; - mHistoryCur.batteryTemperature = (short) temperature; - mHistoryCur.batteryVoltage = (char) voltageMv; - mHistoryCur.batteryChargeUah = chargeUah; + synchronized (this) { + mHaveBatteryLevel = true; + mHistoryCur.batteryStatus = (byte) status; + mHistoryCur.batteryLevel = (byte) level; + mHistoryCur.batteryHealth = (byte) health; + mHistoryCur.batteryPlugType = (byte) plugType; + mHistoryCur.batteryTemperature = (short) temperature; + mHistoryCur.batteryVoltage = (char) voltageMv; + mHistoryCur.batteryChargeUah = chargeUah; + } } /** * Notes the current power plugged-in state to be reflected in the next written history item. */ public void setPluggedInState(boolean pluggedIn) { - if (pluggedIn) { - mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG; - } else { - mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG; + synchronized (this) { + if (pluggedIn) { + mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG; + } else { + mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG; + } } } @@ -1109,10 +1145,12 @@ public class BatteryStatsHistory { * Notes the current battery charging state to be reflected in the next written history item. */ public void setChargingState(boolean charging) { - if (charging) { - mHistoryCur.states2 |= HistoryItem.STATE2_CHARGING_FLAG; - } else { - mHistoryCur.states2 &= ~HistoryItem.STATE2_CHARGING_FLAG; + synchronized (this) { + if (charging) { + mHistoryCur.states2 |= HistoryItem.STATE2_CHARGING_FLAG; + } else { + mHistoryCur.states2 &= ~HistoryItem.STATE2_CHARGING_FLAG; + } } } @@ -1121,38 +1159,44 @@ public class BatteryStatsHistory { */ public void recordEvent(long elapsedRealtimeMs, long uptimeMs, int code, String name, int uid) { - mHistoryCur.eventCode = code; - mHistoryCur.eventTag = mHistoryCur.localEventTag; - mHistoryCur.eventTag.string = name; - mHistoryCur.eventTag.uid = uid; - writeHistoryItem(elapsedRealtimeMs, uptimeMs); + synchronized (this) { + mHistoryCur.eventCode = code; + mHistoryCur.eventTag = mHistoryCur.localEventTag; + mHistoryCur.eventTag.string = name; + mHistoryCur.eventTag.uid = uid; + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } } /** * Records a time change event. */ public void recordCurrentTimeChange(long elapsedRealtimeMs, long uptimeMs, long currentTimeMs) { - if (!mRecordingHistory) { - return; - } + synchronized (this) { + if (!mRecordingHistory) { + return; + } - mHistoryCur.currentTime = currentTimeMs; - writeHistoryItem(elapsedRealtimeMs, uptimeMs, mHistoryCur, - HistoryItem.CMD_CURRENT_TIME); - mHistoryCur.currentTime = 0; + mHistoryCur.currentTime = currentTimeMs; + writeHistoryItem(elapsedRealtimeMs, uptimeMs, mHistoryCur, + HistoryItem.CMD_CURRENT_TIME); + mHistoryCur.currentTime = 0; + } } /** * Records a system shutdown event. */ public void recordShutdownEvent(long elapsedRealtimeMs, long uptimeMs, long currentTimeMs) { - if (!mRecordingHistory) { - return; - } + synchronized (this) { + if (!mRecordingHistory) { + return; + } - mHistoryCur.currentTime = currentTimeMs; - writeHistoryItem(elapsedRealtimeMs, uptimeMs, mHistoryCur, HistoryItem.CMD_SHUTDOWN); - mHistoryCur.currentTime = 0; + mHistoryCur.currentTime = currentTimeMs; + writeHistoryItem(elapsedRealtimeMs, uptimeMs, mHistoryCur, HistoryItem.CMD_SHUTDOWN); + mHistoryCur.currentTime = 0; + } } /** @@ -1160,13 +1204,15 @@ public class BatteryStatsHistory { */ public void recordBatteryState(long elapsedRealtimeMs, long uptimeMs, int batteryLevel, boolean isPlugged) { - mHistoryCur.batteryLevel = (byte) batteryLevel; - setPluggedInState(isPlugged); - if (DEBUG) { - Slog.v(TAG, "Battery unplugged to: " - + Integer.toHexString(mHistoryCur.states)); + synchronized (this) { + mHistoryCur.batteryLevel = (byte) batteryLevel; + setPluggedInState(isPlugged); + if (DEBUG) { + Slog.v(TAG, "Battery unplugged to: " + + Integer.toHexString(mHistoryCur.states)); + } + writeHistoryItem(elapsedRealtimeMs, uptimeMs); } - writeHistoryItem(elapsedRealtimeMs, uptimeMs); } /** @@ -1174,9 +1220,11 @@ public class BatteryStatsHistory { */ public void recordPowerStats(long elapsedRealtimeMs, long uptimeMs, PowerStats powerStats) { - mHistoryCur.powerStats = powerStats; - mHistoryCur.states2 |= HistoryItem.STATE2_EXTENSIONS_FLAG; - writeHistoryItem(elapsedRealtimeMs, uptimeMs); + synchronized (this) { + mHistoryCur.powerStats = powerStats; + mHistoryCur.states2 |= HistoryItem.STATE2_EXTENSIONS_FLAG; + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } } /** @@ -1184,11 +1232,13 @@ public class BatteryStatsHistory { */ public void recordProcessStateChange(long elapsedRealtimeMs, long uptimeMs, int uid, @BatteryConsumer.ProcessState int processState) { - mHistoryCur.processStateChange = mHistoryCur.localProcessStateChange; - mHistoryCur.processStateChange.uid = uid; - mHistoryCur.processStateChange.processState = processState; - mHistoryCur.states2 |= HistoryItem.STATE2_EXTENSIONS_FLAG; - writeHistoryItem(elapsedRealtimeMs, uptimeMs); + synchronized (this) { + mHistoryCur.processStateChange = mHistoryCur.localProcessStateChange; + mHistoryCur.processStateChange.uid = uid; + mHistoryCur.processStateChange.processState = processState; + mHistoryCur.states2 |= HistoryItem.STATE2_EXTENSIONS_FLAG; + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } } /** @@ -1197,8 +1247,10 @@ public class BatteryStatsHistory { */ public void recordWifiConsumedCharge(long elapsedRealtimeMs, long uptimeMs, double monitoredRailChargeMah) { - mHistoryCur.wifiRailChargeMah += monitoredRailChargeMah; - writeHistoryItem(elapsedRealtimeMs, uptimeMs); + synchronized (this) { + mHistoryCur.wifiRailChargeMah += monitoredRailChargeMah; + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } } /** @@ -1206,10 +1258,12 @@ public class BatteryStatsHistory { */ public void recordWakelockStartEvent(long elapsedRealtimeMs, long uptimeMs, String historyName, int uid) { - mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag; - mHistoryCur.wakelockTag.string = historyName; - mHistoryCur.wakelockTag.uid = uid; - recordStateStartEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.STATE_WAKE_LOCK_FLAG); + synchronized (this) { + mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag; + mHistoryCur.wakelockTag.string = historyName; + mHistoryCur.wakelockTag.uid = uid; + recordStateStartEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.STATE_WAKE_LOCK_FLAG); + } } /** @@ -1217,18 +1271,20 @@ public class BatteryStatsHistory { */ public boolean maybeUpdateWakelockTag(long elapsedRealtimeMs, long uptimeMs, String historyName, int uid) { - if (mHistoryLastWritten.cmd != HistoryItem.CMD_UPDATE) { - return false; - } - if (mHistoryLastWritten.wakelockTag != null) { - // We'll try to update the last tag. - mHistoryLastWritten.wakelockTag = null; - mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag; - mHistoryCur.wakelockTag.string = historyName; - mHistoryCur.wakelockTag.uid = uid; - writeHistoryItem(elapsedRealtimeMs, uptimeMs); + synchronized (this) { + if (mHistoryLastWritten.cmd != HistoryItem.CMD_UPDATE) { + return false; + } + if (mHistoryLastWritten.wakelockTag != null) { + // We'll try to update the last tag. + mHistoryLastWritten.wakelockTag = null; + mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag; + mHistoryCur.wakelockTag.string = historyName; + mHistoryCur.wakelockTag.uid = uid; + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } + return true; } - return true; } /** @@ -1236,26 +1292,32 @@ public class BatteryStatsHistory { */ public void recordWakelockStopEvent(long elapsedRealtimeMs, long uptimeMs, String historyName, int uid) { - mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag; - mHistoryCur.wakelockTag.string = historyName != null ? historyName : ""; - mHistoryCur.wakelockTag.uid = uid; - recordStateStopEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.STATE_WAKE_LOCK_FLAG); + synchronized (this) { + mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag; + mHistoryCur.wakelockTag.string = historyName != null ? historyName : ""; + mHistoryCur.wakelockTag.uid = uid; + recordStateStopEvent(elapsedRealtimeMs, uptimeMs, HistoryItem.STATE_WAKE_LOCK_FLAG); + } } /** * Records an event when some state flag changes to true. */ public void recordStateStartEvent(long elapsedRealtimeMs, long uptimeMs, int stateFlags) { - mHistoryCur.states |= stateFlags; - writeHistoryItem(elapsedRealtimeMs, uptimeMs); + synchronized (this) { + mHistoryCur.states |= stateFlags; + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } } /** * Records an event when some state flag changes to false. */ public void recordStateStopEvent(long elapsedRealtimeMs, long uptimeMs, int stateFlags) { - mHistoryCur.states &= ~stateFlags; - writeHistoryItem(elapsedRealtimeMs, uptimeMs); + synchronized (this) { + mHistoryCur.states &= ~stateFlags; + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } } /** @@ -1263,34 +1325,42 @@ public class BatteryStatsHistory { */ public void recordStateChangeEvent(long elapsedRealtimeMs, long uptimeMs, int stateStartFlags, int stateStopFlags) { - mHistoryCur.states = (mHistoryCur.states | stateStartFlags) & ~stateStopFlags; - writeHistoryItem(elapsedRealtimeMs, uptimeMs); + synchronized (this) { + mHistoryCur.states = (mHistoryCur.states | stateStartFlags) & ~stateStopFlags; + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } } /** * Records an event when some state2 flag changes to true. */ public void recordState2StartEvent(long elapsedRealtimeMs, long uptimeMs, int stateFlags) { - mHistoryCur.states2 |= stateFlags; - writeHistoryItem(elapsedRealtimeMs, uptimeMs); + synchronized (this) { + mHistoryCur.states2 |= stateFlags; + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } } /** * Records an event when some state2 flag changes to false. */ public void recordState2StopEvent(long elapsedRealtimeMs, long uptimeMs, int stateFlags) { - mHistoryCur.states2 &= ~stateFlags; - writeHistoryItem(elapsedRealtimeMs, uptimeMs); + synchronized (this) { + mHistoryCur.states2 &= ~stateFlags; + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } } /** * Records an wakeup event. */ public void recordWakeupEvent(long elapsedRealtimeMs, long uptimeMs, String reason) { - mHistoryCur.wakeReasonTag = mHistoryCur.localWakeReasonTag; - mHistoryCur.wakeReasonTag.string = reason; - mHistoryCur.wakeReasonTag.uid = 0; - writeHistoryItem(elapsedRealtimeMs, uptimeMs); + synchronized (this) { + mHistoryCur.wakeReasonTag = mHistoryCur.localWakeReasonTag; + mHistoryCur.wakeReasonTag.string = reason; + mHistoryCur.wakeReasonTag.uid = 0; + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } } /** @@ -1298,10 +1368,12 @@ public class BatteryStatsHistory { */ public void recordScreenBrightnessEvent(long elapsedRealtimeMs, long uptimeMs, int brightnessBin) { - mHistoryCur.states = setBitField(mHistoryCur.states, brightnessBin, - HistoryItem.STATE_BRIGHTNESS_SHIFT, - HistoryItem.STATE_BRIGHTNESS_MASK); - writeHistoryItem(elapsedRealtimeMs, uptimeMs); + synchronized (this) { + mHistoryCur.states = setBitField(mHistoryCur.states, brightnessBin, + HistoryItem.STATE_BRIGHTNESS_SHIFT, + HistoryItem.STATE_BRIGHTNESS_MASK); + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } } /** @@ -1309,20 +1381,24 @@ public class BatteryStatsHistory { */ public void recordGpsSignalQualityEvent(long elapsedRealtimeMs, long uptimeMs, int signalLevel) { - mHistoryCur.states2 = setBitField(mHistoryCur.states2, signalLevel, - HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT, - HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK); - writeHistoryItem(elapsedRealtimeMs, uptimeMs); + synchronized (this) { + mHistoryCur.states2 = setBitField(mHistoryCur.states2, signalLevel, + HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT, + HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK); + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } } /** * Records a device idle mode change event. */ public void recordDeviceIdleEvent(long elapsedRealtimeMs, long uptimeMs, int mode) { - mHistoryCur.states2 = setBitField(mHistoryCur.states2, mode, - HistoryItem.STATE2_DEVICE_IDLE_SHIFT, - HistoryItem.STATE2_DEVICE_IDLE_MASK); - writeHistoryItem(elapsedRealtimeMs, uptimeMs); + synchronized (this) { + mHistoryCur.states2 = setBitField(mHistoryCur.states2, mode, + HistoryItem.STATE2_DEVICE_IDLE_SHIFT, + HistoryItem.STATE2_DEVICE_IDLE_MASK); + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } } /** @@ -1330,20 +1406,22 @@ public class BatteryStatsHistory { */ public void recordPhoneStateChangeEvent(long elapsedRealtimeMs, long uptimeMs, int addStateFlag, int removeStateFlag, int state, int signalStrength) { - mHistoryCur.states = (mHistoryCur.states | addStateFlag) & ~removeStateFlag; - if (state != -1) { - mHistoryCur.states = - setBitField(mHistoryCur.states, state, - HistoryItem.STATE_PHONE_STATE_SHIFT, - HistoryItem.STATE_PHONE_STATE_MASK); - } - if (signalStrength != -1) { - mHistoryCur.states = - setBitField(mHistoryCur.states, signalStrength, - HistoryItem.STATE_PHONE_SIGNAL_STRENGTH_SHIFT, - HistoryItem.STATE_PHONE_SIGNAL_STRENGTH_MASK); + synchronized (this) { + mHistoryCur.states = (mHistoryCur.states | addStateFlag) & ~removeStateFlag; + if (state != -1) { + mHistoryCur.states = + setBitField(mHistoryCur.states, state, + HistoryItem.STATE_PHONE_STATE_SHIFT, + HistoryItem.STATE_PHONE_STATE_MASK); + } + if (signalStrength != -1) { + mHistoryCur.states = + setBitField(mHistoryCur.states, signalStrength, + HistoryItem.STATE_PHONE_SIGNAL_STRENGTH_SHIFT, + HistoryItem.STATE_PHONE_SIGNAL_STRENGTH_MASK); + } + writeHistoryItem(elapsedRealtimeMs, uptimeMs); } - writeHistoryItem(elapsedRealtimeMs, uptimeMs); } /** @@ -1351,10 +1429,12 @@ public class BatteryStatsHistory { */ public void recordDataConnectionTypeChangeEvent(long elapsedRealtimeMs, long uptimeMs, int dataConnectionType) { - mHistoryCur.states = setBitField(mHistoryCur.states, dataConnectionType, - HistoryItem.STATE_DATA_CONNECTION_SHIFT, - HistoryItem.STATE_DATA_CONNECTION_MASK); - writeHistoryItem(elapsedRealtimeMs, uptimeMs); + synchronized (this) { + mHistoryCur.states = setBitField(mHistoryCur.states, dataConnectionType, + HistoryItem.STATE_DATA_CONNECTION_SHIFT, + HistoryItem.STATE_DATA_CONNECTION_MASK); + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } } /** @@ -1362,10 +1442,12 @@ public class BatteryStatsHistory { */ public void recordNrStateChangeEvent(long elapsedRealtimeMs, long uptimeMs, int nrState) { - mHistoryCur.states2 = setBitField(mHistoryCur.states2, nrState, - HistoryItem.STATE2_NR_STATE_SHIFT, - HistoryItem.STATE2_NR_STATE_MASK); - writeHistoryItem(elapsedRealtimeMs, uptimeMs); + synchronized (this) { + mHistoryCur.states2 = setBitField(mHistoryCur.states2, nrState, + HistoryItem.STATE2_NR_STATE_SHIFT, + HistoryItem.STATE2_NR_STATE_MASK); + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } } /** @@ -1373,11 +1455,13 @@ public class BatteryStatsHistory { */ public void recordWifiSupplicantStateChangeEvent(long elapsedRealtimeMs, long uptimeMs, int supplState) { - mHistoryCur.states2 = - setBitField(mHistoryCur.states2, supplState, - HistoryItem.STATE2_WIFI_SUPPL_STATE_SHIFT, - HistoryItem.STATE2_WIFI_SUPPL_STATE_MASK); - writeHistoryItem(elapsedRealtimeMs, uptimeMs); + synchronized (this) { + mHistoryCur.states2 = + setBitField(mHistoryCur.states2, supplState, + HistoryItem.STATE2_WIFI_SUPPL_STATE_SHIFT, + HistoryItem.STATE2_WIFI_SUPPL_STATE_MASK); + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } } /** @@ -1385,11 +1469,13 @@ public class BatteryStatsHistory { */ public void recordWifiSignalStrengthChangeEvent(long elapsedRealtimeMs, long uptimeMs, int strengthBin) { - mHistoryCur.states2 = - setBitField(mHistoryCur.states2, strengthBin, - HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_SHIFT, - HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_MASK); - writeHistoryItem(elapsedRealtimeMs, uptimeMs); + synchronized (this) { + mHistoryCur.states2 = + setBitField(mHistoryCur.states2, strengthBin, + HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_SHIFT, + HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_MASK); + writeHistoryItem(elapsedRealtimeMs, uptimeMs); + } } /** @@ -1446,25 +1532,30 @@ public class BatteryStatsHistory { * Writes the current history item to history. */ public void writeHistoryItem(long elapsedRealtimeMs, long uptimeMs) { - if (mTrackRunningHistoryElapsedRealtimeMs != 0) { - final long diffElapsedMs = elapsedRealtimeMs - mTrackRunningHistoryElapsedRealtimeMs; - final long diffUptimeMs = uptimeMs - mTrackRunningHistoryUptimeMs; - if (diffUptimeMs < (diffElapsedMs - 20)) { - final long wakeElapsedTimeMs = elapsedRealtimeMs - (diffElapsedMs - diffUptimeMs); - mHistoryAddTmp.setTo(mHistoryLastWritten); - mHistoryAddTmp.wakelockTag = null; - mHistoryAddTmp.wakeReasonTag = null; - mHistoryAddTmp.eventCode = HistoryItem.EVENT_NONE; - mHistoryAddTmp.states &= ~HistoryItem.STATE_CPU_RUNNING_FLAG; - writeHistoryItem(wakeElapsedTimeMs, uptimeMs, mHistoryAddTmp); + synchronized (this) { + if (mTrackRunningHistoryElapsedRealtimeMs != 0) { + final long diffElapsedMs = + elapsedRealtimeMs - mTrackRunningHistoryElapsedRealtimeMs; + final long diffUptimeMs = uptimeMs - mTrackRunningHistoryUptimeMs; + if (diffUptimeMs < (diffElapsedMs - 20)) { + final long wakeElapsedTimeMs = + elapsedRealtimeMs - (diffElapsedMs - diffUptimeMs); + mHistoryAddTmp.setTo(mHistoryLastWritten); + mHistoryAddTmp.wakelockTag = null; + mHistoryAddTmp.wakeReasonTag = null; + mHistoryAddTmp.eventCode = HistoryItem.EVENT_NONE; + mHistoryAddTmp.states &= ~HistoryItem.STATE_CPU_RUNNING_FLAG; + writeHistoryItem(wakeElapsedTimeMs, uptimeMs, mHistoryAddTmp); + } } + mHistoryCur.states |= HistoryItem.STATE_CPU_RUNNING_FLAG; + mTrackRunningHistoryElapsedRealtimeMs = elapsedRealtimeMs; + mTrackRunningHistoryUptimeMs = uptimeMs; + writeHistoryItem(elapsedRealtimeMs, uptimeMs, mHistoryCur); } - mHistoryCur.states |= HistoryItem.STATE_CPU_RUNNING_FLAG; - mTrackRunningHistoryElapsedRealtimeMs = elapsedRealtimeMs; - mTrackRunningHistoryUptimeMs = uptimeMs; - writeHistoryItem(elapsedRealtimeMs, uptimeMs, mHistoryCur); } + @GuardedBy("this") private void writeHistoryItem(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) { if (mTracer != null && mTracer.tracingEnabled()) { recordTraceEvents(cur.eventCode, cur.eventTag); @@ -1591,6 +1682,7 @@ public class BatteryStatsHistory { writeHistoryItem(elapsedRealtimeMs, uptimeMs, cur, HistoryItem.CMD_UPDATE); } + @GuardedBy("this") private void writeHistoryItem(long elapsedRealtimeMs, @SuppressWarnings("UnusedVariable") long uptimeMs, HistoryItem cur, byte cmd) { if (!mMutable) { @@ -1701,7 +1793,8 @@ public class BatteryStatsHistory { /** * Writes the delta between the previous and current history items into history buffer. */ - public void writeHistoryDelta(Parcel dest, HistoryItem cur, HistoryItem last) { + @GuardedBy("this") + private void writeHistoryDelta(Parcel dest, HistoryItem cur, HistoryItem last) { if (last == null || cur.cmd != HistoryItem.CMD_UPDATE) { dest.writeInt(BatteryStatsHistory.DELTA_TIME_ABS); cur.writeToParcel(dest, 0); @@ -1921,6 +2014,7 @@ public class BatteryStatsHistory { * while writing the current history buffer, the method returns * <code>(index | TAG_FIRST_OCCURRENCE_FLAG)</code> */ + @GuardedBy("this") private int writeHistoryTag(HistoryTag tag) { if (tag.string == null) { Slog.wtfStack(TAG, "writeHistoryTag called with null name"); @@ -1964,33 +2058,37 @@ public class BatteryStatsHistory { * Don't allow any more batching in to the current history event. */ public void commitCurrentHistoryBatchLocked() { - mHistoryLastWritten.cmd = HistoryItem.CMD_NULL; + synchronized (this) { + mHistoryLastWritten.cmd = HistoryItem.CMD_NULL; + } } /** * Saves the accumulated history buffer in the active file, see {@link #getActiveFile()} . */ public void writeHistory() { - if (isReadOnly()) { - Slog.w(TAG, "writeHistory: this instance instance is read-only"); - return; - } + synchronized (this) { + if (isReadOnly()) { + Slog.w(TAG, "writeHistory: this instance instance is read-only"); + return; + } - // Save the monotonic time first, so that even if the history write below fails, - // we still wouldn't end up with overlapping history timelines. - mMonotonicClock.write(); + // Save the monotonic time first, so that even if the history write below fails, + // we still wouldn't end up with overlapping history timelines. + mMonotonicClock.write(); - Parcel p = Parcel.obtain(); - try { - final long start = SystemClock.uptimeMillis(); - writeHistoryBuffer(p); - if (DEBUG) { - Slog.d(TAG, "writeHistoryBuffer duration ms:" - + (SystemClock.uptimeMillis() - start) + " bytes:" + p.dataSize()); + Parcel p = Parcel.obtain(); + try { + final long start = SystemClock.uptimeMillis(); + writeHistoryBuffer(p); + if (DEBUG) { + Slog.d(TAG, "writeHistoryBuffer duration ms:" + + (SystemClock.uptimeMillis() - start) + " bytes:" + p.dataSize()); + } + writeParcelToFileLocked(p, mActiveFile); + } finally { + p.recycle(); } - writeParcelToFileLocked(p, mActiveFile); - } finally { - p.recycle(); } } @@ -1998,35 +2096,38 @@ public class BatteryStatsHistory { * Reads history buffer from a persisted Parcel. */ public void readHistoryBuffer(Parcel in) throws ParcelFormatException { - final int version = in.readInt(); - if (version != BatteryStatsHistory.VERSION) { - Slog.w("BatteryStats", "readHistoryBuffer: version got " + version - + ", expected " + BatteryStatsHistory.VERSION + "; erasing old stats"); - return; - } - - mHistoryBufferStartTime = in.readLong(); - mHistoryBuffer.setDataSize(0); - mHistoryBuffer.setDataPosition(0); + synchronized (this) { + final int version = in.readInt(); + if (version != BatteryStatsHistory.VERSION) { + Slog.w("BatteryStats", "readHistoryBuffer: version got " + version + + ", expected " + BatteryStatsHistory.VERSION + "; erasing old stats"); + return; + } - int bufSize = in.readInt(); - int curPos = in.dataPosition(); - if (bufSize >= (mMaxHistoryBufferSize * 100)) { - throw new ParcelFormatException( - "File corrupt: history data buffer too large " + bufSize); - } else if ((bufSize & ~3) != bufSize) { - throw new ParcelFormatException( - "File corrupt: history data buffer not aligned " + bufSize); - } else { - if (DEBUG) { - Slog.i(TAG, "***************** READING NEW HISTORY: " + bufSize - + " bytes at " + curPos); + mHistoryBufferStartTime = in.readLong(); + mHistoryBuffer.setDataSize(0); + mHistoryBuffer.setDataPosition(0); + + int bufSize = in.readInt(); + int curPos = in.dataPosition(); + if (bufSize >= (mMaxHistoryBufferSize * 100)) { + throw new ParcelFormatException( + "File corrupt: history data buffer too large " + bufSize); + } else if ((bufSize & ~3) != bufSize) { + throw new ParcelFormatException( + "File corrupt: history data buffer not aligned " + bufSize); + } else { + if (DEBUG) { + Slog.i(TAG, "***************** READING NEW HISTORY: " + bufSize + + " bytes at " + curPos); + } + mHistoryBuffer.appendFrom(in, curPos, bufSize); + in.setDataPosition(curPos + bufSize); } - mHistoryBuffer.appendFrom(in, curPos, bufSize); - in.setDataPosition(curPos + bufSize); } } + @GuardedBy("this") private void writeHistoryBuffer(Parcel out) { out.writeInt(BatteryStatsHistory.VERSION); out.writeLong(mHistoryBufferStartTime); @@ -2038,6 +2139,7 @@ public class BatteryStatsHistory { out.appendFrom(mHistoryBuffer, 0, mHistoryBuffer.dataSize()); } + @GuardedBy("this") private void writeParcelToFileLocked(Parcel p, AtomicFile file) { FileOutputStream fos = null; mWriteLock.lock(); @@ -2066,34 +2168,43 @@ public class BatteryStatsHistory { * Returns the total number of history tags in the tag pool. */ public int getHistoryStringPoolSize() { - return mHistoryTagPool.size(); + synchronized (this) { + return mHistoryTagPool.size(); + } } /** * Returns the total number of bytes occupied by the history tag pool. */ public int getHistoryStringPoolBytes() { - return mNumHistoryTagChars; + synchronized (this) { + return mNumHistoryTagChars; + } } /** * Returns the string held by the requested history tag. */ public String getHistoryTagPoolString(int index) { - ensureHistoryTagArray(); - HistoryTag historyTag = mHistoryTags.get(index); - return historyTag != null ? historyTag.string : null; + synchronized (this) { + ensureHistoryTagArray(); + HistoryTag historyTag = mHistoryTags.get(index); + return historyTag != null ? historyTag.string : null; + } } /** * Returns the UID held by the requested history tag. */ public int getHistoryTagPoolUid(int index) { - ensureHistoryTagArray(); - HistoryTag historyTag = mHistoryTags.get(index); - return historyTag != null ? historyTag.uid : Process.INVALID_UID; + synchronized (this) { + ensureHistoryTagArray(); + HistoryTag historyTag = mHistoryTags.get(index); + return historyTag != null ? historyTag.uid : Process.INVALID_UID; + } } + @GuardedBy("this") private void ensureHistoryTagArray() { if (mHistoryTags != null) { return; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index c4b5d8187845..0171f584a838 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3751,6 +3751,13 @@ <permission android:name="android.permission.MANAGE_DEVICE_POLICY_DEVICE_IDENTIFIERS" android:protectionLevel="internal|role" /> + <!-- Allows an application to manage policy related to content protection. + <p>Protection level: internal|role + @FlaggedApi("android.view.contentprotection.flags.manage_device_policy_enabled") + --> + <permission android:name="android.permission.MANAGE_DEVICE_POLICY_CONTENT_PROTECTION" + android:protectionLevel="internal|role" /> + <!-- Allows an application to set device policies outside the current user that are critical for securing data within the current user. <p>Holding this permission allows the use of other held MANAGE_DEVICE_POLICY_* diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 23c78fdf108e..ebb0d344d7eb 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -6908,4 +6908,7 @@ <!-- Defines the minimum interval (in ms) between two input-based user-activity poke events. --> <integer name="config_minMillisBetweenInputUserActivityEvents">100</integer> + + <!-- Name of the starting activity for DisplayCompat host. specific to automotive.--> + <string name="config_defaultDisplayCompatHostActivity" translatable="false"></string> </resources> diff --git a/core/tests/coretests/src/android/os/BundleTest.java b/core/tests/coretests/src/android/os/BundleTest.java index e7b5dff60110..93c2e0e40593 100644 --- a/core/tests/coretests/src/android/os/BundleTest.java +++ b/core/tests/coretests/src/android/os/BundleTest.java @@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; @@ -121,6 +122,14 @@ public class BundleTest { } @Test + public void testEmpty() throws Exception { + assertNotNull(Bundle.EMPTY); + assertEquals(0, Bundle.EMPTY.size()); + + new Bundle(Bundle.EMPTY); + } + + @Test @IgnoreUnderRavenwood(blockedBy = ParcelFileDescriptor.class) public void testCreateFromParcel() throws Exception { boolean withFd; diff --git a/core/tests/coretests/src/android/os/TestLooperManagerTest.java b/core/tests/coretests/src/android/os/TestLooperManagerTest.java new file mode 100644 index 000000000000..5959444e49cc --- /dev/null +++ b/core/tests/coretests/src/android/os/TestLooperManagerTest.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import android.platform.test.ravenwood.RavenwoodRule; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +@RunWith(AndroidJUnit4.class) +public class TestLooperManagerTest { + private static final String TAG = "TestLooperManagerTest"; + + @Rule + public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder() + .setProvideMainThread(true) + .build(); + + @Test + public void testMainThread() throws Exception { + doTest(Looper.getMainLooper()); + } + + @Test + public void testCustomThread() throws Exception { + final HandlerThread thread = new HandlerThread(TAG); + thread.start(); + doTest(thread.getLooper()); + } + + private void doTest(Looper looper) throws Exception { + final TestLooperManager tlm = + InstrumentationRegistry.getInstrumentation().acquireLooperManager(looper); + + final Handler handler = new Handler(looper); + final CountDownLatch latch = new CountDownLatch(1); + + assertFalse(tlm.hasMessages(handler, null, 42)); + + handler.sendEmptyMessage(42); + handler.post(() -> { + latch.countDown(); + }); + assertTrue(tlm.hasMessages(handler, null, 42)); + assertFalse(latch.await(100, TimeUnit.MILLISECONDS)); + + final Message first = tlm.next(); + assertEquals(42, first.what); + assertNull(first.callback); + tlm.execute(first); + assertFalse(tlm.hasMessages(handler, null, 42)); + assertFalse(latch.await(100, TimeUnit.MILLISECONDS)); + tlm.recycle(first); + + final Message second = tlm.next(); + assertNotNull(second.callback); + tlm.execute(second); + assertFalse(tlm.hasMessages(handler, null, 42)); + assertTrue(latch.await(100, TimeUnit.MILLISECONDS)); + tlm.recycle(second); + + tlm.release(); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java index da1ca8d57940..6250fc5820aa 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeVisualIndicator.java @@ -17,6 +17,8 @@ package com.android.wm.shell.desktopmode; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; +import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; +import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler.FINAL_FREEFORM_SCALE; @@ -86,7 +88,6 @@ public class DesktopModeVisualIndicator { mTaskSurface = taskSurface; mRootTdaOrganizer = taskDisplayAreaOrganizer; mCurrentType = IndicatorType.NO_INDICATOR; - createView(); } /** @@ -127,34 +128,15 @@ public class DesktopModeVisualIndicator { mView = new View(mContext); final SurfaceControl.Builder builder = new SurfaceControl.Builder(); mRootTdaOrganizer.attachToDisplayArea(mTaskInfo.displayId, builder); - String description; - switch (mCurrentType) { - case TO_DESKTOP_INDICATOR: - description = "Desktop indicator"; - break; - case TO_FULLSCREEN_INDICATOR: - description = "Fullscreen indicator"; - break; - case TO_SPLIT_LEFT_INDICATOR: - description = "Split Left indicator"; - break; - case TO_SPLIT_RIGHT_INDICATOR: - description = "Split Right indicator"; - break; - default: - description = "Invalid indicator"; - break; - } mLeash = builder - .setName(description) + .setName("Desktop Mode Visual Indicator") .setContainerLayer() .build(); t.show(mLeash); final WindowManager.LayoutParams lp = - new WindowManager.LayoutParams(screenWidth, screenHeight, - WindowManager.LayoutParams.TYPE_APPLICATION, - WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT); - lp.setTitle(description + " for Task=" + mTaskInfo.taskId); + new WindowManager.LayoutParams(screenWidth, screenHeight, TYPE_APPLICATION, + FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT); + lp.setTitle("Desktop Mode Visual Indicator"); lp.setTrustedOverlay(); final WindowlessWindowManager windowManager = new WindowlessWindowManager( mTaskInfo.configuration, mLeash, @@ -201,6 +183,9 @@ public class DesktopModeVisualIndicator { */ private void transitionIndicator(IndicatorType newType) { if (mCurrentType == newType) return; + if (mView == null) { + createView(); + } if (mCurrentType == IndicatorType.NO_INDICATOR) { fadeInIndicator(newType); } else if (newType == IndicatorType.NO_INDICATOR) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index 4f5c39a60120..28c06a46c516 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -919,22 +919,27 @@ class DesktopTasksController( } if (taskBounds.top <= transitionAreaHeight) { moveToFullscreenWithAnimation(taskInfo, position) + return } if (inputCoordinate.x <= transitionAreaWidth) { releaseVisualIndicator() - var wct = WindowContainerTransaction() + val wct = WindowContainerTransaction() addMoveToSplitChanges(wct, taskInfo) splitScreenController.requestEnterSplitSelect(taskInfo, wct, SPLIT_POSITION_TOP_OR_LEFT, taskBounds) + return } if (inputCoordinate.x >= (displayController.getDisplayLayout(taskInfo.displayId)?.width() ?.minus(transitionAreaWidth) ?: return)) { releaseVisualIndicator() - var wct = WindowContainerTransaction() + val wct = WindowContainerTransaction() addMoveToSplitChanges(wct, taskInfo) splitScreenController.requestEnterSplitSelect(taskInfo, wct, SPLIT_POSITION_BOTTOM_OR_RIGHT, taskBounds) + return } + // A freeform drag-move ended, remove the indicator immediately. + releaseVisualIndicator() } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java index e6faa6391cca..96eaa1edbae4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java @@ -287,7 +287,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL } boolean isHandlingDragResize() { - return mDragResizeListener.isHandlingDragResize(); + return mDragResizeListener != null && mDragResizeListener.isHandlingDragResize(); } private void closeDragResizeListener() { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index 4ba05ce8aef1..1f7cc5a18019 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -345,7 +345,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mTaskOperations.injectBackKey(); } else if (id == R.id.caption_handle || id == R.id.open_menu_button) { if (!decoration.isHandleMenuActive()) { - moveTaskToFront(mTaskOrganizer.getRunningTaskInfo(mTaskId)); + moveTaskToFront(decoration.mTaskInfo); decoration.createHandleMenu(); } else { decoration.closeHandleMenu(); @@ -419,10 +419,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { && id != R.id.maximize_window) { return false; } - moveTaskToFront(mTaskOrganizer.getRunningTaskInfo(mTaskId)); + final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId); + moveTaskToFront(decoration.mTaskInfo); if (!mHasLongClicked && id != R.id.maximize_window) { - final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId); decoration.closeMaximizeMenuIfNeeded(e); } @@ -466,7 +466,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { */ @Override public boolean handleMotionEvent(@Nullable View v, MotionEvent e) { - final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId); + final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId); + final RunningTaskInfo taskInfo = decoration.mTaskInfo; if (DesktopModeStatus.isEnabled() && taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) { return false; @@ -492,8 +493,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { } case MotionEvent.ACTION_MOVE: { mShouldClick = false; - final DesktopModeWindowDecoration decoration = - mWindowDecorByTaskId.get(mTaskId); // If a decor's resize drag zone is active, don't also try to reposition it. if (decoration.isHandlingDragResize()) break; decoration.closeMaximizeMenu(); @@ -557,9 +556,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { && action != MotionEvent.ACTION_CANCEL)) { return false; } - final RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId); - mDesktopTasksController.ifPresent(c -> c.toggleDesktopTaskSize(taskInfo, - mWindowDecorByTaskId.get(taskInfo.taskId))); + mDesktopTasksController.ifPresent(c -> { + final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId); + c.toggleDesktopTaskSize(decoration.mTaskInfo, decoration); + }); return true; } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java index 71e1582d2937..3f0a28118597 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java @@ -387,7 +387,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin } boolean isHandlingDragResize() { - return mDragResizeListener.isHandlingDragResize(); + return mDragResizeListener != null && mDragResizeListener.isHandlingDragResize(); } private void loadAppInfo() { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java index 8b38f991a2db..d902621180d8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DragResizeInputListener.java @@ -353,6 +353,7 @@ class DragResizeInputListener implements AutoCloseable { private boolean mShouldHandleEvents; private int mLastCursorType = PointerIcon.TYPE_DEFAULT; private Rect mDragStartTaskBounds; + private final Rect mTmpRect = new Rect(); private TaskResizeInputEventReceiver( InputChannel inputChannel, Handler handler, Choreographer choreographer) { @@ -477,14 +478,15 @@ class DragResizeInputListener implements AutoCloseable { } private void updateInputSinkRegionForDrag(Rect taskBounds) { + mTmpRect.set(taskBounds); final DisplayLayout layout = mDisplayController.getDisplayLayout(mDisplayId); final Region dragTouchRegion = new Region(-taskBounds.left, -taskBounds.top, -taskBounds.left + layout.width(), -taskBounds.top + layout.height()); // Remove the localized task bounds from the touch region. - taskBounds.offsetTo(0, 0); - dragTouchRegion.op(taskBounds, Region.Op.DIFFERENCE); + mTmpRect.offsetTo(0, 0); + dragTouchRegion.op(mTmpRect, Region.Op.DIFFERENCE); updateSinkInputChannel(dragTouchRegion); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java index 368231e2d7f0..b0d3b5090ef0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java @@ -125,6 +125,7 @@ public class ResizeVeil { relayout(taskBounds, t); if (fadeIn) { + cancelAnimation(); mVeilAnimator = new ValueAnimator(); mVeilAnimator.setFloatValues(0f, 1f); mVeilAnimator.setDuration(RESIZE_ALPHA_DURATION); @@ -210,15 +211,16 @@ public class ResizeVeil { * Animate veil's alpha to 0, fading it out. */ public void hideVeil() { - final ValueAnimator animator = new ValueAnimator(); - animator.setFloatValues(1, 0); - animator.setDuration(RESIZE_ALPHA_DURATION); - animator.addUpdateListener(animation -> { + cancelAnimation(); + mVeilAnimator = new ValueAnimator(); + mVeilAnimator.setFloatValues(1, 0); + mVeilAnimator.setDuration(RESIZE_ALPHA_DURATION); + mVeilAnimator.addUpdateListener(animation -> { SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get(); - t.setAlpha(mVeilSurface, 1 - animator.getAnimatedFraction()); + t.setAlpha(mVeilSurface, 1 - mVeilAnimator.getAnimatedFraction()); t.apply(); }); - animator.addListener(new AnimatorListenerAdapter() { + mVeilAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get(); @@ -226,7 +228,7 @@ public class ResizeVeil { t.apply(); } }); - animator.start(); + mVeilAnimator.start(); } @ColorRes @@ -240,10 +242,20 @@ public class ResizeVeil { } } + private void cancelAnimation() { + if (mVeilAnimator != null) { + mVeilAnimator.removeAllUpdateListeners(); + mVeilAnimator.cancel(); + } + } + /** * Dispose of veil when it is no longer needed, likely on close of its container decor. */ void dispose() { + cancelAnimation(); + mVeilAnimator = null; + if (mViewHost != null) { mViewHost.release(); mViewHost = null; diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index aa307485c32e..bdfa63010adc 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -65,7 +65,8 @@ import java.util.concurrent.Executor; * * <p>A common case of using MediaRecorder to record audio works as follows: * - * <pre>MediaRecorder recorder = new MediaRecorder(); + * <pre> + * MediaRecorder recorder = new MediaRecorder(context); * recorder.setAudioSource(MediaRecorder.AudioSource.MIC); * recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); * recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); diff --git a/media/java/android/media/projection/IMediaProjection.aidl b/media/java/android/media/projection/IMediaProjection.aidl index 388b2c5ca6fe..2fb0af5557b5 100644 --- a/media/java/android/media/projection/IMediaProjection.aidl +++ b/media/java/android/media/projection/IMediaProjection.aidl @@ -18,6 +18,7 @@ package android.media.projection; import android.media.projection.IMediaProjectionCallback; import android.os.IBinder; +import android.app.ActivityOptions.LaunchCookie; /** {@hide} */ interface IMediaProjection { @@ -38,22 +39,22 @@ interface IMediaProjection { void unregisterCallback(IMediaProjectionCallback callback); /** - * Returns the {@link android.os.IBinder} identifying the task to record, or {@code null} if + * Returns the {@link LaunchCookie} identifying the task to record, or {@code null} if * there is none. */ @EnforcePermission("MANAGE_MEDIA_PROJECTION") @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + ".permission.MANAGE_MEDIA_PROJECTION)") - IBinder getLaunchCookie(); + LaunchCookie getLaunchCookie(); /** - * Updates the {@link android.os.IBinder} identifying the task to record, or {@code null} if + * Updates the {@link LaunchCookie} identifying the task to record, or {@code null} if * there is none. */ @EnforcePermission("MANAGE_MEDIA_PROJECTION") @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + ".permission.MANAGE_MEDIA_PROJECTION)") - void setLaunchCookie(in IBinder launchCookie); + void setLaunchCookie(in LaunchCookie launchCookie); /** * Returns {@code true} if this token is still valid. A token is valid as long as the token diff --git a/media/java/android/media/projection/MediaProjectionInfo.java b/media/java/android/media/projection/MediaProjectionInfo.java index c82039297d6e..cd0763d4d459 100644 --- a/media/java/android/media/projection/MediaProjectionInfo.java +++ b/media/java/android/media/projection/MediaProjectionInfo.java @@ -16,7 +16,7 @@ package android.media.projection; -import android.os.IBinder; +import android.app.ActivityOptions.LaunchCookie; import android.os.Parcel; import android.os.Parcelable; import android.os.UserHandle; @@ -27,9 +27,9 @@ import java.util.Objects; public final class MediaProjectionInfo implements Parcelable { private final String mPackageName; private final UserHandle mUserHandle; - private final IBinder mLaunchCookie; + private final LaunchCookie mLaunchCookie; - public MediaProjectionInfo(String packageName, UserHandle handle, IBinder launchCookie) { + public MediaProjectionInfo(String packageName, UserHandle handle, LaunchCookie launchCookie) { mPackageName = packageName; mUserHandle = handle; mLaunchCookie = launchCookie; @@ -38,7 +38,7 @@ public final class MediaProjectionInfo implements Parcelable { public MediaProjectionInfo(Parcel in) { mPackageName = in.readString(); mUserHandle = UserHandle.readFromParcel(in); - mLaunchCookie = in.readStrongBinder(); + mLaunchCookie = LaunchCookie.readFromParcel(in); } public String getPackageName() { @@ -49,7 +49,7 @@ public final class MediaProjectionInfo implements Parcelable { return mUserHandle; } - public IBinder getLaunchCookie() { + public LaunchCookie getLaunchCookie() { return mLaunchCookie; } @@ -72,7 +72,7 @@ public final class MediaProjectionInfo implements Parcelable { public String toString() { return "MediaProjectionInfo{mPackageName=" + mPackageName + ", mUserHandle=" - + mUserHandle + ", mLaunchCookie" + + mUserHandle + ", mLaunchCookie=" + mLaunchCookie + "}"; } @@ -85,7 +85,7 @@ public final class MediaProjectionInfo implements Parcelable { public void writeToParcel(Parcel out, int flags) { out.writeString(mPackageName); UserHandle.writeToParcel(mUserHandle, out); - out.writeStrongBinder(mLaunchCookie); + LaunchCookie.writeToParcel(mLaunchCookie, out); } public static final @android.annotation.NonNull Parcelable.Creator<MediaProjectionInfo> CREATOR = diff --git a/media/java/android/media/projection/MediaProjectionManager.java b/media/java/android/media/projection/MediaProjectionManager.java index 9790d02025b7..e3290d604794 100644 --- a/media/java/android/media/projection/MediaProjectionManager.java +++ b/media/java/android/media/projection/MediaProjectionManager.java @@ -18,8 +18,11 @@ package android.media.projection; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.annotation.SystemService; +import android.annotation.TestApi; import android.app.Activity; +import android.app.ActivityOptions.LaunchCookie; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -73,6 +76,9 @@ public final class MediaProjectionManager { /** @hide */ public static final String EXTRA_MEDIA_PROJECTION = "android.media.projection.extra.EXTRA_MEDIA_PROJECTION"; + /** @hide */ + public static final String EXTRA_LAUNCH_COOKIE = + "android.media.projection.extra.EXTRA_LAUNCH_COOKIE"; /** @hide */ public static final int TYPE_SCREEN_CAPTURE = 0; @@ -158,17 +164,29 @@ public final class MediaProjectionManager { */ @NonNull public Intent createScreenCaptureIntent(@NonNull MediaProjectionConfig config) { - Intent i = new Intent(); - final ComponentName mediaProjectionPermissionDialogComponent = - ComponentName.unflattenFromString(mContext.getResources() - .getString(com.android.internal.R.string - .config_mediaProjectionPermissionDialogComponent)); - i.setComponent(mediaProjectionPermissionDialogComponent); + Intent i = createScreenCaptureIntent(); i.putExtra(EXTRA_MEDIA_PROJECTION_CONFIG, config); return i; } /** + * Returns an intent similar to {@link #createScreenCaptureIntent()} that will enable screen + * recording of the task with the specified launch cookie. This method should only be used for + * testing. + * + * @param launchCookie the launch cookie corresponding to the task to record. + * @hide + */ + @SuppressLint("UnflaggedApi") + @TestApi + @NonNull + public Intent createScreenCaptureIntent(@Nullable LaunchCookie launchCookie) { + Intent i = createScreenCaptureIntent(); + i.putExtra(EXTRA_LAUNCH_COOKIE, launchCookie); + return i; + } + + /** * Retrieves the {@link MediaProjection} obtained from a successful screen * capture request. The result code and data from the request are provided by overriding * {@link Activity#onActivityResult(int, int, Intent) onActivityResult(int, int, Intent)}, diff --git a/media/java/android/media/tv/SignalingDataRequest.aidl b/media/java/android/media/tv/SignalingDataRequest.aidl new file mode 100644 index 000000000000..29e89fe4142b --- /dev/null +++ b/media/java/android/media/tv/SignalingDataRequest.aidl @@ -0,0 +1,3 @@ +package android.media.tv; + +parcelable SignalingDataRequest; diff --git a/media/java/android/media/tv/SignalingDataRequest.java b/media/java/android/media/tv/SignalingDataRequest.java new file mode 100644 index 000000000000..dcf1d48aaf3a --- /dev/null +++ b/media/java/android/media/tv/SignalingDataRequest.java @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv; + +import android.annotation.NonNull; +import android.os.Parcelable; + +/** + * Request to retrieve the Low-level Signalling Tables (LLS) and Service-layer Signalling (SLS) + * metadata. + * + * <p>For more details on each type of metadata that can be requested, refer to the ATSC standard + * A/344:2023-5 9.2.10 - Query Signaling Data API. + * + * @hide + */ +public class SignalingDataRequest extends BroadcastInfoRequest implements Parcelable { + private static final @TvInputManager.BroadcastInfoType int REQUEST_TYPE = + TvInputManager.BROADCAST_INFO_TYPE_SIGNALING_DATA; + + public static final @NonNull Parcelable.Creator<SignalingDataRequest> CREATOR = + new Parcelable.Creator<SignalingDataRequest>() { + @Override + public SignalingDataRequest[] newArray(int size) { + return new SignalingDataRequest[size]; + } + + @Override + public SignalingDataRequest createFromParcel(@NonNull android.os.Parcel in) { + return new SignalingDataRequest(in); + } + }; + + /** SLS Metadata: All metadata objects for the requested service(s) */ + public static final int SLS_METADATA_ALL = 0x7FFFFFF; + + /** SLS Metadata: APD for the requested service(s) */ + public static final int SLS_METADATA_APD = 1; + + /** SLS Metadata: USBD for the requested service(s) */ + public static final int SLS_METADATA_USBD = 1 << 1; + + /** SLS Metadata: S-TSID for the requested service(s) */ + public static final int SLS_METADATA_STSID = 1 << 2; + + /** SLS Metadata: DASH MPD for the requested service(s) */ + public static final int SLS_METADATA_MPD = 1 << 3; + + /** SLS Metadata: User Service Description for MMTP */ + public static final int SLS_METADATA_USD = 1 << 4; + + /** SLS Metadata: MMT Package Access Table for the requested service(s) */ + public static final int SLS_METADATA_PAT = 1 << 5; + + /** SLS Metadata: MMT Package Table for the requested service(s) */ + public static final int SLS_METADATA_MPT = 1 << 6; + + /** SLS Metadata: MMT Media Presentation Information Table for the requested service(s) */ + public static final int SLS_METADATA_MPIT = 1 << 7; + + /** SLS Metadata: MMT Clock Relation Information for the requested service(s) */ + public static final int SLS_METADATA_CRIT = 1 << 8; + + /** SLS Metadata: MMT Device Capabilities Information Table for the requested service(s) */ + public static final int SLS_METADATA_DCIT = 1 << 9; + + /** SLS Metadata: HTML Entry Pages Location Description for the requested service(s) */ + public static final int SLS_METADATA_HELD = 1 << 10; + + /** SLS Metadata: Distribution Window Desciription for the requested service(s) */ + public static final int SLS_METADATA_DWD = 1 << 11; + + /** SLS Metadata: MMT Application Event Information for the requested service(s) */ + public static final int SLS_METADATA_AEI = 1 << 12; + + /** SLS Metadata: Video Stream Properties Descriptor */ + public static final int SLS_METADATA_VSPD = 1 << 13; + + /** SLS Metadata: ATSC Staggercast Descriptor */ + public static final int SLS_METADATA_ASD = 1 << 14; + + /** SLS Metadata: Inband Event Descriptor */ + public static final int SLS_METADATA_IED = 1 << 15; + + /** SLS Metadata: Caption Asset Descriptor */ + public static final int SLS_METADATA_CAD = 1 << 16; + + /** SLS Metadata: Audio Stream Properties Descriptor */ + public static final int SLS_METADATA_ASPD = 1 << 17; + + /** SLS Metadata: Security Properties Descriptor */ + public static final int SLS_METADATA_SSD = 1 << 18; + + /** SLS Metadata: ROUTE/DASH Application Dynamic Event for the requested service(s) */ + public static final int SLS_METADATA_EMSG = 1 << 19; + + /** SLS Metadata: MMT Application Dynamic Event for the requested service(s) */ + public static final int SLS_METADATA_EVTI = 1 << 20; + + /** Regional Service Availability Table for the requested service(s) */ + public static final int SLS_METADATA_RSAT = 1 << 21; + + private final int mGroup; + private @NonNull final int[] mLlsTableIds; + private final int mSlsMetadataTypes; + + SignalingDataRequest( + int requestId, + int option, + int group, + @NonNull int[] llsTableIds, + int slsMetadataTypes) { + super(REQUEST_TYPE, requestId, option); + mGroup = group; + mLlsTableIds = llsTableIds; + mSlsMetadataTypes = slsMetadataTypes; + } + + SignalingDataRequest(@NonNull android.os.Parcel in) { + super(REQUEST_TYPE, in); + + int group = in.readInt(); + int[] llsTableIds = in.createIntArray(); + int slsMetadataTypes = in.readInt(); + + this.mGroup = group; + this.mLlsTableIds = llsTableIds; + com.android.internal.util.AnnotationValidations.validate(NonNull.class, null, mLlsTableIds); + this.mSlsMetadataTypes = slsMetadataTypes; + } + + public int getGroup() { + return mGroup; + } + + public @NonNull int[] getLlsTableIds() { + return mLlsTableIds; + } + + public int getSlsMetadataTypes() { + return mSlsMetadataTypes; + } + + @Override + public void writeToParcel(@NonNull android.os.Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeInt(mGroup); + dest.writeIntArray(mLlsTableIds); + dest.writeInt(mSlsMetadataTypes); + } + + @Override + public int describeContents() { + return 0; + } +} diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index 2b31bfef412e..be1b67547103 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -489,10 +489,19 @@ public final class TvInputManager { /** @hide */ @Retention(RetentionPolicy.SOURCE) - @IntDef(prefix = "BROADCAST_INFO_TYPE_", value = - {BROADCAST_INFO_TYPE_TS, BROADCAST_INFO_TYPE_TABLE, BROADCAST_INFO_TYPE_SECTION, - BROADCAST_INFO_TYPE_PES, BROADCAST_INFO_STREAM_EVENT, BROADCAST_INFO_TYPE_DSMCC, - BROADCAST_INFO_TYPE_COMMAND, BROADCAST_INFO_TYPE_TIMELINE}) + @IntDef( + prefix = "BROADCAST_INFO_TYPE_", + value = { + BROADCAST_INFO_TYPE_TS, + BROADCAST_INFO_TYPE_TABLE, + BROADCAST_INFO_TYPE_SECTION, + BROADCAST_INFO_TYPE_PES, + BROADCAST_INFO_STREAM_EVENT, + BROADCAST_INFO_TYPE_DSMCC, + BROADCAST_INFO_TYPE_COMMAND, + BROADCAST_INFO_TYPE_TIMELINE, + BROADCAST_INFO_TYPE_SIGNALING_DATA + }) public @interface BroadcastInfoType {} public static final int BROADCAST_INFO_TYPE_TS = 1; @@ -505,6 +514,9 @@ public final class TvInputManager { public static final int BROADCAST_INFO_TYPE_TIMELINE = 8; /** @hide */ + public static final int BROADCAST_INFO_TYPE_SIGNALING_DATA = 9; + + /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = "SIGNAL_STRENGTH_", value = {SIGNAL_STRENGTH_LOST, SIGNAL_STRENGTH_WEAK, SIGNAL_STRENGTH_STRONG}) diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl index 7b5853169923..1404d7c9841c 100644 --- a/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl +++ b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl @@ -63,6 +63,8 @@ oneway interface ITvInteractiveAppClient { void onRequestTvRecordingInfoList(in int type, int seq); void onRequestSigning(in String id, in String algorithm, in String alias, in byte[] data, int seq); + void onRequestSigning2(in String id, in String algorithm, in String host, + int port, in byte[] data, int seq); void onRequestCertificate(in String host, int port, int seq); void onAdRequest(in AdRequest request, int Seq); } diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl index cb89181fd714..3c91a3eeb1dc 100644 --- a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl +++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl @@ -61,6 +61,7 @@ oneway interface ITvInteractiveAppSessionCallback { void onRequestTvRecordingInfo(in String recordingId); void onRequestTvRecordingInfoList(in int type); void onRequestSigning(in String id, in String algorithm, in String alias, in byte[] data); + void onRequestSigning2(in String id, in String algorithm, in String host, int port, in byte[] data); void onRequestCertificate(in String host, int port); void onAdRequest(in AdRequest request); } diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java index 011744f94edb..498eec604b9c 100755 --- a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java +++ b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java @@ -657,6 +657,19 @@ public final class TvInteractiveAppManager { } @Override + public void onRequestSigning2( + String id, String algorithm, String host, int port, byte[] data, int seq) { + synchronized (mSessionCallbackRecordMap) { + SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); + if (record == null) { + Log.e(TAG, "Callback not found for seq " + seq); + return; + } + record.postRequestSigning(id, algorithm, host, port, data); + } + } + + @Override public void onRequestCertificate(String host, int port, int seq) { synchronized (mSessionCallbackRecordMap) { SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq); @@ -2258,6 +2271,17 @@ public final class TvInteractiveAppManager { }); } + void postRequestSigning(String id, String algorithm, String host, int port, + byte[] data) { + mHandler.post(new Runnable() { + @Override + public void run() { + mSessionCallback.onRequestSigning(mSession, id, algorithm, host, + port, data); + } + }); + } + void postRequestCertificate(String host, int port) { mHandler.post(new Runnable() { @Override @@ -2609,6 +2633,25 @@ public final class TvInteractiveAppManager { } /** + * This is called when + * {@link TvInteractiveAppService.Session#requestSigning(String, String, String, int, byte[])} is + * called. + * + * @param session A {@link TvInteractiveAppService.Session} associated with this callback. + * @param signingId the ID to identify the request. + * @param algorithm the standard name of the signature algorithm requested, such as + * MD5withRSA, SHA256withDSA, etc. + * @param host The host of the SSL CLient Authentication Server + * @param port The port of the SSL Client Authentication Server + * @param data the original bytes to be signed. + * @hide + */ + public void onRequestSigning( + Session session, String signingId, String algorithm, String host, + int port, byte[] data) { + } + + /** * This is called when the service requests a SSL certificate for client validation. * * @param session A {@link TvInteractiveAppService.Session} associated with this callback. diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java index 054b272d820f..7b6dc38fe22c 100755 --- a/media/java/android/media/tv/interactive/TvInteractiveAppService.java +++ b/media/java/android/media/tv/interactive/TvInteractiveAppService.java @@ -1645,6 +1645,50 @@ public abstract class TvInteractiveAppService extends Service { } /** + * Requests signing of the given data. + * + * <p>This is used when the corresponding server of the broadcast-independent interactive + * app requires signing during handshaking, and the interactive app service doesn't have + * the built-in private key. The private key is provided by the content providers and + * pre-built in the related app, such as TV app. + * + * @param signingId the ID to identify the request. When a result is received, this ID can + * be used to correlate the result with the request. + * @param algorithm the standard name of the signature algorithm requested, such as + * MD5withRSA, SHA256withDSA, etc. The name is from standards like + * FIPS PUB 186-4 and PKCS #1. + * @param host the host of the SSL client authentication server. + * @param port the port of the SSL client authentication server. + * @param data the original bytes to be signed. + * + * @see #onSigningResult(String, byte[]) + * @see TvInteractiveAppView#createBiInteractiveApp(Uri, Bundle) + * @see TvInteractiveAppView#BI_INTERACTIVE_APP_KEY_ALIAS + * @hide + */ + @CallSuper + public void requestSigning(@NonNull String signingId, @NonNull String algorithm, + @NonNull String host, int port, @NonNull byte[] data) { + executeOrPostRunnableOnMainThread(new Runnable() { + @MainThread + @Override + public void run() { + try { + if (DEBUG) { + Log.d(TAG, "requestSigning"); + } + if (mSessionCallback != null) { + mSessionCallback.onRequestSigning2(signingId, algorithm, + host, port, data); + } + } catch (RemoteException e) { + Log.w(TAG, "error in requestSigning", e); + } + } + }); + } + + /** * Requests a SSL certificate for client validation. * * @param host the host name of the SSL authentication server. diff --git a/media/jni/soundpool/SoundDecoder.cpp b/media/jni/soundpool/SoundDecoder.cpp index 5ed10b0d785f..ae576342c6cc 100644 --- a/media/jni/soundpool/SoundDecoder.cpp +++ b/media/jni/soundpool/SoundDecoder.cpp @@ -29,14 +29,15 @@ static constexpr size_t kMaxQueueSize = 128; // before the SoundDecoder thread closes. static constexpr int32_t kWaitTimeBeforeCloseMs = 1000; -SoundDecoder::SoundDecoder(SoundManager* soundManager, size_t threads) +SoundDecoder::SoundDecoder(SoundManager* soundManager, size_t threads, int32_t threadPriority) : mSoundManager(soundManager) { ALOGV("%s(%p, %zu)", __func__, soundManager, threads); // ThreadPool is created, but we don't launch any threads. mThreadPool = std::make_unique<ThreadPool>( std::min(threads, (size_t)std::thread::hardware_concurrency()), - "SoundDecoder_"); + "SoundDecoder_", + threadPriority); } SoundDecoder::~SoundDecoder() diff --git a/media/jni/soundpool/SoundDecoder.h b/media/jni/soundpool/SoundDecoder.h index 7b62114483cf..3f44a0d977e1 100644 --- a/media/jni/soundpool/SoundDecoder.h +++ b/media/jni/soundpool/SoundDecoder.h @@ -28,7 +28,7 @@ namespace android::soundpool { */ class SoundDecoder { public: - SoundDecoder(SoundManager* soundManager, size_t threads); + SoundDecoder(SoundManager* soundManager, size_t threads, int32_t threadPriority); ~SoundDecoder(); void loadSound(int32_t soundID) NO_THREAD_SAFETY_ANALYSIS; // uses unique_lock void quit(); diff --git a/media/jni/soundpool/SoundManager.cpp b/media/jni/soundpool/SoundManager.cpp index 5b16174eef2b..fa35813391a6 100644 --- a/media/jni/soundpool/SoundManager.cpp +++ b/media/jni/soundpool/SoundManager.cpp @@ -29,7 +29,7 @@ namespace android::soundpool { static const size_t kDecoderThreads = std::thread::hardware_concurrency() >= 4 ? 2 : 1; SoundManager::SoundManager() - : mDecoder{std::make_unique<SoundDecoder>(this, kDecoderThreads)} + : mDecoder{std::make_unique<SoundDecoder>(this, kDecoderThreads, ANDROID_PRIORITY_NORMAL)} { ALOGV("%s()", __func__); } diff --git a/media/jni/soundpool/StreamManager.cpp b/media/jni/soundpool/StreamManager.cpp index 66fec1c528e7..e11ccbc0448b 100644 --- a/media/jni/soundpool/StreamManager.cpp +++ b/media/jni/soundpool/StreamManager.cpp @@ -126,7 +126,8 @@ StreamManager::StreamManager( mThreadPool = std::make_unique<ThreadPool>( std::min((size_t)streams, // do not make more threads than streams to play std::min(threads, (size_t)std::thread::hardware_concurrency())), - "SoundPool_"); + "SoundPool_", + ANDROID_PRIORITY_AUDIO); } #pragma clang diagnostic pop diff --git a/media/jni/soundpool/StreamManager.h b/media/jni/soundpool/StreamManager.h index 340b49bc6d6c..a4cb28663bda 100644 --- a/media/jni/soundpool/StreamManager.h +++ b/media/jni/soundpool/StreamManager.h @@ -46,9 +46,9 @@ namespace android::soundpool { */ class JavaThread { public: - JavaThread(std::function<void()> f, const char *name) + JavaThread(std::function<void()> f, const char *name, int32_t threadPriority) : mF{std::move(f)} { - createThreadEtc(staticFunction, this, name, ANDROID_PRIORITY_AUDIO); + createThreadEtc(staticFunction, this, name, threadPriority); } JavaThread(JavaThread &&) = delete; // uses "this" ptr, not moveable. @@ -109,9 +109,11 @@ private: */ class ThreadPool { public: - ThreadPool(size_t maxThreadCount, std::string name) + ThreadPool(size_t maxThreadCount, std::string name, + int32_t threadPriority = ANDROID_PRIORITY_NORMAL) : mMaxThreadCount(maxThreadCount) - , mName{std::move(name)} { } + , mName{std::move(name)} + , mThreadPriority(threadPriority) {} ~ThreadPool() { quit(); } @@ -159,7 +161,8 @@ public: const int32_t id = mNextThreadId; mThreads.emplace_back(std::make_unique<JavaThread>( [this, id, mf = std::move(f)] { mf(id); --mActiveThreadCount; }, - (mName + std::to_string(id)).c_str())); + (mName + std::to_string(id)).c_str(), + mThreadPriority)); ++mActiveThreadCount; return id; } @@ -180,6 +183,7 @@ public: private: const size_t mMaxThreadCount; const std::string mName; + const int32_t mThreadPriority; std::atomic_size_t mActiveThreadCount = 0; diff --git a/media/tests/projection/src/android/media/projection/FakeIMediaProjection.java b/media/tests/projection/src/android/media/projection/FakeIMediaProjection.java index 774de5fbbe3e..0df36afddf25 100644 --- a/media/tests/projection/src/android/media/projection/FakeIMediaProjection.java +++ b/media/tests/projection/src/android/media/projection/FakeIMediaProjection.java @@ -19,7 +19,7 @@ package android.media.projection; import static android.Manifest.permission.MANAGE_MEDIA_PROJECTION; import android.annotation.EnforcePermission; -import android.os.IBinder; +import android.app.ActivityOptions.LaunchCookie; import android.os.PermissionEnforcer; import android.os.RemoteException; @@ -29,7 +29,7 @@ import android.os.RemoteException; */ public final class FakeIMediaProjection extends IMediaProjection.Stub { boolean mIsStarted = false; - IBinder mLaunchCookie = null; + LaunchCookie mLaunchCookie = null; IMediaProjectionCallback mIMediaProjectionCallback = null; FakeIMediaProjection(PermissionEnforcer enforcer) { @@ -80,14 +80,14 @@ public final class FakeIMediaProjection extends IMediaProjection.Stub { @Override @EnforcePermission(MANAGE_MEDIA_PROJECTION) - public IBinder getLaunchCookie() throws RemoteException { + public LaunchCookie getLaunchCookie() throws RemoteException { getLaunchCookie_enforcePermission(); return mLaunchCookie; } @Override @EnforcePermission(MANAGE_MEDIA_PROJECTION) - public void setLaunchCookie(IBinder launchCookie) throws RemoteException { + public void setLaunchCookie(LaunchCookie launchCookie) throws RemoteException { setLaunchCookie_enforcePermission(); mLaunchCookie = launchCookie; } diff --git a/media/tests/projection/src/android/media/projection/MediaProjectionTest.java b/media/tests/projection/src/android/media/projection/MediaProjectionTest.java index 2e0396fc6119..1323e89202ee 100644 --- a/media/tests/projection/src/android/media/projection/MediaProjectionTest.java +++ b/media/tests/projection/src/android/media/projection/MediaProjectionTest.java @@ -31,15 +31,14 @@ import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; import android.annotation.Nullable; +import android.app.ActivityOptions.LaunchCookie; import android.compat.testing.PlatformCompatChangeRule; import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; import android.hardware.display.VirtualDisplayConfig; import android.os.Handler; -import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.os.test.FakePermissionEnforcer; @@ -117,7 +116,7 @@ public class MediaProjectionTest { permissionEnforcer.grant(MANAGE_MEDIA_PROJECTION); // Support the MediaProjection instance. mFakeIMediaProjection = new FakeIMediaProjection(permissionEnforcer); - mFakeIMediaProjection.setLaunchCookie(mock(IBinder.class)); + mFakeIMediaProjection.setLaunchCookie(new LaunchCookie()); mMediaProjection = new MediaProjection(mTestableContext, mFakeIMediaProjection, mDisplayManager); diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index a2530d59e2e6..375fe131b4b7 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -371,3 +371,10 @@ flag { description: "Enables refactored logic for SysUI+WM unlock/occlusion code paths" bug: "278086361" } + +flag { + name: "enable_keyguard_compose" + namespace: "systemui" + description: "Enables the compose version of keyguard." + bug: "301968149" +} diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt index e2beaeea6402..b11edf7b47b7 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt @@ -58,6 +58,8 @@ import com.android.systemui.battery.BatteryMeterViewController import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation import com.android.systemui.common.ui.compose.windowinsets.LocalDisplayCutout import com.android.systemui.res.R +import com.android.systemui.scene.ui.composable.QuickSettings +import com.android.systemui.scene.ui.composable.Shade as ShadeKey import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel import com.android.systemui.statusbar.phone.StatusBarIconController import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager @@ -348,7 +350,7 @@ private fun ShadeCarrierGroup( } @Composable -private fun StatusIcons( +private fun SceneScope.StatusIcons( viewModel: ShadeHeaderViewModel, createTintedIconManager: (ViewGroup, StatusBarLocation) -> TintedIconManager, statusBarIconController: StatusBarIconController, @@ -358,7 +360,6 @@ private fun StatusIcons( val carrierIconSlots = listOf(stringResource(id = com.android.internal.R.string.status_bar_mobile)) val isSingleCarrier by viewModel.isSingleCarrier.collectAsState() - val isTransitioning by viewModel.isTransitioning.collectAsState() AndroidView( factory = { context -> @@ -373,7 +374,9 @@ private fun StatusIcons( iconContainer }, update = { iconContainer -> - iconContainer.setQsExpansionTransitioning(isTransitioning) + iconContainer.setQsExpansionTransitioning( + layoutState.isTransitioningBetween(ShadeKey, QuickSettings) + ) if (isSingleCarrier || !useExpandedFormat) { iconContainer.removeIgnoredSlots(carrierIconSlots) } else { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt index 34f703bc0ca7..db414b724b63 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt @@ -27,6 +27,7 @@ import com.android.systemui.animation.DialogLaunchAnimator import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.coroutines.collectValues import com.android.systemui.dock.DockManager import com.android.systemui.dock.DockManagerFake import com.android.systemui.flags.FakeFeatureFlags @@ -49,16 +50,17 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.res.R import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker +import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots import com.android.systemui.statusbar.policy.KeyguardStateController -import com.android.systemui.testKosmos import com.android.systemui.util.FakeSharedPreferences import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.android.systemui.util.settings.FakeSettings import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent @@ -82,6 +84,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { @Mock private lateinit var activityStarter: ActivityStarter @Mock private lateinit var launchAnimator: DialogLaunchAnimator @Mock private lateinit var devicePolicyManager: DevicePolicyManager + @Mock private lateinit var shadeInteractor: ShadeInteractor @Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger private lateinit var underTest: KeyguardQuickAffordanceInteractor @@ -95,8 +98,6 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { private lateinit var dockManager: DockManagerFake private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository - private val kosmos = testKosmos() - @Before fun setUp() { MockitoAnnotations.initMocks(this) @@ -183,7 +184,7 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { underTest = KeyguardQuickAffordanceInteractor( keyguardInteractor = withDeps.keyguardInteractor, - shadeInteractor = kosmos.shadeInteractor, + shadeInteractor = shadeInteractor, lockPatternUtils = lockPatternUtils, keyguardStateController = keyguardStateController, userTracker = userTracker, @@ -198,6 +199,8 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { backgroundDispatcher = testDispatcher, appContext = context, ) + + whenever(shadeInteractor.anyExpansion).thenReturn(MutableStateFlow(0f)) } @Test @@ -344,6 +347,25 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { } @Test + fun quickAffordance_updateOncePerShadeExpansion() = + testScope.runTest { + val shadeExpansion = MutableStateFlow(0f) + whenever(shadeInteractor.anyExpansion).thenReturn(shadeExpansion) + + val collectedValue by + collectValues( + underTest.quickAffordance(KeyguardQuickAffordancePosition.BOTTOM_START) + ) + + val initialSize = collectedValue.size + for (i in 0..10) { + shadeExpansion.value = i / 10f + } + + assertThat(collectedValue.size).isEqualTo(initialSize + 1) + } + + @Test fun quickAffordanceAlwaysVisible_notVisible_restrictedByPolicyManager() = testScope.runTest { whenever(devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId)) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt index bf99a8687aa4..942fbc229e8a 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt @@ -174,105 +174,6 @@ class SceneInteractorTest : SysuiTestCase() { } @Test - fun transitioning_idle_false() = - testScope.runTest { - val transitionState = - MutableStateFlow<ObservableTransitionState>( - ObservableTransitionState.Idle(SceneKey.Shade) - ) - val transitioning by - collectLastValue(underTest.transitioning(SceneKey.Shade, SceneKey.Lockscreen)) - underTest.setTransitionState(transitionState) - - assertThat(transitioning).isFalse() - } - - @Test - fun transitioning_wrongFromScene_false() = - testScope.runTest { - val transitionState = - MutableStateFlow<ObservableTransitionState>( - ObservableTransitionState.Transition( - fromScene = SceneKey.Gone, - toScene = SceneKey.Lockscreen, - progress = flowOf(0.5f), - isInitiatedByUserInput = false, - isUserInputOngoing = flowOf(false), - ) - ) - val transitioning by - collectLastValue(underTest.transitioning(SceneKey.Shade, SceneKey.Lockscreen)) - underTest.setTransitionState(transitionState) - - assertThat(transitioning).isFalse() - } - - @Test - fun transitioning_wrongToScene_false() = - testScope.runTest { - val transitionState = - MutableStateFlow<ObservableTransitionState>( - ObservableTransitionState.Transition( - fromScene = SceneKey.Shade, - toScene = SceneKey.QuickSettings, - progress = flowOf(0.5f), - isInitiatedByUserInput = false, - isUserInputOngoing = flowOf(false), - ) - ) - underTest.setTransitionState(transitionState) - - assertThat(underTest.transitioning(SceneKey.Shade, SceneKey.Lockscreen).value).isFalse() - } - - @Test - fun transitioning_correctFromAndToScenes_true() = - testScope.runTest { - val transitionState = - MutableStateFlow<ObservableTransitionState>( - ObservableTransitionState.Transition( - fromScene = SceneKey.Shade, - toScene = SceneKey.Lockscreen, - progress = flowOf(0.5f), - isInitiatedByUserInput = false, - isUserInputOngoing = flowOf(false), - ) - ) - val transitioning by - collectLastValue(underTest.transitioning(SceneKey.Shade, SceneKey.Lockscreen)) - underTest.setTransitionState(transitionState) - - assertThat(transitioning).isTrue() - } - - @Test - fun transitioning_updates() = - testScope.runTest { - val transitionState = - MutableStateFlow<ObservableTransitionState>( - ObservableTransitionState.Idle(SceneKey.Shade) - ) - val transitioning by - collectLastValue(underTest.transitioning(SceneKey.Shade, SceneKey.Lockscreen)) - underTest.setTransitionState(transitionState) - - assertThat(transitioning).isFalse() - - transitionState.value = - ObservableTransitionState.Transition( - fromScene = SceneKey.Shade, - toScene = SceneKey.Lockscreen, - progress = flowOf(0.5f), - isInitiatedByUserInput = false, - isUserInputOngoing = flowOf(false), - ) - assertThat(transitioning).isTrue() - - transitionState.value = ObservableTransitionState.Idle(SceneKey.Lockscreen) - assertThat(transitioning).isFalse() - } - - @Test fun isTransitionUserInputOngoing_idle_false() = testScope.runTest { val transitionState = diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt index e9a2a3befb03..c0aaab3ad6e1 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt @@ -9,8 +9,6 @@ import com.android.systemui.flags.FakeFeatureFlagsClassic import com.android.systemui.flags.Flags import com.android.systemui.kosmos.testScope import com.android.systemui.scene.domain.interactor.sceneInteractor -import com.android.systemui.scene.shared.model.ObservableTransitionState -import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel @@ -22,8 +20,6 @@ import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnec import com.android.systemui.testKosmos import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test @@ -74,74 +70,6 @@ class ShadeHeaderViewModelTest : SysuiTestCase() { } @Test - fun isTransitioning_idle_false() = - testScope.runTest { - val isTransitioning by collectLastValue(underTest.isTransitioning) - sceneInteractor.setTransitionState( - MutableStateFlow(ObservableTransitionState.Idle(SceneKey.Shade)) - ) - - assertThat(isTransitioning).isFalse() - } - - @Test - fun isTransitioning_shadeToQs_true() = - testScope.runTest { - val isTransitioning by collectLastValue(underTest.isTransitioning) - sceneInteractor.setTransitionState( - MutableStateFlow( - ObservableTransitionState.Transition( - fromScene = SceneKey.Shade, - toScene = SceneKey.QuickSettings, - progress = MutableStateFlow(0.5f), - isInitiatedByUserInput = false, - isUserInputOngoing = flowOf(false), - ) - ) - ) - - assertThat(isTransitioning).isTrue() - } - - @Test - fun isTransitioning_qsToShade_true() = - testScope.runTest { - val isTransitioning by collectLastValue(underTest.isTransitioning) - sceneInteractor.setTransitionState( - MutableStateFlow( - ObservableTransitionState.Transition( - fromScene = SceneKey.QuickSettings, - toScene = SceneKey.Shade, - progress = MutableStateFlow(0.5f), - isInitiatedByUserInput = false, - isUserInputOngoing = flowOf(false), - ) - ) - ) - - assertThat(isTransitioning).isTrue() - } - - @Test - fun isTransitioning_otherTransition_false() = - testScope.runTest { - val isTransitioning by collectLastValue(underTest.isTransitioning) - sceneInteractor.setTransitionState( - MutableStateFlow( - ObservableTransitionState.Transition( - fromScene = SceneKey.Gone, - toScene = SceneKey.Shade, - progress = MutableStateFlow(0.5f), - isInitiatedByUserInput = false, - isUserInputOngoing = flowOf(false), - ) - ) - ) - - assertThat(isTransitioning).isFalse() - } - - @Test fun mobileSubIds_update() = testScope.runTest { val mobileSubIds by collectLastValue(underTest.mobileSubIds) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt index 388834597f60..a1f94250e149 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt @@ -56,6 +56,7 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map @@ -102,10 +103,10 @@ constructor( quickAffordanceAlwaysVisible(position), keyguardInteractor.isDozing, keyguardInteractor.isKeyguardShowing, - shadeInteractor.anyExpansion, + shadeInteractor.anyExpansion.map { it < 1.0f }.distinctUntilChanged(), biometricSettingsRepository.isCurrentUserInLockdown, - ) { affordance, isDozing, isKeyguardShowing, qsExpansion, isUserInLockdown -> - if (!isDozing && isKeyguardShowing && (qsExpansion < 1.0f) && !isUserInLockdown) { + ) { affordance, isDozing, isKeyguardShowing, isQuickSettingsVisible, isUserInLockdown -> + if (!isDozing && isKeyguardShowing && isQuickSettingsVisible && !isUserInLockdown) { affordance } else { KeyguardQuickAffordanceModel.Hidden diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/PreviewKeyguardBlueprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/PreviewKeyguardBlueprintViewBinder.kt deleted file mode 100644 index 2feaa2e81c0f..000000000000 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/PreviewKeyguardBlueprintViewBinder.kt +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2023 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.keyguard.ui.binder - -import android.os.Trace -import androidx.constraintlayout.widget.ConstraintLayout -import androidx.constraintlayout.widget.ConstraintSet -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.repeatOnLifecycle -import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel -import com.android.systemui.lifecycle.repeatWhenAttached -import kotlinx.coroutines.DisposableHandle -import kotlinx.coroutines.launch - -/** - * Binds the existing blueprint to the constraint layout that previews keyguard. - * - * This view binder should only inflate and add relevant views and apply the constraints. Actual - * data binding should be done in {@link KeyguardPreviewRenderer} - */ -class PreviewKeyguardBlueprintViewBinder { - companion object { - - /** - * Binds the existing blueprint to the constraint layout that previews keyguard. - * - * @param constraintLayout The root view to bind to - * @param viewModel The instance of the view model that contains flows we collect on. - * @param finishedAddViewCallback Called when we have finished inflating the views. - */ - fun bind( - constraintLayout: ConstraintLayout, - viewModel: KeyguardBlueprintViewModel, - finishedAddViewCallback: () -> Unit - ): DisposableHandle { - return constraintLayout.repeatWhenAttached { - repeatOnLifecycle(Lifecycle.State.CREATED) { - launch { - viewModel.blueprint.collect { blueprint -> - val prevBluePrint = viewModel.currentBluePrint - Trace.beginSection("PreviewKeyguardBlueprint#applyBlueprint") - - ConstraintSet().apply { - clone(constraintLayout) - val emptyLayout = ConstraintSet.Layout() - knownIds.forEach { getConstraint(it).layout.copyFrom(emptyLayout) } - blueprint.applyConstraints(this) - // Add and remove views of sections that are not contained by the - // other. - blueprint.replaceViews( - prevBluePrint, - constraintLayout, - bindData = false - ) - applyTo(constraintLayout) - } - - viewModel.currentBluePrint = blueprint - finishedAddViewCallback.invoke() - Trace.endSection() - } - } - } - } - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt index 841bad4c15cc..a0c0095b34a2 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/preview/KeyguardPreviewRenderer.kt @@ -41,6 +41,7 @@ import android.view.WindowManager import android.widget.FrameLayout import android.widget.TextView import androidx.constraintlayout.widget.ConstraintLayout +import androidx.constraintlayout.widget.ConstraintSet import androidx.core.view.isInvisible import com.android.keyguard.ClockEventController import com.android.keyguard.KeyguardClockSwitch @@ -54,15 +55,12 @@ import com.android.systemui.communal.ui.viewmodel.CommunalTutorialIndicatorViewM import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.flags.FeatureFlagsClassic -import com.android.systemui.keyguard.shared.KeyguardShadeMigrationNssl import com.android.systemui.keyguard.ui.binder.KeyguardPreviewClockViewBinder import com.android.systemui.keyguard.ui.binder.KeyguardPreviewSmartspaceViewBinder import com.android.systemui.keyguard.ui.binder.KeyguardQuickAffordanceViewBinder import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder -import com.android.systemui.keyguard.ui.binder.PreviewKeyguardBlueprintViewBinder import com.android.systemui.keyguard.ui.view.KeyguardRootView -import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel +import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewClockViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewSmartspaceViewModel @@ -124,19 +122,18 @@ constructor( private val broadcastDispatcher: BroadcastDispatcher, private val lockscreenSmartspaceController: LockscreenSmartspaceController, private val udfpsOverlayInteractor: UdfpsOverlayInteractor, - private val featureFlags: FeatureFlagsClassic, private val falsingManager: FalsingManager, private val vibratorHelper: VibratorHelper, private val indicationController: KeyguardIndicationController, private val keyguardRootViewModel: KeyguardRootViewModel, @Assisted bundle: Bundle, - private val keyguardBlueprintViewModel: KeyguardBlueprintViewModel, private val occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel, private val chipbarCoordinator: ChipbarCoordinator, private val screenOffAnimationController: ScreenOffAnimationController, private val shadeInteractor: ShadeInteractor, private val secureSettings: SecureSettings, private val communalTutorialViewModel: CommunalTutorialIndicatorViewModel, + private val defaultShortcutsSection: DefaultShortcutsSection, ) { val hostToken: IBinder? = bundle.getBinder(KEY_HOST_TOKEN) private val width: Int = bundle.getInt(KEY_VIEW_WIDTH) @@ -393,32 +390,32 @@ constructor( setUpUdfps(previewContext, rootView) - disposables.add( - PreviewKeyguardBlueprintViewBinder.bind(keyguardRootView, keyguardBlueprintViewModel) { - if (keyguardBottomAreaRefactor()) { - setupShortcuts(keyguardRootView) - } - - if (!shouldHideClock) { - setUpClock(previewContext, rootView) - KeyguardPreviewClockViewBinder.bind( - largeClockHostView, - smallClockHostView, - clockViewModel, - ) - } + if (keyguardBottomAreaRefactor()) { + setupShortcuts(keyguardRootView) + } - setUpSmartspace(previewContext, rootView) - smartSpaceView?.let { - KeyguardPreviewSmartspaceViewBinder.bind(it, smartspaceViewModel) - } + if (!shouldHideClock) { + setUpClock(previewContext, rootView) + KeyguardPreviewClockViewBinder.bind( + largeClockHostView, + smallClockHostView, + clockViewModel, + ) + } - setupCommunalTutorialIndicator(keyguardRootView) - } - ) + setUpSmartspace(previewContext, rootView) + smartSpaceView?.let { KeyguardPreviewSmartspaceViewBinder.bind(it, smartspaceViewModel) } + setupCommunalTutorialIndicator(keyguardRootView) } private fun setupShortcuts(keyguardRootView: ConstraintLayout) { + // Add shortcuts + val cs = ConstraintSet() + cs.clone(keyguardRootView) + defaultShortcutsSection.addViews(keyguardRootView) + defaultShortcutsSection.applyConstraints(cs) + cs.applyTo(keyguardRootView) + keyguardRootView.findViewById<LaunchableImageView?>(R.id.start_button)?.let { imageView -> shortcutsBindings.add( KeyguardQuickAffordanceViewBinder.bind( @@ -476,53 +473,40 @@ constructor( } private fun setUpClock(previewContext: Context, parentView: ViewGroup) { - largeClockHostView = - if (KeyguardShadeMigrationNssl.isEnabled) { - parentView.requireViewById<FrameLayout>(R.id.lockscreen_clock_view_large) - } else { - val hostView = FrameLayout(previewContext) - hostView.layoutParams = - FrameLayout.LayoutParams( - FrameLayout.LayoutParams.MATCH_PARENT, - FrameLayout.LayoutParams.MATCH_PARENT, - ) - parentView.addView(hostView) - hostView - } + val resources = parentView.resources + largeClockHostView = FrameLayout(previewContext) + largeClockHostView.layoutParams = + FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.MATCH_PARENT, + ) + parentView.addView(largeClockHostView) largeClockHostView.isInvisible = true - smallClockHostView = - if (KeyguardShadeMigrationNssl.isEnabled) { - parentView.requireViewById<FrameLayout>(R.id.lockscreen_clock_view) - } else { - val resources = parentView.resources - val hostView = FrameLayout(previewContext) - val layoutParams = - FrameLayout.LayoutParams( - FrameLayout.LayoutParams.WRAP_CONTENT, - resources.getDimensionPixelSize( - com.android.systemui.customization.R.dimen.small_clock_height - ) - ) - layoutParams.topMargin = - KeyguardPreviewSmartspaceViewModel.getStatusBarHeight(resources) + - resources.getDimensionPixelSize( - com.android.systemui.customization.R.dimen.small_clock_padding_top - ) - hostView.layoutParams = layoutParams - - hostView.setPaddingRelative( - resources.getDimensionPixelSize( - com.android.systemui.customization.R.dimen.clock_padding_start - ), - 0, - 0, - 0 + smallClockHostView = FrameLayout(previewContext) + val layoutParams = + FrameLayout.LayoutParams( + FrameLayout.LayoutParams.WRAP_CONTENT, + resources.getDimensionPixelSize( + com.android.systemui.customization.R.dimen.small_clock_height ) - hostView.clipChildren = false - parentView.addView(hostView) - hostView - } + ) + layoutParams.topMargin = + KeyguardPreviewSmartspaceViewModel.getStatusBarHeight(resources) + + resources.getDimensionPixelSize( + com.android.systemui.customization.R.dimen.small_clock_padding_top + ) + smallClockHostView.layoutParams = layoutParams + smallClockHostView.setPaddingRelative( + resources.getDimensionPixelSize( + com.android.systemui.customization.R.dimen.clock_padding_start + ), + 0, + 0, + 0 + ) + smallClockHostView.clipChildren = false + parentView.addView(smallClockHostView) smallClockHostView.isInvisible = true // TODO (b/283465254): Move the listeners to KeyguardClockRepository diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionCaptureTarget.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionCaptureTarget.kt index 11d0be5fc8bf..a618490c1b53 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionCaptureTarget.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionCaptureTarget.kt @@ -16,7 +16,7 @@ package com.android.systemui.mediaprojection -import android.os.IBinder +import android.app.ActivityOptions.LaunchCookie import android.os.Parcel import android.os.Parcelable @@ -24,12 +24,12 @@ import android.os.Parcelable * Class that represents an area that should be captured. Currently it has only a launch cookie that * represents a task but we potentially could add more identifiers e.g. for a pair of tasks. */ -data class MediaProjectionCaptureTarget(val launchCookie: IBinder?) : Parcelable { +data class MediaProjectionCaptureTarget(val launchCookie: LaunchCookie?) : Parcelable { - constructor(parcel: Parcel) : this(parcel.readStrongBinder()) + constructor(parcel: Parcel) : this(LaunchCookie.readFromParcel(parcel)) override fun writeToParcel(dest: Parcel, flags: Int) { - dest.writeStrongBinder(launchCookie) + LaunchCookie.writeToParcel(launchCookie, dest) } override fun describeContents(): Int = 0 diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt index 50e9e7517a0f..4685c5a0cb21 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt @@ -16,6 +16,7 @@ package com.android.systemui.mediaprojection.appselector import android.app.ActivityOptions +import android.app.ActivityOptions.LaunchCookie import android.content.Intent import android.content.res.Configuration import android.content.res.Resources @@ -24,9 +25,7 @@ import android.media.projection.IMediaProjectionManager.EXTRA_USER_REVIEW_GRANTE import android.media.projection.MediaProjectionManager.EXTRA_MEDIA_PROJECTION import android.media.projection.ReviewGrantedConsentResult.RECORD_CANCEL import android.media.projection.ReviewGrantedConsentResult.RECORD_CONTENT_TASK -import android.os.Binder import android.os.Bundle -import android.os.IBinder import android.os.ResultReceiver import android.os.UserHandle import android.util.Log @@ -163,9 +162,9 @@ class MediaProjectionAppSelectorActivity( val intent = createIntent(targetInfo) - val launchToken: IBinder = Binder("media_projection_launch_token") + val launchCookie = LaunchCookie("media_projection_launch_token") val activityOptions = ActivityOptions.makeBasic() - activityOptions.launchCookie = launchToken + activityOptions.setLaunchCookie(launchCookie) val userHandle = mMultiProfilePagerAdapter.activeListAdapter.userHandle @@ -175,7 +174,7 @@ class MediaProjectionAppSelectorActivity( // is created and ready to be captured. val activityStarted = activityLauncher.startActivityAsUser(intent, userHandle, activityOptions.toBundle()) { - returnSelectedApp(launchToken) + returnSelectedApp(launchCookie) } // Rely on the ActivityManager to pop up a dialog regarding app suspension @@ -233,7 +232,7 @@ class MediaProjectionAppSelectorActivity( } } - override fun returnSelectedApp(launchCookie: IBinder) { + override fun returnSelectedApp(launchCookie: LaunchCookie) { taskSelected = true if (intent.hasExtra(EXTRA_CAPTURE_REGION_RESULT_RECEIVER)) { // The client requested to return the result in the result receiver instead of @@ -255,7 +254,7 @@ class MediaProjectionAppSelectorActivity( val mediaProjectionBinder = intent.getIBinderExtra(EXTRA_MEDIA_PROJECTION) val projection = IMediaProjection.Stub.asInterface(mediaProjectionBinder) - projection.launchCookie = launchCookie + projection.setLaunchCookie(launchCookie) val intent = Intent() intent.putExtra(EXTRA_MEDIA_PROJECTION, projection.asBinder()) diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorResultHandler.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorResultHandler.kt index 93c3bce87ad3..f204b3e74f4b 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorResultHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorResultHandler.kt @@ -1,6 +1,6 @@ package com.android.systemui.mediaprojection.appselector -import android.os.IBinder +import android.app.ActivityOptions.LaunchCookie /** * Interface that allows to continue the media projection flow and return the selected app @@ -11,5 +11,5 @@ interface MediaProjectionAppSelectorResultHandler { * Return selected app to the original caller of the media projection app picker. * @param launchCookie launch cookie of the launched activity of the target app */ - fun returnSelectedApp(launchCookie: IBinder) + fun returnSelectedApp(launchCookie: LaunchCookie) } diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt index ba837dba5354..a811065fdc65 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt @@ -17,10 +17,10 @@ package com.android.systemui.mediaprojection.appselector.view import android.app.ActivityOptions +import android.app.ActivityOptions.LaunchCookie import android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED import android.app.IActivityTaskManager import android.graphics.Rect -import android.os.Binder import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -121,7 +121,7 @@ constructor( } override fun onRecentAppClicked(task: RecentTask, view: View) { - val launchCookie = Binder() + val launchCookie = LaunchCookie() val activityOptions = ActivityOptions.makeScaleUpAnimation( view, @@ -132,7 +132,7 @@ constructor( ) activityOptions.pendingIntentBackgroundActivityStartMode = MODE_BACKGROUND_ACTIVITY_START_ALLOWED - activityOptions.launchCookie = launchCookie + activityOptions.setLaunchCookie(launchCookie) activityOptions.launchDisplayId = task.displayId activityTaskManager.startActivityFromRecents(task.taskId, activityOptions.toBundle()) diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java index 039372d87835..8b034b293dcb 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java @@ -28,6 +28,7 @@ import static com.android.systemui.mediaprojection.permission.ScreenShareOptionK import android.annotation.Nullable; import android.app.Activity; import android.app.ActivityManager; +import android.app.ActivityOptions.LaunchCookie; import android.app.AlertDialog; import android.app.StatusBarManager; import android.content.Context; @@ -146,6 +147,13 @@ public class MediaProjectionPermissionActivity extends Activity final IMediaProjection projection = MediaProjectionServiceHelper.createOrReuseProjection(mUid, mPackageName, mReviewGrantedConsentRequired); + + LaunchCookie launchCookie = launchingIntent.getParcelableExtra( + MediaProjectionManager.EXTRA_LAUNCH_COOKIE, LaunchCookie.class); + if (launchCookie != null) { + projection.setLaunchCookie(launchCookie); + } + // Automatically grant consent if a system-privileged component is recording. final Intent intent = new Intent(); intent.putExtra(MediaProjectionManager.EXTRA_MEDIA_PROJECTION, diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt index b3d2e0918db6..b9e9fe7684e9 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt @@ -171,28 +171,6 @@ constructor( return repository.setVisible(isVisible) } - /** True if there is a transition happening from and to the specified scenes. */ - fun transitioning(from: SceneKey, to: SceneKey): StateFlow<Boolean> { - fun transitioning( - state: ObservableTransitionState, - from: SceneKey, - to: SceneKey, - ): Boolean { - return (state as? ObservableTransitionState.Transition)?.let { - it.fromScene == from && it.toScene == to - } - ?: false - } - - return transitionState - .map { state -> transitioning(state, from, to) } - .stateIn( - scope = applicationScope, - started = SharingStarted.WhileSubscribed(), - initialValue = transitioning(transitionState.value, from, to), - ) - } - /** * Binds the given flow so the system remembers it. * diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt index 51276c6560a4..314637e4b27e 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt @@ -22,12 +22,11 @@ import android.content.IntentFilter import android.icu.text.DateFormat import android.icu.text.DisplayContext import android.os.UserHandle -import com.android.systemui.res.R import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.res.R import com.android.systemui.scene.domain.interactor.SceneInteractor -import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel import java.util.Date @@ -38,7 +37,6 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach @@ -57,16 +55,6 @@ constructor( val mobileIconsViewModel: MobileIconsViewModel, broadcastDispatcher: BroadcastDispatcher, ) { - /** True if we are transitioning between Shade and QuickSettings scenes, in either direction. */ - val isTransitioning = - combine( - sceneInteractor.transitioning(from = SceneKey.Shade, to = SceneKey.QuickSettings), - sceneInteractor.transitioning(from = SceneKey.QuickSettings, to = SceneKey.Shade) - ) { shadeToQuickSettings, quickSettingsToShade -> - shadeToQuickSettings || quickSettingsToShade - } - .stateIn(applicationScope, SharingStarted.WhileSubscribed(), false) - /** True if there is exactly one mobile connection. */ val isSingleCarrier: StateFlow<Boolean> = mobileIconsInteractor.isSingleCarrier diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index ade417d7bf5c..64fcef51755d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -125,6 +125,7 @@ import com.android.systemui.charging.WiredChargingRippleController; import com.android.systemui.charging.WirelessChargingAnimation; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.communal.domain.interactor.CommunalInteractor; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.UiBackground; @@ -245,6 +246,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.Executor; +import java.util.function.Consumer; import javax.inject.Inject; import javax.inject.Named; @@ -551,6 +553,25 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private final WakefulnessLifecycle mWakefulnessLifecycle; protected final PowerInteractor mPowerInteractor; + private final CommunalInteractor mCommunalInteractor; + + /** + * True if the device is showing the glanceable hub. See + * {@link CommunalInteractor#isIdleOnCommunal()} for more details. + */ + private boolean mIsIdleOnCommunal = false; + private final Consumer<Boolean> mIdleOnCommunalConsumer = (Boolean idleOnCommunal) -> { + if (idleOnCommunal == mIsIdleOnCommunal) { + // Ignore initial value coming through the flow. + return; + } + + mIsIdleOnCommunal = idleOnCommunal; + // Trigger an update for the scrim state when we enter or exit glanceable hub, so that we + // can transition to/from ScrimState.GLANCEABLE_HUB if needed. + updateScrimController(); + }; + private boolean mNoAnimationOnNextBarModeChange; private final SysuiStatusBarStateController mStatusBarStateController; @@ -618,6 +639,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { ScreenLifecycle screenLifecycle, WakefulnessLifecycle wakefulnessLifecycle, PowerInteractor powerInteractor, + CommunalInteractor communalInteractor, SysuiStatusBarStateController statusBarStateController, Optional<Bubbles> bubblesOptional, Lazy<NoteTaskController> noteTaskControllerLazy, @@ -722,6 +744,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mScreenLifecycle = screenLifecycle; mWakefulnessLifecycle = wakefulnessLifecycle; mPowerInteractor = powerInteractor; + mCommunalInteractor = communalInteractor; mStatusBarStateController = statusBarStateController; mBubblesOptional = bubblesOptional; mNoteTaskControllerLazy = noteTaskControllerLazy; @@ -1051,6 +1074,10 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { //TODO(b/264502026) move the rest of the listeners here. mDeviceStateManager.registerCallback(mMainExecutor, new FoldStateListener(mContext, this::onFoldedStateChanged)); + + mJavaAdapter.alwaysCollectFlow( + mCommunalInteractor.isIdleOnCommunal(), + mIdleOnCommunalConsumer); } /** @@ -2795,6 +2822,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { // This will cancel the keyguardFadingAway animation if it is running. We need to do // this as otherwise it can remain pending and leave keyguard in a weird state. mUnlockScrimCallback.onCancelled(); + } else if (mIsIdleOnCommunal) { + mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB); } else if (mKeyguardStateController.isShowing() && !mKeyguardStateController.isOccluded() && !unlocking) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 3f20eaf45260..6f78604e996f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -17,7 +17,9 @@ package com.android.systemui.statusbar.phone; import static com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER; +import static com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB; import static com.android.systemui.keyguard.shared.model.KeyguardState.GONE; +import static com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN; import static com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER; import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; @@ -62,6 +64,7 @@ import com.android.systemui.dock.DockManager; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; +import com.android.systemui.keyguard.shared.model.KeyguardState; import com.android.systemui.keyguard.shared.model.ScrimAlpha; import com.android.systemui.keyguard.shared.model.TransitionState; import com.android.systemui.keyguard.shared.model.TransitionStep; @@ -292,6 +295,30 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump mScrimBehind.setViewAlpha(mBehindAlpha); }; + /** + * Consumer that fades the behind scrim in and out during the transition between the lock screen + * and the glanceable hub. + * + * While the lock screen is showing, the behind scrim is used to slightly darken the lock screen + * wallpaper underneath. Since the glanceable hub is under all of the scrims, we want to fade + * out the scrim so that the glanceable hub isn't darkened when it opens. + * + * {@link #applyState()} handles the scrim alphas once on the glanceable hub, this is only + * responsible for setting the behind alpha during the transition. + */ + private final Consumer<TransitionStep> mGlanceableHubConsumer = (TransitionStep step) -> { + final float baseAlpha = ScrimState.KEYGUARD.getBehindAlpha(); + final float transitionProgress = step.getValue(); + if (step.getTo() == KeyguardState.LOCKSCREEN) { + // Transitioning back to lock screen, fade in behind scrim again. + mBehindAlpha = baseAlpha * transitionProgress; + } else if (step.getTo() == GLANCEABLE_HUB) { + // Transitioning to glanceable hub, fade out behind scrim. + mBehindAlpha = baseAlpha * (1 - transitionProgress); + } + mScrimBehind.setViewAlpha(mBehindAlpha); + }; + Consumer<TransitionStep> mBouncerToGoneTransition; @Inject @@ -444,6 +471,14 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump mBouncerToGoneTransition, mMainDispatcher); collectFlow(behindScrim, mAlternateBouncerToGoneTransitionViewModel.getScrimAlpha(), mScrimAlphaConsumer, mMainDispatcher); + + // LOCKSCREEN<->GLANCEABLE_HUB + collectFlow(behindScrim, + mKeyguardTransitionInteractor.transition(LOCKSCREEN, GLANCEABLE_HUB), + mGlanceableHubConsumer, mMainDispatcher); + collectFlow(behindScrim, + mKeyguardTransitionInteractor.transition(GLANCEABLE_HUB, LOCKSCREEN), + mGlanceableHubConsumer, mMainDispatcher); } // TODO(b/270984686) recompute scrim height accurately, based on shade contents. @@ -815,9 +850,9 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump return; } mBouncerHiddenFraction = bouncerHiddenAmount; - if (mState == ScrimState.DREAMING) { - // Only the dreaming state requires this for the scrim calculation, so we should - // only trigger an update if dreaming. + if (mState == ScrimState.DREAMING || mState == ScrimState.GLANCEABLE_HUB) { + // The dreaming and glanceable hub states requires this for the scrim calculation, so we + // should only trigger an update in those states. applyAndDispatchState(); } } @@ -939,7 +974,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump } else if (mState == ScrimState.AUTH_SCRIMMED_SHADE) { mNotificationsAlpha = (float) Math.pow(getInterpolatedFraction(), 0.8f); } else if (mState == ScrimState.KEYGUARD || mState == ScrimState.SHADE_LOCKED - || mState == ScrimState.PULSING) { + || mState == ScrimState.PULSING || mState == ScrimState.GLANCEABLE_HUB) { Pair<Integer, Float> result = calculateBackStateForState(mState); int behindTint = result.first; float behindAlpha = result.second; @@ -950,6 +985,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump mTransitionToFullShadeProgress); behindTint = ColorUtils.blendARGB(behindTint, shadeResult.first, mTransitionToFullShadeProgress); + } else if (mState == ScrimState.GLANCEABLE_HUB && mTransitionToFullShadeProgress == 0.0f + && mBouncerHiddenFraction == KeyguardBouncerConstants.EXPANSION_HIDDEN) { + // Behind scrim should not be visible when idle on the glanceable hub and neither + // bouncer nor shade are showing. + behindAlpha = 0f; } mInFrontAlpha = mState.getFrontAlpha(); if (mClipsQsScrim) { @@ -965,6 +1005,13 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump } else if (mState == ScrimState.SHADE_LOCKED) { // going from KEYGUARD to SHADE_LOCKED state mNotificationsAlpha = getInterpolatedFraction(); + } else if (mState == ScrimState.GLANCEABLE_HUB + && mTransitionToFullShadeProgress == 0.0f) { + // Notification scrim should not be visible on the glanceable hub unless the + // shade is showing or transitioning in. Otherwise the notification scrim will + // be visible as the bouncer transitions in or after the notification shade + // closes. + mNotificationsAlpha = 0; } else { mNotificationsAlpha = Math.max(1.0f - getInterpolatedFraction(), mQsExpansion); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java index 61bd112121bc..f2a649ba2e32 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java @@ -296,6 +296,21 @@ public enum ScrimState { updateScrimColor(mScrimBehind, 1f /* alpha */, mBackgroundColor); } } + }, + + /** + * Device is locked or on dream and user has swiped from the right edge to enter the glanceable + * hub UI. From this state, the user can swipe from the left edge to go back to the lock screen + * or dream, as well as swipe down for the notifications and up for the bouncer. + */ + GLANCEABLE_HUB { + @Override + public void prepare(ScrimState previousState) { + // No scrims should be visible by default in this state. + mBehindAlpha = 0; + mNotifAlpha = 0; + mFrontAlpha = 0; + } }; boolean mBlankScreen = false; 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 a2aed988a423..9ce77e58a5f2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java @@ -28,10 +28,10 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import android.app.ActivityOptions.LaunchCookie; import android.app.Notification; import android.app.NotificationManager; import android.content.Intent; -import android.os.Binder; import android.os.Handler; import android.os.RemoteException; import android.os.UserHandle; @@ -146,7 +146,7 @@ public class RecordingServiceTest extends SysuiTestCase { @Test public void testLogStartPartialRecording() { - MediaProjectionCaptureTarget target = new MediaProjectionCaptureTarget(new Binder()); + MediaProjectionCaptureTarget target = new MediaProjectionCaptureTarget(new LaunchCookie()); Intent startIntent = RecordingService.getStartIntent(mContext, 0, 0, false, target); mRecordingService.onStartCommand(startIntent, 0, 0); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java index 9c4984ee4769..849a13be58ff 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java @@ -41,6 +41,8 @@ import static org.mockito.Mockito.when; import static java.util.Collections.emptySet; +import static kotlinx.coroutines.flow.FlowKt.flowOf; + import android.app.ActivityManager; import android.app.IWallpaperManager; import android.app.WallpaperManager; @@ -77,7 +79,6 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.logging.testing.FakeMetricsLogger; import com.android.internal.statusbar.IStatusBarService; import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.keyguard.TestScopeProvider; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.InitController; import com.android.systemui.SysuiTestCase; @@ -92,6 +93,10 @@ import com.android.systemui.charging.WiredChargingRippleController; import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.communal.data.repository.CommunalRepository; +import com.android.systemui.communal.domain.interactor.CommunalInteractor; +import com.android.systemui.communal.shared.model.CommunalSceneKey; +import com.android.systemui.communal.shared.model.ObservableCommunalTransitionState; import com.android.systemui.demomode.DemoModeController; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FakeFeatureFlags; @@ -102,6 +107,7 @@ import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel; +import com.android.systemui.kosmos.KosmosJavaAdapter; import com.android.systemui.log.LogBuffer; import com.android.systemui.navigationbar.NavigationBarController; import com.android.systemui.notetask.NoteTaskController; @@ -195,6 +201,8 @@ import java.util.Optional; import javax.inject.Provider; +import kotlinx.coroutines.test.TestScope; + @SmallTest @RunWith(AndroidTestingRunner.class) @RunWithLooper(setAsMainLooper = true) @@ -203,11 +211,17 @@ public class CentralSurfacesImplTest extends SysuiTestCase { private static final int FOLD_STATE_FOLDED = 0; private static final int FOLD_STATE_UNFOLDED = 1; + private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this); + private CentralSurfacesImpl mCentralSurfaces; private FakeMetricsLogger mMetricsLogger; private PowerManager mPowerManager; private VisualInterruptionDecisionProvider mVisualInterruptionDecisionProvider; + + private final TestScope mTestScope = mKosmos.getTestScope(); + private final CommunalInteractor mCommunalInteractor = mKosmos.getCommunalInteractor(); + private final CommunalRepository mCommunalRepository = mKosmos.getCommunalRepository(); @Mock private NotificationsController mNotificationsController; @Mock private LightBarController mLightBarController; @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; @@ -461,7 +475,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { new DisplayMetrics(), mMetricsLogger, mShadeLogger, - new JavaAdapter(TestScopeProvider.getTestScope()), + new JavaAdapter(mTestScope), mUiBgExecutor, mNotificationPanelViewController, mNotificationMediaManager, @@ -473,6 +487,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mScreenLifecycle, mWakefulnessLifecycle, mPowerInteractor, + mCommunalInteractor, mStatusBarStateController, Optional.of(mBubbles), () -> mNoteTaskController, @@ -821,6 +836,25 @@ public class CentralSurfacesImplTest extends SysuiTestCase { } @Test + public void testEnteringGlanceableHub_updatesScrim() { + // Transition to the glanceable hub. + mCommunalRepository.setTransitionState(flowOf(new ObservableCommunalTransitionState.Idle( + CommunalSceneKey.Communal.INSTANCE))); + mTestScope.getTestScheduler().runCurrent(); + + // ScrimState also transitions. + verify(mScrimController).transitionTo(ScrimState.GLANCEABLE_HUB); + + // Transition away from the glanceable hub. + mCommunalRepository.setTransitionState(flowOf(new ObservableCommunalTransitionState.Idle( + CommunalSceneKey.Blank.INSTANCE))); + mTestScope.getTestScheduler().runCurrent(); + + // ScrimState goes back to UNLOCKED. + verify(mScrimController).transitionTo(eq(ScrimState.UNLOCKED), any()); + } + + @Test public void testShowKeyguardImplementation_setsState() { when(mLockscreenUserManager.getCurrentProfiles()).thenReturn(new SparseArray<>()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index 423cc8478dda..3bde6e36a51f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -51,6 +51,7 @@ import android.content.res.TypedArray; import android.graphics.Color; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; +import android.testing.ViewUtils; import android.util.MathUtils; import android.view.View; @@ -59,13 +60,13 @@ import androidx.test.filters.SmallTest; import com.android.internal.colorextraction.ColorExtractor.GradientColors; import com.android.keyguard.BouncerPanelExpansionCalculator; import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.keyguard.TestScopeProvider; import com.android.systemui.DejankUtils; import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.ShadeInterpolation; import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants; import com.android.systemui.dock.DockManager; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; +import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository; import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.keyguard.shared.model.KeyguardState; @@ -73,6 +74,7 @@ import com.android.systemui.keyguard.shared.model.TransitionState; import com.android.systemui.keyguard.shared.model.TransitionStep; import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToGoneTransitionViewModel; import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel; +import com.android.systemui.kosmos.KosmosJavaAdapter; import com.android.systemui.scrim.ScrimView; import com.android.systemui.shade.transition.LargeScreenShadeInterpolator; import com.android.systemui.shade.transition.LinearLargeScreenShadeInterpolator; @@ -103,7 +105,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; -import kotlinx.coroutines.CoroutineDispatcher; import kotlinx.coroutines.test.TestScope; @RunWith(AndroidTestingRunner.class) @@ -112,13 +113,14 @@ import kotlinx.coroutines.test.TestScope; public class ScrimControllerTest extends SysuiTestCase { @Rule public Expect mExpect = Expect.create(); + private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this); private final FakeConfigurationController mConfigurationController = new FakeConfigurationController(); private final LargeScreenShadeInterpolator mLinearLargeScreenShadeInterpolator = new LinearLargeScreenShadeInterpolator(); - private final TestScope mTestScope = TestScopeProvider.getTestScope(); + private final TestScope mTestScope = mKosmos.getTestScope(); private final JavaAdapter mJavaAdapter = new JavaAdapter(mTestScope.getBackgroundScope()); private ScrimController mScrimController; @@ -145,10 +147,12 @@ public class ScrimControllerTest extends SysuiTestCase { @Mock private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel; @Mock private AlternateBouncerToGoneTransitionViewModel mAlternateBouncerToGoneTransitionViewModel; - @Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor; + private final KeyguardTransitionInteractor mKeyguardTransitionInteractor = + mKosmos.getKeyguardTransitionInteractor(); + private final FakeKeyguardTransitionRepository mKeyguardTransitionRepository = + mKosmos.getKeyguardTransitionRepository(); @Mock private KeyguardInteractor mKeyguardInteractor; private final FakeWallpaperRepository mWallpaperRepository = new FakeWallpaperRepository(); - @Mock private CoroutineDispatcher mMainDispatcher; @Mock private TypedArray mMockTypedArray; // TODO(b/204991468): Use a real PanelExpansionStateManager object once this bug is fixed. (The @@ -265,8 +269,6 @@ public class ScrimControllerTest extends SysuiTestCase { when(mDelayedWakeLockFactory.create(any(String.class))).thenReturn(mWakeLock); when(mDockManager.isDocked()).thenReturn(false); - when(mKeyguardTransitionInteractor.transition(any(), any())) - .thenReturn(emptyFlow()); when(mPrimaryBouncerToGoneTransitionViewModel.getScrimAlpha()) .thenReturn(emptyFlow()); when(mAlternateBouncerToGoneTransitionViewModel.getScrimAlpha()) @@ -292,13 +294,16 @@ public class ScrimControllerTest extends SysuiTestCase { mKeyguardTransitionInteractor, mKeyguardInteractor, mWallpaperRepository, - mMainDispatcher, + mKosmos.getTestDispatcher(), mLinearLargeScreenShadeInterpolator); mScrimController.start(); mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible); mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront); mScrimController.setAnimatorListener(mAnimatorListener); + // Attach behind scrim so flows that are collecting on it start running. + ViewUtils.attachView(mScrimBehind); + mScrimController.setHasBackdrop(false); mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(false); @@ -629,6 +634,164 @@ public class ScrimControllerTest extends SysuiTestCase { } @Test + public void lockscreenToHubTransition_setsBehindScrimAlpha() { + // Start on lockscreen. + mScrimController.transitionTo(ScrimState.KEYGUARD); + finishAnimationsImmediately(); + + // Behind scrim starts at default alpha. + final float transitionProgress = 0f; + float expectedAlpha = ScrimState.KEYGUARD.getBehindAlpha(); + mKeyguardTransitionRepository.sendTransitionStepJava(mKosmos.getTestScope(), + new TransitionStep( + KeyguardState.LOCKSCREEN, + KeyguardState.GLANCEABLE_HUB, + transitionProgress, + TransitionState.STARTED + ), true); + mTestScope.getTestScheduler().runCurrent(); + assertThat(mScrimBehind.getViewAlpha()).isEqualTo(expectedAlpha); + + // Scrim fades out as transition runs. + final float runningProgress = 0.2f; + expectedAlpha = (1 - runningProgress) * ScrimState.KEYGUARD.getBehindAlpha(); + mKeyguardTransitionRepository.sendTransitionStepJava(mKosmos.getTestScope(), + new TransitionStep( + KeyguardState.LOCKSCREEN, + KeyguardState.GLANCEABLE_HUB, + runningProgress, + TransitionState.RUNNING + ), true); + mTestScope.getTestScheduler().runCurrent(); + assertThat(mScrimBehind.getViewAlpha()).isEqualTo(expectedAlpha); + + // Scrim invisible at end of transition. + final float finishedProgress = 1f; + expectedAlpha = 0f; + mKeyguardTransitionRepository.sendTransitionStepJava(mKosmos.getTestScope(), + new TransitionStep( + KeyguardState.LOCKSCREEN, + KeyguardState.GLANCEABLE_HUB, + finishedProgress, + TransitionState.FINISHED + ), true); + mTestScope.getTestScheduler().runCurrent(); + assertThat(mScrimBehind.getViewAlpha()).isEqualTo(expectedAlpha); + } + + @Test + public void hubToLockscreenTransition_setsViewAlpha() { + // Start on glanceable hub. + mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB); + finishAnimationsImmediately(); + + // Behind scrim starts at 0 alpha. + final float transitionProgress = 0f; + float expectedAlpha = 0f; + mKeyguardTransitionRepository.sendTransitionStepJava(mKosmos.getTestScope(), + new TransitionStep( + KeyguardState.GLANCEABLE_HUB, + KeyguardState.LOCKSCREEN, + transitionProgress, + TransitionState.STARTED + ), true); + mTestScope.getTestScheduler().runCurrent(); + assertThat(mScrimBehind.getViewAlpha()).isEqualTo(expectedAlpha); + + // Scrim fades in as transition runs. + final float runningProgress = 0.2f; + expectedAlpha = runningProgress * ScrimState.KEYGUARD.getBehindAlpha(); + mKeyguardTransitionRepository.sendTransitionStepJava(mKosmos.getTestScope(), + new TransitionStep( + KeyguardState.GLANCEABLE_HUB, + KeyguardState.LOCKSCREEN, + runningProgress, + TransitionState.RUNNING + ), true); + mTestScope.getTestScheduler().runCurrent(); + assertThat(mScrimBehind.getViewAlpha()).isEqualTo(expectedAlpha); + + // Scrim at default visibility at end of transition. + final float finishedProgress = 1f; + expectedAlpha = finishedProgress * ScrimState.KEYGUARD.getBehindAlpha(); + mKeyguardTransitionRepository.sendTransitionStepJava(mKosmos.getTestScope(), + new TransitionStep( + KeyguardState.GLANCEABLE_HUB, + KeyguardState.LOCKSCREEN, + finishedProgress, + TransitionState.FINISHED + ), true); + mTestScope.getTestScheduler().runCurrent(); + assertThat(mScrimBehind.getViewAlpha()).isEqualTo(expectedAlpha); + } + + @Test + public void transitionToHub() { + mScrimController.setRawPanelExpansionFraction(0f); + mScrimController.setBouncerHiddenFraction(KeyguardBouncerConstants.EXPANSION_HIDDEN); + mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB); + finishAnimationsImmediately(); + + // All scrims transparent on the hub. + assertScrimAlpha(Map.of( + mScrimInFront, TRANSPARENT, + mNotificationsScrim, TRANSPARENT, + mScrimBehind, TRANSPARENT)); + } + + @Test + public void openBouncerOnHub() { + mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB); + + // Open the bouncer. + mScrimController.setRawPanelExpansionFraction(0f); + mScrimController.setBouncerHiddenFraction(KeyguardBouncerConstants.EXPANSION_VISIBLE); + finishAnimationsImmediately(); + + // Only behind widget is visible. + assertScrimAlpha(Map.of( + mScrimInFront, TRANSPARENT, + mNotificationsScrim, TRANSPARENT, + mScrimBehind, OPAQUE)); + + // Bouncer is closed. + mScrimController.setBouncerHiddenFraction(KeyguardBouncerConstants.EXPANSION_HIDDEN); + mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB); + finishAnimationsImmediately(); + + // All scrims are transparent. + assertScrimAlpha(Map.of( + mScrimInFront, TRANSPARENT, + mNotificationsScrim, TRANSPARENT, + mScrimBehind, TRANSPARENT)); + } + + @Test + public void openShadeOnHub() { + mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB); + + // Open the shade. + mScrimController.transitionTo(SHADE_LOCKED); + mScrimController.setQsPosition(1f, 0); + finishAnimationsImmediately(); + + // Shade scrims are visible. + assertScrimAlpha(Map.of( + mNotificationsScrim, OPAQUE, + mScrimInFront, TRANSPARENT, + mScrimBehind, OPAQUE)); + + mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB); + finishAnimationsImmediately(); + + // All scrims are transparent. + assertScrimAlpha(Map.of( + mScrimInFront, TRANSPARENT, + mNotificationsScrim, TRANSPARENT, + mScrimBehind, TRANSPARENT)); + } + + @Test public void onThemeChange_bouncerBehindTint_isUpdatedToSurfaceColor() { assertEquals(BOUNCER.getBehindTint(), 0x112233); mSurfaceColor = 0x223344; @@ -1001,7 +1164,7 @@ public class ScrimControllerTest extends SysuiTestCase { mKeyguardTransitionInteractor, mKeyguardInteractor, mWallpaperRepository, - mMainDispatcher, + mKosmos.getTestDispatcher(), mLinearLargeScreenShadeInterpolator); mScrimController.start(); mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible); @@ -1267,7 +1430,7 @@ public class ScrimControllerTest extends SysuiTestCase { ScrimState.UNINITIALIZED, ScrimState.KEYGUARD, BOUNCER, ScrimState.DREAMING, ScrimState.BOUNCER_SCRIMMED, ScrimState.BRIGHTNESS_MIRROR, ScrimState.UNLOCKED, SHADE_LOCKED, ScrimState.AUTH_SCRIMMED, - ScrimState.AUTH_SCRIMMED_SHADE)); + ScrimState.AUTH_SCRIMMED_SHADE, ScrimState.GLANCEABLE_HUB)); for (ScrimState state : ScrimState.values()) { if (!lowPowerModeStates.contains(state) && !regularStates.contains(state)) { diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt index 0c1dbfebfb34..e20a0ab4190e 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt @@ -28,9 +28,12 @@ import dagger.Module import java.util.UUID import javax.inject.Inject import junit.framework.Assert.fail +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.launch import kotlinx.coroutines.test.TestCoroutineScheduler import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent @@ -150,6 +153,15 @@ class FakeKeyguardTransitionRepository @Inject constructor() : KeyguardTransitio _transitions.emit(step) } + /** Version of [sendTransitionStep] that's usable from Java tests. */ + fun sendTransitionStepJava( + coroutineScope: CoroutineScope, + step: TransitionStep, + validateStep: Boolean = true + ): Job { + return coroutineScope.launch { sendTransitionStep(step, validateStep) } + } + suspend fun sendTransitionSteps( steps: List<TransitionStep>, testScope: TestScope, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt index 11f2938141b4..083de107c971 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt @@ -32,6 +32,8 @@ import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteract import com.android.systemui.flags.fakeFeatureFlagsClassic import com.android.systemui.jank.interactionJankMonitor import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository +import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository +import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.model.sceneContainerPlugin import com.android.systemui.plugins.statusbar.statusBarStateController import com.android.systemui.power.data.repository.fakePowerRepository @@ -61,6 +63,8 @@ class KosmosJavaAdapter( val bouncerRepository by lazy { kosmos.bouncerRepository } val communalRepository by lazy { kosmos.fakeCommunalRepository } val keyguardRepository by lazy { kosmos.fakeKeyguardRepository } + val keyguardTransitionRepository by lazy { kosmos.fakeKeyguardTransitionRepository } + val keyguardTransitionInteractor by lazy { kosmos.keyguardTransitionInteractor } val powerRepository by lazy { kosmos.fakePowerRepository } val clock by lazy { kosmos.systemClock } val mobileConnectionsRepository by lazy { kosmos.fakeMobileConnectionsRepository } diff --git a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java index 3aa9cc84e9f6..155c523a96a7 100644 --- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java +++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java @@ -132,7 +132,7 @@ public class CameraExtensionsProxyService extends Service { private static final String CAMERA_EXTENSION_VERSION_NAME = "androidx.camera.extensions.impl.ExtensionVersionImpl"; - private static final String LATEST_VERSION = "1.4.0"; + private static final String LATEST_VERSION = "1.5.0"; // No support for the init sequence private static final String NON_INIT_VERSION_PREFIX = "1.0"; // Support advanced API and latency queries @@ -1693,6 +1693,7 @@ public class CameraExtensionsProxyService extends Service { private final Size mSize; private final int mImageFormat; private final int mDataspace; + private final long mUsage; public OutputSurfaceImplStub(OutputSurface outputSurface) { mSurface = outputSurface.surface; @@ -1700,8 +1701,10 @@ public class CameraExtensionsProxyService extends Service { mImageFormat = outputSurface.imageFormat; if (mSurface != null) { mDataspace = SurfaceUtils.getSurfaceDataspace(mSurface); + mUsage = SurfaceUtils.getSurfaceUsage(mSurface); } else { mDataspace = -1; + mUsage = 0; } } @@ -1724,6 +1727,11 @@ public class CameraExtensionsProxyService extends Service { public int getDataspace() { return mDataspace; } + + @Override + public long getUsage() { + return mUsage; + } } private class PreviewExtenderImplStub extends IPreviewExtenderImpl.Stub implements @@ -2471,6 +2479,11 @@ public class CameraExtensionsProxyService extends Service { ret.size.height = imageReaderOutputConfig.getSize().getHeight(); ret.imageFormat = imageReaderOutputConfig.getImageFormat(); ret.capacity = imageReaderOutputConfig.getMaxImages(); + if (EFV_SUPPORTED) { + ret.usage = imageReaderOutputConfig.getUsage(); + } else { + ret.usage = 0; + } } else if (output instanceof MultiResolutionImageReaderOutputConfigImpl) { MultiResolutionImageReaderOutputConfigImpl multiResReaderConfig = (MultiResolutionImageReaderOutputConfigImpl) output; diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp index e013a3e41896..1ac69f6c4fc8 100644 --- a/ravenwood/Android.bp +++ b/ravenwood/Android.bp @@ -30,6 +30,9 @@ java_library { "junit-src/**/*.java", "junit-impl-src/**/*.java", ], + static_libs: [ + "androidx.test.monitor-for-device", + ], libs: [ "framework-minus-apex.ravenwood", "junit", @@ -61,3 +64,17 @@ java_host_for_device { "core-xml-for-host", ], } + +java_host_for_device { + name: "androidx.test.monitor-for-device", + libs: [ + "androidx.test.monitor-for-host", + ], +} + +java_device_for_host { + name: "androidx.test.monitor-for-host", + libs: [ + "androidx.test.monitor", + ], +} diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java index b3dbcde9d324..a797b1d61b2a 100644 --- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java +++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java @@ -16,12 +16,35 @@ package android.platform.test.ravenwood; +import android.app.Instrumentation; +import android.os.Bundle; import android.os.HandlerThread; import android.os.Looper; +import androidx.test.platform.app.InstrumentationRegistry; + +import java.io.PrintStream; +import java.util.Map; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + public class RavenwoodRuleImpl { private static final String MAIN_THREAD_NAME = "RavenwoodMain"; + /** + * When enabled, attempt to dump all thread stacks just before we hit the + * overall Tradefed timeout, to aid in debugging deadlocks. + */ + private static final boolean ENABLE_TIMEOUT_STACKS = false; + private static final int TIMEOUT_MILLIS = 9_000; + + private static final ScheduledExecutorService sTimeoutExecutor = + Executors.newScheduledThreadPool(1); + + private static ScheduledFuture<?> sPendingTimeout; + public static boolean isOnRavenwood() { return true; } @@ -41,9 +64,22 @@ public class RavenwoodRuleImpl { main.start(); Looper.setMainLooperForTest(main.getLooper()); } + + InstrumentationRegistry.registerInstance(new Instrumentation(), Bundle.EMPTY); + + if (ENABLE_TIMEOUT_STACKS) { + sPendingTimeout = sTimeoutExecutor.schedule(RavenwoodRuleImpl::dumpStacks, + TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); + } } public static void reset(RavenwoodRule rule) { + if (ENABLE_TIMEOUT_STACKS) { + sPendingTimeout.cancel(false); + } + + InstrumentationRegistry.registerInstance(null, Bundle.EMPTY); + if (rule.mProvideMainThread) { Looper.getMainLooper().quit(); Looper.clearMainLooperForTest(); @@ -55,4 +91,19 @@ public class RavenwoodRuleImpl { android.os.Binder.reset$ravenwood(); android.os.Process.reset$ravenwood(); } + + private static void dumpStacks() { + final PrintStream out = System.err; + out.println("-----BEGIN ALL THREAD STACKS-----"); + final Map<Thread, StackTraceElement[]> stacks = Thread.getAllStackTraces(); + for (Map.Entry<Thread, StackTraceElement[]> stack : stacks.entrySet()) { + out.println(); + Thread t = stack.getKey(); + out.println(t.toString() + " ID=" + t.getId()); + for (StackTraceElement e : stack.getValue()) { + out.println("\tat " + e); + } + } + out.println("-----END ALL THREAD STACKS-----"); + } } diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodClassRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodClassRule.java index 68b163ede89c..8d76970f9ad4 100644 --- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodClassRule.java +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodClassRule.java @@ -19,6 +19,7 @@ package android.platform.test.ravenwood; import static android.platform.test.ravenwood.RavenwoodRule.ENABLE_PROBE_IGNORED; import static android.platform.test.ravenwood.RavenwoodRule.IS_ON_RAVENWOOD; import static android.platform.test.ravenwood.RavenwoodRule.shouldEnableOnRavenwood; +import static android.platform.test.ravenwood.RavenwoodRule.shouldStillIgnoreInProbeIgnoreMode; import android.platform.test.annotations.DisabledOnRavenwood; import android.platform.test.annotations.EnabledOnRavenwood; @@ -45,6 +46,7 @@ public class RavenwoodClassRule implements TestRule { } if (ENABLE_PROBE_IGNORED) { + Assume.assumeFalse(shouldStillIgnoreInProbeIgnoreMode(description)); // Pass through to possible underlying RavenwoodRule for both environment // configuration and handling method-level annotations return base; diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java index 952ee0e184b1..1e7cbf6d802d 100644 --- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java +++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodRule.java @@ -27,7 +27,9 @@ import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; +import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; +import java.util.regex.Pattern; /** * {@code @Rule} that configures the Ravenwood test environment. This rule has no effect when @@ -55,6 +57,43 @@ public class RavenwoodRule implements TestRule { static final boolean ENABLE_PROBE_IGNORED = "1".equals( System.getenv("RAVENWOOD_RUN_DISABLED_TESTS")); + /** + * When using ENABLE_PROBE_IGNORED, you may still want to skip certain tests, + * for example because the test would crash the JVM. + * + * This regex defines the tests that should still be disabled even if ENABLE_PROBE_IGNORED + * is set. + * + * Before running each test class and method, we check if this pattern can be found in + * the full test name (either [class full name], or [class full name] + "#" + [method name]), + * and if so, we skip it. + * + * For example, if you want to skip an entire test class, use: + * RAVENWOOD_REALLY_DISABLE='\.CustomTileDefaultsRepositoryTest$' + * + * For example, if you want to skip an entire test class, use: + * RAVENWOOD_REALLY_DISABLE='\.CustomTileDefaultsRepositoryTest#testSimple$' + * + * To ignore multiple classes, use (...|...), for example: + * RAVENWOOD_REALLY_DISABLE='\.(ClassA|ClassB)$' + * + * Because we use a regex-find, setting "." would disable all tests. + */ + private static final Pattern REALLY_DISABLE_PATTERN = Pattern.compile( + Objects.requireNonNullElse(System.getenv("RAVENWOOD_REALLY_DISABLE"), "")); + + private static final boolean ENABLE_REALLY_DISABLE_PATTERN = + !REALLY_DISABLE_PATTERN.pattern().isEmpty(); + + static { + if (ENABLE_PROBE_IGNORED) { + System.out.println("$RAVENWOOD_RUN_DISABLED_TESTS enabled: force running all tests"); + if (ENABLE_REALLY_DISABLE_PATTERN) { + System.out.println("$RAVENWOOD_REALLY_DISABLE=" + REALLY_DISABLE_PATTERN.pattern()); + } + } + } + private static final int SYSTEM_UID = 1000; private static final int NOBODY_UID = 9999; private static final int FIRST_APPLICATION_UID = 10000; @@ -203,6 +242,21 @@ public class RavenwoodRule implements TestRule { return true; } + static boolean shouldStillIgnoreInProbeIgnoreMode(Description description) { + if (!ENABLE_REALLY_DISABLE_PATTERN) { + return false; + } + + final var fullname = description.getTestClass().getName() + + (description.isTest() ? "#" + description.getMethodName() : ""); + + if (REALLY_DISABLE_PATTERN.matcher(fullname).find()) { + System.out.println("Still ignoring " + fullname); + return true; + } + return false; + } + @Override public Statement apply(Statement base, Description description) { // No special treatment when running outside Ravenwood; run tests as-is @@ -245,6 +299,7 @@ public class RavenwoodRule implements TestRule { return new Statement() { @Override public void evaluate() throws Throwable { + Assume.assumeFalse(shouldStillIgnoreInProbeIgnoreMode(description)); RavenwoodRuleImpl.init(RavenwoodRule.this); try { base.evaluate(); diff --git a/ravenwood/ravenwood-annotation-allowed-classes.txt b/ravenwood/ravenwood-annotation-allowed-classes.txt index eaf01a32592e..b775f9ad47ad 100644 --- a/ravenwood/ravenwood-annotation-allowed-classes.txt +++ b/ravenwood/ravenwood-annotation-allowed-classes.txt @@ -72,6 +72,7 @@ android.os.Process android.os.ServiceSpecificException android.os.SystemClock android.os.SystemProperties +android.os.TestLooperManager android.os.ThreadLocalWorkSource android.os.TimestampedValue android.os.Trace @@ -141,6 +142,8 @@ android.graphics.RectF android.content.ContentProvider +android.app.Instrumentation + android.metrics.LogMaker android.view.Display$HdrCapabilities diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java index b87184aa5582..416b36feb94d 100644 --- a/services/core/java/com/android/server/BinderCallsStatsService.java +++ b/services/core/java/com/android/server/BinderCallsStatsService.java @@ -288,22 +288,23 @@ public class BinderCallsStatsService extends Binder { CachedDeviceState.Readonly.class); mBinderCallsStats.setDeviceState(deviceState); - BatteryStatsInternal batteryStatsInternal = getLocalService( - BatteryStatsInternal.class); - mBinderCallsStats.setCallStatsObserver(new BinderInternal.CallStatsObserver() { - @Override - public void noteCallStats(int workSourceUid, long incrementalCallCount, - Collection<BinderCallsStats.CallStat> callStats) { - batteryStatsInternal.noteBinderCallStats(workSourceUid, - incrementalCallCount, callStats); - } - - @Override - public void noteBinderThreadNativeIds(int[] binderThreadNativeTids) { - batteryStatsInternal.noteBinderThreadNativeIds(binderThreadNativeTids); - } - }); - + if (!com.android.server.power.optimization.Flags.disableSystemServicePowerAttr()) { + BatteryStatsInternal batteryStatsInternal = getLocalService( + BatteryStatsInternal.class); + mBinderCallsStats.setCallStatsObserver(new BinderInternal.CallStatsObserver() { + @Override + public void noteCallStats(int workSourceUid, long incrementalCallCount, + Collection<BinderCallsStats.CallStat> callStats) { + batteryStatsInternal.noteBinderCallStats(workSourceUid, + incrementalCallCount, callStats); + } + + @Override + public void noteBinderThreadNativeIds(int[] binderThreadNativeTids) { + batteryStatsInternal.noteBinderThreadNativeIds(binderThreadNativeTids); + } + }); + } // It needs to be called before mService.systemReady to make sure the observer is // initialized before installing it. mWorkSourceProvider.systemReady(getContext()); diff --git a/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java b/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java index c3916422159e..f619ca3f66a2 100644 --- a/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java +++ b/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java @@ -27,6 +27,7 @@ import android.media.projection.MediaProjectionManager; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; +import android.os.Trace; import android.os.UserHandle; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationListenerService.RankingMap; @@ -49,12 +50,12 @@ public final class SensitiveContentProtectionManagerService extends SystemServic private static final String TAG = "SensitiveContentProtect"; private static final boolean DEBUG = false; - @VisibleForTesting - NotificationListener mNotificationListener; + @VisibleForTesting NotificationListener mNotificationListener; private @Nullable MediaProjectionManager mProjectionManager; private @Nullable WindowManagerInternal mWindowManager; final Object mSensitiveContentProtectionLock = new Object(); + @GuardedBy("mSensitiveContentProtectionLock") private boolean mProjectionActive = false; @@ -63,13 +64,24 @@ public final class SensitiveContentProtectionManagerService extends SystemServic @Override public void onStart(MediaProjectionInfo info) { if (DEBUG) Log.d(TAG, "onStart projection: " + info); - onProjectionStart(); + Trace.beginSection( + "SensitiveContentProtectionManagerService.onProjectionStart"); + try { + onProjectionStart(); + } finally { + Trace.endSection(); + } } @Override public void onStop(MediaProjectionInfo info) { if (DEBUG) Log.d(TAG, "onStop projection: " + info); - onProjectionEnd(); + Trace.beginSection("SensitiveContentProtectionManagerService.onProjectionStop"); + try { + onProjectionEnd(); + } finally { + Trace.endSection(); + } } }; @@ -94,8 +106,7 @@ public final class SensitiveContentProtectionManagerService extends SystemServic } @VisibleForTesting - void init(MediaProjectionManager projectionManager, - WindowManagerInternal windowManager) { + void init(MediaProjectionManager projectionManager, WindowManagerInternal windowManager) { if (DEBUG) Log.d(TAG, "init"); checkNotNull(projectionManager, "Failed to get valid MediaProjectionManager"); @@ -109,7 +120,8 @@ public final class SensitiveContentProtectionManagerService extends SystemServic mProjectionManager.addCallback(mProjectionCallback, new Handler(Looper.getMainLooper())); try { - mNotificationListener.registerAsSystemService(getContext(), + mNotificationListener.registerAsSystemService( + getContext(), new ComponentName(getContext(), NotificationListener.class), UserHandle.USER_ALL); } catch (RemoteException e) { @@ -174,8 +186,8 @@ public final class SensitiveContentProtectionManagerService extends SystemServic } // notify windowmanager of any currently posted sensitive content notifications - ArraySet<PackageInfo> packageInfos = getSensitivePackagesFromNotifications( - notifications, rankingMap); + ArraySet<PackageInfo> packageInfos = + getSensitivePackagesFromNotifications(notifications, rankingMap); mWindowManager.addBlockScreenCaptureForApps(packageInfos); } @@ -197,8 +209,8 @@ public final class SensitiveContentProtectionManagerService extends SystemServic return sensitivePackages; } - private PackageInfo getSensitivePackageFromNotification(StatusBarNotification sbn, - RankingMap rankingMap) { + private PackageInfo getSensitivePackageFromNotification( + StatusBarNotification sbn, RankingMap rankingMap) { if (sbn == null) { Log.w(TAG, "Unable to protect null notification"); return null; @@ -220,38 +232,55 @@ public final class SensitiveContentProtectionManagerService extends SystemServic @Override public void onListenerConnected() { super.onListenerConnected(); - // Projection started before notification listener was connected - synchronized (mSensitiveContentProtectionLock) { - if (mProjectionActive) { - updateAppsThatShouldBlockScreenCapture(); + Trace.beginSection("SensitiveContentProtectionManagerService.onListenerConnected"); + try { + // Projection started before notification listener was connected + synchronized (mSensitiveContentProtectionLock) { + if (mProjectionActive) { + updateAppsThatShouldBlockScreenCapture(); + } } + } finally { + Trace.endSection(); } } @Override public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) { super.onNotificationPosted(sbn, rankingMap); - synchronized (mSensitiveContentProtectionLock) { - if (!mProjectionActive) { - return; - } - - // notify windowmanager of any currently posted sensitive content notifications - PackageInfo packageInfo = getSensitivePackageFromNotification(sbn, rankingMap); - - if (packageInfo != null) { - mWindowManager.addBlockScreenCaptureForApps(new ArraySet(Set.of(packageInfo))); + Trace.beginSection("SensitiveContentProtectionManagerService.onNotificationPosted"); + try { + synchronized (mSensitiveContentProtectionLock) { + if (!mProjectionActive) { + return; + } + + // notify windowmanager of any currently posted sensitive content notifications + PackageInfo packageInfo = getSensitivePackageFromNotification(sbn, rankingMap); + + if (packageInfo != null) { + mWindowManager.addBlockScreenCaptureForApps( + new ArraySet(Set.of(packageInfo))); + } } + } finally { + Trace.endSection(); } } @Override public void onNotificationRankingUpdate(RankingMap rankingMap) { super.onNotificationRankingUpdate(rankingMap); - synchronized (mSensitiveContentProtectionLock) { - if (mProjectionActive) { - updateAppsThatShouldBlockScreenCapture(rankingMap); + Trace.beginSection( + "SensitiveContentProtectionManagerService.onNotificationRankingUpdate"); + try { + synchronized (mSensitiveContentProtectionLock) { + if (mProjectionActive) { + updateAppsThatShouldBlockScreenCapture(rankingMap); + } } + } finally { + Trace.endSection(); } } } diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 3487ae3c6e6c..4f46ecdb9228 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -422,7 +422,9 @@ public final class BatteryStatsService extends IBatteryStats.Stub mStats.setExternalStatsSyncLocked(mWorker); mStats.setRadioScanningTimeoutLocked(mContext.getResources().getInteger( com.android.internal.R.integer.config_radioScanningTimeout) * 1000L); - mStats.startTrackingSystemServerCpuTime(); + if (!Flags.disableSystemServicePowerAttr()) { + mStats.startTrackingSystemServerCpuTime(); + } mAggregatedPowerStatsConfig = createAggregatedPowerStatsConfig(); mPowerStatsStore = new PowerStatsStore(systemDir, mHandler, mAggregatedPowerStatsConfig); diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index df179a9b6d65..5b23364cf546 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -138,6 +138,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.NoSuchElementException; import java.util.Objects; import java.util.Random; import java.util.Set; @@ -3870,8 +3871,12 @@ public class SyncManager { final SyncStorageEngine.EndPoint info = syncOperation.target; if (activeSyncContext.mIsLinkedToDeath) { - activeSyncContext.mSyncAdapter.asBinder().unlinkToDeath(activeSyncContext, 0); - activeSyncContext.mIsLinkedToDeath = false; + try { + activeSyncContext.mSyncAdapter.asBinder().unlinkToDeath(activeSyncContext, 0); + activeSyncContext.mIsLinkedToDeath = false; + } catch (NoSuchElementException e) { + Slog.wtf(TAG, "Failed to unlink active sync adapter to death", e); + } } final long elapsedTime = SystemClock.elapsedRealtime() - activeSyncContext.mStartTime; String historyMessage; diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 245fcea06eac..93addcd84d12 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -1656,14 +1656,16 @@ public final class DisplayManagerService extends SystemService { ContentRecordingSession session = null; try { if (projection != null) { - IBinder launchCookie = projection.getLaunchCookie(); - if (launchCookie == null) { + IBinder taskWindowContainerToken = projection.getLaunchCookie() == null ? null + : projection.getLaunchCookie().binder; + if (taskWindowContainerToken == null) { // Record a particular display. session = ContentRecordingSession.createDisplaySession( virtualDisplayConfig.getDisplayIdToMirror()); } else { // Record a single task indicated by the launch cookie. - session = ContentRecordingSession.createTaskSession(launchCookie); + session = ContentRecordingSession.createTaskSession( + taskWindowContainerToken); } } } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/inputmethod/ClientController.java b/services/core/java/com/android/server/inputmethod/ClientController.java index ece236a5f18c..86f4db959409 100644 --- a/services/core/java/com/android/server/inputmethod/ClientController.java +++ b/services/core/java/com/android/server/inputmethod/ClientController.java @@ -17,6 +17,7 @@ package com.android.server.inputmethod; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.pm.PackageManagerInternal; import android.os.IBinder; import android.os.RemoteException; @@ -29,6 +30,7 @@ import com.android.internal.inputmethod.IRemoteInputConnection; import java.util.ArrayList; import java.util.List; +import java.util.function.Consumer; /** * Store and manage {@link InputMethodManagerService} clients. This class was designed to be a @@ -62,7 +64,7 @@ final class ClientController { // TODO(b/314150112): Make this field private when breaking the cycle with IMMS. @GuardedBy("ImfLock.class") - final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<>(); + private final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<>(); @GuardedBy("ImfLock.class") private final List<ClientControllerCallback> mCallbacks = new ArrayList<>(); @@ -145,6 +147,19 @@ final class ClientController { } @GuardedBy("ImfLock.class") + @Nullable + ClientState getClient(IBinder binder) { + return mClients.get(binder); + } + + @GuardedBy("ImfLock.class") + void forAllClients(Consumer<ClientState> consumer) { + for (int i = 0; i < mClients.size(); i++) { + consumer.accept(mClients.valueAt(i)); + } + } + + @GuardedBy("ImfLock.class") boolean verifyClientAndPackageMatch( @NonNull IInputMethodClient client, @NonNull String packageName) { final ClientState cs = mClients.get(client.asBinder()); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 4767ebd0aab0..f031b7b677ac 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -205,6 +205,7 @@ import java.util.WeakHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; import java.util.function.IntConsumer; /** @@ -270,7 +271,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @NonNull private final String[] mNonPreemptibleInputMethods; - // TODO(b/314150112): Move this to ClientController. @UserIdInt private int mLastSwitchUserId; @@ -1819,10 +1819,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } mLastSwitchUserId = newUserId; - if (mIsInteractive && clientToBeReset != null) { - final ClientState cs = - mClientController.mClients.get(clientToBeReset.asBinder()); + final ClientState cs = mClientController.getClient(clientToBeReset.asBinder()); if (cs == null) { // The client is already gone. return; @@ -2165,26 +2163,25 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub /** * Hide the IME if the removed user is the current user. */ + @GuardedBy("ImfLock.class") private void onClientRemoved(ClientState client) { - synchronized (ImfLock.class) { - clearClientSessionLocked(client); - clearClientSessionForAccessibilityLocked(client); - if (mCurClient == client) { - hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */, 0 /* flags */, - null /* resultReceiver */, SoftInputShowHideReason.HIDE_REMOVE_CLIENT); - if (mBoundToMethod) { - mBoundToMethod = false; - IInputMethodInvoker curMethod = getCurMethodLocked(); - if (curMethod != null) { - // When we unbind input, we are unbinding the client, so we always - // unbind ime and a11y together. - curMethod.unbindInput(); - AccessibilityManagerInternal.get().unbindInput(); - } + clearClientSessionLocked(client); + clearClientSessionForAccessibilityLocked(client); + if (mCurClient == client) { + hideCurrentInputLocked(mCurFocusedWindow, null /* statsToken */, 0 /* flags */, + null /* resultReceiver */, SoftInputShowHideReason.HIDE_REMOVE_CLIENT); + if (mBoundToMethod) { + mBoundToMethod = false; + IInputMethodInvoker curMethod = getCurMethodLocked(); + if (curMethod != null) { + // When we unbind input, we are unbinding the client, so we always + // unbind ime and a11y together. + curMethod.unbindInput(); + AccessibilityManagerInternal.get().unbindInput(); } - mBoundToAccessibility = false; - mCurClient = null; } + mBoundToAccessibility = false; + mCurClient = null; if (mCurFocusedWindowClient == client) { mCurFocusedWindowClient = null; mCurFocusedWindowEditorInfo = null; @@ -2192,7 +2189,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } } - // TODO(b/314150112): Move this to ClientController. @GuardedBy("ImfLock.class") void unbindCurrentClientLocked(@UnbindReason int unbindClientReason) { if (mCurClient != null) { @@ -2883,11 +2879,16 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @GuardedBy("ImfLock.class") void clearClientSessionsLocked() { if (getCurMethodLocked() != null) { - final int numClients = mClientController.mClients.size(); - for (int i = 0; i < numClients; ++i) { - clearClientSessionLocked(mClientController.mClients.valueAt(i)); - clearClientSessionForAccessibilityLocked(mClientController.mClients.valueAt(i)); - } + // TODO(b/322816970): Replace this with lambda. + mClientController.forAllClients(new Consumer<ClientState>() { + + @GuardedBy("ImfLock.class") + @Override + public void accept(ClientState c) { + clearClientSessionLocked(c); + clearClientSessionForAccessibilityLocked(c); + } + }); finishSessionLocked(mEnabledSession); for (int i = 0; i < mEnabledAccessibilitySessions.size(); i++) { @@ -3732,9 +3733,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub return InputBindResult.INVALID_USER; } - final ClientState cs = mClientController.mClients.get(client.asBinder()); + final ClientState cs = mClientController.getClient(client.asBinder()); if (cs == null) { - throw new IllegalArgumentException("unknown client " + client.asBinder()); + throw new IllegalArgumentException("Unknown client " + client.asBinder()); } final int imeClientFocus = mWindowManagerInternal.hasInputMethodClientFocus( @@ -3906,8 +3907,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub // We need to check if this is the current client with // focus in the window manager, to allow this call to // be made before input is started in it. - final ClientState cs = - mClientController.mClients.get(client.asBinder()); + final ClientState cs = mClientController.getClient(client.asBinder()); if (cs == null) { ImeTracker.forLogging().onFailed(statsToken, ImeTracker.PHASE_SERVER_CLIENT_KNOWN); throw new IllegalArgumentException("unknown client " + client.asBinder()); @@ -4518,16 +4518,17 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @Override public void startImeTrace() { super.startImeTrace_enforcePermission(); - ImeTracing.getInstance().startTrace(null /* printwriter */); - ArrayMap<IBinder, ClientState> clients; synchronized (ImfLock.class) { - clients = new ArrayMap<>(mClientController.mClients); - } - for (ClientState state : clients.values()) { - if (state != null) { - state.mClient.setImeTraceEnabled(true /* enabled */); - } + // TODO(b/322816970): Replace this with lambda. + mClientController.forAllClients(new Consumer<ClientState>() { + + @GuardedBy("ImfLock.class") + @Override + public void accept(ClientState c) { + c.mClient.setImeTraceEnabled(true /* enabled */); + } + }); } } @@ -4538,14 +4539,16 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub super.stopImeTrace_enforcePermission(); ImeTracing.getInstance().stopTrace(null /* printwriter */); - ArrayMap<IBinder, ClientState> clients; synchronized (ImfLock.class) { - clients = new ArrayMap<>(mClientController.mClients); - } - for (ClientState state : clients.values()) { - if (state != null) { - state.mClient.setImeTraceEnabled(false /* enabled */); - } + // TODO(b/322816970): Replace this with lambda. + mClientController.forAllClients(new Consumer<ClientState>() { + + @GuardedBy("ImfLock.class") + @Override + public void accept(ClientState c) { + c.mClient.setImeTraceEnabled(false /* enabled */); + } + }); } } @@ -5779,11 +5782,15 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub // We only have sessions when we bound to an input method. Remove this session // from all clients. if (getCurMethodLocked() != null) { - final int numClients = mClientController.mClients.size(); - for (int i = 0; i < numClients; ++i) { - clearClientSessionForAccessibilityLocked( - mClientController.mClients.valueAt(i), accessibilityConnectionId); - } + // TODO(b/322816970): Replace this with lambda. + mClientController.forAllClients(new Consumer<ClientState>() { + + @GuardedBy("ImfLock.class") + @Override + public void accept(ClientState c) { + clearClientSessionForAccessibilityLocked(c, accessibilityConnectionId); + } + }); AccessibilitySessionState session = mEnabledAccessibilitySessions.get( accessibilityConnectionId); if (session != null) { @@ -5967,19 +5974,26 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub p.println(" InputMethod #" + i + ":"); info.dump(p, " "); } + // Dump ClientController#mClients p.println(" ClientStates:"); - // TODO(b/314150112): move client related dump info to ClientController#dump - final int numClients = mClientController.mClients.size(); - for (int i = 0; i < numClients; ++i) { - final ClientState ci = mClientController.mClients.valueAt(i); - p.println(" " + ci + ":"); - p.println(" client=" + ci.mClient); - p.println(" fallbackInputConnection=" + ci.mFallbackInputConnection); - p.println(" sessionRequested=" + ci.mSessionRequested); - p.println(" sessionRequestedForAccessibility=" - + ci.mSessionRequestedForAccessibility); - p.println(" curSession=" + ci.mCurSession); - } + // TODO(b/322816970): Replace this with lambda. + mClientController.forAllClients(new Consumer<ClientState>() { + + @GuardedBy("ImfLock.class") + @Override + public void accept(ClientState c) { + p.println(" " + c + ":"); + p.println(" client=" + c.mClient); + p.println(" fallbackInputConnection=" + + c.mFallbackInputConnection); + p.println(" sessionRequested=" + + c.mSessionRequested); + p.println( + " sessionRequestedForAccessibility=" + + c.mSessionRequestedForAccessibility); + p.println(" curSession=" + c.mCurSession); + } + }); p.println(" mCurMethodId=" + getSelectedMethodIdLocked()); client = mCurClient; p.println(" mCurClient=" + client + " mCurSeq=" + getSequenceNumberLocked()); @@ -6583,14 +6597,16 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } } boolean isImeTraceEnabled = ImeTracing.getInstance().isEnabled(); - ArrayMap<IBinder, ClientState> clients; synchronized (ImfLock.class) { - clients = new ArrayMap<>(mClientController.mClients); - } - for (ClientState state : clients.values()) { - if (state != null) { - state.mClient.setImeTraceEnabled(isImeTraceEnabled); - } + // TODO(b/322816970): Replace this with lambda. + mClientController.forAllClients(new Consumer<ClientState>() { + + @GuardedBy("ImfLock.class") + @Override + public void accept(ClientState c) { + c.mClient.setImeTraceEnabled(isImeTraceEnabled); + } + }); } return ShellCommandResult.SUCCESS; } diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index 1d516e2931d7..7fabdf293b39 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -34,6 +34,8 @@ import android.app.ForegroundServiceDelegationOptions; import android.app.KeyguardManager; import android.app.NotificationManager; import android.app.PendingIntent; +import android.app.usage.UsageStatsManager; +import android.app.usage.UsageStatsManagerInternal; import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -68,6 +70,7 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.Message; +import android.os.PersistableBundle; import android.os.PowerExemptionManager; import android.os.PowerManager; import android.os.Process; @@ -100,7 +103,9 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Set; /** * System implementation of MediaSessionManager @@ -145,6 +150,8 @@ public class MediaSessionService extends SystemService implements Monitor { private AudioManager mAudioManager; private boolean mHasFeatureLeanback; private ActivityManagerInternal mActivityManagerInternal; + private UsageStatsManagerInternal mUsageStatsManagerInternal; + private final Set<Integer> mUserEngagingSessions = new HashSet<>(); // The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile) // It's always not null after the MediaSessionService is started. @@ -230,6 +237,7 @@ public class MediaSessionService extends SystemService implements Monitor { mContext.registerReceiver(mNotificationListenerEnabledChangedReceiver, filter); mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); + mUsageStatsManagerInternal = LocalServices.getService(UsageStatsManagerInternal.class); } @Override @@ -582,12 +590,43 @@ public class MediaSessionService extends SystemService implements Monitor { if (allowRunningInForeground) { mActivityManagerInternal.startForegroundServiceDelegate( foregroundServiceDelegationOptions, /* connection= */ null); + reportMediaInteractionEvent(record, /* userEngaged= */ true); } else { mActivityManagerInternal.stopForegroundServiceDelegate( foregroundServiceDelegationOptions); + reportMediaInteractionEvent(record, /* userEngaged= */ false); } } + private void reportMediaInteractionEvent(MediaSessionRecordImpl record, boolean userEngaged) { + if (!android.app.usage.Flags.userInteractionTypeApi()) { + return; + } + + String packageName = record.getPackageName(); + int sessionUid = record.getUid(); + String actionToLog = null; + if (userEngaged) { + if (!mUserEngagingSessions.contains(sessionUid)) { + actionToLog = "start"; + } + mUserEngagingSessions.add(sessionUid); + } else { + if (mUserEngagingSessions.contains(sessionUid)) { + actionToLog = "stop"; + } + mUserEngagingSessions.remove(sessionUid); + } + + if (actionToLog != null) { + PersistableBundle extras = new PersistableBundle(); + extras.putString(UsageStatsManager.EXTRA_EVENT_CATEGORY, "android.media"); + extras.putString(UsageStatsManager.EXTRA_EVENT_ACTION, actionToLog); + mUsageStatsManagerInternal.reportUserInteractionEvent( + packageName, record.getUserId(), extras); + } + } + void tempAllowlistTargetPkgIfPossible(int targetUid, String targetPackage, int callingPid, int callingUid, String callingPackage, String reason) { final long token = Binder.clearCallingIdentity(); diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java index 978f46808e3b..bbb19e351b5d 100644 --- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java +++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java @@ -35,6 +35,7 @@ import android.annotation.EnforcePermission; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManagerInternal; +import android.app.ActivityOptions.LaunchCookie; import android.app.AppOpsManager; import android.app.IProcessObserver; import android.app.compat.CompatChanges; @@ -548,8 +549,11 @@ public final class MediaProjectionManagerService extends SystemService DEFAULT_DISPLAY)); break; case RECORD_CONTENT_TASK: - setReviewedConsentSessionLocked(ContentRecordingSession.createTaskSession( - mProjectionGrant.getLaunchCookie())); + IBinder taskWindowContainerToken = + mProjectionGrant.getLaunchCookie() == null ? null + : mProjectionGrant.getLaunchCookie().binder; + setReviewedConsentSessionLocked( + ContentRecordingSession.createTaskSession(taskWindowContainerToken)); break; } } @@ -973,7 +977,7 @@ public final class MediaProjectionManagerService extends SystemService private IBinder mToken; private IBinder.DeathRecipient mDeathEater; private boolean mRestoreSystemAlertWindow; - private IBinder mLaunchCookie = null; + private LaunchCookie mLaunchCookie = null; // Values for tracking token validity. // Timeout value to compare creation time against. @@ -1186,14 +1190,14 @@ public final class MediaProjectionManagerService extends SystemService @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_MEDIA_PROJECTION) @Override // Binder call - public void setLaunchCookie(IBinder launchCookie) { + public void setLaunchCookie(LaunchCookie launchCookie) { setLaunchCookie_enforcePermission(); mLaunchCookie = launchCookie; } @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_MEDIA_PROJECTION) @Override // Binder call - public IBinder getLaunchCookie() { + public LaunchCookie getLaunchCookie() { getLaunchCookie_enforcePermission(); return mLaunchCookie; } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 135bd4f911f9..9617098d1016 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -7807,7 +7807,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE; mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS | ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS - | ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES; + | ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES + | ActivityInfo.FLAG_HARDWARE_ACCELERATED; mResolveActivity.theme = 0; mResolveActivity.exported = true; mResolveActivity.enabled = true; @@ -7841,7 +7842,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService mResolveActivity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NEVER; mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS | ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY - | ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES; + | ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES + | ActivityInfo.FLAG_HARDWARE_ACCELERATED; mResolveActivity.theme = R.style.Theme_Material_Dialog_Alert; mResolveActivity.exported = true; mResolveActivity.enabled = true; diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java index 09b19e6196a1..25e749f08782 100644 --- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java +++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java @@ -138,6 +138,7 @@ import com.android.internal.util.XmlUtils; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; import com.android.net.module.util.NetworkCapabilitiesUtils; +import com.android.server.power.optimization.Flags; import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes; import libcore.util.EmptyArray; @@ -185,7 +186,8 @@ public class BatteryStatsImpl extends BatteryStats { // TODO: remove "tcp" from network methods, since we measure total stats. // Current on-disk Parcel version. Must be updated when the format of the parcelable changes - public static final int VERSION = 214; + public static final int VERSION = + !Flags.disableSystemServicePowerAttr() ? 214 : 215; // The maximum number of names wakelocks we will keep track of // per uid; once the limit is reached, we batch the remaining wakelocks @@ -1753,7 +1755,9 @@ public class BatteryStatsImpl extends BatteryStats { mCpuUidActiveTimeReader = new KernelCpuUidActiveTimeReader(true, mClock); mCpuUidClusterTimeReader = new KernelCpuUidClusterTimeReader(true, mClock); mKernelWakelockReader = new KernelWakelockReader(); - mSystemServerCpuThreadReader = SystemServerCpuThreadReader.create(); + if (!Flags.disableSystemServicePowerAttr()) { + mSystemServerCpuThreadReader = SystemServerCpuThreadReader.create(); + } mKernelMemoryBandwidthStats = new KernelMemoryBandwidthStats(); mTmpRailStats = new RailStats(); } @@ -11459,7 +11463,7 @@ public class BatteryStatsImpl extends BatteryStats { @Override public BatteryStatsHistoryIterator iterateBatteryStatsHistory(long startTimeMs, long endTimeMs) { - return mHistory.copy().iterate(startTimeMs, endTimeMs); + return mHistory.iterate(startTimeMs, endTimeMs); } @Override @@ -11702,7 +11706,9 @@ public class BatteryStatsImpl extends BatteryStats { EnergyConsumerStats.resetIfNotNull(mGlobalEnergyConsumerStats); - resetIfNotNull(mBinderThreadCpuTimesUs, false, elapsedRealtimeUs); + if (!Flags.disableSystemServicePowerAttr()) { + resetIfNotNull(mBinderThreadCpuTimesUs, false, elapsedRealtimeUs); + } mNumAllUidCpuTimeReads = 0; mNumUidsRemoved = 0; @@ -13676,7 +13682,9 @@ public class BatteryStatsImpl extends BatteryStats { mKernelCpuSpeedReaders[i].readDelta(); } } - mSystemServerCpuThreadReader.readDelta(); + if (!Flags.disableSystemServicePowerAttr()) { + mSystemServerCpuThreadReader.readDelta(); + } return; } @@ -15696,23 +15704,25 @@ public class BatteryStatsImpl extends BatteryStats { } } - updateSystemServiceCallStats(); - if (mBinderThreadCpuTimesUs != null) { - pw.println("Per UID System server binder time in ms:"); - long[] systemServiceTimeAtCpuSpeeds = getSystemServiceTimeAtCpuSpeeds(); - for (int i = 0; i < size; i++) { - int u = mUidStats.keyAt(i); - Uid uid = mUidStats.get(u); - double proportionalSystemServiceUsage = uid.getProportionalSystemServiceUsage(); - long timeUs = 0; - for (int j = systemServiceTimeAtCpuSpeeds.length - 1; j >= 0; j--) { - timeUs += systemServiceTimeAtCpuSpeeds[j] * proportionalSystemServiceUsage; - } + if (!Flags.disableSystemServicePowerAttr()) { + updateSystemServiceCallStats(); + if (mBinderThreadCpuTimesUs != null) { + pw.println("Per UID System server binder time in ms:"); + long[] systemServiceTimeAtCpuSpeeds = getSystemServiceTimeAtCpuSpeeds(); + for (int i = 0; i < size; i++) { + int u = mUidStats.keyAt(i); + Uid uid = mUidStats.get(u); + double proportionalSystemServiceUsage = uid.getProportionalSystemServiceUsage(); + long timeUs = 0; + for (int j = systemServiceTimeAtCpuSpeeds.length - 1; j >= 0; j--) { + timeUs += systemServiceTimeAtCpuSpeeds[j] * proportionalSystemServiceUsage; + } - pw.print(" "); - pw.print(u); - pw.print(": "); - pw.println(timeUs / 1000); + pw.print(" "); + pw.print(u); + pw.print(": "); + pw.println(timeUs / 1000); + } } } } @@ -16428,8 +16438,10 @@ public class BatteryStatsImpl extends BatteryStats { } } - mBinderThreadCpuTimesUs = - LongSamplingCounterArray.readSummaryFromParcelLocked(in, mOnBatteryTimeBase); + if (!Flags.disableSystemServicePowerAttr()) { + mBinderThreadCpuTimesUs = + LongSamplingCounterArray.readSummaryFromParcelLocked(in, mOnBatteryTimeBase); + } } /** @@ -16973,7 +16985,9 @@ public class BatteryStatsImpl extends BatteryStats { } } - LongSamplingCounterArray.writeSummaryToParcelLocked(out, mBinderThreadCpuTimesUs); + if (!Flags.disableSystemServicePowerAttr()) { + LongSamplingCounterArray.writeSummaryToParcelLocked(out, mBinderThreadCpuTimesUs); + } } @GuardedBy("this") @@ -16985,7 +16999,9 @@ public class BatteryStatsImpl extends BatteryStats { // if we had originally pulled a time before the RTC was set. getStartClockTime(); - updateSystemServiceCallStats(); + if (!Flags.disableSystemServicePowerAttr()) { + updateSystemServiceCallStats(); + } } @GuardedBy("this") diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java index c3221e4929bd..30b80ae781ff 100644 --- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java +++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java @@ -96,11 +96,13 @@ public class BatteryUsageStatsProvider { mPowerCalculators.add(new CustomEnergyConsumerPowerCalculator(mPowerProfile)); mPowerCalculators.add(new UserPowerCalculator()); - // It is important that SystemServicePowerCalculator be applied last, - // because it re-attributes some of the power estimated by the other - // calculators. - mPowerCalculators.add( - new SystemServicePowerCalculator(mCpuScalingPolicies, mPowerProfile)); + if (!com.android.server.power.optimization.Flags.disableSystemServicePowerAttr()) { + // It is important that SystemServicePowerCalculator be applied last, + // because it re-attributes some of the power estimated by the other + // calculators. + mPowerCalculators.add( + new SystemServicePowerCalculator(mCpuScalingPolicies, mPowerProfile)); + } } } return mPowerCalculators; diff --git a/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java b/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java index cd3db36662d6..ba4c127ac3d0 100644 --- a/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java +++ b/services/core/java/com/android/server/power/stats/PowerStatsAggregator.java @@ -74,8 +74,7 @@ public class PowerStatsAggregator { boolean clockUpdateAdded = false; long baseTime = startTimeMs > 0 ? startTimeMs : UNINITIALIZED; long lastTime = 0; - try (BatteryStatsHistoryIterator iterator = - mHistory.copy().iterate(startTimeMs, endTimeMs)) { + try (BatteryStatsHistoryIterator iterator = mHistory.iterate(startTimeMs, endTimeMs)) { while (iterator.hasNext()) { BatteryStats.HistoryItem item = iterator.next(); diff --git a/services/core/java/com/android/server/power/stats/flags.aconfig b/services/core/java/com/android/server/power/stats/flags.aconfig index 0f135715ebc3..65466461c82e 100644 --- a/services/core/java/com/android/server/power/stats/flags.aconfig +++ b/services/core/java/com/android/server/power/stats/flags.aconfig @@ -13,3 +13,11 @@ flag { description: "Feature flag for streamlined battery stats" bug: "285646152" } + +flag { + name: "disable_system_service_power_attr" + namespace: "backstage_power" + description: "Deprecation of system service power re-attribution" + bug: "311793616" + is_fixed_read_only: true +} diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index 285bcc328c0c..0ffd002197c4 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -2058,7 +2058,8 @@ public class StatsPullAtomService extends SystemService { } private void registerCpuCyclesPerThreadGroupCluster() { - if (KernelCpuBpfTracking.isSupported()) { + if (KernelCpuBpfTracking.isSupported() + && !com.android.server.power.optimization.Flags.disableSystemServicePowerAttr()) { int tagId = FrameworkStatsLog.CPU_CYCLES_PER_THREAD_GROUP_CLUSTER; PullAtomMetadata metadata = new PullAtomMetadata.Builder() .setAdditiveFields(new int[]{3, 4}) @@ -2073,6 +2074,10 @@ public class StatsPullAtomService extends SystemService { } int pullCpuCyclesPerThreadGroupCluster(int atomTag, List<StatsEvent> pulledData) { + if (com.android.server.power.optimization.Flags.disableSystemServicePowerAttr()) { + return StatsManager.PULL_SKIP; + } + SystemServiceCpuThreadTimes times = LocalServices.getService(BatteryStatsInternal.class) .getSystemServiceCpuThreadTimes(); if (times == null) { diff --git a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java index b6d0ca19d484..eacd3f8d4d86 100644 --- a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java +++ b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java @@ -4152,6 +4152,25 @@ public class TvInteractiveAppManagerService extends SystemService { } @Override + public void onRequestSigning2(String id, String algorithm, String host, + int port, byte[] data) { + synchronized (mLock) { + if (DEBUG) { + Slogf.d(TAG, "onRequestSigning"); + } + if (mSessionState.mSession == null || mSessionState.mClient == null) { + return; + } + try { + mSessionState.mClient.onRequestSigning2( + id, algorithm, host, port, data, mSessionState.mSeq); + } catch (RemoteException e) { + Slogf.e(TAG, "error in onRequestSigning", e); + } + } + } + + @Override public void onRequestCertificate(String host, int port) { synchronized (mLock) { if (DEBUG) { diff --git a/services/core/java/com/android/server/wm/ScreenRecordingCallbackController.java b/services/core/java/com/android/server/wm/ScreenRecordingCallbackController.java index 5f488b769885..bdb45884887c 100644 --- a/services/core/java/com/android/server/wm/ScreenRecordingCallbackController.java +++ b/services/core/java/com/android/server/wm/ScreenRecordingCallbackController.java @@ -97,7 +97,7 @@ public class ScreenRecordingCallbackController { mRecordedWC = (WindowContainer) mWms.mRoot.getDefaultDisplay(); } else { mRecordedWC = mWms.mRoot.getActivity(activity -> activity.mLaunchCookie - == mediaProjectionInfo.getLaunchCookie()).getTask(); + == mediaProjectionInfo.getLaunchCookie().binder).getTask(); } } diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp index 7b084132ed1c..4403bce484ad 100644 --- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp +++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp @@ -571,8 +571,8 @@ static jstring com_android_server_am_CachedAppOptimizer_getFreezerCheckPath(JNIE } static jboolean com_android_server_am_CachedAppOptimizer_isFreezerProfileValid(JNIEnv* env) { - int uid = getuid(); - int pid = getpid(); + uid_t uid = getuid(); + pid_t pid = getpid(); return isProfileValidForProcess("Frozen", uid, pid) && isProfileValidForProcess("Unfrozen", uid, pid); diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java index 667e086c8b40..281fb1c4635b 100644 --- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java +++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java @@ -27,6 +27,7 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.content.ComponentName; +import android.content.ContentResolver; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -201,7 +202,7 @@ public final class CredentialManagerService @SuppressWarnings("GuardedBy") // ErrorProne requires service.mLock which is the same // this.mLock protected void handlePackageRemovedMultiModeLocked(String packageName, int userId) { - updateProvidersWhenPackageRemoved(mContext, packageName); + updateProvidersWhenPackageRemoved(new SettingsWrapper(mContext), packageName); List<CredentialManagerServiceImpl> services = peekServiceListForUserLocked(userId); if (services == null) { @@ -1134,13 +1135,14 @@ public final class CredentialManagerService } /** Updates the list of providers when an app is uninstalled. */ - public static void updateProvidersWhenPackageRemoved(Context context, String packageName) { + public static void updateProvidersWhenPackageRemoved( + SettingsWrapper settingsWrapper, String packageName) { + Slog.i(TAG, "updateProvidersWhenPackageRemoved"); + // Get the current providers. String rawProviders = - Settings.Secure.getStringForUser( - context.getContentResolver(), - Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, - UserHandle.myUserId()); + settingsWrapper.getStringForUser( + Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, UserHandle.myUserId()); if (rawProviders == null) { Slog.w(TAG, "settings key is null"); return; @@ -1148,44 +1150,44 @@ public final class CredentialManagerService // Remove any providers from the primary setting that contain the package name // being removed. - Set<String> primaryProviders = - getStoredProviders(rawProviders, packageName); - if (!Settings.Secure.putString( - context.getContentResolver(), + Set<String> primaryProviders = getStoredProviders(rawProviders, packageName); + if (!settingsWrapper.putStringForUser( Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, - String.join(":", primaryProviders))) { - Slog.w(TAG, "Failed to remove primary package: " + packageName); + String.join(":", primaryProviders), + UserHandle.myUserId(), + /* overrideableByRestore= */ true)) { + Slog.e(TAG, "Failed to remove primary package: " + packageName); return; } // Read the autofill provider so we don't accidentally erase it. String autofillProvider = - Settings.Secure.getStringForUser( - context.getContentResolver(), - Settings.Secure.AUTOFILL_SERVICE, - UserHandle.myUserId()); + settingsWrapper.getStringForUser( + Settings.Secure.AUTOFILL_SERVICE, UserHandle.myUserId()); // If there is an autofill provider and it is the placeholder indicating // that the currently selected primary provider does not support autofill // then we should wipe the setting to keep it in sync. if (autofillProvider != null && primaryProviders.isEmpty()) { if (autofillProvider.equals(AUTOFILL_PLACEHOLDER_VALUE)) { - if (!Settings.Secure.putString( - context.getContentResolver(), + if (!settingsWrapper.putStringForUser( Settings.Secure.AUTOFILL_SERVICE, - "")) { - Slog.w(TAG, "Failed to remove autofill package: " + packageName); + "", + UserHandle.myUserId(), + /* overrideableByRestore= */ true)) { + Slog.e(TAG, "Failed to remove autofill package: " + packageName); } } else { // If the existing autofill provider is from the app being removed // then erase the autofill service setting. ComponentName cn = ComponentName.unflattenFromString(autofillProvider); if (cn != null && cn.getPackageName().equals(packageName)) { - if (!Settings.Secure.putString( - context.getContentResolver(), + if (!settingsWrapper.putStringForUser( Settings.Secure.AUTOFILL_SERVICE, - "")) { - Slog.w(TAG, "Failed to remove autofill package: " + packageName); + "", + UserHandle.myUserId(), + /* overrideableByRestore= */ true)) { + Slog.e(TAG, "Failed to remove autofill package: " + packageName); } } } @@ -1193,19 +1195,17 @@ public final class CredentialManagerService // Read the credential providers to remove any reference of the removed app. String rawCredentialProviders = - Settings.Secure.getStringForUser( - context.getContentResolver(), - Settings.Secure.CREDENTIAL_SERVICE, - UserHandle.myUserId()); + settingsWrapper.getStringForUser( + Settings.Secure.CREDENTIAL_SERVICE, UserHandle.myUserId()); // Remove any providers that belong to the removed app. - Set<String> credentialProviders = - getStoredProviders(rawCredentialProviders, packageName); - if (!Settings.Secure.putString( - context.getContentResolver(), + Set<String> credentialProviders = getStoredProviders(rawCredentialProviders, packageName); + if (!settingsWrapper.putStringForUser( Settings.Secure.CREDENTIAL_SERVICE, - String.join(":", credentialProviders))) { - Slog.w(TAG, "Failed to remove secondary package: " + packageName); + String.join(":", credentialProviders), + UserHandle.myUserId(), + /* overrideableByRestore= */ true)) { + Slog.e(TAG, "Failed to remove secondary package: " + packageName); } } @@ -1232,4 +1232,38 @@ public final class CredentialManagerService return providers; } + + /** A wrapper class that can be used by tests for intercepting reads/writes. */ + public static class SettingsWrapper { + private final Context mContext; + + public SettingsWrapper(@NonNull Context context) { + this.mContext = context; + } + + ContentResolver getContentResolver() { + return mContext.getContentResolver(); + } + + /** Retrieves the string value of a system setting */ + public String getStringForUser(String name, int userHandle) { + return Settings.Secure.getStringForUser(getContentResolver(), name, userHandle); + } + + /** Updates the string value of a system setting */ + public boolean putStringForUser( + String name, + String value, + int userHandle, + boolean overrideableByRestore) { + return Settings.Secure.putStringForUser( + getContentResolver(), + name, + value, + null, + false, + userHandle, + overrideableByRestore); + } + } } diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ClientControllerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ClientControllerTest.java index b9f1ea06aebe..dc9631a8f2e2 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ClientControllerTest.java +++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/ClientControllerTest.java @@ -116,7 +116,7 @@ public final class ClientControllerTest { ANY_CALLER_PID); verify(invoker.asBinder()).linkToDeath(any(IBinder.DeathRecipient.class), eq(0)); - assertThat(mController.mClients).containsEntry(invoker.asBinder(), added); + assertThat(mController.getClient(invoker.asBinder())).isSameInstanceAs(added); } } @@ -133,7 +133,7 @@ public final class ClientControllerTest { var invoker = IInputMethodClientInvoker.create(mClient, mHandler); added = mController.addClient(invoker, mConnection, ANY_DISPLAY_ID, ANY_CALLER_UID, ANY_CALLER_PID); - assertThat(mController.mClients).containsEntry(invoker.asBinder(), added); + assertThat(mController.getClient(invoker.asBinder())).isSameInstanceAs(added); assertThat(mController.removeClient(mClient)).isTrue(); } diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java index 02e3ef4d5f0b..75febd902dcf 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -61,6 +61,7 @@ import static org.mockito.Mockito.when; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.ActivityOptions.LaunchCookie; import android.app.PropertyInvalidatedCache; import android.companion.virtual.IVirtualDevice; import android.companion.virtual.IVirtualDeviceManager; @@ -1557,7 +1558,7 @@ public class DisplayManagerServiceTest { when(mMockProjectionService .setContentRecordingSession(any(ContentRecordingSession.class), eq(projection))) .thenReturn(true); - doReturn(mock(IBinder.class)).when(projection).getLaunchCookie(); + doReturn(new LaunchCookie()).when(projection).getLaunchCookie(); doReturn(true).when(mMockProjectionService).isCurrentProjection(eq(projection)); final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder( diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java index bb70080362b1..92513760fa4a 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java @@ -21,6 +21,9 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import android.content.Context; @@ -96,18 +99,11 @@ public class BatteryStatsHistoryTest { mClock.realtime = 123; mHistory = new BatteryStatsHistory(mHistoryBuffer, mSystemDir, 32, 1024, - mStepDetailsCalculator, mClock, mMonotonicClock, mTracer) { - @Override - public boolean readFileToParcel(Parcel out, AtomicFile file) { - mReadFiles.add(file.getBaseFile().getName()); - return super.readFileToParcel(out, file); - } - }; + mStepDetailsCalculator, mClock, mMonotonicClock, mTracer); when(mStepDetailsCalculator.getHistoryStepDetails()) .thenReturn(new BatteryStats.HistoryStepDetails()); - mHistoryPrinter = new BatteryStats.HistoryPrinter(); } @@ -276,6 +272,15 @@ public class BatteryStatsHistoryTest { mReadFiles.clear(); + // Make an immutable copy and spy on it + mHistory = spy(mHistory.copy()); + + doAnswer(invocation -> { + AtomicFile file = invocation.getArgument(1); + mReadFiles.add(file.getBaseFile().getName()); + return invocation.callRealMethod(); + }).when(mHistory).readFileToParcel(any(), any()); + // Prepare history for iteration mHistory.iterate(0, MonotonicClock.UNDEFINED); @@ -309,6 +314,15 @@ public class BatteryStatsHistoryTest { mReadFiles.clear(); + // Make an immutable copy and spy on it + mHistory = spy(mHistory.copy()); + + doAnswer(invocation -> { + AtomicFile file = invocation.getArgument(1); + mReadFiles.add(file.getBaseFile().getName()); + return invocation.callRealMethod(); + }).when(mHistory).readFileToParcel(any(), any()); + // Prepare history for iteration mHistory.iterate(1000, 3000); diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java index 4dae2d548057..8e53d5285cc4 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/SystemServicePowerCalculatorTest.java @@ -28,6 +28,9 @@ import static org.mockito.Mockito.when; import android.os.BatteryConsumer; import android.os.Binder; import android.os.Process; +import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -38,6 +41,7 @@ import com.android.internal.os.KernelCpuUidTimeReader; import com.android.internal.os.KernelSingleUidTimeReader; import com.android.internal.os.PowerProfile; import com.android.internal.power.EnergyConsumerStats; +import com.android.server.power.optimization.Flags; import org.junit.Before; import org.junit.Rule; @@ -54,6 +58,8 @@ import java.util.Collection; @RunWith(AndroidJUnit4.class) @SuppressWarnings("GuardedBy") public class SystemServicePowerCalculatorTest { + @Rule + public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); private static final double PRECISION = 0.000001; private static final int APP_UID1 = 100; @@ -108,6 +114,7 @@ public class SystemServicePowerCalculatorTest { } @Test + @RequiresFlagsDisabled(Flags.FLAG_DISABLE_SYSTEM_SERVICE_POWER_ATTR) public void testPowerProfileBasedModel() { prepareBatteryStats(null); @@ -135,6 +142,7 @@ public class SystemServicePowerCalculatorTest { } @Test + @RequiresFlagsDisabled(Flags.FLAG_DISABLE_SYSTEM_SERVICE_POWER_ATTR) public void testMeasuredEnergyBasedModel() { final boolean[] supportedPowerBuckets = new boolean[EnergyConsumerStats.NUMBER_STANDARD_POWER_BUCKETS]; diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index 8958fac87bb6..e22d99d45521 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -36,6 +36,7 @@ android_test { "-Werror", ], static_libs: [ + "cts-input-lib", "frameworks-base-testutils", "services.accessibility", "services.appwidget", diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterInputTest.kt b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterInputTest.kt new file mode 100644 index 000000000000..52c7d8d2bd2e --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterInputTest.kt @@ -0,0 +1,280 @@ +/* + * Copyright 2024 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.accessibility + +import android.hardware.display.DisplayManagerGlobal +import android.os.SystemClock +import android.view.Display +import android.view.Display.DEFAULT_DISPLAY +import android.view.DisplayAdjustments +import android.view.DisplayInfo +import android.view.IInputFilterHost +import android.view.InputDevice.SOURCE_TOUCHSCREEN +import android.view.InputEvent +import android.view.MotionEvent +import android.view.MotionEvent.ACTION_DOWN +import android.view.MotionEvent.ACTION_MOVE +import android.view.MotionEvent.ACTION_UP +import android.view.MotionEvent.ACTION_HOVER_ENTER +import android.view.MotionEvent.ACTION_HOVER_EXIT +import android.view.MotionEvent.ACTION_HOVER_MOVE +import android.view.WindowManagerPolicyConstants.FLAG_PASS_TO_USER +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import com.android.cts.input.inputeventmatchers.withDeviceId +import com.android.cts.input.inputeventmatchers.withMotionAction +import com.android.server.LocalServices +import com.android.server.accessibility.magnification.MagnificationProcessor +import com.android.server.wm.WindowManagerInternal +import java.util.concurrent.LinkedBlockingQueue +import org.hamcrest.Matchers.allOf +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.any +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.doAnswer +import org.mockito.Mockito.`when` +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule +import org.mockito.stubbing.OngoingStubbing + + +/** + * Create a MotionEvent with the provided action, eventTime, and source + */ +fun createMotionEvent(action: Int, downTime: Long, eventTime: Long, source: Int, deviceId: Int): + MotionEvent { + val x = 1f + val y = 2f + val pressure = 3f + val size = 1f + val metaState = 0 + val xPrecision = 0f + val yPrecision = 0f + val edgeFlags = 0 + val displayId = 0 + return MotionEvent.obtain(downTime, eventTime, action, x, y, pressure, size, metaState, + xPrecision, yPrecision, deviceId, edgeFlags, source, displayId) +} + +/** + * Tests for AccessibilityInputFilter, focusing on the input event processing as seen by the callers + * of the InputFilter interface. + * The main interaction with AccessibilityInputFilter in these tests is with the filterInputEvent + * and sendInputEvent APIs of InputFilter. + */ +@RunWith(AndroidJUnit4::class) +class AccessibilityInputFilterInputTest { + private val instrumentation = InstrumentationRegistry.getInstrumentation() + + private companion object{ + const val ALL_A11Y_FEATURES = (AccessibilityInputFilter.FLAG_FEATURE_AUTOCLICK + or AccessibilityInputFilter.FLAG_FEATURE_TOUCH_EXPLORATION + or AccessibilityInputFilter.FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER + or AccessibilityInputFilter.FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER + or AccessibilityInputFilter.FLAG_FEATURE_INJECT_MOTION_EVENTS + or AccessibilityInputFilter.FLAG_FEATURE_FILTER_KEY_EVENTS) + } + + @Rule + @JvmField + val mocks: MockitoRule = MockitoJUnit.rule() + + @Mock + private lateinit var mockA11yController: WindowManagerInternal.AccessibilityControllerInternal + + @Mock + private lateinit var mockWindowManagerService: WindowManagerInternal + + @Mock + private lateinit var mockMagnificationProcessor: MagnificationProcessor + + private val inputEvents = LinkedBlockingQueue<InputEvent>() + private val verifier = BlockingQueueEventVerifier(inputEvents) + + @Mock + private lateinit var host: IInputFilterHost + private lateinit var ams: AccessibilityManagerService + private lateinit var a11yInputFilter: AccessibilityInputFilter + private val touchDeviceId = 1 + + @Before + fun setUp() { + val context = instrumentation.context + LocalServices.removeServiceForTest(WindowManagerInternal::class.java) + LocalServices.addService(WindowManagerInternal::class.java, mockWindowManagerService) + + whenever(mockA11yController.isAccessibilityTracingEnabled).thenReturn(false) + whenever( + mockWindowManagerService.accessibilityController).thenReturn( + mockA11yController) + + ams = Mockito.spy(AccessibilityManagerService(context)) + val displayList = arrayListOf(createStubDisplay(DEFAULT_DISPLAY, DisplayInfo())) + whenever(ams.validDisplayList).thenReturn(displayList) + whenever(ams.magnificationProcessor).thenReturn(mockMagnificationProcessor) + + doAnswer { + val event = it.getArgument(0) as MotionEvent + inputEvents.add(MotionEvent.obtain(event)) + }.`when`(host).sendInputEvent(any(), anyInt()) + + a11yInputFilter = AccessibilityInputFilter(context, ams) + a11yInputFilter.install(host) + } + + @After + fun tearDown() { + if (this::a11yInputFilter.isInitialized) { + a11yInputFilter.uninstall() + } + } + + /** + * When no features are enabled, the events pass through the filter without getting modified. + */ + @Test + fun testSingleDeviceTouchEventsWithoutA11yFeatures() { + enableFeatures(0) + + val downTime = SystemClock.uptimeMillis() + val downEvent = createMotionEvent( + ACTION_DOWN, downTime, downTime, SOURCE_TOUCHSCREEN, touchDeviceId) + send(downEvent) + verifier.assertReceivedMotion( + allOf(withMotionAction(ACTION_DOWN), withDeviceId(touchDeviceId))) + + val moveEvent = createMotionEvent( + ACTION_MOVE, downTime, SystemClock.uptimeMillis(), SOURCE_TOUCHSCREEN, touchDeviceId) + send(moveEvent) + verifier.assertReceivedMotion( + allOf(withMotionAction(ACTION_MOVE), withDeviceId(touchDeviceId))) + + val upEvent = createMotionEvent( + ACTION_UP, downTime, SystemClock.uptimeMillis(), SOURCE_TOUCHSCREEN, touchDeviceId) + send(upEvent) + verifier.assertReceivedMotion( + allOf(withMotionAction(ACTION_UP), withDeviceId(touchDeviceId))) + + verifier.assertNoEvents() + } + + /** + * Enable all a11y features and send a touchscreen stream of DOWN -> MOVE -> UP events. + * These get converted into HOVER_ENTER -> HOVER_MOVE -> HOVER_EXIT events by the input filter. + */ + @Test + fun testSingleDeviceTouchEventsWithAllA11yFeatures() { + enableFeatures(ALL_A11Y_FEATURES) + + val downTime = SystemClock.uptimeMillis() + val downEvent = createMotionEvent( + ACTION_DOWN, downTime, downTime, SOURCE_TOUCHSCREEN, touchDeviceId) + send(MotionEvent.obtain(downEvent)) + + // DOWN event gets transformed to HOVER_ENTER + verifier.assertReceivedMotion( + allOf(withMotionAction(ACTION_HOVER_ENTER), withDeviceId(touchDeviceId))) + + // MOVE becomes HOVER_MOVE + val moveEvent = createMotionEvent( + ACTION_MOVE, downTime, SystemClock.uptimeMillis(), SOURCE_TOUCHSCREEN, touchDeviceId) + send(moveEvent) + verifier.assertReceivedMotion( + allOf(withMotionAction(ACTION_HOVER_MOVE), withDeviceId(touchDeviceId))) + + // UP becomes HOVER_EXIT + val upEvent = createMotionEvent( + ACTION_UP, downTime, SystemClock.uptimeMillis(), SOURCE_TOUCHSCREEN, touchDeviceId) + send(upEvent) + + verifier.assertReceivedMotion( + allOf(withMotionAction(ACTION_HOVER_EXIT), withDeviceId(touchDeviceId))) + + verifier.assertNoEvents() + } + + /** + * Enable all a11y features and send a touchscreen event stream. In the middle of the gesture, + * disable the a11y features. + * When the a11y features are disabled, the filter generates HOVER_EXIT without further input + * from the dispatcher. + */ + @Test + fun testSingleDeviceTouchEventsDisableFeaturesMidGesture() { + enableFeatures(ALL_A11Y_FEATURES) + + val downTime = SystemClock.uptimeMillis() + val downEvent = createMotionEvent( + ACTION_DOWN, downTime, downTime, SOURCE_TOUCHSCREEN, touchDeviceId) + send(MotionEvent.obtain(downEvent)) + + // DOWN event gets transformed to HOVER_ENTER + verifier.assertReceivedMotion( + allOf(withMotionAction(ACTION_HOVER_ENTER), withDeviceId(touchDeviceId))) + verifier.assertNoEvents() + + enableFeatures(0) + verifier.assertReceivedMotion( + allOf(withMotionAction(ACTION_HOVER_EXIT), withDeviceId(touchDeviceId))) + verifier.assertNoEvents() + + val moveEvent = createMotionEvent( + ACTION_MOVE, downTime, SystemClock.uptimeMillis(), SOURCE_TOUCHSCREEN, touchDeviceId) + send(moveEvent) + val upEvent = createMotionEvent( + ACTION_UP, downTime, SystemClock.uptimeMillis(), SOURCE_TOUCHSCREEN, touchDeviceId) + send(upEvent) + // As the original gesture continues, no additional events should be getting sent by the + // filter because the HOVER_EXIT above already effectively finished the current gesture and + // the DOWN event was never sent to the host. + + // Bug: the down event was swallowed, so the remainder of the gesture should be swallowed + // too. However, the MOVE and UP events are currently passed back to the dispatcher. + // TODO(b/310014874) - ensure a11y sends consistent input streams to the dispatcher + verifier.assertReceivedMotion( + allOf(withMotionAction(ACTION_MOVE), withDeviceId(touchDeviceId))) + verifier.assertReceivedMotion( + allOf(withMotionAction(ACTION_UP), withDeviceId(touchDeviceId))) + + verifier.assertNoEvents() + } + + private fun createStubDisplay(displayId: Int, displayInfo: DisplayInfo): Display { + val display = Display(DisplayManagerGlobal.getInstance(), displayId, + displayInfo, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS) + return display + } + + private fun send(event: InputEvent) { + // We need to make a copy of the event before sending it to the filter, because the filter + // will recycle it, but the caller of this function might want to still be able to use + // this event for subsequent checks + val eventCopy = if (event is MotionEvent) MotionEvent.obtain(event) else event + a11yInputFilter.filterInputEvent(eventCopy, FLAG_PASS_TO_USER) + } + + private fun enableFeatures(features: Int) { + instrumentation.runOnMainSync { a11yInputFilter.setUserAndEnabledFeatures(0, features) } + } +} + +private fun <T> whenever(methodCall: T): OngoingStubbing<T> = `when`(methodCall) diff --git a/services/tests/servicestests/src/com/android/server/accessibility/BlockingQueueEventVerifier.kt b/services/tests/servicestests/src/com/android/server/accessibility/BlockingQueueEventVerifier.kt new file mode 100644 index 000000000000..b12f537d1482 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/accessibility/BlockingQueueEventVerifier.kt @@ -0,0 +1,57 @@ +/* + * Copyright 2024 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.accessibility + +import android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS +import android.view.InputEvent +import android.view.MotionEvent +import java.time.Duration +import java.util.concurrent.BlockingQueue +import java.util.concurrent.TimeUnit +import org.junit.Assert.fail + +import org.hamcrest.Matcher +import org.hamcrest.MatcherAssert.assertThat +import org.junit.Assert.assertNull + +private fun <T> getEvent(queue: BlockingQueue<T>, timeout: Duration): T? { + return queue.poll(timeout.toMillis(), TimeUnit.MILLISECONDS) +} + +class BlockingQueueEventVerifier(val queue: BlockingQueue<InputEvent>) { + fun assertReceivedMotion(matcher: Matcher<MotionEvent>) { + val event = getMotionEvent() + assertThat("MotionEvent checks", event, matcher) + } + + fun assertNoEvents() { + val event = getEvent(queue, Duration.ofMillis(50)) + assertNull(event) + } + + private fun getMotionEvent(): MotionEvent { + val event = getEvent(queue, Duration.ofMillis(DEFAULT_DISPATCHING_TIMEOUT_MILLIS.toLong())) + if (event == null) { + fail("Did not get an event") + } + if (event is MotionEvent) { + return event + } + fail("Instead of motion, got $event") + throw RuntimeException("should not reach here") + } +} + diff --git a/services/tests/servicestests/src/com/android/server/credentials/CredentialManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/credentials/CredentialManagerServiceTest.java index d850c73ebc26..57f3cc03980e 100644 --- a/services/tests/servicestests/src/com/android/server/credentials/CredentialManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/credentials/CredentialManagerServiceTest.java @@ -22,6 +22,7 @@ import android.content.Context; import android.os.UserHandle; import android.provider.Settings; +import androidx.annotation.NonNull; import androidx.test.core.app.ApplicationProvider; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -40,10 +41,12 @@ import java.util.Set; public final class CredentialManagerServiceTest { Context mContext = null; + MockSettingsWrapper mSettingsWrapper = null; @Before public void setUp() throws CertificateException { mContext = ApplicationProvider.getApplicationContext(); + mSettingsWrapper = new MockSettingsWrapper(mContext); } @Test @@ -81,7 +84,8 @@ public final class CredentialManagerServiceTest { Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, "com.example.test/com.example.test.TestActivity"); - CredentialManagerService.updateProvidersWhenPackageRemoved(mContext, "com.example.test"); + CredentialManagerService.updateProvidersWhenPackageRemoved( + mSettingsWrapper, "com.example.test"); assertThat(getSettingsKey(Settings.Secure.AUTOFILL_SERVICE)).isEqualTo(""); assertThat(getSettingsKey(Settings.Secure.CREDENTIAL_SERVICE)) @@ -101,7 +105,8 @@ public final class CredentialManagerServiceTest { setSettingsKey(Settings.Secure.CREDENTIAL_SERVICE, testCredentialValue); setSettingsKey(Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, testCredentialPrimaryValue); - CredentialManagerService.updateProvidersWhenPackageRemoved(mContext, "com.example.test3"); + CredentialManagerService.updateProvidersWhenPackageRemoved( + mSettingsWrapper, "com.example.test3"); // Since the provider removed was not a primary provider then we should do nothing. assertThat(getSettingsKey(Settings.Secure.AUTOFILL_SERVICE)) @@ -125,7 +130,8 @@ public final class CredentialManagerServiceTest { Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, "com.example.test/com.example.test.TestActivity"); - CredentialManagerService.updateProvidersWhenPackageRemoved(mContext, "com.example.test"); + CredentialManagerService.updateProvidersWhenPackageRemoved( + mSettingsWrapper, "com.example.test"); assertThat(getSettingsKey(Settings.Secure.AUTOFILL_SERVICE)).isEqualTo(""); assertThat(getSettingsKey(Settings.Secure.CREDENTIAL_SERVICE)) @@ -144,7 +150,8 @@ public final class CredentialManagerServiceTest { setSettingsKey(Settings.Secure.CREDENTIAL_SERVICE, testCredentialValue); setSettingsKey(Settings.Secure.CREDENTIAL_SERVICE_PRIMARY, testCredentialPrimaryValue); - CredentialManagerService.updateProvidersWhenPackageRemoved(mContext, "com.example.test3"); + CredentialManagerService.updateProvidersWhenPackageRemoved( + mSettingsWrapper, "com.example.test3"); // Since the provider removed was not a primary provider then we should do nothing. assertCredentialPropertyEquals( @@ -176,12 +183,36 @@ public final class CredentialManagerServiceTest { assertThat(actualValueSet).isEqualTo(newValueSet); } - private void setSettingsKey(String key, String value) { - assertThat(Settings.Secure.putString(mContext.getContentResolver(), key, value)).isTrue(); + private void setSettingsKey(String name, String value) { + assertThat( + mSettingsWrapper.putStringForUser( + name, value, UserHandle.myUserId(), true)) + .isTrue(); } - private String getSettingsKey(String key) { - return Settings.Secure.getStringForUser( - mContext.getContentResolver(), key, UserHandle.myUserId()); + private String getSettingsKey(String name) { + return mSettingsWrapper.getStringForUser(name, UserHandle.myUserId()); + } + + private static final class MockSettingsWrapper + extends CredentialManagerService.SettingsWrapper { + + MockSettingsWrapper(@NonNull Context context) { + super(context); + } + + /** Updates the string value of a system setting */ + @Override + public boolean putStringForUser( + String name, + String value, + int userHandle, + boolean overrideableByRestore) { + // This will ensure that when the settings putStringForUser method is called by + // CredentialManagerService that the overrideableByRestore bit is true. + assertThat(overrideableByRestore).isTrue(); + + return Settings.Secure.putStringForUser(getContentResolver(), name, value, userHandle); + } } } diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java index 097cc5177a83..abd3abee82fb 100644 --- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java @@ -49,6 +49,7 @@ import static org.mockito.Mockito.when; import static org.testng.Assert.assertThrows; import android.app.ActivityManagerInternal; +import android.app.ActivityOptions.LaunchCookie; import android.content.Context; import android.content.ContextWrapper; import android.content.pm.ApplicationInfo; @@ -784,7 +785,7 @@ public class MediaProjectionManagerServiceTest { @RecordContent int recordedContent) throws NameNotFoundException { MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions(); - projection.setLaunchCookie(mock(IBinder.class)); + projection.setLaunchCookie(new LaunchCookie()); projection.start(mIMediaProjectionCallback); projection.notifyVirtualDisplayCreated(10); // Waiting for user to review consent. @@ -825,7 +826,7 @@ public class MediaProjectionManagerServiceTest { public void testSetUserReviewGrantedConsentResult_displayMirroring_noPriorSession() throws NameNotFoundException { MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions(); - projection.setLaunchCookie(mock(IBinder.class)); + projection.setLaunchCookie(new LaunchCookie()); projection.start(mIMediaProjectionCallback); // Skip setting the prior session details. @@ -844,7 +845,7 @@ public class MediaProjectionManagerServiceTest { public void testSetUserReviewGrantedConsentResult_displayMirroring_sessionNotWaiting() throws NameNotFoundException { MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions(); - projection.setLaunchCookie(mock(IBinder.class)); + projection.setLaunchCookie(new LaunchCookie()); projection.start(mIMediaProjectionCallback); // Session is not waiting for user's consent. doReturn(true).when(mWindowManagerInternal).setContentRecordingSession( diff --git a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt index 757abde2041e..e3ee21a450c7 100644 --- a/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt +++ b/services/tests/servicestests/src/com/android/server/pm/parsing/AndroidPackageParsingValidationTest.kt @@ -436,6 +436,13 @@ class AndroidPackageParsingValidationTest { validateTagCount("action", 20000, tag) validateTagCount("category", 40000, tag) validateTagCount("data", 40000, tag) + validateTagCount("uri-relative-filter-group", 100, tag) + } + + @Test + fun parseUriRelativeFilterGroupTag() { + val tag = "uri-relative-filter-group" + validateTagCount("data", 100, tag) } @Test @@ -465,6 +472,54 @@ class AndroidPackageParsingValidationTest { R.styleable.AndroidManifestData_pathAdvancedPattern, 4000 ) + validateTagAttr(tag, "query", R.styleable.AndroidManifestData_query, 4000) + validateTagAttr( + tag, + "queryPattern", + R.styleable.AndroidManifestData_queryPattern, + 4000 + ) + validateTagAttr( + tag, + "queryPrefix", + R.styleable.AndroidManifestData_queryPrefix, + 4000 + ) + validateTagAttr(tag, + "querySuffix", + R.styleable.AndroidManifestData_querySuffix, + 4000 + ) + validateTagAttr( + tag, + "queryAdvancedPattern", + R.styleable.AndroidManifestData_queryAdvancedPattern, + 4000 + ) + validateTagAttr(tag, "fragment", R.styleable.AndroidManifestData_query, 4000) + validateTagAttr( + tag, + "fragmentPattern", + R.styleable.AndroidManifestData_fragmentPattern, + 4000 + ) + validateTagAttr( + tag, + "fragmentPrefix", + R.styleable.AndroidManifestData_fragmentPrefix, + 4000 + ) + validateTagAttr(tag, + "fragmentSuffix", + R.styleable.AndroidManifestData_fragmentSuffix, + 4000 + ) + validateTagAttr( + tag, + "fragmentAdvancedPattern", + R.styleable.AndroidManifestData_fragmentAdvancedPattern, + 4000 + ) validateTagAttr(tag, "mimeType", R.styleable.AndroidManifestData_mimeType, 255) validateTagAttr(tag, "mimeGroup", R.styleable.AndroidManifestData_mimeGroup, 1024) } diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp index f3f183815d0b..642a5618b6ad 100644 --- a/tools/aapt2/cmd/Link.cpp +++ b/tools/aapt2/cmd/Link.cpp @@ -1988,6 +1988,8 @@ class Linker { context_->SetNameManglerPolicy(NameManglerPolicy{context_->GetCompilationPackage()}); context_->SetSplitNameDependencies(app_info_.split_name_dependencies); + std::unique_ptr<xml::XmlResource> pre_flags_filter_manifest_xml = manifest_xml->Clone(); + FeatureFlagsFilterOptions flags_filter_options; if (context_->GetMinSdkVersion() > SDK_UPSIDE_DOWN_CAKE) { // For API version > U, PackageManager will dynamically read the flag values and disable @@ -2297,7 +2299,12 @@ class Linker { } if (options_.generate_java_class_path) { - if (!WriteManifestJavaFile(manifest_xml.get())) { + // The FeatureFlagsFilter may remove <permission> and <permission-group> elements that + // generate constants in the Manifest Java file. While we want those permissions and + // permission groups removed in the SDK (i.e., if a feature flag is disabled), the + // constants should still remain so that code referencing it (e.g., within a feature + // flag check) will still compile. Therefore we use the manifest XML before the filter. + if (!WriteManifestJavaFile(pre_flags_filter_manifest_xml.get())) { error = true; } } diff --git a/tools/aapt2/cmd/Link_test.cpp b/tools/aapt2/cmd/Link_test.cpp index 9323f3b95eac..6cc42f17c0a1 100644 --- a/tools/aapt2/cmd/Link_test.cpp +++ b/tools/aapt2/cmd/Link_test.cpp @@ -1021,9 +1021,11 @@ TEST_F(LinkTest, FeatureFlagDisabled_SdkAtMostUDC) { .AddContents(manifest_contents) .Build(); + const std::string app_java = GetTestPath("app-java"); auto app_link_args = LinkCommandBuilder(this) .SetManifestFile(app_manifest) .AddParameter("-I", android_apk) + .AddParameter("--java", app_java) .AddParameter("--feature-flags", "flag=false"); const std::string app_apk = GetTestPath("app.apk"); @@ -1038,6 +1040,12 @@ TEST_F(LinkTest, FeatureFlagDisabled_SdkAtMostUDC) { ASSERT_THAT(root, NotNull()); auto maybe_removed = root->FindChild({}, "permission"); ASSERT_THAT(maybe_removed, IsNull()); + + // Code for the permission should be generated even if the element is removed + const std::string manifest_java = app_java + "/com/example/app/Manifest.java"; + std::string manifest_java_contents; + ASSERT_TRUE(android::base::ReadFileToString(manifest_java, &manifest_java_contents)); + EXPECT_THAT(manifest_java_contents, HasSubstr(" public static final String FOO=\"FOO\";")); } TEST_F(LinkTest, FeatureFlagEnabled_SdkAtMostUDC) { diff --git a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/MessageQueue_host.java b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/MessageQueue_host.java index 2e47d48f4fa0..65da4a144160 100644 --- a/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/MessageQueue_host.java +++ b/tools/hoststubgen/hoststubgen/helper-framework-runtime-src/framework/com/android/hoststubgen/nativesubstitution/MessageQueue_host.java @@ -28,6 +28,7 @@ public class MessageQueue_host { private final Object mPoller = new Object(); private volatile boolean mPolling; + private volatile boolean mPendingWake; private void validate() { if (mDeleted) { @@ -62,7 +63,9 @@ public class MessageQueue_host { synchronized (q.mPoller) { q.mPolling = true; try { - if (timeoutMillis == 0) { + if (q.mPendingWake) { + // Calling with pending wake returns immediately + } else if (timeoutMillis == 0) { // Calling epoll_wait() with 0 returns immediately } else if (timeoutMillis == -1) { q.mPoller.wait(); @@ -72,6 +75,8 @@ public class MessageQueue_host { } catch (InterruptedException e) { Thread.currentThread().interrupt(); } + // Any reason for returning counts as a "wake", so clear pending + q.mPendingWake = false; q.mPolling = false; } } @@ -79,6 +84,7 @@ public class MessageQueue_host { public static void nativeWake(long ptr) { var q = getInstance(ptr); synchronized (q.mPoller) { + q.mPendingWake = true; q.mPoller.notifyAll(); } } |