diff options
102 files changed, 2303 insertions, 1566 deletions
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 ac6eb3229a25..c5d3b7a726b9 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java @@ -53,6 +53,7 @@ import android.net.Uri; import android.os.BatteryStats; import android.os.BatteryStatsInternal; import android.os.Binder; +import android.os.Build; import android.os.Handler; import android.os.LimitExceededException; import android.os.Looper; @@ -151,6 +152,9 @@ public class JobSchedulerService extends com.android.server.SystemService private static final boolean ENFORCE_MAX_JOBS = true; /** The maximum number of jobs that we allow an unprivileged app to schedule */ private static final int MAX_JOBS_PER_APP = 100; + /** The number of the most recently completed jobs to keep track of for debugging purposes. */ + private static final int NUM_COMPLETED_JOB_HISTORY = + Build.IS_USERDEBUG || Build.IS_ENG ? 25 : 0; @VisibleForTesting public static Clock sSystemClock = Clock.systemUTC(); @@ -297,6 +301,10 @@ public class JobSchedulerService extends com.android.server.SystemService */ boolean mReportedActive; + private int mLastCompletedJobIndex = 0; + private final JobStatus[] mLastCompletedJobs = new JobStatus[NUM_COMPLETED_JOB_HISTORY]; + private final long[] mLastCompletedJobTimeElapsed = new long[NUM_COMPLETED_JOB_HISTORY]; + /** * A mapping of which uids are currently in the foreground to their effective priority. */ @@ -1752,6 +1760,10 @@ public class JobSchedulerService extends com.android.server.SystemService Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule); } + mLastCompletedJobs[mLastCompletedJobIndex] = jobStatus; + mLastCompletedJobTimeElapsed[mLastCompletedJobIndex] = sElapsedRealtimeClock.millis(); + mLastCompletedJobIndex = (mLastCompletedJobIndex + 1) % NUM_COMPLETED_JOB_HISTORY; + // Intentionally not checking expedited job quota here. An app can't find out if it's run // out of quota when it asks JS to reschedule an expedited job. Instead, the rescheduled // EJ will just be demoted to a regular job if the app has no EJ quota left. @@ -3298,6 +3310,37 @@ public class JobSchedulerService extends com.android.server.SystemService } } pw.decreaseIndent(); + + pw.println(); + boolean recentPrinted = false; + pw.println("Recently completed jobs:"); + pw.increaseIndent(); + for (int r = 1; r <= NUM_COMPLETED_JOB_HISTORY; ++r) { + // Print most recent first + final int idx = (mLastCompletedJobIndex + NUM_COMPLETED_JOB_HISTORY - r) + % NUM_COMPLETED_JOB_HISTORY; + final JobStatus job = mLastCompletedJobs[idx]; + if (job != null) { + if (!predicate.test(job)) { + continue; + } + recentPrinted = true; + TimeUtils.formatDuration(mLastCompletedJobTimeElapsed[idx], nowElapsed, pw); + pw.println(); + // Double indent for readability + pw.increaseIndent(); + pw.increaseIndent(); + job.dump(pw, true, nowElapsed); + pw.decreaseIndent(); + pw.decreaseIndent(); + } + } + if (!recentPrinted) { + pw.println("None"); + } + pw.decreaseIndent(); + pw.println(); + if (filterUid == -1) { pw.println(); pw.print("mReadyToRock="); pw.println(mReadyToRock); diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java index 58396eb69d14..a230b23f03a4 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java @@ -17,6 +17,7 @@ package com.android.server.job.controllers; import static com.android.server.job.JobSchedulerService.NEVER_INDEX; +import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import android.os.SystemClock; import android.os.UserHandle; @@ -59,6 +60,8 @@ public final class BackgroundJobsController extends StateController { private final AppStateTrackerImpl mAppStateTracker; + private final UpdateJobFunctor mUpdateJobFunctor = new UpdateJobFunctor(); + public BackgroundJobsController(JobSchedulerService service) { super(service); @@ -69,7 +72,7 @@ public final class BackgroundJobsController extends StateController { @Override public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) { - updateSingleJobRestrictionLocked(jobStatus, UNKNOWN); + updateSingleJobRestrictionLocked(jobStatus, sElapsedRealtimeClock.millis(), UNKNOWN); } @Override @@ -79,7 +82,7 @@ public final class BackgroundJobsController extends StateController { @Override public void evaluateStateLocked(JobStatus jobStatus) { - updateSingleJobRestrictionLocked(jobStatus, UNKNOWN); + updateSingleJobRestrictionLocked(jobStatus, sElapsedRealtimeClock.millis(), UNKNOWN); } @Override @@ -163,33 +166,34 @@ public final class BackgroundJobsController extends StateController { } private void updateJobRestrictionsLocked(int filterUid, int newActiveState) { - final UpdateJobFunctor updateTrackedJobs = new UpdateJobFunctor(newActiveState); + mUpdateJobFunctor.prepare(newActiveState); final long start = DEBUG ? SystemClock.elapsedRealtimeNanos() : 0; final JobStore store = mService.getJobStore(); if (filterUid > 0) { - store.forEachJobForSourceUid(filterUid, updateTrackedJobs); + store.forEachJobForSourceUid(filterUid, mUpdateJobFunctor); } else { - store.forEachJob(updateTrackedJobs); + store.forEachJob(mUpdateJobFunctor); } final long time = DEBUG ? (SystemClock.elapsedRealtimeNanos() - start) : 0; if (DEBUG) { Slog.d(TAG, String.format( "Job status updated: %d/%d checked/total jobs, %d us", - updateTrackedJobs.mCheckedCount, - updateTrackedJobs.mTotalCount, + mUpdateJobFunctor.mCheckedCount, + mUpdateJobFunctor.mTotalCount, (time / 1000) - )); + )); } - if (updateTrackedJobs.mChanged) { + if (mUpdateJobFunctor.mChanged) { mStateChangedListener.onControllerStateChanged(); } } - boolean updateSingleJobRestrictionLocked(JobStatus jobStatus, int activeState) { + boolean updateSingleJobRestrictionLocked(JobStatus jobStatus, final long nowElapsed, + int activeState) { final int uid = jobStatus.getSourceUid(); final String packageName = jobStatus.getSourcePackageName(); @@ -205,26 +209,32 @@ public final class BackgroundJobsController extends StateController { if (isActive && jobStatus.getStandbyBucket() == NEVER_INDEX) { jobStatus.maybeLogBucketMismatch(); } - boolean didChange = jobStatus.setBackgroundNotRestrictedConstraintSatisfied(canRun); + boolean didChange = + jobStatus.setBackgroundNotRestrictedConstraintSatisfied(nowElapsed, canRun); didChange |= jobStatus.setUidActive(isActive); return didChange; } private final class UpdateJobFunctor implements Consumer<JobStatus> { - final int activeState; + int mActiveState; boolean mChanged = false; int mTotalCount = 0; int mCheckedCount = 0; - - public UpdateJobFunctor(int newActiveState) { - activeState = newActiveState; + long mUpdateTimeElapsed = 0; + + void prepare(int newActiveState) { + mActiveState = newActiveState; + mUpdateTimeElapsed = sElapsedRealtimeClock.millis(); + mChanged = false; + mTotalCount = 0; + mCheckedCount = 0; } @Override public void accept(JobStatus jobStatus) { mTotalCount++; mCheckedCount++; - if (updateSingleJobRestrictionLocked(jobStatus, activeState)) { + if (updateSingleJobRestrictionLocked(jobStatus, mUpdateTimeElapsed, mActiveState)) { mChanged = true; } } diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java index 28269c89d13b..6fd094844cd6 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java @@ -65,10 +65,12 @@ public final class BatteryController extends RestrictingController { @Override public void maybeStartTrackingJobLocked(JobStatus taskStatus, JobStatus lastJob) { if (taskStatus.hasPowerConstraint()) { + final long nowElapsed = sElapsedRealtimeClock.millis(); mTrackedTasks.add(taskStatus); taskStatus.setTrackingController(JobStatus.TRACKING_BATTERY); - taskStatus.setChargingConstraintSatisfied(mChargeTracker.isOnStablePower()); - taskStatus.setBatteryNotLowConstraintSatisfied(mChargeTracker.isBatteryNotLow()); + taskStatus.setChargingConstraintSatisfied(nowElapsed, mChargeTracker.isOnStablePower()); + taskStatus.setBatteryNotLowConstraintSatisfied( + nowElapsed, mChargeTracker.isBatteryNotLow()); } } @@ -97,14 +99,15 @@ public final class BatteryController extends RestrictingController { if (DEBUG) { Slog.d(TAG, "maybeReportNewChargingStateLocked: " + stablePower); } + final long nowElapsed = sElapsedRealtimeClock.millis(); boolean reportChange = false; for (int i = mTrackedTasks.size() - 1; i >= 0; i--) { final JobStatus ts = mTrackedTasks.valueAt(i); - boolean previous = ts.setChargingConstraintSatisfied(stablePower); + boolean previous = ts.setChargingConstraintSatisfied(nowElapsed, stablePower); if (previous != stablePower) { reportChange = true; } - previous = ts.setBatteryNotLowConstraintSatisfied(batteryNotLow); + previous = ts.setBatteryNotLowConstraintSatisfied(nowElapsed, batteryNotLow); if (previous != batteryNotLow) { reportChange = true; } diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java index 14484ff441ca..6e542f346f81 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java @@ -20,6 +20,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX; +import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import android.annotation.Nullable; import android.app.job.JobInfo; @@ -461,11 +462,12 @@ public final class ConnectivityController extends RestrictingController implemen final Network network = mConnManager.getActiveNetworkForUid( jobStatus.getSourceUid(), jobStatus.shouldIgnoreNetworkBlocking()); final NetworkCapabilities capabilities = getNetworkCapabilities(network); - return updateConstraintsSatisfied(jobStatus, network, capabilities); + return updateConstraintsSatisfied(jobStatus, sElapsedRealtimeClock.millis(), + network, capabilities); } - private boolean updateConstraintsSatisfied(JobStatus jobStatus, Network network, - NetworkCapabilities capabilities) { + private boolean updateConstraintsSatisfied(JobStatus jobStatus, final long nowElapsed, + Network network, NetworkCapabilities capabilities) { // TODO: consider matching against non-active networks final boolean ignoreBlocked = jobStatus.shouldIgnoreNetworkBlocking(); @@ -476,7 +478,7 @@ public final class ConnectivityController extends RestrictingController implemen final boolean satisfied = isSatisfied(jobStatus, network, capabilities, mConstants); final boolean changed = jobStatus - .setConnectivityConstraintSatisfied(connected && satisfied); + .setConnectivityConstraintSatisfied(nowElapsed, connected && satisfied); // Pass along the evaluated network for job to use; prevents race // conditions as default routes change over time, and opens the door to @@ -530,6 +532,7 @@ public final class ConnectivityController extends RestrictingController implemen NetworkCapabilities exemptedNetworkCapabilities = null; boolean exemptedNetworkMatch = false; + final long nowElapsed = sElapsedRealtimeClock.millis(); boolean changed = false; for (int i = jobs.size() - 1; i >= 0; i--) { final JobStatus js = jobs.valueAt(i); @@ -555,7 +558,7 @@ public final class ConnectivityController extends RestrictingController implemen // job hasn't yet been evaluated against the currently // active network; typically when we just lost a network. if (match || !Objects.equals(js.network, net)) { - changed |= updateConstraintsSatisfied(js, net, netCap); + changed |= updateConstraintsSatisfied(js, nowElapsed, net, netCap); } } return changed; diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java index 131a6d4f4791..8b0da3471781 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java @@ -16,6 +16,8 @@ package com.android.server.job.controllers; +import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; + import android.annotation.UserIdInt; import android.app.job.JobInfo; import android.database.ContentObserver; @@ -74,6 +76,7 @@ public final class ContentObserverController extends StateController { @Override public void maybeStartTrackingJobLocked(JobStatus taskStatus, JobStatus lastJob) { if (taskStatus.hasContentTriggerConstraint()) { + final long nowElapsed = sElapsedRealtimeClock.millis(); if (taskStatus.contentObserverJobInstance == null) { taskStatus.contentObserverJobInstance = new JobInstance(taskStatus); } @@ -110,7 +113,7 @@ public final class ContentObserverController extends StateController { } taskStatus.changedAuthorities = null; taskStatus.changedUris = null; - taskStatus.setContentTriggerConstraintSatisfied(havePendingUris); + taskStatus.setContentTriggerConstraintSatisfied(nowElapsed, havePendingUris); } if (lastJob != null && lastJob.contentObserverJobInstance != null) { // And now we can detach the instance state from the last job. @@ -295,7 +298,8 @@ public final class ContentObserverController extends StateController { boolean reportChange = false; synchronized (mLock) { if (mTriggerPending) { - if (mJobStatus.setContentTriggerConstraintSatisfied(true)) { + final long nowElapsed = sElapsedRealtimeClock.millis(); + if (mJobStatus.setContentTriggerConstraintSatisfied(nowElapsed, true)) { reportChange = true; } unscheduleLocked(); diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java index 04b41646b48b..192f5e66255d 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java @@ -16,6 +16,8 @@ package com.android.server.job.controllers; +import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; + import android.app.job.JobInfo; import android.content.BroadcastReceiver; import android.content.Context; @@ -104,8 +106,10 @@ public final class DeviceIdleJobsController extends StateController { + Arrays.toString(mPowerSaveTempWhitelistAppIds)); } boolean changed = false; + final long nowElapsed = sElapsedRealtimeClock.millis(); for (int i = 0; i < mAllowInIdleJobs.size(); i++) { - changed |= updateTaskStateLocked(mAllowInIdleJobs.valueAt(i)); + changed |= + updateTaskStateLocked(mAllowInIdleJobs.valueAt(i), nowElapsed); } if (changed) { mStateChangedListener.onControllerStateChanged(); @@ -147,6 +151,7 @@ public final class DeviceIdleJobsController extends StateController { } mDeviceIdleMode = enabled; if (DEBUG) Slog.d(TAG, "mDeviceIdleMode=" + mDeviceIdleMode); + mDeviceIdleUpdateFunctor.prepare(); if (enabled) { mHandler.removeMessages(PROCESS_BACKGROUND_JOBS); mService.getJobStore().forEachJob(mDeviceIdleUpdateFunctor); @@ -180,7 +185,7 @@ public final class DeviceIdleJobsController extends StateController { Slog.d(TAG, "uid " + uid + " going " + (active ? "active" : "inactive")); } mForegroundUids.put(uid, active); - mDeviceIdleUpdateFunctor.mChanged = false; + mDeviceIdleUpdateFunctor.prepare(); mService.getJobStore().forEachJobForSourceUid(uid, mDeviceIdleUpdateFunctor); if (mDeviceIdleUpdateFunctor.mChanged) { mStateChangedListener.onControllerStateChanged(); @@ -203,12 +208,12 @@ public final class DeviceIdleJobsController extends StateController { UserHandle.getAppId(job.getSourceUid())); } - private boolean updateTaskStateLocked(JobStatus task) { + private boolean updateTaskStateLocked(JobStatus task, final long nowElapsed) { final boolean allowInIdle = ((task.getFlags()&JobInfo.FLAG_IMPORTANT_WHILE_FOREGROUND) != 0) && (mForegroundUids.get(task.getSourceUid()) || isTempWhitelistedLocked(task)); final boolean whitelisted = isWhitelistedLocked(task); final boolean enableTask = !mDeviceIdleMode || whitelisted || allowInIdle; - return task.setDeviceNotDozingConstraintSatisfied(enableTask, whitelisted); + return task.setDeviceNotDozingConstraintSatisfied(nowElapsed, enableTask, whitelisted); } @Override @@ -216,7 +221,7 @@ public final class DeviceIdleJobsController extends StateController { if ((jobStatus.getFlags()&JobInfo.FLAG_IMPORTANT_WHILE_FOREGROUND) != 0) { mAllowInIdleJobs.add(jobStatus); } - updateTaskStateLocked(jobStatus); + updateTaskStateLocked(jobStatus, sElapsedRealtimeClock.millis()); } @Override @@ -282,10 +287,16 @@ public final class DeviceIdleJobsController extends StateController { final class DeviceIdleUpdateFunctor implements Consumer<JobStatus> { boolean mChanged; + long mUpdateTimeElapsed = 0; + + void prepare() { + mChanged = false; + mUpdateTimeElapsed = sElapsedRealtimeClock.millis(); + } @Override public void accept(JobStatus jobStatus) { - mChanged |= updateTaskStateLocked(jobStatus); + mChanged |= updateTaskStateLocked(jobStatus, mUpdateTimeElapsed); } } @@ -300,7 +311,7 @@ public final class DeviceIdleJobsController extends StateController { case PROCESS_BACKGROUND_JOBS: // Just process all the jobs, the ones in foreground should already be running. synchronized (mLock) { - mDeviceIdleUpdateFunctor.mChanged = false; + mDeviceIdleUpdateFunctor.prepare(); mService.getJobStore().forEachJob(mDeviceIdleUpdateFunctor); if (mDeviceIdleUpdateFunctor.mChanged) { mStateChangedListener.onControllerStateChanged(); diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java index 2fe827e338e9..e26a3c6962d5 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java @@ -16,6 +16,8 @@ package com.android.server.job.controllers; +import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; + import android.content.Context; import android.content.pm.PackageManager; import android.os.UserHandle; @@ -58,9 +60,10 @@ public final class IdleController extends RestrictingController implements Idlen @Override public void maybeStartTrackingJobLocked(JobStatus taskStatus, JobStatus lastJob) { if (taskStatus.hasIdleConstraint()) { + final long nowElapsed = sElapsedRealtimeClock.millis(); mTrackedTasks.add(taskStatus); taskStatus.setTrackingController(JobStatus.TRACKING_IDLE); - taskStatus.setIdleConstraintSatisfied(mIdleTracker.isIdle()); + taskStatus.setIdleConstraintSatisfied(nowElapsed, mIdleTracker.isIdle()); } } @@ -90,8 +93,9 @@ public final class IdleController extends RestrictingController implements Idlen @Override public void reportNewIdleState(boolean isIdle) { synchronized (mLock) { + final long nowElapsed = sElapsedRealtimeClock.millis(); for (int i = mTrackedTasks.size()-1; i >= 0; i--) { - mTrackedTasks.valueAt(i).setIdleConstraintSatisfied(isIdle); + mTrackedTasks.valueAt(i).setIdleConstraintSatisfied(nowElapsed, isIdle); } } mStateChangedListener.onControllerStateChanged(); 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 6917fb531ac4..5bdeb38a1424 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 @@ -73,6 +73,8 @@ public final class JobStatus { private static final String TAG = "JobScheduler.JobStatus"; static final boolean DEBUG = JobSchedulerService.DEBUG; + private static final int NUM_CONSTRAINT_CHANGE_HISTORY = 10; + public static final long NO_LATEST_RUNTIME = Long.MAX_VALUE; public static final long NO_EARLIEST_RUNTIME = 0L; @@ -350,6 +352,10 @@ public final class JobStatus { */ private Pair<Long, Long> mPersistedUtcTimes; + private int mConstraintChangeHistoryIndex = 0; + private final long[] mConstraintUpdatedTimesElapsed = new long[NUM_CONSTRAINT_CHANGE_HISTORY]; + private final int[] mConstraintStatusHistory = new int[NUM_CONSTRAINT_CHANGE_HISTORY]; + /** * For use only by ContentObserverController: state it is maintaining about content URIs * being observed. @@ -1090,28 +1096,28 @@ public final class JobStatus { } /** @return true if the constraint was changed, false otherwise. */ - boolean setChargingConstraintSatisfied(boolean state) { - return setConstraintSatisfied(CONSTRAINT_CHARGING, state); + boolean setChargingConstraintSatisfied(final long nowElapsed, boolean state) { + return setConstraintSatisfied(CONSTRAINT_CHARGING, nowElapsed, state); } /** @return true if the constraint was changed, false otherwise. */ - boolean setBatteryNotLowConstraintSatisfied(boolean state) { - return setConstraintSatisfied(CONSTRAINT_BATTERY_NOT_LOW, state); + boolean setBatteryNotLowConstraintSatisfied(final long nowElapsed, boolean state) { + return setConstraintSatisfied(CONSTRAINT_BATTERY_NOT_LOW, nowElapsed, state); } /** @return true if the constraint was changed, false otherwise. */ - boolean setStorageNotLowConstraintSatisfied(boolean state) { - return setConstraintSatisfied(CONSTRAINT_STORAGE_NOT_LOW, state); + boolean setStorageNotLowConstraintSatisfied(final long nowElapsed, boolean state) { + return setConstraintSatisfied(CONSTRAINT_STORAGE_NOT_LOW, nowElapsed, state); } /** @return true if the constraint was changed, false otherwise. */ - boolean setTimingDelayConstraintSatisfied(boolean state) { - return setConstraintSatisfied(CONSTRAINT_TIMING_DELAY, state); + boolean setTimingDelayConstraintSatisfied(final long nowElapsed, boolean state) { + return setConstraintSatisfied(CONSTRAINT_TIMING_DELAY, nowElapsed, state); } /** @return true if the constraint was changed, false otherwise. */ - boolean setDeadlineConstraintSatisfied(boolean state) { - if (setConstraintSatisfied(CONSTRAINT_DEADLINE, state)) { + boolean setDeadlineConstraintSatisfied(final long nowElapsed, boolean state) { + if (setConstraintSatisfied(CONSTRAINT_DEADLINE, nowElapsed, state)) { // The constraint was changed. Update the ready flag. mReadyDeadlineSatisfied = !job.isPeriodic() && hasDeadlineConstraint() && state; return true; @@ -1120,24 +1126,25 @@ public final class JobStatus { } /** @return true if the constraint was changed, false otherwise. */ - boolean setIdleConstraintSatisfied(boolean state) { - return setConstraintSatisfied(CONSTRAINT_IDLE, state); + boolean setIdleConstraintSatisfied(final long nowElapsed, boolean state) { + return setConstraintSatisfied(CONSTRAINT_IDLE, nowElapsed, state); } /** @return true if the constraint was changed, false otherwise. */ - boolean setConnectivityConstraintSatisfied(boolean state) { - return setConstraintSatisfied(CONSTRAINT_CONNECTIVITY, state); + boolean setConnectivityConstraintSatisfied(final long nowElapsed, boolean state) { + return setConstraintSatisfied(CONSTRAINT_CONNECTIVITY, nowElapsed, state); } /** @return true if the constraint was changed, false otherwise. */ - boolean setContentTriggerConstraintSatisfied(boolean state) { - return setConstraintSatisfied(CONSTRAINT_CONTENT_TRIGGER, state); + boolean setContentTriggerConstraintSatisfied(final long nowElapsed, boolean state) { + return setConstraintSatisfied(CONSTRAINT_CONTENT_TRIGGER, nowElapsed, state); } /** @return true if the constraint was changed, false otherwise. */ - boolean setDeviceNotDozingConstraintSatisfied(boolean state, boolean whitelisted) { + boolean setDeviceNotDozingConstraintSatisfied(final long nowElapsed, + boolean state, boolean whitelisted) { dozeWhitelisted = whitelisted; - if (setConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING, state)) { + if (setConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING, nowElapsed, state)) { // The constraint was changed. Update the ready flag. mReadyNotDozing = state || canRunInDoze(); return true; @@ -1146,8 +1153,8 @@ public final class JobStatus { } /** @return true if the constraint was changed, false otherwise. */ - boolean setBackgroundNotRestrictedConstraintSatisfied(boolean state) { - if (setConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED, state)) { + boolean setBackgroundNotRestrictedConstraintSatisfied(final long nowElapsed, boolean state) { + if (setConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED, nowElapsed, state)) { // The constraint was changed. Update the ready flag. mReadyNotRestrictedInBg = state; return true; @@ -1156,8 +1163,8 @@ public final class JobStatus { } /** @return true if the constraint was changed, false otherwise. */ - boolean setQuotaConstraintSatisfied(boolean state) { - if (setConstraintSatisfied(CONSTRAINT_WITHIN_QUOTA, state)) { + boolean setQuotaConstraintSatisfied(final long nowElapsed, boolean state) { + if (setConstraintSatisfied(CONSTRAINT_WITHIN_QUOTA, nowElapsed, state)) { // The constraint was changed. Update the ready flag. mReadyWithinQuota = state; return true; @@ -1166,8 +1173,8 @@ public final class JobStatus { } /** @return true if the constraint was changed, false otherwise. */ - boolean setExpeditedJobQuotaConstraintSatisfied(boolean state) { - if (setConstraintSatisfied(CONSTRAINT_WITHIN_EXPEDITED_QUOTA, state)) { + boolean setExpeditedJobQuotaConstraintSatisfied(final long nowElapsed, boolean state) { + if (setConstraintSatisfied(CONSTRAINT_WITHIN_EXPEDITED_QUOTA, nowElapsed, state)) { // The constraint was changed. Update the ready flag. mReadyWithinExpeditedQuota = state; // DeviceIdleJobsController currently only tracks jobs with the WILL_BE_FOREGROUND flag. @@ -1190,7 +1197,7 @@ public final class JobStatus { } /** @return true if the constraint was changed, false otherwise. */ - boolean setConstraintSatisfied(int constraint, boolean state) { + boolean setConstraintSatisfied(int constraint, final long nowElapsed, boolean state) { boolean old = (satisfiedConstraints&constraint) != 0; if (old == state) { return false; @@ -1212,6 +1219,12 @@ public final class JobStatus { : FrameworkStatsLog .SCHEDULED_JOB_CONSTRAINT_CHANGED__STATE__UNSATISFIED); } + + mConstraintUpdatedTimesElapsed[mConstraintChangeHistoryIndex] = nowElapsed; + mConstraintStatusHistory[mConstraintChangeHistoryIndex] = satisfiedConstraints; + mConstraintChangeHistoryIndex = + (mConstraintChangeHistoryIndex + 1) % NUM_CONSTRAINT_CHANGE_HISTORY; + return true; } @@ -1700,7 +1713,7 @@ public final class JobStatus { } // Dumpsys infrastructure - public void dump(IndentingPrintWriter pw, boolean full, long elapsedRealtimeMillis) { + public void dump(IndentingPrintWriter pw, boolean full, long nowElapsed) { UserHandle.formatUid(pw, callingUid); pw.print(" tag="); pw.println(tag); @@ -1830,6 +1843,22 @@ public final class JobStatus { dumpConstraints(pw, ((requiredConstraints | CONSTRAINT_WITHIN_QUOTA) & ~satisfiedConstraints)); pw.println(); + + pw.println("Constraint history:"); + pw.increaseIndent(); + for (int h = 0; h < NUM_CONSTRAINT_CHANGE_HISTORY; ++h) { + final int idx = (h + mConstraintChangeHistoryIndex) % NUM_CONSTRAINT_CHANGE_HISTORY; + if (mConstraintUpdatedTimesElapsed[idx] == 0) { + continue; + } + TimeUtils.formatDuration(mConstraintUpdatedTimesElapsed[idx], nowElapsed, pw); + // dumpConstraints prepends with a space, so no need to add a space after the = + pw.print(" ="); + dumpConstraints(pw, mConstraintStatusHistory[idx]); + pw.println(); + } + pw.decreaseIndent(); + if (dozeWhitelisted) { pw.println("Doze whitelisted: true"); } @@ -1910,26 +1939,25 @@ public final class JobStatus { pw.increaseIndent(); if (whenStandbyDeferred != 0) { pw.print("Deferred since: "); - TimeUtils.formatDuration(whenStandbyDeferred, elapsedRealtimeMillis, pw); + TimeUtils.formatDuration(whenStandbyDeferred, nowElapsed, pw); pw.println(); } if (mFirstForceBatchedTimeElapsed != 0) { pw.print("Time since first force batch attempt: "); - TimeUtils.formatDuration(mFirstForceBatchedTimeElapsed, elapsedRealtimeMillis, pw); + TimeUtils.formatDuration(mFirstForceBatchedTimeElapsed, nowElapsed, pw); pw.println(); } pw.decreaseIndent(); pw.print("Enqueue time: "); - TimeUtils.formatDuration(enqueueTime, elapsedRealtimeMillis, pw); + TimeUtils.formatDuration(enqueueTime, nowElapsed, pw); pw.println(); pw.print("Run time: earliest="); - formatRunTime(pw, earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME, elapsedRealtimeMillis); + formatRunTime(pw, earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME, nowElapsed); pw.print(", latest="); - formatRunTime(pw, latestRunTimeElapsedMillis, NO_LATEST_RUNTIME, elapsedRealtimeMillis); + formatRunTime(pw, latestRunTimeElapsedMillis, NO_LATEST_RUNTIME, nowElapsed); pw.print(", original latest="); - formatRunTime(pw, mOriginalLatestRunTimeElapsedMillis, - NO_LATEST_RUNTIME, elapsedRealtimeMillis); + formatRunTime(pw, mOriginalLatestRunTimeElapsedMillis, NO_LATEST_RUNTIME, nowElapsed); pw.println(); if (numFailures != 0) { pw.print("Num failures: "); pw.println(numFailures); diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java index 2196b16e0846..824fa7fc1659 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java @@ -626,6 +626,7 @@ public final class QuotaController extends StateController { @Override public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) { + final long nowElapsed = sElapsedRealtimeClock.millis(); final int userId = jobStatus.getSourceUserId(); final String pkgName = jobStatus.getSourcePackageName(); ArraySet<JobStatus> jobs = mTrackedJobs.get(userId, pkgName); @@ -636,11 +637,11 @@ public final class QuotaController extends StateController { jobs.add(jobStatus); jobStatus.setTrackingController(JobStatus.TRACKING_QUOTA); final boolean isWithinQuota = isWithinQuotaLocked(jobStatus); - setConstraintSatisfied(jobStatus, isWithinQuota); + setConstraintSatisfied(jobStatus, nowElapsed, isWithinQuota); final boolean outOfEJQuota; if (jobStatus.isRequestedExpeditedJob()) { final boolean isWithinEJQuota = isWithinEJQuotaLocked(jobStatus); - setExpeditedConstraintSatisfied(jobStatus, isWithinEJQuota); + setExpeditedConstraintSatisfied(jobStatus, nowElapsed, isWithinEJQuota); outOfEJQuota = !isWithinEJQuota; } else { outOfEJQuota = false; @@ -1397,7 +1398,8 @@ public final class QuotaController extends StateController { synchronized (mLock) { final ShrinkableDebits quota = getEJQuotaLocked(userId, packageName); quota.transactOnDebitsLocked(-credit); - if (maybeUpdateConstraintForPkgLocked(userId, packageName)) { + if (maybeUpdateConstraintForPkgLocked(sElapsedRealtimeClock.millis(), + userId, packageName)) { mStateChangedListener.onControllerStateChanged(); } } @@ -1499,11 +1501,12 @@ public final class QuotaController extends StateController { private void maybeUpdateAllConstraintsLocked() { boolean changed = false; + final long nowElapsed = sElapsedRealtimeClock.millis(); for (int u = 0; u < mTrackedJobs.numMaps(); ++u) { final int userId = mTrackedJobs.keyAt(u); for (int p = 0; p < mTrackedJobs.numElementsForKey(userId); ++p) { final String packageName = mTrackedJobs.keyAt(u, p); - changed |= maybeUpdateConstraintForPkgLocked(userId, packageName); + changed |= maybeUpdateConstraintForPkgLocked(nowElapsed, userId, packageName); } } if (changed) { @@ -1516,7 +1519,7 @@ public final class QuotaController extends StateController { * * @return true if at least one job had its bit changed */ - private boolean maybeUpdateConstraintForPkgLocked(final int userId, + private boolean maybeUpdateConstraintForPkgLocked(final long nowElapsed, final int userId, @NonNull final String packageName) { ArraySet<JobStatus> jobs = mTrackedJobs.get(userId, packageName); if (jobs == null || jobs.size() == 0) { @@ -1533,21 +1536,21 @@ public final class QuotaController extends StateController { if (isTopStartedJobLocked(js)) { // Job was started while the app was in the TOP state so we should allow it to // finish. - changed |= js.setQuotaConstraintSatisfied(true); + changed |= js.setQuotaConstraintSatisfied(nowElapsed, true); } else if (realStandbyBucket != ACTIVE_INDEX && realStandbyBucket == js.getEffectiveStandbyBucket()) { // An app in the ACTIVE bucket may be out of quota while the job could be in quota // for some reason. Therefore, avoid setting the real value here and check each job // individually. - changed |= setConstraintSatisfied(js, realInQuota); + changed |= setConstraintSatisfied(js, nowElapsed, realInQuota); } else { // This job is somehow exempted. Need to determine its own quota status. - changed |= setConstraintSatisfied(js, isWithinQuotaLocked(js)); + changed |= setConstraintSatisfied(js, nowElapsed, isWithinQuotaLocked(js)); } if (js.isRequestedExpeditedJob()) { boolean isWithinEJQuota = isWithinEJQuotaLocked(js); - changed |= setExpeditedConstraintSatisfied(js, isWithinEJQuota); + changed |= setExpeditedConstraintSatisfied(js, nowElapsed, isWithinEJQuota); outOfEJQuota |= !isWithinEJQuota; } } @@ -1566,14 +1569,21 @@ public final class QuotaController extends StateController { private final SparseArrayMap<String, Integer> mToScheduleStartAlarms = new SparseArrayMap<>(); public boolean wasJobChanged; + long mUpdateTimeElapsed = 0; + + void prepare() { + mUpdateTimeElapsed = sElapsedRealtimeClock.millis(); + } @Override public void accept(JobStatus jobStatus) { - wasJobChanged |= setConstraintSatisfied(jobStatus, isWithinQuotaLocked(jobStatus)); + wasJobChanged |= setConstraintSatisfied( + jobStatus, mUpdateTimeElapsed, isWithinQuotaLocked(jobStatus)); final boolean outOfEJQuota; if (jobStatus.isRequestedExpeditedJob()) { final boolean isWithinEJQuota = isWithinEJQuotaLocked(jobStatus); - wasJobChanged |= setExpeditedConstraintSatisfied(jobStatus, isWithinEJQuota); + wasJobChanged |= setExpeditedConstraintSatisfied( + jobStatus, mUpdateTimeElapsed, isWithinEJQuota); outOfEJQuota = !isWithinEJQuota; } else { outOfEJQuota = false; @@ -1611,6 +1621,7 @@ public final class QuotaController extends StateController { private final UidConstraintUpdater mUpdateUidConstraints = new UidConstraintUpdater(); private boolean maybeUpdateConstraintForUidLocked(final int uid) { + mUpdateUidConstraints.prepare(); mService.getJobStore().forEachJobForSourceUid(uid, mUpdateUidConstraints); mUpdateUidConstraints.postProcess(); @@ -1716,21 +1727,22 @@ public final class QuotaController extends StateController { mInQuotaAlarmListener.addAlarmLocked(userId, packageName, inQuotaTimeElapsed); } - private boolean setConstraintSatisfied(@NonNull JobStatus jobStatus, boolean isWithinQuota) { + private boolean setConstraintSatisfied(@NonNull JobStatus jobStatus, long nowElapsed, + boolean isWithinQuota) { if (!isWithinQuota && jobStatus.getWhenStandbyDeferred() == 0) { // Mark that the job is being deferred due to buckets. - jobStatus.setWhenStandbyDeferred(sElapsedRealtimeClock.millis()); + jobStatus.setWhenStandbyDeferred(nowElapsed); } - return jobStatus.setQuotaConstraintSatisfied(isWithinQuota); + return jobStatus.setQuotaConstraintSatisfied(nowElapsed, isWithinQuota); } /** * If the satisfaction changes, this will tell connectivity & background jobs controller to * also re-evaluate their state. */ - private boolean setExpeditedConstraintSatisfied(@NonNull JobStatus jobStatus, + private boolean setExpeditedConstraintSatisfied(@NonNull JobStatus jobStatus, long nowElapsed, boolean isWithinQuota) { - if (jobStatus.setExpeditedJobQuotaConstraintSatisfied(isWithinQuota)) { + if (jobStatus.setExpeditedJobQuotaConstraintSatisfied(nowElapsed, isWithinQuota)) { mBackgroundJobsController.evaluateStateLocked(jobStatus); mConnectivityController.evaluateStateLocked(jobStatus); if (isWithinQuota && jobStatus.isReady()) { @@ -2188,7 +2200,8 @@ public final class QuotaController extends StateController { final ShrinkableDebits quota = getEJQuotaLocked(mPkg.userId, mPkg.packageName); quota.transactOnDebitsLocked(-mEJRewardTopAppMs * numTimeChunks); - if (maybeUpdateConstraintForPkgLocked(mPkg.userId, mPkg.packageName)) { + if (maybeUpdateConstraintForPkgLocked(nowElapsed, + mPkg.userId, mPkg.packageName)) { mStateChangedListener.onControllerStateChanged(); } } @@ -2292,7 +2305,8 @@ public final class QuotaController extends StateController { if (timer != null && timer.isActive()) { timer.rescheduleCutoff(); } - if (maybeUpdateConstraintForPkgLocked(userId, packageName)) { + if (maybeUpdateConstraintForPkgLocked(sElapsedRealtimeClock.millis(), + userId, packageName)) { mStateChangedListener.onControllerStateChanged(); } } @@ -2417,7 +2431,8 @@ public final class QuotaController extends StateController { if (timeRemainingMs <= 50) { // Less than 50 milliseconds left. Start process of shutting down jobs. if (DEBUG) Slog.d(TAG, pkg + " has reached its quota."); - if (maybeUpdateConstraintForPkgLocked(pkg.userId, pkg.packageName)) { + if (maybeUpdateConstraintForPkgLocked(sElapsedRealtimeClock.millis(), + pkg.userId, pkg.packageName)) { mStateChangedListener.onControllerStateChanged(); } } else { @@ -2444,7 +2459,8 @@ public final class QuotaController extends StateController { pkg.userId, pkg.packageName); if (timeRemainingMs <= 0) { if (DEBUG) Slog.d(TAG, pkg + " has reached its EJ quota."); - if (maybeUpdateConstraintForPkgLocked(pkg.userId, pkg.packageName)) { + if (maybeUpdateConstraintForPkgLocked(sElapsedRealtimeClock.millis(), + pkg.userId, pkg.packageName)) { mStateChangedListener.onControllerStateChanged(); } } else { @@ -2475,7 +2491,8 @@ public final class QuotaController extends StateController { if (DEBUG) { Slog.d(TAG, "Checking pkg " + string(userId, packageName)); } - if (maybeUpdateConstraintForPkgLocked(userId, packageName)) { + if (maybeUpdateConstraintForPkgLocked(sElapsedRealtimeClock.millis(), + userId, packageName)) { mStateChangedListener.onControllerStateChanged(); } break; diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/StorageController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/StorageController.java index 0731918d83a1..867891363912 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/StorageController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/StorageController.java @@ -61,9 +61,11 @@ public final class StorageController extends StateController { @Override public void maybeStartTrackingJobLocked(JobStatus taskStatus, JobStatus lastJob) { if (taskStatus.hasStorageNotLowConstraint()) { + final long nowElapsed = sElapsedRealtimeClock.millis(); mTrackedTasks.add(taskStatus); taskStatus.setTrackingController(JobStatus.TRACKING_STORAGE); - taskStatus.setStorageNotLowConstraintSatisfied(mStorageTracker.isStorageNotLow()); + taskStatus.setStorageNotLowConstraintSatisfied( + nowElapsed, mStorageTracker.isStorageNotLow()); } } @@ -76,12 +78,13 @@ public final class StorageController extends StateController { } private void maybeReportNewStorageState() { + final long nowElapsed = sElapsedRealtimeClock.millis(); final boolean storageNotLow = mStorageTracker.isStorageNotLow(); boolean reportChange = false; synchronized (mLock) { for (int i = mTrackedTasks.size() - 1; i >= 0; i--) { final JobStatus ts = mTrackedTasks.valueAt(i); - reportChange |= ts.setStorageNotLowConstraintSatisfied(storageNotLow); + reportChange |= ts.setStorageNotLowConstraintSatisfied(nowElapsed, storageNotLow); } } if (storageNotLow) { diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java index ede14ec06c71..e8ebfb53fde8 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java @@ -258,9 +258,9 @@ public final class TimeController extends StateController { if (jobDeadline <= nowElapsedMillis) { if (job.hasTimingDelayConstraint()) { - job.setTimingDelayConstraintSatisfied(true); + job.setTimingDelayConstraintSatisfied(nowElapsedMillis, true); } - job.setDeadlineConstraintSatisfied(true); + job.setDeadlineConstraintSatisfied(nowElapsedMillis, true); return true; } return false; @@ -332,7 +332,7 @@ public final class TimeController extends StateController { private boolean evaluateTimingDelayConstraint(JobStatus job, long nowElapsedMillis) { final long jobDelayTime = job.getEarliestRunTime(); if (jobDelayTime <= nowElapsedMillis) { - job.setTimingDelayConstraintSatisfied(true); + job.setTimingDelayConstraintSatisfied(nowElapsedMillis, true); return true; } return false; diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp index 5c08704a6623..d4da5e554591 100644 --- a/cmds/screencap/screencap.cpp +++ b/cmds/screencap/screencap.cpp @@ -34,7 +34,6 @@ #include <gui/SurfaceComposerClient.h> #include <gui/SyncScreenCaptureListener.h> -#include <ui/DisplayInfo.h> #include <ui/GraphicTypes.h> #include <ui/PixelFormat.h> diff --git a/core/api/current.txt b/core/api/current.txt index 4382b3207e1e..dddca3e58b0d 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -14615,11 +14615,6 @@ package android.graphics { enum_constant public static final android.graphics.BlurMaskFilter.Blur SOLID; } - public final class BlurShader extends android.graphics.Shader { - ctor public BlurShader(float, float, @Nullable android.graphics.Shader); - ctor public BlurShader(float, float, @Nullable android.graphics.Shader, @NonNull android.graphics.Shader.TileMode); - } - public class Camera { ctor public Camera(); method public void applyToCanvas(android.graphics.Canvas); diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 7b7518d05345..fbe24c43f027 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -8580,7 +8580,7 @@ package android.os { method @RequiresPermission(allOf={android.Manifest.permission.READ_DREAM_STATE, android.Manifest.permission.WRITE_DREAM_STATE}) public void dream(long); method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public boolean forceSuspend(); method @NonNull public android.os.BatterySaverPolicyConfig getFullPowerSavePolicy(); - method @RequiresPermission(android.Manifest.permission.POWER_SAVER) public int getPowerSaveModeTrigger(); + method public int getPowerSaveModeTrigger(); method @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) public boolean isAmbientDisplayAvailable(); method @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) public boolean isAmbientDisplaySuppressed(); method @RequiresPermission(android.Manifest.permission.READ_DREAM_STATE) public boolean isAmbientDisplaySuppressedForToken(@NonNull String); diff --git a/core/api/test-current.txt b/core/api/test-current.txt index e0e84537368f..d7b43c0bc48e 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -1640,6 +1640,7 @@ package android.os.storage { public class StorageManager { method @NonNull public static java.util.UUID convert(@NonNull String); method @NonNull public static String convert(@NonNull java.util.UUID); + method public static boolean isUserKeyUnlocked(int); } public final class StorageVolume implements android.os.Parcelable { diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 1906ee4d85d2..b8735c731817 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -6553,14 +6553,15 @@ public class AppOpsManager { public interface OnOpNotedListener { /** * Called when an op was noted. - * * @param code The op code. * @param uid The UID performing the operation. * @param packageName The package performing the operation. + * @param attributionTag The attribution tag performing the operation. * @param flags The flags of this op * @param result The result of the note. */ - void onOpNoted(int code, int uid, String packageName, @OpFlags int flags, @Mode int result); + void onOpNoted(int code, int uid, String packageName, String attributionTag, + @OpFlags int flags, @Mode int result); } /** @@ -6593,14 +6594,15 @@ public class AppOpsManager { * Called when an op was started. * * Note: This is only for op starts. It is not called when an op is noted or stopped. - * * @param op The op code. * @param uid The UID performing the operation. * @param packageName The package performing the operation. + * @param attributionTag The attribution tag performing the operation. * @param flags The flags of this op * @param result The result of the start. */ - void onOpStarted(int op, int uid, String packageName, @OpFlags int flags, @Mode int result); + void onOpStarted(int op, int uid, String packageName, String attributionTag, + @OpFlags int flags, @Mode int result); } AppOpsManager(Context context, IAppOpsService service) { @@ -7183,8 +7185,9 @@ public class AppOpsManager { } cb = new IAppOpsStartedCallback.Stub() { @Override - public void opStarted(int op, int uid, String packageName, int flags, int mode) { - callback.onOpStarted(op, uid, packageName, flags, mode); + public void opStarted(int op, int uid, String packageName, String attributionTag, + int flags, int mode) { + callback.onOpStarted(op, uid, packageName, attributionTag, flags, mode); } }; mStartedWatchers.put(callback, cb); @@ -7250,8 +7253,9 @@ public class AppOpsManager { } cb = new IAppOpsNotedCallback.Stub() { @Override - public void opNoted(int op, int uid, String packageName, int flags, int mode) { - callback.onOpNoted(op, uid, packageName, flags, mode); + public void opNoted(int op, int uid, String packageName, String attributionTag, + int flags, int mode) { + callback.onOpNoted(op, uid, packageName, attributionTag, flags, mode); } }; mNotedWatchers.put(callback, cb); diff --git a/core/java/android/app/time/ExternalTimeSuggestion.java b/core/java/android/app/time/ExternalTimeSuggestion.java index b566eab9867d..61defb588170 100644 --- a/core/java/android/app/time/ExternalTimeSuggestion.java +++ b/core/java/android/app/time/ExternalTimeSuggestion.java @@ -16,6 +16,8 @@ package android.app.time; +import android.annotation.CurrentTimeMillisLong; +import android.annotation.ElapsedRealtimeLong; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Parcel; @@ -31,9 +33,9 @@ import java.util.Objects; /** * A time signal from an External source. * - * External time suggestions are for use in situations where the Android device is part of a wider - * network of devices that are required to use a single time source, and where authority for the - * time is external to the Android device. For example, for the Android Auto use case where the + * <p>External time suggestions are for use in situations where the Android device is part of a + * wider network of devices that are required to use a single time source, and where authority for + * the time is external to the Android device. For example, for the Android Auto use case where the * Android device is part of a wider in-car network of devices that should display the same time. * * <p>Android allows for a single external source for time. If there are several external sources @@ -49,19 +51,19 @@ import java.util.Objects; * capture the elapsed realtime reference clock, e.g. via {@link SystemClock#elapsedRealtime()}, * when the UTC time is first obtained (usually under a wakelock). This enables Android to adjust * for latency introduced between suggestion creation and eventual use. Adjustments for other - * sources of latency, i.e. those before the external time suggestion is created, must be handled - * by the creator. + * sources of latency, i.e. those before the external time suggestion is created, must be handled by + * the creator. * - * <p>{@code utcTime} is the suggested time. The {@code utcTime.value} is the number of milliseconds - * elapsed since 1/1/1970 00:00:00 UTC. The {@code utcTime.referenceTimeMillis} is the value of the - * elapsed realtime clock when the {@code utcTime.value} was established. - * Note that the elapsed realtime clock is considered accurate but it is volatile, so time - * suggestions cannot be persisted across device resets. + * <p>{@code elapsedRealtimeMillis} and {@code suggestionMillis} represent the suggested time. + * {@code suggestionMillis} is the number of milliseconds elapsed since 1/1/1970 00:00:00 UTC. + * {@code elapsedRealtimeMillis} is the value of the elapsed realtime clock when {@code + * suggestionMillis} was established. Note that the elapsed realtime clock is considered accurate + * but it is volatile, so time suggestions cannot be persisted across device resets. * * <p>{@code debugInfo} contains debugging metadata associated with the suggestion. This is used to * record why the suggestion exists and how it was entered. This information exists only to aid in - * debugging and therefore is used by {@link #toString()}, but it is not for use in detection - * logic and is not considered in {@link #hashCode()} or {@link #equals(Object)}. + * debugging and therefore is used by {@link #toString()}, but it is not for use in detection logic + * and is not considered in {@link #hashCode()} or {@link #equals(Object)}. * * @hide */ @@ -78,17 +80,28 @@ public final class ExternalTimeSuggestion implements Parcelable { } }; - @NonNull private final TimestampedValue<Long> mUtcTime; - @Nullable private ArrayList<String> mDebugInfo; + @NonNull + private final TimestampedValue<Long> mUtcTime; + @Nullable + private ArrayList<String> mDebugInfo; - public ExternalTimeSuggestion(@NonNull TimestampedValue<Long> utcTime) { - mUtcTime = Objects.requireNonNull(utcTime); - Objects.requireNonNull(utcTime.getValue()); + /** + * Creates a time suggestion cross-referenced to the elapsed realtime clock. See {@link + * ExternalTimeSuggestion} for more details. + * + * @param elapsedRealtimeMillis the elapsed realtime clock reference for the suggestion + * @param suggestionMillis the suggested UTC time in milliseconds since the start of the + * Unix epoch + */ + public ExternalTimeSuggestion(@ElapsedRealtimeLong long elapsedRealtimeMillis, + @CurrentTimeMillisLong long suggestionMillis) { + mUtcTime = new TimestampedValue(elapsedRealtimeMillis, suggestionMillis); } private static ExternalTimeSuggestion createFromParcel(Parcel in) { TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */); - ExternalTimeSuggestion suggestion = new ExternalTimeSuggestion(utcTime); + ExternalTimeSuggestion suggestion = + new ExternalTimeSuggestion(utcTime.getReferenceTimeMillis(), utcTime.getValue()); @SuppressWarnings("unchecked") ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */); suggestion.mDebugInfo = debugInfo; @@ -106,23 +119,31 @@ public final class ExternalTimeSuggestion implements Parcelable { dest.writeList(mDebugInfo); } + /** + * {@hide} + */ @NonNull public TimestampedValue<Long> getUtcTime() { return mUtcTime; } + /** + * Returns information that can be useful for debugging / logging. See {@link #addDebugInfo}. + * {@hide} + */ @NonNull public List<String> getDebugInfo() { return mDebugInfo == null - ? Collections.emptyList() : Collections.unmodifiableList(mDebugInfo); + ? Collections.emptyList() + : Collections.unmodifiableList(mDebugInfo); } /** * Associates information with the instance that can be useful for debugging / logging. The - * information is present in {@link #toString()} but is not considered for - * {@link #equals(Object)} and {@link #hashCode()}. + * information is present in {@link #toString()} but is not considered for {@link + * #equals(Object)} and {@link #hashCode()}. */ - public void addDebugInfo(String... debugInfos) { + public void addDebugInfo(@NonNull String... debugInfos) { if (mDebugInfo == null) { mDebugInfo = new ArrayList<>(); } @@ -148,9 +169,7 @@ public final class ExternalTimeSuggestion implements Parcelable { @Override public String toString() { - return "ExternalTimeSuggestion{" - + "mUtcTime=" + mUtcTime - + ", mDebugInfo=" + mDebugInfo + return "ExternalTimeSuggestion{" + "mUtcTime=" + mUtcTime + ", mDebugInfo=" + mDebugInfo + '}'; } } diff --git a/core/java/android/app/time/TimeManager.java b/core/java/android/app/time/TimeManager.java index 262d244c5b1c..430960fb11a8 100644 --- a/core/java/android/app/time/TimeManager.java +++ b/core/java/android/app/time/TimeManager.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; +import android.app.timedetector.ITimeDetectorService; import android.app.timezonedetector.ITimeZoneDetectorService; import android.content.Context; import android.os.RemoteException; @@ -45,6 +46,7 @@ public final class TimeManager { private final Object mLock = new Object(); private final ITimeZoneDetectorService mITimeZoneDetectorService; + private final ITimeDetectorService mITimeDetectorService; @GuardedBy("mLock") private ITimeZoneDetectorListener mTimeZoneDetectorReceiver; @@ -62,6 +64,8 @@ public final class TimeManager { // internal refactoring. mITimeZoneDetectorService = ITimeZoneDetectorService.Stub.asInterface( ServiceManager.getServiceOrThrow(Context.TIME_ZONE_DETECTOR_SERVICE)); + mITimeDetectorService = ITimeDetectorService.Stub.asInterface( + ServiceManager.getServiceOrThrow(Context.TIME_DETECTOR_SERVICE)); } /** @@ -214,4 +218,23 @@ public final class TimeManager { } } } + + /** + * Suggests the current time from an external time source. For example, a form factor-specific + * HAL. This time <em>may</em> be used to set the device system clock, depending on the device + * configuration and user settings. This method call is processed asynchronously. + * See {@link ExternalTimeSuggestion} for more details. + * {@hide} + */ + @RequiresPermission(android.Manifest.permission.SET_TIME) + public void suggestExternalTime(@NonNull ExternalTimeSuggestion timeSuggestion) { + if (DEBUG) { + Log.d(TAG, "suggestExternalTime called: " + timeSuggestion); + } + try { + mITimeDetectorService.suggestExternalTime(timeSuggestion); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/app/timedetector/TimeDetector.java b/core/java/android/app/timedetector/TimeDetector.java index 76f378590ae2..52016b65688b 100644 --- a/core/java/android/app/timedetector/TimeDetector.java +++ b/core/java/android/app/timedetector/TimeDetector.java @@ -19,7 +19,6 @@ package android.app.timedetector; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemService; -import android.app.time.ExternalTimeSuggestion; import android.content.Context; import android.os.SystemClock; import android.os.TimestampedValue; @@ -80,12 +79,4 @@ public interface TimeDetector { */ @RequiresPermission(android.Manifest.permission.SET_TIME) void suggestGnssTime(GnssTimeSuggestion timeSuggestion); - - /** - * Suggests the time according to an external time source (form factor specific HAL, etc). - * - * @hide - */ - @RequiresPermission(android.Manifest.permission.SET_TIME) - void suggestExternalTime(ExternalTimeSuggestion timeSuggestion); } diff --git a/core/java/android/app/timedetector/TimeDetectorImpl.java b/core/java/android/app/timedetector/TimeDetectorImpl.java index ef818ef647d6..b0aa3c8d4575 100644 --- a/core/java/android/app/timedetector/TimeDetectorImpl.java +++ b/core/java/android/app/timedetector/TimeDetectorImpl.java @@ -17,7 +17,6 @@ package android.app.timedetector; import android.annotation.NonNull; -import android.app.time.ExternalTimeSuggestion; import android.content.Context; import android.os.RemoteException; import android.os.ServiceManager; @@ -87,16 +86,4 @@ public final class TimeDetectorImpl implements TimeDetector { throw e.rethrowFromSystemServer(); } } - - @Override - public void suggestExternalTime(ExternalTimeSuggestion timeSuggestion) { - if (DEBUG) { - Log.d(TAG, "suggestExternalTime called: " + timeSuggestion); - } - try { - mITimeDetectorService.suggestExternalTime(timeSuggestion); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } } diff --git a/core/java/android/content/pm/dex/DexMetadataHelper.java b/core/java/android/content/pm/dex/DexMetadataHelper.java index bc5d14a533e2..0d5b33cd8672 100644 --- a/core/java/android/content/pm/dex/DexMetadataHelper.java +++ b/core/java/android/content/pm/dex/DexMetadataHelper.java @@ -24,16 +24,16 @@ import android.content.pm.parsing.ApkLiteParseUtils; import android.content.pm.parsing.PackageLite; import android.os.SystemProperties; import android.util.ArrayMap; -import android.util.jar.StrictJarFile; import android.util.JsonReader; import android.util.Log; +import android.util.jar.StrictJarFile; import com.android.internal.annotations.VisibleForTesting; import java.io.File; +import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.io.IOException; import java.io.UnsupportedEncodingException; import java.nio.file.Files; import java.nio.file.Paths; @@ -53,7 +53,10 @@ public class DexMetadataHelper { /** $> adb shell 'setprop log.tag.DexMetadataHelper VERBOSE' */ public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); /** $> adb shell 'setprop pm.dexopt.dm.require_manifest true' */ - private static String PROPERTY_DM_JSON_MANIFEST_REQUIRED = "pm.dexopt.dm.require_manifest"; + private static final String PROPERTY_DM_JSON_MANIFEST_REQUIRED = + "pm.dexopt.dm.require_manifest"; + /** $> adb shell 'setprop pm.dexopt.dm.require_fsverity true' */ + private static final String PROPERTY_DM_FSVERITY_REQUIRED = "pm.dexopt.dm.require_fsverity"; private static final String DEX_METADATA_FILE_EXTENSION = ".dm"; @@ -70,6 +73,13 @@ public class DexMetadataHelper { } /** + * Returns whether fs-verity is required to install a dex metadata + */ + public static boolean isFsVerityRequired() { + return SystemProperties.getBoolean(PROPERTY_DM_FSVERITY_REQUIRED, false); + } + + /** * Return the size (in bytes) of all dex metadata files associated with the given package. */ public static long getPackageDexMetadataSize(PackageLite pkg) { diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java index 1ffd18fc1ac8..788afe3bdb8e 100644 --- a/core/java/android/hardware/SystemSensorManager.java +++ b/core/java/android/hardware/SystemSensorManager.java @@ -667,7 +667,7 @@ public class SystemSensorManager extends SensorManager { private abstract static class BaseEventQueue { private static native long nativeInitBaseEventQueue(long nativeManager, WeakReference<BaseEventQueue> eventQWeak, MessageQueue msgQ, - String packageName, int mode, String opPackageName); + String packageName, int mode, String opPackageName, String attributionTag); private static native int nativeEnableSensor(long eventQ, int handle, int rateUs, int maxBatchReportLatencyUs); private static native int nativeDisableSensor(long eventQ, int handle); @@ -689,7 +689,8 @@ public class SystemSensorManager extends SensorManager { if (packageName == null) packageName = ""; mNativeSensorEventQueue = nativeInitBaseEventQueue(manager.mNativeInstance, new WeakReference<>(this), looper.getQueue(), - packageName, mode, manager.mContext.getOpPackageName()); + packageName, mode, manager.mContext.getOpPackageName(), + manager.mContext.getAttributionTag()); mCloseGuard.open("dispose"); mManager = manager; } diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index ff9b4f41ef3b..e5163d83de69 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -1881,6 +1881,10 @@ public final class PowerManager { * Returns the current battery saver control mode. Values it may return are defined in * AutoPowerSaveModeTriggers. Note that this is a global device state, not a per user setting. * + * <p>Note: Prior to Android version {@link Build.VERSION_CODES#S}, any app calling this method + * was required to hold the {@link android.Manifest.permission#POWER_SAVER} permission. Starting + * from Android version {@link Build.VERSION_CODES#S}, that permission is no longer required. + * * @return The current value power saver mode for the system. * * @see AutoPowerSaveModeTriggers @@ -1889,7 +1893,6 @@ public final class PowerManager { */ @AutoPowerSaveModeTriggers @SystemApi - @RequiresPermission(android.Manifest.permission.POWER_SAVER) public int getPowerSaveModeTrigger() { try { return mService.getPowerSaveModeTrigger(); diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index 54d2df865c39..136dc388022f 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -207,6 +207,12 @@ public class Process { public static final int SE_UID = 1068; /** + * Defines the UID/GID for the iorapd. + * @hide + */ + public static final int IORAPD_UID = 1071; + + /** * Defines the UID/GID for the NetworkStack app. * @hide */ diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index a5d3c2acc577..3a5426c60b24 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -1536,6 +1536,7 @@ public class StorageManager { } /** {@hide} */ + @TestApi public static boolean isUserKeyUnlocked(int userId) { if (sStorageManager == null) { sStorageManager = IStorageManager.Stub diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index 219190f554ea..21dd1fb05615 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -20,8 +20,6 @@ import static android.view.InsetsStateProto.DISPLAY_CUTOUT; import static android.view.InsetsStateProto.DISPLAY_FRAME; import static android.view.InsetsStateProto.SOURCES; import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE; -import static android.view.WindowInsets.Type.MANDATORY_SYSTEM_GESTURES; -import static android.view.WindowInsets.Type.SYSTEM_GESTURES; import static android.view.WindowInsets.Type.displayCutout; import static android.view.WindowInsets.Type.ime; import static android.view.WindowInsets.Type.indexOf; @@ -106,14 +104,11 @@ public class InsetsState implements Parcelable { public static final int ITYPE_NAVIGATION_BAR = 1; public static final int ITYPE_CAPTION_BAR = 2; - // The always visible types are visible to all windows regardless of the z-order. - public static final int FIRST_ALWAYS_VISIBLE_TYPE = 3; - public static final int ITYPE_TOP_GESTURES = FIRST_ALWAYS_VISIBLE_TYPE; + public static final int ITYPE_TOP_GESTURES = 3; public static final int ITYPE_BOTTOM_GESTURES = 4; public static final int ITYPE_LEFT_GESTURES = 5; public static final int ITYPE_RIGHT_GESTURES = 6; - /** Additional gesture inset types that map into {@link Type.MANDATORY_SYSTEM_GESTURES}. */ public static final int ITYPE_TOP_MANDATORY_GESTURES = 7; public static final int ITYPE_BOTTOM_MANDATORY_GESTURES = 8; public static final int ITYPE_LEFT_MANDATORY_GESTURES = 9; @@ -123,7 +118,6 @@ public class InsetsState implements Parcelable { public static final int ITYPE_TOP_DISPLAY_CUTOUT = 12; public static final int ITYPE_RIGHT_DISPLAY_CUTOUT = 13; public static final int ITYPE_BOTTOM_DISPLAY_CUTOUT = 14; - public static final int LAST_ALWAYS_VISIBLE_TYPE = ITYPE_BOTTOM_DISPLAY_CUTOUT; public static final int ITYPE_LEFT_TAPPABLE_ELEMENT = 15; public static final int ITYPE_TOP_TAPPABLE_ELEMENT = 16; @@ -188,18 +182,6 @@ public class InsetsState implements Parcelable { } /** - * Mirror the always visible sources from the other state. They will share the same object for - * the always visible types. - * - * @param other the state to mirror the mirrored sources from. - */ - public void mirrorAlwaysVisibleInsetsSources(InsetsState other) { - for (int type = FIRST_ALWAYS_VISIBLE_TYPE; type <= LAST_ALWAYS_VISIBLE_TYPE; type++) { - mSources[type] = other.mSources[type]; - } - } - - /** * Calculates {@link WindowInsets} based on the current source configuration. * * @param frame The frame to calculate the insets relative to. @@ -380,14 +362,14 @@ public class InsetsState implements Parcelable { processSourceAsPublicType(source, typeInsetsMap, typeSideMap, typeVisibilityMap, insets, type); - if (type == MANDATORY_SYSTEM_GESTURES) { + if (type == Type.MANDATORY_SYSTEM_GESTURES) { // Mandatory system gestures are also system gestures. // TODO: find a way to express this more generally. One option would be to define // Type.systemGestureInsets() as NORMAL | MANDATORY, but then we lose the // ability to set systemGestureInsets() independently from // mandatorySystemGestureInsets() in the Builder. processSourceAsPublicType(source, typeInsetsMap, typeSideMap, typeVisibilityMap, - insets, SYSTEM_GESTURES); + insets, Type.SYSTEM_GESTURES); } } @@ -493,9 +475,14 @@ public class InsetsState implements Parcelable { * to the client. * * @param type The {@link InternalInsetsType} of the source to remove + * @return {@code true} if this InsetsState was modified; {@code false} otherwise. */ - public void removeSource(@InternalInsetsType int type) { + public boolean removeSource(@InternalInsetsType int type) { + if (mSources[type] == null) { + return false; + } mSources[type] = null; + return true; } /** @@ -552,6 +539,24 @@ public class InsetsState implements Parcelable { } } + /** + * Sets the values from the other InsetsState. But for sources, only specific types of source + * would be set. + * + * @param other the other InsetsState. + * @param types the only types of sources would be set. + */ + public void set(InsetsState other, @InsetsType int types) { + mDisplayFrame.set(other.mDisplayFrame); + mDisplayCutout.set(other.mDisplayCutout); + mRoundedCorners = other.getRoundedCorners(); + final ArraySet<Integer> t = toInternalType(types); + for (int i = t.size() - 1; i >= 0; i--) { + final int type = t.valueAt(i); + mSources[type] = other.mSources[type]; + } + } + public void addSource(InsetsSource source) { mSources[source.getType()] = source; } @@ -575,6 +580,18 @@ public class InsetsState implements Parcelable { if ((types & Type.CAPTION_BAR) != 0) { result.add(ITYPE_CAPTION_BAR); } + if ((types & Type.SYSTEM_GESTURES) != 0) { + result.add(ITYPE_LEFT_GESTURES); + result.add(ITYPE_TOP_GESTURES); + result.add(ITYPE_RIGHT_GESTURES); + result.add(ITYPE_BOTTOM_GESTURES); + } + if ((types & Type.MANDATORY_SYSTEM_GESTURES) != 0) { + result.add(ITYPE_LEFT_MANDATORY_GESTURES); + result.add(ITYPE_TOP_MANDATORY_GESTURES); + result.add(ITYPE_RIGHT_MANDATORY_GESTURES); + result.add(ITYPE_BOTTOM_MANDATORY_GESTURES); + } if ((types & Type.DISPLAY_CUTOUT) != 0) { result.add(ITYPE_LEFT_DISPLAY_CUTOUT); result.add(ITYPE_TOP_DISPLAY_CUTOUT); diff --git a/core/java/android/view/RoundedCorners.java b/core/java/android/view/RoundedCorners.java index 015e804da2f6..569c287901c7 100644 --- a/core/java/android/view/RoundedCorners.java +++ b/core/java/android/view/RoundedCorners.java @@ -335,7 +335,7 @@ public class RoundedCorners implements Parcelable { } if (o instanceof RoundedCorners) { RoundedCorners r = (RoundedCorners) o; - return Arrays.deepEquals(mRoundedCorners, ((RoundedCorners) o).mRoundedCorners); + return Arrays.deepEquals(mRoundedCorners, r.mRoundedCorners); } return false; } diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 66b9617714a6..7d049d09a677 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -68,6 +68,7 @@ import java.lang.ref.WeakReference; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; +import java.util.Arrays; import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -161,25 +162,21 @@ public final class SurfaceControl implements Parcelable { int L, int T, int R, int B); private static native void nativeSetDisplaySize(long transactionObj, IBinder displayToken, int width, int height); - private static native DisplayInfo nativeGetDisplayInfo(IBinder displayToken); - private static native DisplayMode[] nativeGetDisplayModes( - IBinder displayToken); + private static native StaticDisplayInfo nativeGetStaticDisplayInfo(IBinder displayToken); + private static native DynamicDisplayInfo nativeGetDynamicDisplayInfo(IBinder displayToken); private static native DisplayedContentSamplingAttributes nativeGetDisplayedContentSamplingAttributes(IBinder displayToken); private static native boolean nativeSetDisplayedContentSamplingEnabled(IBinder displayToken, boolean enable, int componentMask, int maxFrames); private static native DisplayedContentSample nativeGetDisplayedContentSample( IBinder displayToken, long numFrames, long timestamp); - private static native int nativeGetActiveDisplayMode(IBinder displayToken); private static native boolean nativeSetDesiredDisplayModeSpecs(IBinder displayToken, DesiredDisplayModeSpecs desiredDisplayModeSpecs); private static native DesiredDisplayModeSpecs nativeGetDesiredDisplayModeSpecs(IBinder displayToken); - private static native int[] nativeGetDisplayColorModes(IBinder displayToken); private static native DisplayPrimaries nativeGetDisplayNativePrimaries( IBinder displayToken); private static native int[] nativeGetCompositionDataspaces(); - private static native int nativeGetActiveColorMode(IBinder displayToken); private static native boolean nativeSetActiveColorMode(IBinder displayToken, int colorMode); private static native void nativeSetAutoLowLatencyMode(IBinder displayToken, boolean on); @@ -191,8 +188,6 @@ public final class SurfaceControl implements Parcelable { private static native void nativeReparent(long transactionObj, long nativeObject, long newParentNativeObject); - private static native Display.HdrCapabilities nativeGetHdrCapabilities(IBinder displayToken); - private static native boolean nativeGetAutoLowLatencyModeSupport(IBinder displayToken); private static native boolean nativeGetGameContentTypeSupport(IBinder displayToken); @@ -1707,7 +1702,7 @@ public final class SurfaceControl implements Parcelable { * * @hide */ - public static final class DisplayInfo { + public static final class StaticDisplayInfo { public boolean isInternal; public float density; public boolean secure; @@ -1715,7 +1710,7 @@ public final class SurfaceControl implements Parcelable { @Override public String toString() { - return "DisplayInfo{isInternal=" + isInternal + return "StaticDisplayInfo{isInternal=" + isInternal + ", density=" + density + ", secure=" + secure + ", deviceProductInfo=" + deviceProductInfo + "}"; @@ -1725,7 +1720,7 @@ public final class SurfaceControl implements Parcelable { public boolean equals(@Nullable Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - DisplayInfo that = (DisplayInfo) o; + StaticDisplayInfo that = (StaticDisplayInfo) o; return isInternal == that.isInternal && density == that.density && secure == that.secure @@ -1739,6 +1734,49 @@ public final class SurfaceControl implements Parcelable { } /** + * Dynamic information about physical display. + * + * @hide + */ + public static final class DynamicDisplayInfo { + public DisplayMode[] supportedDisplayModes; + public int activeDisplayModeId; + + public int[] supportedColorModes; + public int activeColorMode; + + public Display.HdrCapabilities hdrCapabilities; + + @Override + public String toString() { + return "DynamicDisplayInfo{" + + "supportedDisplayModes=" + Arrays.toString(supportedDisplayModes) + + ", activeDisplayModeId=" + activeDisplayModeId + + ", supportedColorModes=" + Arrays.toString(supportedColorModes) + + ", activeColorMode=" + activeColorMode + + ", hdrCapabilities=" + hdrCapabilities + "}"; + } + + @Override + public boolean equals(@Nullable Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DynamicDisplayInfo that = (DynamicDisplayInfo) o; + return Arrays.equals(supportedDisplayModes, that.supportedDisplayModes) + && activeDisplayModeId == that.activeDisplayModeId + && Arrays.equals(supportedColorModes, that.supportedColorModes) + && activeColorMode == that.activeColorMode + && Objects.equals(hdrCapabilities, that.hdrCapabilities); + } + + @Override + public int hashCode() { + return Objects.hash(supportedDisplayModes, activeDisplayModeId, activeDisplayModeId, + activeColorMode, hdrCapabilities); + } + } + + /** * Configuration supported by physical display. * * @hide @@ -1749,6 +1787,7 @@ public final class SurfaceControl implements Parcelable { */ public static final int INVALID_DISPLAY_MODE_ID = -1; + public int id; public int width; public int height; public float xDpi; @@ -1768,7 +1807,8 @@ public final class SurfaceControl implements Parcelable { @Override public String toString() { - return "DisplayConfig{width=" + width + return "DisplayMode{id=" + id + + ", width=" + width + ", height=" + height + ", xDpi=" + xDpi + ", yDpi=" + yDpi @@ -1777,46 +1817,58 @@ public final class SurfaceControl implements Parcelable { + ", presentationDeadlineNanos=" + presentationDeadlineNanos + ", group=" + group + "}"; } - } - /** - * @hide - */ - public static void setDisplayPowerMode(IBinder displayToken, int mode) { - if (displayToken == null) { - throw new IllegalArgumentException("displayToken must not be null"); + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DisplayMode that = (DisplayMode) o; + return id == that.id + && width == that.width + && height == that.height + && Float.compare(that.xDpi, xDpi) == 0 + && Float.compare(that.yDpi, yDpi) == 0 + && Float.compare(that.refreshRate, refreshRate) == 0 + && appVsyncOffsetNanos == that.appVsyncOffsetNanos + && presentationDeadlineNanos == that.presentationDeadlineNanos + && group == that.group; + } + + @Override + public int hashCode() { + return Objects.hash(id, width, height, xDpi, yDpi, refreshRate, appVsyncOffsetNanos, + presentationDeadlineNanos, group); } - nativeSetDisplayPowerMode(displayToken, mode); } /** * @hide */ - public static SurfaceControl.DisplayInfo getDisplayInfo(IBinder displayToken) { + public static void setDisplayPowerMode(IBinder displayToken, int mode) { if (displayToken == null) { throw new IllegalArgumentException("displayToken must not be null"); } - return nativeGetDisplayInfo(displayToken); + nativeSetDisplayPowerMode(displayToken, mode); } /** * @hide */ - public static DisplayMode[] getDisplayModes(IBinder displayToken) { + public static StaticDisplayInfo getStaticDisplayInfo(IBinder displayToken) { if (displayToken == null) { throw new IllegalArgumentException("displayToken must not be null"); } - return nativeGetDisplayModes(displayToken); + return nativeGetStaticDisplayInfo(displayToken); } /** * @hide */ - public static int getActiveDisplayMode(IBinder displayToken) { + public static DynamicDisplayInfo getDynamicDisplayInfo(IBinder displayToken) { if (displayToken == null) { throw new IllegalArgumentException("displayToken must not be null"); } - return nativeGetActiveDisplayMode(displayToken); + return nativeGetDynamicDisplayInfo(displayToken); } /** @@ -1978,16 +2030,6 @@ public final class SurfaceControl implements Parcelable { } /** - * @hide - */ - public static int[] getDisplayColorModes(IBinder displayToken) { - if (displayToken == null) { - throw new IllegalArgumentException("displayToken must not be null"); - } - return nativeGetDisplayColorModes(displayToken); - } - - /** * Color coordinates in CIE1931 XYZ color space * * @hide @@ -2057,16 +2099,6 @@ public final class SurfaceControl implements Parcelable { /** * @hide */ - public static int getActiveColorMode(IBinder displayToken) { - if (displayToken == null) { - throw new IllegalArgumentException("displayToken must not be null"); - } - return nativeGetActiveColorMode(displayToken); - } - - /** - * @hide - */ public static boolean setActiveColorMode(IBinder displayToken, int colorMode) { if (displayToken == null) { throw new IllegalArgumentException("displayToken must not be null"); @@ -2169,16 +2201,6 @@ public final class SurfaceControl implements Parcelable { /** * @hide */ - public static Display.HdrCapabilities getHdrCapabilities(IBinder displayToken) { - if (displayToken == null) { - throw new IllegalArgumentException("displayToken must not be null"); - } - return nativeGetHdrCapabilities(displayToken); - } - - /** - * @hide - */ public static boolean getAutoLowLatencyModeSupport(IBinder displayToken) { if (displayToken == null) { throw new IllegalArgumentException("displayToken must not be null"); diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index d5f97743c257..05b177ebbb45 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -715,7 +715,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te */ @NonNull @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769408) - private EdgeEffect mEdgeGlowTop = new EdgeEffect(mContext); + private EdgeEffect mEdgeGlowTop; /** * Tracks the state of the bottom edge glow. @@ -725,7 +725,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te */ @NonNull @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768444) - private EdgeEffect mEdgeGlowBottom = new EdgeEffect(mContext); + private EdgeEffect mEdgeGlowBottom; /** * An estimate of how many pixels are between the top of the list and @@ -847,6 +847,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te public AbsListView(Context context) { super(context); + mEdgeGlowBottom = new EdgeEffect(context); + mEdgeGlowTop = new EdgeEffect(context); initAbsListView(); mOwnerThread = Thread.currentThread(); @@ -867,6 +869,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te public AbsListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); + mEdgeGlowBottom = new EdgeEffect(context, attrs); + mEdgeGlowTop = new EdgeEffect(context, attrs); initAbsListView(); mOwnerThread = Thread.currentThread(); @@ -3584,6 +3588,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mLastY != Integer.MIN_VALUE ? y - mLastY + scrollConsumedCorrection : deltaY; int lastYCorrection = 0; + // First allow releasing existing overscroll effect: + incrementalDeltaY = releaseGlow(incrementalDeltaY, x); + if (mTouchMode == TOUCH_MODE_SCROLL) { if (PROFILE_SCROLLING) { if (!mScrollProfilingStarted) { @@ -3666,14 +3673,14 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mTouchMode = TOUCH_MODE_OVERSCROLL; } if (incrementalDeltaY > 0) { - mEdgeGlowTop.onPull((float) -overscroll / getHeight(), + mEdgeGlowTop.onPullDistance((float) -overscroll / getHeight(), (float) x / getWidth()); if (!mEdgeGlowBottom.isFinished()) { mEdgeGlowBottom.onRelease(); } invalidateTopGlow(); } else if (incrementalDeltaY < 0) { - mEdgeGlowBottom.onPull((float) overscroll / getHeight(), + mEdgeGlowBottom.onPullDistance((float) overscroll / getHeight(), 1.f - (float) x / getWidth()); if (!mEdgeGlowTop.isFinished()) { mEdgeGlowTop.onRelease(); @@ -3713,14 +3720,15 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && !contentFits())) { if (rawDeltaY > 0) { - mEdgeGlowTop.onPull((float) overScrollDistance / getHeight(), + mEdgeGlowTop.onPullDistance((float) overScrollDistance / getHeight(), (float) x / getWidth()); if (!mEdgeGlowBottom.isFinished()) { mEdgeGlowBottom.onRelease(); } invalidateTopGlow(); } else if (rawDeltaY < 0) { - mEdgeGlowBottom.onPull((float) overScrollDistance / getHeight(), + mEdgeGlowBottom.onPullDistance( + (float) -overScrollDistance / getHeight(), 1.f - (float) x / getWidth()); if (!mEdgeGlowTop.isFinished()) { mEdgeGlowTop.onRelease(); @@ -3757,6 +3765,44 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } + /** + * If the edge glow is currently active, this consumes part or all of deltaY + * on the edge glow. + * + * @param deltaY The pointer motion, in pixels, in the vertical direction, positive + * for moving down and negative for moving up. + * @param x The horizontal position of the pointer. + * @return The remainder of <code>deltaY</code> that has not been consumed by the + * edge glow. + */ + private int releaseGlow(int deltaY, int x) { + // First allow releasing existing overscroll effect: + float consumed = 0; + if (mEdgeGlowTop.getDistance() != 0) { + consumed = mEdgeGlowTop.onPullDistance((float) deltaY / getHeight(), + (float) x / getWidth()); + if (consumed != 0f) { + invalidateTopGlow(); + } + } else if (mEdgeGlowBottom.getDistance() != 0) { + consumed = -mEdgeGlowBottom.onPullDistance((float) -deltaY / getHeight(), + 1f - (float) x / getWidth()); + if (consumed != 0f) { + invalidateBottomGlow(); + } + } + int pixelsConsumed = Math.round(consumed * getHeight()); + return deltaY - pixelsConsumed; + } + + /** + * @return <code>true</code> if either the top or bottom edge glow is currently active or + * <code>false</code> if it has no value to release. + */ + private boolean isGlowActive() { + return mEdgeGlowBottom.getDistance() != 0 || mEdgeGlowTop.getDistance() != 0; + } + private void invalidateTopGlow() { if (!shouldDisplayEdgeEffects()) { return; @@ -3926,7 +3972,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te if (mTouchMode == TOUCH_MODE_OVERFLING) { // Stopped the fling. It is a scroll. - mFlingRunnable.endFling(); + if (mFlingRunnable != null) { + mFlingRunnable.endFling(); + } if (mPositionScroller != null) { mPositionScroller.stop(); } @@ -3936,6 +3984,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mLastY = mMotionY; mMotionCorrection = 0; mDirection = 0; + stopEdgeGlowRecede(ev.getX()); } else { final int x = (int) ev.getX(); final int y = (int) ev.getY(); @@ -3948,7 +3997,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mTouchMode = TOUCH_MODE_SCROLL; mMotionCorrection = 0; motionPosition = findMotionRow(y); - mFlingRunnable.flywheelTouch(); + if (mFlingRunnable != null) { + mFlingRunnable.flywheelTouch(); + } + stopEdgeGlowRecede(x); } else if ((motionPosition >= 0) && getAdapter().isEnabled(motionPosition)) { // User clicked on an actual view (and was not stopping a // fling). It might be a click or a scroll. Assume it is a @@ -3984,6 +4036,15 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } + private void stopEdgeGlowRecede(float x) { + if (mEdgeGlowTop.getDistance() != 0) { + mEdgeGlowTop.onPullDistance(0, x / getWidth()); + } + if (mEdgeGlowBottom.getDistance() != 0) { + mEdgeGlowBottom.onPullDistance(0, x / getWidth()); + } + } + private void onTouchMove(MotionEvent ev, MotionEvent vtev) { if (mHasPerformedLongPress) { // Consume all move events following a successful long press. @@ -4489,73 +4550,76 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } switch (actionMasked) { - case MotionEvent.ACTION_DOWN: { - int touchMode = mTouchMode; - if (touchMode == TOUCH_MODE_OVERFLING || touchMode == TOUCH_MODE_OVERSCROLL) { - mMotionCorrection = 0; - return true; - } - - final int x = (int) ev.getX(); - final int y = (int) ev.getY(); - mActivePointerId = ev.getPointerId(0); + case MotionEvent.ACTION_DOWN: { + int touchMode = mTouchMode; + if (touchMode == TOUCH_MODE_OVERFLING || touchMode == TOUCH_MODE_OVERSCROLL) { + mMotionCorrection = 0; + return true; + } - int motionPosition = findMotionRow(y); - if (touchMode != TOUCH_MODE_FLING && motionPosition >= 0) { - // User clicked on an actual view (and was not stopping a fling). - // Remember where the motion event started - v = getChildAt(motionPosition - mFirstPosition); - mMotionViewOriginalTop = v.getTop(); - mMotionX = x; - mMotionY = y; - mMotionPosition = motionPosition; - mTouchMode = TOUCH_MODE_DOWN; - clearScrollingCache(); - } - mLastY = Integer.MIN_VALUE; - initOrResetVelocityTracker(); - mVelocityTracker.addMovement(ev); - mNestedYOffset = 0; - startNestedScroll(SCROLL_AXIS_VERTICAL); - if (touchMode == TOUCH_MODE_FLING) { - return true; - } - break; - } + final int x = (int) ev.getX(); + final int y = (int) ev.getY(); + mActivePointerId = ev.getPointerId(0); - case MotionEvent.ACTION_MOVE: { - switch (mTouchMode) { - case TOUCH_MODE_DOWN: - int pointerIndex = ev.findPointerIndex(mActivePointerId); - if (pointerIndex == -1) { - pointerIndex = 0; - mActivePointerId = ev.getPointerId(pointerIndex); + int motionPosition = findMotionRow(y); + if (isGlowActive()) { + // Pressed during edge effect, so this is considered the same as a fling catch. + mTouchMode = TOUCH_MODE_FLING; + } else if (touchMode != TOUCH_MODE_FLING && motionPosition >= 0) { + // User clicked on an actual view (and was not stopping a fling). + // Remember where the motion event started + v = getChildAt(motionPosition - mFirstPosition); + mMotionViewOriginalTop = v.getTop(); + mMotionX = x; + mMotionY = y; + mMotionPosition = motionPosition; + mTouchMode = TOUCH_MODE_DOWN; + clearScrollingCache(); } - final int y = (int) ev.getY(pointerIndex); - initVelocityTrackerIfNotExists(); + mLastY = Integer.MIN_VALUE; + initOrResetVelocityTracker(); mVelocityTracker.addMovement(ev); - if (startScrollIfNeeded((int) ev.getX(pointerIndex), y, null)) { + mNestedYOffset = 0; + startNestedScroll(SCROLL_AXIS_VERTICAL); + if (touchMode == TOUCH_MODE_FLING) { return true; } break; } - break; - } - case MotionEvent.ACTION_CANCEL: - case MotionEvent.ACTION_UP: { - mTouchMode = TOUCH_MODE_REST; - mActivePointerId = INVALID_POINTER; - recycleVelocityTracker(); - reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); - stopNestedScroll(); - break; - } + case MotionEvent.ACTION_MOVE: { + switch (mTouchMode) { + case TOUCH_MODE_DOWN: + int pointerIndex = ev.findPointerIndex(mActivePointerId); + if (pointerIndex == -1) { + pointerIndex = 0; + mActivePointerId = ev.getPointerId(pointerIndex); + } + final int y = (int) ev.getY(pointerIndex); + initVelocityTrackerIfNotExists(); + mVelocityTracker.addMovement(ev); + if (startScrollIfNeeded((int) ev.getX(pointerIndex), y, null)) { + return true; + } + break; + } + break; + } - case MotionEvent.ACTION_POINTER_UP: { - onSecondaryPointerUp(ev); - break; - } + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: { + mTouchMode = TOUCH_MODE_REST; + mActivePointerId = INVALID_POINTER; + recycleVelocityTracker(); + reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); + stopNestedScroll(); + break; + } + + case MotionEvent.ACTION_POINTER_UP: { + onSecondaryPointerUp(ev); + break; + } } return false; diff --git a/core/java/com/android/internal/app/IAppOpsNotedCallback.aidl b/core/java/com/android/internal/app/IAppOpsNotedCallback.aidl index cb280cd14180..f3759e091ed6 100644 --- a/core/java/com/android/internal/app/IAppOpsNotedCallback.aidl +++ b/core/java/com/android/internal/app/IAppOpsNotedCallback.aidl @@ -18,5 +18,5 @@ package com.android.internal.app; // Iterface to observe op note/checks of ops oneway interface IAppOpsNotedCallback { - void opNoted(int op, int uid, String packageName, int flags, int mode); + void opNoted(int op, int uid, String packageName, String attributionTag, int flags, int mode); } diff --git a/core/java/com/android/internal/app/IAppOpsStartedCallback.aidl b/core/java/com/android/internal/app/IAppOpsStartedCallback.aidl index b0cb2a8ceb64..3a108e7e1d94 100644 --- a/core/java/com/android/internal/app/IAppOpsStartedCallback.aidl +++ b/core/java/com/android/internal/app/IAppOpsStartedCallback.aidl @@ -18,5 +18,5 @@ package com.android.internal.app; // Iterface to observe op starts oneway interface IAppOpsStartedCallback { - void opStarted(int op, int uid, String packageName, int flags, int mode); + void opStarted(int op, int uid, String packageName, String attributionTag, int flags, int mode); } diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp index 5b327d40ac69..d0504fb481ca 100644 --- a/core/jni/android_hardware_SensorManager.cpp +++ b/core/jni/android_hardware_SensorManager.cpp @@ -421,11 +421,18 @@ private: }; static jlong nativeInitSensorEventQueue(JNIEnv *env, jclass clazz, jlong sensorManager, - jobject eventQWeak, jobject msgQ, jstring packageName, jint mode) { + jobject eventQWeak, jobject msgQ, jstring packageName, + jint mode, jstring opPackageName, jstring attributionTag) { SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager); ScopedUtfChars packageUtf(env, packageName); String8 clientName(packageUtf.c_str()); - sp<SensorEventQueue> queue(mgr->createEventQueue(clientName, mode)); + + String16 attributionTagName(""); + if (attributionTag != nullptr) { + ScopedUtfChars attrUtf(env, attributionTag); + attributionTagName = String16(attrUtf.c_str()); + } + sp<SensorEventQueue> queue(mgr->createEventQueue(clientName, mode, attributionTagName)); if (queue == NULL) { jniThrowRuntimeException(env, "Cannot construct native SensorEventQueue."); @@ -517,29 +524,20 @@ static const JNINativeMethod gSystemSensorManagerMethods[] = { }; static const JNINativeMethod gBaseEventQueueMethods[] = { - {"nativeInitBaseEventQueue", - "(JLjava/lang/ref/WeakReference;Landroid/os/MessageQueue;Ljava/lang/String;ILjava/lang/String;)J", - (void*)nativeInitSensorEventQueue }, + {"nativeInitBaseEventQueue", + "(JLjava/lang/ref/WeakReference;Landroid/os/MessageQueue;Ljava/lang/String;ILjava/lang/" + "String;Ljava/lang/String;)J", + (void *)nativeInitSensorEventQueue}, - {"nativeEnableSensor", - "(JIII)I", - (void*)nativeEnableSensor }, + {"nativeEnableSensor", "(JIII)I", (void *)nativeEnableSensor}, - {"nativeDisableSensor", - "(JI)I", - (void*)nativeDisableSensor }, + {"nativeDisableSensor", "(JI)I", (void *)nativeDisableSensor}, - {"nativeDestroySensorEventQueue", - "(J)V", - (void*)nativeDestroySensorEventQueue }, + {"nativeDestroySensorEventQueue", "(J)V", (void *)nativeDestroySensorEventQueue}, - {"nativeFlushSensor", - "(J)I", - (void*)nativeFlushSensor }, + {"nativeFlushSensor", "(J)I", (void *)nativeFlushSensor}, - {"nativeInjectSensorData", - "(JI[FIJ)I", - (void*)nativeInjectSensorData }, + {"nativeInjectSensorData", "(JI[FIJ)I", (void *)nativeInjectSensorData}, }; } //unnamed namespace diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 961d4cb23679..094ca1843eea 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -45,14 +45,15 @@ #include <ui/BlurRegion.h> #include <ui/ConfigStoreTypes.h> #include <ui/DeviceProductInfo.h> -#include <ui/DisplayInfo.h> #include <ui/DisplayMode.h> #include <ui/DisplayedFrameStats.h> +#include <ui/DynamicDisplayInfo.h> #include <ui/FrameStats.h> #include <ui/GraphicTypes.h> #include <ui/HdrCapabilities.h> #include <ui/Rect.h> #include <ui/Region.h> +#include <ui/StaticDisplayInfo.h> #include <utils/LightRefBase.h> #include <utils/Log.h> @@ -87,11 +88,22 @@ static struct { jfieldID density; jfieldID secure; jfieldID deviceProductInfo; -} gDisplayInfoClassInfo; +} gStaticDisplayInfoClassInfo; static struct { jclass clazz; jmethodID ctor; + jfieldID supportedDisplayModes; + jfieldID activeDisplayModeId; + jfieldID supportedColorModes; + jfieldID activeColorMode; + jfieldID hdrCapabilities; +} gDynamicDisplayInfoClassInfo; + +static struct { + jclass clazz; + jmethodID ctor; + jfieldID id; jfieldID width; jfieldID height; jfieldID xDpi; @@ -1020,51 +1032,100 @@ static jobject convertDeviceProductInfoToJavaObject( connectionToSinkType); } -static jobject nativeGetDisplayInfo(JNIEnv* env, jclass clazz, jobject tokenObj) { - DisplayInfo info; +static jobject nativeGetStaticDisplayInfo(JNIEnv* env, jclass clazz, jobject tokenObj) { + ui::StaticDisplayInfo info; if (const auto token = ibinderForJavaObject(env, tokenObj); - !token || SurfaceComposerClient::getDisplayInfo(token, &info) != NO_ERROR) { + !token || SurfaceComposerClient::getStaticDisplayInfo(token, &info) != NO_ERROR) { return nullptr; } - jobject object = env->NewObject(gDisplayInfoClassInfo.clazz, gDisplayInfoClassInfo.ctor); - env->SetBooleanField(object, gDisplayInfoClassInfo.isInternal, - info.connectionType == DisplayConnectionType::Internal); - env->SetFloatField(object, gDisplayInfoClassInfo.density, info.density); - env->SetBooleanField(object, gDisplayInfoClassInfo.secure, info.secure); - env->SetObjectField(object, gDisplayInfoClassInfo.deviceProductInfo, + jobject object = + env->NewObject(gStaticDisplayInfoClassInfo.clazz, gStaticDisplayInfoClassInfo.ctor); + env->SetBooleanField(object, gStaticDisplayInfoClassInfo.isInternal, + info.connectionType == ui::DisplayConnectionType::Internal); + env->SetFloatField(object, gStaticDisplayInfoClassInfo.density, info.density); + env->SetBooleanField(object, gStaticDisplayInfoClassInfo.secure, info.secure); + env->SetObjectField(object, gStaticDisplayInfoClassInfo.deviceProductInfo, convertDeviceProductInfoToJavaObject(env, info.deviceProductInfo)); return object; } -static jobjectArray nativeGetDisplayModes(JNIEnv* env, jclass clazz, jobject tokenObj) { - Vector<ui::DisplayMode> modes; - if (const auto token = ibinderForJavaObject(env, tokenObj); !token || - SurfaceComposerClient::getDisplayModes(token, &modes) != NO_ERROR || modes.isEmpty()) { +static jobject convertDisplayModeToJavaObject(JNIEnv* env, const ui::DisplayMode& config) { + jobject object = env->NewObject(gDisplayModeClassInfo.clazz, gDisplayModeClassInfo.ctor); + env->SetIntField(object, gDisplayModeClassInfo.id, config.id); + env->SetIntField(object, gDisplayModeClassInfo.width, config.resolution.getWidth()); + env->SetIntField(object, gDisplayModeClassInfo.height, config.resolution.getHeight()); + env->SetFloatField(object, gDisplayModeClassInfo.xDpi, config.xDpi); + env->SetFloatField(object, gDisplayModeClassInfo.yDpi, config.yDpi); + + env->SetFloatField(object, gDisplayModeClassInfo.refreshRate, config.refreshRate); + env->SetLongField(object, gDisplayModeClassInfo.appVsyncOffsetNanos, config.appVsyncOffset); + env->SetLongField(object, gDisplayModeClassInfo.presentationDeadlineNanos, + config.presentationDeadline); + env->SetIntField(object, gDisplayModeClassInfo.group, config.group); + return object; +} + +jobject convertDeviceProductInfoToJavaObject(JNIEnv* env, const HdrCapabilities& capabilities) { + const auto& types = capabilities.getSupportedHdrTypes(); + std::vector<int32_t> intTypes; + for (auto type : types) { + intTypes.push_back(static_cast<int32_t>(type)); + } + auto typesArray = env->NewIntArray(types.size()); + env->SetIntArrayRegion(typesArray, 0, intTypes.size(), intTypes.data()); + + return env->NewObject(gHdrCapabilitiesClassInfo.clazz, gHdrCapabilitiesClassInfo.ctor, + typesArray, capabilities.getDesiredMaxLuminance(), + capabilities.getDesiredMaxAverageLuminance(), + capabilities.getDesiredMinLuminance()); +} + +static jobject nativeGetDynamicDisplayInfo(JNIEnv* env, jclass clazz, jobject tokenObj) { + ui::DynamicDisplayInfo info; + if (const auto token = ibinderForJavaObject(env, tokenObj); + !token || SurfaceComposerClient::getDynamicDisplayInfo(token, &info) != NO_ERROR) { return nullptr; } - jobjectArray modesArray = - env->NewObjectArray(modes.size(), gDisplayModeClassInfo.clazz, nullptr); + jobject object = + env->NewObject(gDynamicDisplayInfoClassInfo.clazz, gDynamicDisplayInfoClassInfo.ctor); + if (object == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", NULL); + return NULL; + } - for (size_t c = 0; c < modes.size(); ++c) { - const ui::DisplayMode& mode = modes[c]; - jobject object = env->NewObject(gDisplayModeClassInfo.clazz, gDisplayModeClassInfo.ctor); - env->SetIntField(object, gDisplayModeClassInfo.width, mode.resolution.getWidth()); - env->SetIntField(object, gDisplayModeClassInfo.height, mode.resolution.getHeight()); - env->SetFloatField(object, gDisplayModeClassInfo.xDpi, mode.xDpi); - env->SetFloatField(object, gDisplayModeClassInfo.yDpi, mode.yDpi); + const auto numModes = info.supportedDisplayModes.size(); + jobjectArray modesArray = env->NewObjectArray(numModes, gDisplayModeClassInfo.clazz, nullptr); + for (size_t i = 0; i < numModes; i++) { + const ui::DisplayMode& mode = info.supportedDisplayModes[i]; + jobject displayModeObj = convertDisplayModeToJavaObject(env, mode); + env->SetObjectArrayElement(modesArray, static_cast<jsize>(i), displayModeObj); + env->DeleteLocalRef(displayModeObj); + } + env->SetObjectField(object, gDynamicDisplayInfoClassInfo.supportedDisplayModes, modesArray); + env->SetIntField(object, gDynamicDisplayInfoClassInfo.activeDisplayModeId, + info.activeDisplayModeId); - env->SetFloatField(object, gDisplayModeClassInfo.refreshRate, mode.refreshRate); - env->SetLongField(object, gDisplayModeClassInfo.appVsyncOffsetNanos, mode.appVsyncOffset); - env->SetLongField(object, gDisplayModeClassInfo.presentationDeadlineNanos, - mode.presentationDeadline); - env->SetIntField(object, gDisplayModeClassInfo.group, mode.group); - env->SetObjectArrayElement(modesArray, static_cast<jsize>(c), object); - env->DeleteLocalRef(object); + jintArray colorModesArray = env->NewIntArray(info.supportedColorModes.size()); + if (colorModesArray == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", NULL); + return NULL; } + jint* colorModesArrayValues = env->GetIntArrayElements(colorModesArray, 0); + for (size_t i = 0; i < info.supportedColorModes.size(); i++) { + colorModesArrayValues[i] = static_cast<jint>(info.supportedColorModes[i]); + } + env->ReleaseIntArrayElements(colorModesArray, colorModesArrayValues, 0); + env->SetObjectField(object, gDynamicDisplayInfoClassInfo.supportedColorModes, colorModesArray); + + env->SetIntField(object, gDynamicDisplayInfoClassInfo.activeColorMode, + static_cast<jint>(info.activeColorMode)); - return modesArray; + env->SetObjectField(object, gDynamicDisplayInfoClassInfo.hdrCapabilities, + convertDeviceProductInfoToJavaObject(env, info.hdrCapabilities)); + + return object; } static jboolean nativeSetDesiredDisplayModeSpecs(JNIEnv* env, jclass clazz, jobject tokenObj, @@ -1072,9 +1133,8 @@ static jboolean nativeSetDesiredDisplayModeSpecs(JNIEnv* env, jclass clazz, jobj sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); if (token == nullptr) return JNI_FALSE; - size_t defaultMode = - static_cast<size_t>(env->GetIntField(DesiredDisplayModeSpecs, - gDesiredDisplayModeSpecsClassInfo.defaultMode)); + ui::DisplayModeId defaultMode = env->GetIntField(DesiredDisplayModeSpecs, + gDesiredDisplayModeSpecsClassInfo.defaultMode); jboolean allowGroupSwitching = env->GetBooleanField(DesiredDisplayModeSpecs, gDesiredDisplayModeSpecsClassInfo.allowGroupSwitching); @@ -1104,7 +1164,7 @@ static jobject nativeGetDesiredDisplayModeSpecs(JNIEnv* env, jclass clazz, jobje sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); if (token == nullptr) return nullptr; - size_t defaultMode; + ui::DisplayModeId defaultMode; bool allowGroupSwitching; float primaryRefreshRateMin; float primaryRefreshRateMax; @@ -1124,34 +1184,6 @@ static jobject nativeGetDesiredDisplayModeSpecs(JNIEnv* env, jclass clazz, jobje appRequestRefreshRateMax); } -static jint nativeGetActiveDisplayMode(JNIEnv* env, jclass clazz, jobject tokenObj) { - sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); - if (token == NULL) return -1; - return static_cast<jint>(SurfaceComposerClient::getActiveDisplayModeId(token)); -} - -static jintArray nativeGetDisplayColorModes(JNIEnv* env, jclass, jobject tokenObj) { - sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); - if (token == NULL) return NULL; - Vector<ui::ColorMode> colorModes; - if (SurfaceComposerClient::getDisplayColorModes(token, &colorModes) != NO_ERROR || - colorModes.isEmpty()) { - return NULL; - } - - jintArray colorModesArray = env->NewIntArray(colorModes.size()); - if (colorModesArray == NULL) { - jniThrowException(env, "java/lang/OutOfMemoryError", NULL); - return NULL; - } - jint* colorModesArrayValues = env->GetIntArrayElements(colorModesArray, 0); - for (size_t i = 0; i < colorModes.size(); i++) { - colorModesArrayValues[i] = static_cast<jint>(colorModes[i]); - } - env->ReleaseIntArrayElements(colorModesArray, colorModesArrayValues, 0); - return colorModesArray; -} - static jobject nativeGetDisplayNativePrimaries(JNIEnv* env, jclass, jobject tokenObj) { sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); if (token == NULL) return NULL; @@ -1212,12 +1244,6 @@ static jobject nativeGetDisplayNativePrimaries(JNIEnv* env, jclass, jobject toke return jprimaries; } -static jint nativeGetActiveColorMode(JNIEnv* env, jclass, jobject tokenObj) { - sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); - if (token == NULL) return -1; - return static_cast<jint>(SurfaceComposerClient::getActiveColorMode(token)); -} - static jintArray nativeGetCompositionDataspaces(JNIEnv* env, jclass) { ui::Dataspace defaultDataspace, wcgDataspace; ui::PixelFormat defaultPixelFormat, wcgPixelFormat; @@ -1423,26 +1449,6 @@ static void nativeReparent(JNIEnv* env, jclass clazz, jlong transactionObj, transaction->reparent(ctrl, newParent); } -static jobject nativeGetHdrCapabilities(JNIEnv* env, jclass clazz, jobject tokenObject) { - sp<IBinder> token(ibinderForJavaObject(env, tokenObject)); - if (token == NULL) return NULL; - - HdrCapabilities capabilities; - SurfaceComposerClient::getHdrCapabilities(token, &capabilities); - - const auto& types = capabilities.getSupportedHdrTypes(); - std::vector<int32_t> intTypes; - for (auto type : types) { - intTypes.push_back(static_cast<int32_t>(type)); - } - auto typesArray = env->NewIntArray(types.size()); - env->SetIntArrayRegion(typesArray, 0, intTypes.size(), intTypes.data()); - - return env->NewObject(gHdrCapabilitiesClassInfo.clazz, gHdrCapabilitiesClassInfo.ctor, - typesArray, capabilities.getDesiredMaxLuminance(), - capabilities.getDesiredMaxAverageLuminance(), capabilities.getDesiredMinLuminance()); -} - static jboolean nativeGetAutoLowLatencyModeSupport(JNIEnv* env, jclass clazz, jobject tokenObject) { sp<IBinder> token(ibinderForJavaObject(env, tokenObject)); if (token == NULL) return NULL; @@ -1787,24 +1793,21 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeSetDisplayProjection }, {"nativeSetDisplaySize", "(JLandroid/os/IBinder;II)V", (void*)nativeSetDisplaySize }, - {"nativeGetDisplayInfo", "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DisplayInfo;", - (void*)nativeGetDisplayInfo }, - {"nativeGetDisplayModes", "(Landroid/os/IBinder;)[Landroid/view/SurfaceControl$DisplayMode;", - (void*)nativeGetDisplayModes }, - {"nativeGetActiveDisplayMode", "(Landroid/os/IBinder;)I", - (void*)nativeGetActiveDisplayMode }, + {"nativeGetStaticDisplayInfo", + "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$StaticDisplayInfo;", + (void*)nativeGetStaticDisplayInfo }, + {"nativeGetDynamicDisplayInfo", + "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DynamicDisplayInfo;", + (void*)nativeGetDynamicDisplayInfo }, {"nativeSetDesiredDisplayModeSpecs", "(Landroid/os/IBinder;Landroid/view/SurfaceControl$DesiredDisplayModeSpecs;)Z", (void*)nativeSetDesiredDisplayModeSpecs }, {"nativeGetDesiredDisplayModeSpecs", "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DesiredDisplayModeSpecs;", (void*)nativeGetDesiredDisplayModeSpecs }, - {"nativeGetDisplayColorModes", "(Landroid/os/IBinder;)[I", - (void*)nativeGetDisplayColorModes}, - {"nativeGetDisplayNativePrimaries", "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DisplayPrimaries;", + {"nativeGetDisplayNativePrimaries", + "(Landroid/os/IBinder;)Landroid/view/SurfaceControl$DisplayPrimaries;", (void*)nativeGetDisplayNativePrimaries }, - {"nativeGetActiveColorMode", "(Landroid/os/IBinder;)I", - (void*)nativeGetActiveColorMode}, {"nativeSetActiveColorMode", "(Landroid/os/IBinder;I)Z", (void*)nativeSetActiveColorMode}, {"nativeGetAutoLowLatencyModeSupport", "(Landroid/os/IBinder;)Z", @@ -1817,8 +1820,6 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeSetGameContentType }, {"nativeGetCompositionDataspaces", "()[I", (void*)nativeGetCompositionDataspaces}, - {"nativeGetHdrCapabilities", "(Landroid/os/IBinder;)Landroid/view/Display$HdrCapabilities;", - (void*)nativeGetHdrCapabilities }, {"nativeClearContentFrameStats", "(J)Z", (void*)nativeClearContentFrameStats }, {"nativeGetContentFrameStats", "(JLandroid/view/WindowContentFrameStats;)Z", @@ -1897,19 +1898,36 @@ int register_android_view_SurfaceControl(JNIEnv* env) gIntegerClassInfo.clazz = MakeGlobalRefOrDie(env, integerClass); gIntegerClassInfo.ctor = GetMethodIDOrDie(env, gIntegerClassInfo.clazz, "<init>", "(I)V"); - jclass infoClazz = FindClassOrDie(env, "android/view/SurfaceControl$DisplayInfo"); - gDisplayInfoClassInfo.clazz = MakeGlobalRefOrDie(env, infoClazz); - gDisplayInfoClassInfo.ctor = GetMethodIDOrDie(env, infoClazz, "<init>", "()V"); - gDisplayInfoClassInfo.isInternal = GetFieldIDOrDie(env, infoClazz, "isInternal", "Z"); - gDisplayInfoClassInfo.density = GetFieldIDOrDie(env, infoClazz, "density", "F"); - gDisplayInfoClassInfo.secure = GetFieldIDOrDie(env, infoClazz, "secure", "Z"); - gDisplayInfoClassInfo.deviceProductInfo = + jclass infoClazz = FindClassOrDie(env, "android/view/SurfaceControl$StaticDisplayInfo"); + gStaticDisplayInfoClassInfo.clazz = MakeGlobalRefOrDie(env, infoClazz); + gStaticDisplayInfoClassInfo.ctor = GetMethodIDOrDie(env, infoClazz, "<init>", "()V"); + gStaticDisplayInfoClassInfo.isInternal = GetFieldIDOrDie(env, infoClazz, "isInternal", "Z"); + gStaticDisplayInfoClassInfo.density = GetFieldIDOrDie(env, infoClazz, "density", "F"); + gStaticDisplayInfoClassInfo.secure = GetFieldIDOrDie(env, infoClazz, "secure", "Z"); + gStaticDisplayInfoClassInfo.deviceProductInfo = GetFieldIDOrDie(env, infoClazz, "deviceProductInfo", "Landroid/hardware/display/DeviceProductInfo;"); + jclass dynamicInfoClazz = FindClassOrDie(env, "android/view/SurfaceControl$DynamicDisplayInfo"); + gDynamicDisplayInfoClassInfo.clazz = MakeGlobalRefOrDie(env, dynamicInfoClazz); + gDynamicDisplayInfoClassInfo.ctor = GetMethodIDOrDie(env, dynamicInfoClazz, "<init>", "()V"); + gDynamicDisplayInfoClassInfo.supportedDisplayModes = + GetFieldIDOrDie(env, dynamicInfoClazz, "supportedDisplayModes", + "[Landroid/view/SurfaceControl$DisplayMode;"); + gDynamicDisplayInfoClassInfo.activeDisplayModeId = + GetFieldIDOrDie(env, dynamicInfoClazz, "activeDisplayModeId", "I"); + gDynamicDisplayInfoClassInfo.supportedColorModes = + GetFieldIDOrDie(env, dynamicInfoClazz, "supportedColorModes", "[I"); + gDynamicDisplayInfoClassInfo.activeColorMode = + GetFieldIDOrDie(env, dynamicInfoClazz, "activeColorMode", "I"); + gDynamicDisplayInfoClassInfo.hdrCapabilities = + GetFieldIDOrDie(env, dynamicInfoClazz, "hdrCapabilities", + "Landroid/view/Display$HdrCapabilities;"); + jclass modeClazz = FindClassOrDie(env, "android/view/SurfaceControl$DisplayMode"); gDisplayModeClassInfo.clazz = MakeGlobalRefOrDie(env, modeClazz); gDisplayModeClassInfo.ctor = GetMethodIDOrDie(env, modeClazz, "<init>", "()V"); + gDisplayModeClassInfo.id = GetFieldIDOrDie(env, modeClazz, "id", "I"); gDisplayModeClassInfo.width = GetFieldIDOrDie(env, modeClazz, "width", "I"); gDisplayModeClassInfo.height = GetFieldIDOrDie(env, modeClazz, "height", "I"); gDisplayModeClassInfo.xDpi = GetFieldIDOrDie(env, modeClazz, "xDpi", "F"); diff --git a/core/tests/coretests/src/android/app/time/ExternalTimeSuggestionTest.java b/core/tests/coretests/src/android/app/time/ExternalTimeSuggestionTest.java index 266ff3dd09f6..1c6b3cc47a45 100644 --- a/core/tests/coretests/src/android/app/time/ExternalTimeSuggestionTest.java +++ b/core/tests/coretests/src/android/app/time/ExternalTimeSuggestionTest.java @@ -33,17 +33,20 @@ public class ExternalTimeSuggestionTest { @Test public void testEquals() { - ExternalTimeSuggestion one = new ExternalTimeSuggestion(ARBITRARY_TIME); + ExternalTimeSuggestion one = new ExternalTimeSuggestion( + ARBITRARY_TIME.getReferenceTimeMillis(), + ARBITRARY_TIME.getValue()); assertEquals(one, one); - ExternalTimeSuggestion two = new ExternalTimeSuggestion(ARBITRARY_TIME); + ExternalTimeSuggestion two = new ExternalTimeSuggestion( + ARBITRARY_TIME.getReferenceTimeMillis(), + ARBITRARY_TIME.getValue()); assertEquals(one, two); assertEquals(two, one); - TimestampedValue<Long> differentTime = new TimestampedValue<>( + ExternalTimeSuggestion three = new ExternalTimeSuggestion( ARBITRARY_TIME.getReferenceTimeMillis() + 1, ARBITRARY_TIME.getValue()); - ExternalTimeSuggestion three = new ExternalTimeSuggestion(differentTime); assertNotEquals(one, three); assertNotEquals(three, one); @@ -55,7 +58,9 @@ public class ExternalTimeSuggestionTest { @Test public void testParcelable() { - ExternalTimeSuggestion suggestion = new ExternalTimeSuggestion(ARBITRARY_TIME); + ExternalTimeSuggestion suggestion = new ExternalTimeSuggestion( + ARBITRARY_TIME.getReferenceTimeMillis(), + ARBITRARY_TIME.getValue()); assertRoundTripParcelable(suggestion); // DebugInfo should also be stored (but is not checked by equals()) diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 5633de348433..cabfad44cfb9 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -1321,6 +1321,12 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/DisplayRotation.java" }, + "-567946587": { + "message": "Requested redraw for orientation change: %s", + "level": "VERBOSE", + "group": "WM_DEBUG_ORIENTATION", + "at": "com\/android\/server\/wm\/WindowState.java" + }, "-561092364": { "message": "onPointerDownOutsideFocusLocked called on %s", "level": "INFO", diff --git a/graphics/java/android/graphics/BlurShader.java b/graphics/java/android/graphics/BlurShader.java deleted file mode 100644 index 2e4bd7d9be43..000000000000 --- a/graphics/java/android/graphics/BlurShader.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.graphics; - -import android.annotation.NonNull; -import android.annotation.Nullable; - -/** - * A subclass of shader that blurs input from another {@link android.graphics.Shader} instance - * or all the drawing commands with the {@link android.graphics.Paint} that this shader is - * attached to. - */ -public final class BlurShader extends Shader { - - private final float mRadiusX; - private final float mRadiusY; - private final Shader mInputShader; - private final TileMode mEdgeTreatment; - - private long mNativeInputShader = 0; - - /** - * Create a {@link BlurShader} that blurs the contents of the optional input shader - * with the specified radius along the x and y axis. If no input shader is provided - * then all drawing commands issued with a {@link android.graphics.Paint} that this - * shader is installed in will be blurred. - * - * This uses a default {@link TileMode#DECAL} for edge treatment - * - * @param radiusX Radius of blur along the X axis - * @param radiusY Radius of blur along the Y axis - * @param inputShader Input shader that provides the content to be blurred - */ - public BlurShader(float radiusX, float radiusY, @Nullable Shader inputShader) { - this(radiusX, radiusY, inputShader, TileMode.DECAL); - } - - /** - * Create a {@link BlurShader} that blurs the contents of the optional input shader - * with the specified radius along the x and y axis. If no input shader is provided - * then all drawing commands issued with a {@link android.graphics.Paint} that this - * shader is installed in will be blurred - * @param radiusX Radius of blur along the X axis - * @param radiusY Radius of blur along the Y axis - * @param inputShader Input shader that provides the content to be blurred - * @param edgeTreatment Policy for how to blur content near edges of the blur shader - */ - public BlurShader(float radiusX, float radiusY, @Nullable Shader inputShader, - @NonNull TileMode edgeTreatment) { - mRadiusX = radiusX; - mRadiusY = radiusY; - mInputShader = inputShader; - mEdgeTreatment = edgeTreatment; - } - - /** @hide **/ - @Override - protected long createNativeInstance(long nativeMatrix, boolean filterFromPaint) { - mNativeInputShader = mInputShader != null - ? mInputShader.getNativeInstance(filterFromPaint) : 0; - return nativeCreate(nativeMatrix, mRadiusX, mRadiusY, mNativeInputShader, - mEdgeTreatment.nativeInt); - } - - /** @hide **/ - @Override - protected boolean shouldDiscardNativeInstance(boolean filterFromPaint) { - long currentNativeInstance = mInputShader != null - ? mInputShader.getNativeInstance(filterFromPaint) : 0; - return mNativeInputShader != currentNativeInstance; - } - - private static native long nativeCreate(long nativeMatrix, float radiusX, float radiusY, - long inputShader, int edgeTreatment); -} diff --git a/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml b/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml index 347c2b47767e..0dea87c6b7fc 100644 --- a/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml +++ b/libs/WindowManager/Shell/res/layout/size_compat_mode_hint.xml @@ -14,35 +14,52 @@ See the License for the specific language governing permissions and limitations under the License. --> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +<com.android.wm.shell.sizecompatui.SizeCompatHintPopup + xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:background="@android:color/background_light" - android:orientation="vertical"> + android:layout_height="wrap_content"> - <TextView - android:layout_width="180dp" - android:layout_height="wrap_content" - android:paddingLeft="10dp" - android:paddingRight="10dp" - android:paddingTop="10dp" - android:text="@string/restart_button_description" - android:textAlignment="viewStart" - android:textColor="@android:color/primary_text_light" - android:textSize="16sp" /> - - <Button - android:id="@+id/got_it" + <FrameLayout android:layout_width="wrap_content" android:layout_height="wrap_content" - android:includeFontPadding="false" - android:layout_gravity="end" - android:minHeight="36dp" - android:background="?android:attr/selectableItemBackground" - android:text="@string/got_it" - android:textAllCaps="true" - android:textColor="#3c78d8" - android:textSize="16sp" - android:textStyle="bold" /> - -</LinearLayout> + android:gravity="center" + android:clipToPadding="false" + android:padding="@dimen/bubble_elevation"> + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:background="@android:color/background_light" + android:elevation="@dimen/bubble_elevation" + android:orientation="vertical"> + + <TextView + android:layout_width="180dp" + android:layout_height="wrap_content" + android:paddingLeft="10dp" + android:paddingRight="10dp" + android:paddingTop="10dp" + android:text="@string/restart_button_description" + android:textAlignment="viewStart" + android:textColor="@android:color/primary_text_light" + android:textSize="16sp"/> + + <Button + android:id="@+id/got_it" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:includeFontPadding="false" + android:layout_gravity="end" + android:minHeight="36dp" + android:background="?android:attr/selectableItemBackground" + android:text="@string/got_it" + android:textAllCaps="true" + android:textColor="#3c78d8" + android:textSize="16sp" + android:textStyle="bold"/> + + </LinearLayout> + + </FrameLayout> + +</com.android.wm.shell.sizecompatui.SizeCompatHintPopup> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java new file mode 100644 index 000000000000..78af9df30e6a --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopup.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2021 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.wm.shell.sizecompatui; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.Color; +import android.graphics.drawable.RippleDrawable; +import android.util.AttributeSet; +import android.view.View; +import android.widget.Button; +import android.widget.FrameLayout; + +import androidx.annotation.Nullable; + +import com.android.wm.shell.R; + +/** Popup to show the hint about the {@link SizeCompatRestartButton}. */ +public class SizeCompatHintPopup extends FrameLayout implements View.OnClickListener { + + private SizeCompatUILayout mLayout; + + public SizeCompatHintPopup(Context context) { + super(context); + } + + public SizeCompatHintPopup(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public SizeCompatHintPopup(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public SizeCompatHintPopup(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + void inject(SizeCompatUILayout layout) { + mLayout = layout; + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + final Button gotItButton = findViewById(R.id.got_it); + gotItButton.setBackground(new RippleDrawable(ColorStateList.valueOf(Color.LTGRAY), + null /* content */, null /* mask */)); + gotItButton.setOnClickListener(this); + } + + @Override + public void onClick(View v) { + mLayout.dismissHint(); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java index 9094d7de8d63..08a840297df1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButton.java @@ -22,19 +22,13 @@ import android.graphics.Color; import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.RippleDrawable; import android.util.AttributeSet; -import android.view.LayoutInflater; import android.view.View; -import android.view.WindowManager; -import android.widget.Button; import android.widget.FrameLayout; import android.widget.ImageButton; -import android.widget.LinearLayout; -import android.widget.PopupWindow; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.android.internal.annotations.VisibleForTesting; import com.android.wm.shell.R; /** Button to restart the size compat activity. */ @@ -42,10 +36,6 @@ public class SizeCompatRestartButton extends FrameLayout implements View.OnClick View.OnLongClickListener { private SizeCompatUILayout mLayout; - private ImageButton mRestartButton; - @VisibleForTesting - PopupWindow mShowingHint; - private WindowManager.LayoutParams mWinParams; public SizeCompatRestartButton(@NonNull Context context) { super(context); @@ -67,24 +57,19 @@ public class SizeCompatRestartButton extends FrameLayout implements View.OnClick void inject(SizeCompatUILayout layout) { mLayout = layout; - mWinParams = layout.getWindowLayoutParams(); - } - - void remove() { - dismissHint(); } @Override protected void onFinishInflate() { super.onFinishInflate(); - mRestartButton = findViewById(R.id.size_compat_restart_button); + final ImageButton restartButton = findViewById(R.id.size_compat_restart_button); final ColorStateList color = ColorStateList.valueOf(Color.LTGRAY); final GradientDrawable mask = new GradientDrawable(); mask.setShape(GradientDrawable.OVAL); mask.setColor(color); - mRestartButton.setBackground(new RippleDrawable(color, null /* content */, mask)); - mRestartButton.setOnClickListener(this); - mRestartButton.setOnLongClickListener(this); + restartButton.setBackground(new RippleDrawable(color, null /* content */, mask)); + restartButton.setOnClickListener(this); + restartButton.setOnLongClickListener(this); } @Override @@ -94,69 +79,7 @@ public class SizeCompatRestartButton extends FrameLayout implements View.OnClick @Override public boolean onLongClick(View v) { - showHint(); + mLayout.onRestartButtonLongClicked(); return true; } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - if (mLayout.mShouldShowHint) { - mLayout.mShouldShowHint = false; - showHint(); - } - } - - @Override - public void setVisibility(@Visibility int visibility) { - if (visibility == View.GONE && mShowingHint != null) { - // Also dismiss the popup. - dismissHint(); - } - super.setVisibility(visibility); - } - - @Override - public void setLayoutDirection(int layoutDirection) { - final int gravity = SizeCompatUILayout.getGravity(layoutDirection); - if (mWinParams.gravity != gravity) { - mWinParams.gravity = gravity; - getContext().getSystemService(WindowManager.class).updateViewLayout(this, - mWinParams); - } - super.setLayoutDirection(layoutDirection); - } - - void showHint() { - if (mShowingHint != null) { - return; - } - - // TODO: popup is not attached to the button surface. Need to handle this differently for - // non-fullscreen task. - final View popupView = LayoutInflater.from(getContext()).inflate( - R.layout.size_compat_mode_hint, null); - final PopupWindow popupWindow = new PopupWindow(popupView, - LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); - popupWindow.setWindowLayoutType(mWinParams.type); - popupWindow.setElevation(getResources().getDimension(R.dimen.bubble_elevation)); - popupWindow.setAnimationStyle(android.R.style.Animation_InputMethod); - popupWindow.setClippingEnabled(false); - popupWindow.setOnDismissListener(() -> mShowingHint = null); - mShowingHint = popupWindow; - - final Button gotItButton = popupView.findViewById(R.id.got_it); - gotItButton.setBackground(new RippleDrawable(ColorStateList.valueOf(Color.LTGRAY), - null /* content */, null /* mask */)); - gotItButton.setOnClickListener(view -> dismissHint()); - popupWindow.showAtLocation(mRestartButton, mWinParams.gravity, mLayout.mPopupOffsetX, - mLayout.mPopupOffsetY); - } - - void dismissHint() { - if (mShowingHint != null) { - mShowingHint.dismiss(); - mShowingHint = null; - } - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java index a3880f497ff3..c981adee9b5c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIController.java @@ -50,7 +50,7 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang /** Whether the IME is shown on display id. */ private final Set<Integer> mDisplaysWithIme = new ArraySet<>(1); - /** The showing buttons by task id. */ + /** The showing UIs by task id. */ private final SparseArray<SizeCompatUILayout> mActiveLayouts = new SparseArray<>(0); /** Avoid creating display context frequently for non-default display. */ @@ -77,12 +77,12 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang } /** - * Called when the Task info changed. Creates and updates the restart button if there is an - * activity in size compat, or removes the restart button if there is no size compat activity. + * Called when the Task info changed. Creates and updates the size compat UI if there is an + * activity in size compat, or removes the UI if there is no size compat activity. * * @param displayId display the task and activity are in. * @param taskId task the activity is in. - * @param taskConfig task config to place the restart button with. + * @param taskConfig task config to place the size compat UI with. * @param sizeCompatActivity the size compat activity in the task. Can be {@code null} if the * top activity in this Task is not in size compat. * @param taskListener listener to handle the Task Surface placement. @@ -94,10 +94,10 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang // Null token means the current foreground activity is not in size compatibility mode. removeLayout(taskId); } else if (mActiveLayouts.contains(taskId)) { - // Button already exists, update the button layout. + // UI already exists, update the UI layout. updateLayout(taskId, taskConfig, sizeCompatActivity, taskListener); } else { - // Create a new restart button. + // Create a new size compat UI. createLayout(displayId, taskId, taskConfig, sizeCompatActivity, taskListener); } } @@ -106,7 +106,7 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang public void onDisplayRemoved(int displayId) { mDisplayContextCache.remove(displayId); - // Remove all buttons on the removed display. + // Remove all size compat UIs on the removed display. final List<Integer> toRemoveTaskIds = new ArrayList<>(); forAllLayoutsOnDisplay(displayId, layout -> toRemoveTaskIds.add(layout.getTaskId())); for (int i = toRemoveTaskIds.size() - 1; i >= 0; i--) { @@ -128,7 +128,7 @@ public class SizeCompatUIController implements DisplayController.OnDisplaysChang mDisplaysWithIme.remove(displayId); } - // Hide the button when input method is showing. + // Hide the size compat UIs when input method is showing. forAllLayoutsOnDisplay(displayId, layout -> layout.updateImeVisibility(isShowing)); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java index 5924b53f822c..32f3648be19a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUILayout.java @@ -30,7 +30,6 @@ import android.graphics.PixelFormat; import android.graphics.Rect; import android.os.Binder; import android.os.IBinder; -import android.view.Gravity; import android.view.SurfaceControl; import android.view.View; import android.view.WindowManager; @@ -43,7 +42,7 @@ import com.android.wm.shell.common.SyncTransactionQueue; /** * Records and handles layout of size compat UI on a task with size compat activity. Helps to - * calculate proper bounds when configuration or button position changes. + * calculate proper bounds when configuration or UI position changes. */ class SizeCompatUILayout { private static final String TAG = "SizeCompatUILayout"; @@ -56,12 +55,18 @@ class SizeCompatUILayout { private IBinder mActivityToken; private ShellTaskOrganizer.TaskListener mTaskListener; private DisplayLayout mDisplayLayout; - @VisibleForTesting - final SizeCompatUIWindowManager mWindowManager; @VisibleForTesting + final SizeCompatUIWindowManager mButtonWindowManager; + @VisibleForTesting + @Nullable + SizeCompatUIWindowManager mHintWindowManager; + @VisibleForTesting @Nullable SizeCompatRestartButton mButton; + @VisibleForTesting + @Nullable + SizeCompatHintPopup mHint; final int mButtonSize; final int mPopupOffsetX; final int mPopupOffsetY; @@ -79,7 +84,7 @@ class SizeCompatUILayout { mTaskListener = taskListener; mDisplayLayout = displayLayout; mShouldShowHint = !hasShownHint; - mWindowManager = new SizeCompatUIWindowManager(mContext, taskConfig, this); + mButtonWindowManager = new SizeCompatUIWindowManager(mContext, taskConfig, this); mButtonSize = mContext.getResources().getDimensionPixelSize(R.dimen.size_compat_button_size); @@ -87,21 +92,52 @@ class SizeCompatUILayout { mPopupOffsetY = mButtonSize; } - /** Creates the button window. */ + /** Creates the activity restart button window. */ void createSizeCompatButton(boolean isImeShowing) { if (isImeShowing || mButton != null) { // When ime is showing, wait until ime is dismiss to create UI. return; } - mButton = mWindowManager.createSizeCompatUI(); - updateSurfacePosition(); + mButton = mButtonWindowManager.createSizeCompatButton(); + updateButtonSurfacePosition(); + + if (mShouldShowHint) { + // Only show by default for the first time. + mShouldShowHint = false; + createSizeCompatHint(); + } + } + + /** Creates the restart button hint window. */ + private void createSizeCompatHint() { + if (mHint != null) { + // Hint already shown. + return; + } + mHintWindowManager = createHintWindowManager(); + mHint = mHintWindowManager.createSizeCompatHint(); + updateHintSurfacePosition(); } - /** Releases the button window. */ + @VisibleForTesting + SizeCompatUIWindowManager createHintWindowManager() { + return new SizeCompatUIWindowManager(mContext, mTaskConfig, this); + } + + /** Dismisses the hint window. */ + void dismissHint() { + mHint = null; + if (mHintWindowManager != null) { + mHintWindowManager.release(); + mHintWindowManager = null; + } + } + + /** Releases the UI windows. */ void release() { - mButton.remove(); + dismissHint(); mButton = null; - mWindowManager.release(); + mButtonWindowManager.release(); } /** Called when size compat info changed. */ @@ -115,7 +151,10 @@ class SizeCompatUILayout { // Update configuration. mContext = mContext.createConfigurationContext(taskConfig); - mWindowManager.setConfiguration(taskConfig); + mButtonWindowManager.setConfiguration(taskConfig); + if (mHintWindowManager != null) { + mHintWindowManager.setConfiguration(taskConfig); + } if (mButton == null || prevTaskListener != taskListener) { // TaskListener changed, recreate the button for new surface parent. @@ -126,14 +165,19 @@ class SizeCompatUILayout { if (!taskConfig.windowConfiguration.getBounds() .equals(prevTaskConfig.windowConfiguration.getBounds())) { - // Reposition the button surface. - updateSurfacePosition(); + // Reposition the UI surfaces. + updateButtonSurfacePosition(); + updateHintSurfacePosition(); } if (taskConfig.getLayoutDirection() != prevTaskConfig.getLayoutDirection()) { // Update layout for RTL. mButton.setLayoutDirection(taskConfig.getLayoutDirection()); - updateSurfacePosition(); + updateButtonSurfacePosition(); + if (mHint != null) { + mHint.setLayoutDirection(taskConfig.getLayoutDirection()); + updateHintSurfacePosition(); + } } } @@ -149,8 +193,9 @@ class SizeCompatUILayout { displayLayout.getStableBounds(curStableBounds); mDisplayLayout = displayLayout; if (!prevStableBounds.equals(curStableBounds)) { - // Stable bounds changed, update button surface position. - updateSurfacePosition(); + // Stable bounds changed, update UI surface positions. + updateButtonSurfacePosition(); + updateHintSurfacePosition(); } } @@ -162,27 +207,46 @@ class SizeCompatUILayout { return; } + // Hide size compat UIs when IME is showing. final int newVisibility = isImeShowing ? View.GONE : View.VISIBLE; if (mButton.getVisibility() != newVisibility) { mButton.setVisibility(newVisibility); } + if (mHint != null && mHint.getVisibility() != newVisibility) { + mHint.setVisibility(newVisibility); + } } /** Gets the layout params for restart button. */ - WindowManager.LayoutParams getWindowLayoutParams() { + WindowManager.LayoutParams getButtonWindowLayoutParams() { final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams( + // Cannot be wrap_content as this determines the actual window size mButtonSize, mButtonSize, TYPE_APPLICATION_OVERLAY, FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL, PixelFormat.TRANSLUCENT); - winParams.gravity = getGravity(getLayoutDirection()); winParams.token = new Binder(); - winParams.setTitle(SizeCompatRestartButton.class.getSimpleName() + mContext.getDisplayId()); + winParams.setTitle(SizeCompatRestartButton.class.getSimpleName() + getTaskId()); winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY; return winParams; } - /** Called when it is ready to be placed button surface button. */ + /** Gets the layout params for hint popup. */ + WindowManager.LayoutParams getHintWindowLayoutParams(SizeCompatHintPopup hint) { + final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams( + // Cannot be wrap_content as this determines the actual window size + hint.getMeasuredWidth(), hint.getMeasuredHeight(), + TYPE_APPLICATION_OVERLAY, + FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL, + PixelFormat.TRANSLUCENT); + winParams.token = new Binder(); + winParams.setTitle(SizeCompatHintPopup.class.getSimpleName() + getTaskId()); + winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY; + winParams.windowAnimations = android.R.style.Animation_InputMethod; + return winParams; + } + + /** Called when it is ready to be placed size compat UI surface. */ void attachToParentSurface(SurfaceControl.Builder b) { mTaskListener.attachChildSurfaceToTask(mTaskId, b); } @@ -192,13 +256,17 @@ class SizeCompatUILayout { ActivityClient.getInstance().restartActivityProcessIfVisible(mActivityToken); } + /** Called when the restart button is long clicked. */ + void onRestartButtonLongClicked() { + createSizeCompatHint(); + } + @VisibleForTesting - void updateSurfacePosition() { - if (mButton == null || mWindowManager.getSurfaceControl() == null) { + void updateButtonSurfacePosition() { + if (mButton == null || mButtonWindowManager.getSurfaceControl() == null) { return; } - // The hint popup won't be at the correct position. - mButton.dismissHint(); + final SurfaceControl leash = mButtonWindowManager.getSurfaceControl(); // Use stable bounds to prevent the button from overlapping with system bars. final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds(); @@ -212,8 +280,30 @@ class SizeCompatUILayout { : stableBounds.right - taskBounds.left - mButtonSize; final int positionY = stableBounds.bottom - taskBounds.top - mButtonSize; - mSyncQueue.runInSync(t -> - t.setPosition(mWindowManager.getSurfaceControl(), positionX, positionY)); + mSyncQueue.runInSync(t -> t.setPosition(leash, positionX, positionY)); + } + + void updateHintSurfacePosition() { + if (mHint == null || mHintWindowManager == null + || mHintWindowManager.getSurfaceControl() == null) { + return; + } + final SurfaceControl leash = mHintWindowManager.getSurfaceControl(); + + // Use stable bounds to prevent the hint from overlapping with system bars. + final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds(); + final Rect stableBounds = new Rect(); + mDisplayLayout.getStableBounds(stableBounds); + stableBounds.intersect(taskBounds); + + // Position of the hint in the container coordinate. + final int positionX = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL + ? stableBounds.left - taskBounds.left + mPopupOffsetX + : stableBounds.right - taskBounds.left - mPopupOffsetX - mHint.getMeasuredWidth(); + final int positionY = + stableBounds.bottom - taskBounds.top - mPopupOffsetY - mHint.getMeasuredHeight(); + + mSyncQueue.runInSync(t -> t.setPosition(leash, positionX, positionY)); } int getDisplayId() { @@ -227,9 +317,4 @@ class SizeCompatUILayout { private int getLayoutDirection() { return mContext.getResources().getConfiguration().getLayoutDirection(); } - - static int getGravity(int layoutDirection) { - return Gravity.BOTTOM - | (layoutDirection == View.LAYOUT_DIRECTION_RTL ? Gravity.START : Gravity.END); - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java index a7ad982a4736..f634c4586e39 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sizecompatui/SizeCompatUIWindowManager.java @@ -24,12 +24,14 @@ import android.view.LayoutInflater; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost; import android.view.SurfaceSession; +import android.view.View; import android.view.WindowlessWindowManager; import com.android.wm.shell.R; /** - * Holds view hierarchy of a root surface and helps to inflate {@link SizeCompatRestartButton}. + * Holds view hierarchy of a root surface and helps to inflate {@link SizeCompatRestartButton} or + * {@link SizeCompatHintPopup}. */ class SizeCompatUIWindowManager extends WindowlessWindowManager { @@ -67,18 +69,39 @@ class SizeCompatUIWindowManager extends WindowlessWindowManager { } /** Inflates {@link SizeCompatRestartButton} on to the root surface. */ - SizeCompatRestartButton createSizeCompatUI() { - if (mViewHost == null) { - mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this); + SizeCompatRestartButton createSizeCompatButton() { + if (mViewHost != null) { + throw new IllegalStateException( + "A UI has already been created with this window manager."); } + mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this); + final SizeCompatRestartButton button = (SizeCompatRestartButton) LayoutInflater.from(mContext).inflate(R.layout.size_compat_ui, null); button.inject(mLayout); - mViewHost.setView(button, mLayout.getWindowLayoutParams()); + mViewHost.setView(button, mLayout.getButtonWindowLayoutParams()); return button; } + /** Inflates {@link SizeCompatHintPopup} on to the root surface. */ + SizeCompatHintPopup createSizeCompatHint() { + if (mViewHost != null) { + throw new IllegalStateException( + "A UI has already been created with this window manager."); + } + + mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this); + + final SizeCompatHintPopup hint = (SizeCompatHintPopup) + LayoutInflater.from(mContext).inflate(R.layout.size_compat_mode_hint, null); + // Measure how big the hint is. + hint.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); + hint.inject(mLayout); + mViewHost.setView(hint, mLayout.getHintWindowLayoutParams(hint)); + return hint; + } + /** Releases the surface control and tears down the view hierarchy. */ void release() { if (mViewHost != null) { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java new file mode 100644 index 000000000000..9845d4650d20 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatHintPopupTest.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2021 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.wm.shell.sizecompatui; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; + +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.verify; + +import android.content.res.Configuration; +import android.os.IBinder; +import android.testing.AndroidTestingRunner; +import android.view.LayoutInflater; +import android.widget.Button; + +import androidx.test.filters.SmallTest; + +import com.android.wm.shell.R; +import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.ShellTestCase; +import com.android.wm.shell.common.DisplayLayout; +import com.android.wm.shell.common.SyncTransactionQueue; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Tests for {@link SizeCompatHintPopup}. + * + * Build/Install/Run: + * atest WMShellUnitTests:SizeCompatHintPopupTest + */ +@RunWith(AndroidTestingRunner.class) +@SmallTest +public class SizeCompatHintPopupTest extends ShellTestCase { + + @Mock private SyncTransactionQueue mSyncTransactionQueue; + @Mock private IBinder mActivityToken; + @Mock private ShellTaskOrganizer.TaskListener mTaskListener; + @Mock private DisplayLayout mDisplayLayout; + + private SizeCompatUILayout mLayout; + private SizeCompatHintPopup mHint; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + final int taskId = 1; + mLayout = new SizeCompatUILayout(mSyncTransactionQueue, mContext, new Configuration(), + taskId, mActivityToken, mTaskListener, mDisplayLayout, false /* hasShownHint*/); + mHint = (SizeCompatHintPopup) + LayoutInflater.from(mContext).inflate(R.layout.size_compat_mode_hint, null); + mHint.inject(mLayout); + + spyOn(mLayout); + } + + @Test + public void testOnClick() { + doNothing().when(mLayout).dismissHint(); + + final Button button = mHint.findViewById(R.id.got_it); + button.performClick(); + + verify(mLayout).dismissHint(); + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java index d9086a6ccdc1..5a43925a5677 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatRestartButtonTest.java @@ -19,13 +19,13 @@ package com.android.wm.shell.sizecompatui; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import android.content.res.Configuration; import android.os.IBinder; import android.testing.AndroidTestingRunner; import android.view.LayoutInflater; +import android.widget.ImageButton; import androidx.test.filters.SmallTest; @@ -71,45 +71,25 @@ public class SizeCompatRestartButtonTest extends ShellTestCase { mButton.inject(mLayout); spyOn(mLayout); - spyOn(mButton); - doNothing().when(mButton).showHint(); } @Test public void testOnClick() { doNothing().when(mLayout).onRestartButtonClicked(); - mButton.onClick(mButton); + final ImageButton button = mButton.findViewById(R.id.size_compat_restart_button); + button.performClick(); verify(mLayout).onRestartButtonClicked(); } @Test public void testOnLongClick() { - verify(mButton, never()).showHint(); + doNothing().when(mLayout).onRestartButtonLongClicked(); - mButton.onLongClick(mButton); + final ImageButton button = mButton.findViewById(R.id.size_compat_restart_button); + button.performLongClick(); - verify(mButton).showHint(); - } - - @Test - public void testOnAttachedToWindow_showHint() { - mLayout.mShouldShowHint = false; - mButton.onAttachedToWindow(); - - verify(mButton, never()).showHint(); - - mLayout.mShouldShowHint = true; - mButton.onAttachedToWindow(); - - verify(mButton).showHint(); - } - - @Test - public void testRemove() { - mButton.remove(); - - verify(mButton).dismissHint(); + verify(mLayout).onRestartButtonLongClicked(); } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java index 236db44bdce0..f33cfe86224f 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sizecompatui/SizeCompatUILayoutTest.java @@ -18,6 +18,7 @@ package com.android.wm.shell.sizecompatui; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.any; @@ -27,6 +28,7 @@ import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.app.ActivityClient; @@ -68,6 +70,7 @@ public class SizeCompatUILayoutTest extends ShellTestCase { @Mock private ShellTaskOrganizer.TaskListener mTaskListener; @Mock private DisplayLayout mDisplayLayout; @Mock private SizeCompatRestartButton mButton; + @Mock private SizeCompatHintPopup mHint; private Configuration mTaskConfig; private SizeCompatUILayout mLayout; @@ -81,8 +84,13 @@ public class SizeCompatUILayoutTest extends ShellTestCase { TASK_ID, mActivityToken, mTaskListener, mDisplayLayout, false /* hasShownHint*/); spyOn(mLayout); - spyOn(mLayout.mWindowManager); - doReturn(mButton).when(mLayout.mWindowManager).createSizeCompatUI(); + spyOn(mLayout.mButtonWindowManager); + doReturn(mButton).when(mLayout.mButtonWindowManager).createSizeCompatButton(); + + final SizeCompatUIWindowManager hintWindowManager = mLayout.createHintWindowManager(); + spyOn(hintWindowManager); + doReturn(mHint).when(hintWindowManager).createSizeCompatHint(); + doReturn(hintWindowManager).when(mLayout).createHintWindowManager(); } @Test @@ -90,24 +98,45 @@ public class SizeCompatUILayoutTest extends ShellTestCase { // Not create button if IME is showing. mLayout.createSizeCompatButton(true /* isImeShowing */); - verify(mLayout.mWindowManager, never()).createSizeCompatUI(); + verify(mLayout.mButtonWindowManager, never()).createSizeCompatButton(); assertNull(mLayout.mButton); + assertNull(mLayout.mHintWindowManager); + assertNull(mLayout.mHint); + // Not create hint popup. + mLayout.mShouldShowHint = false; mLayout.createSizeCompatButton(false /* isImeShowing */); - verify(mLayout.mWindowManager).createSizeCompatUI(); + verify(mLayout.mButtonWindowManager).createSizeCompatButton(); assertNotNull(mLayout.mButton); + assertNull(mLayout.mHintWindowManager); + assertNull(mLayout.mHint); + + // Create hint popup. + mLayout.release(); + mLayout.mShouldShowHint = true; + mLayout.createSizeCompatButton(false /* isImeShowing */); + + verify(mLayout.mButtonWindowManager, times(2)).createSizeCompatButton(); + assertNotNull(mLayout.mButton); + assertNotNull(mLayout.mHintWindowManager); + verify(mLayout.mHintWindowManager).createSizeCompatHint(); + assertNotNull(mLayout.mHint); + assertFalse(mLayout.mShouldShowHint); } @Test public void testRelease() { mLayout.createSizeCompatButton(false /* isImeShowing */); + final SizeCompatUIWindowManager hintWindowManager = mLayout.mHintWindowManager; mLayout.release(); assertNull(mLayout.mButton); - verify(mButton).remove(); - verify(mLayout.mWindowManager).release(); + assertNull(mLayout.mHint); + verify(hintWindowManager).release(); + assertNull(mLayout.mHintWindowManager); + verify(mLayout.mButtonWindowManager).release(); } @Test @@ -119,7 +148,7 @@ public class SizeCompatUILayoutTest extends ShellTestCase { mLayout.updateSizeCompatInfo(mTaskConfig, mActivityToken, mTaskListener, false /* isImeShowing */); - verify(mLayout, never()).updateSurfacePosition(); + verify(mLayout, never()).updateButtonSurfacePosition(); verify(mLayout, never()).release(); verify(mLayout, never()).createSizeCompatButton(anyBoolean()); @@ -140,7 +169,8 @@ public class SizeCompatUILayoutTest extends ShellTestCase { mLayout.updateSizeCompatInfo(newTaskConfiguration, mActivityToken, newTaskListener, false /* isImeShowing */); - verify(mLayout).updateSurfacePosition(); + verify(mLayout).updateButtonSurfacePosition(); + verify(mLayout).updateHintSurfacePosition(); } @Test @@ -152,14 +182,16 @@ public class SizeCompatUILayoutTest extends ShellTestCase { mContext.getResources(), false, false); mLayout.updateDisplayLayout(displayLayout1); - verify(mLayout).updateSurfacePosition(); + verify(mLayout).updateButtonSurfacePosition(); + verify(mLayout).updateHintSurfacePosition(); // No update if the display bounds is the same. clearInvocations(mLayout); final DisplayLayout displayLayout2 = new DisplayLayout(displayInfo, mContext.getResources(), false, false); mLayout.updateDisplayLayout(displayLayout2); - verify(mLayout, never()).updateSurfacePosition(); + verify(mLayout, never()).updateButtonSurfacePosition(); + verify(mLayout, never()).updateHintSurfacePosition(); } @Test @@ -203,4 +235,29 @@ public class SizeCompatUILayoutTest extends ShellTestCase { verify(ActivityClient.getInstance()).restartActivityProcessIfVisible(mActivityToken); } + + @Test + public void testOnRestartButtonLongClicked_showHint() { + mLayout.dismissHint(); + + assertNull(mLayout.mHint); + + mLayout.onRestartButtonLongClicked(); + + assertNotNull(mLayout.mHint); + } + + @Test + public void testDismissHint() { + mLayout.onRestartButtonLongClicked(); + final SizeCompatUIWindowManager hintWindowManager = mLayout.mHintWindowManager; + assertNotNull(mLayout.mHint); + assertNotNull(hintWindowManager); + + mLayout.dismissHint(); + + assertNull(mLayout.mHint); + assertNull(mLayout.mHintWindowManager); + verify(hintWindowManager).release(); + } } diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index 64b8b711f0a8..170f73120414 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -384,10 +384,11 @@ struct DrawImageLattice final : Op { struct DrawTextBlob final : Op { static const auto kType = Type::DrawTextBlob; DrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint) - : blob(sk_ref_sp(blob)), x(x), y(y), paint(paint) {} + : blob(sk_ref_sp(blob)), x(x), y(y), paint(paint), drawTextBlobMode(gDrawTextBlobMode) {} sk_sp<const SkTextBlob> blob; SkScalar x, y; SkPaint paint; + DrawTextBlobMode drawTextBlobMode; void draw(SkCanvas* c, const SkMatrix&) const { c->drawTextBlob(blob.get(), x, y, paint); } }; @@ -832,6 +833,24 @@ constexpr color_transform_fn colorTransformForOp() { } } +template<> +constexpr color_transform_fn colorTransformForOp<DrawTextBlob>() { + return [](const void *opRaw, ColorTransform transform) { + const DrawTextBlob *op = reinterpret_cast<const DrawTextBlob*>(opRaw); + switch (op->drawTextBlobMode) { + case DrawTextBlobMode::HctOutline: + const_cast<SkPaint&>(op->paint).setColor(SK_ColorBLACK); + break; + case DrawTextBlobMode::HctInner: + const_cast<SkPaint&>(op->paint).setColor(SK_ColorWHITE); + break; + default: + transformPaint(transform, const_cast<SkPaint*>(&(op->paint))); + break; + } + }; +} + #define X(T) colorTransformForOp<T>(), static const color_transform_fn color_transform_fns[] = { #include "DisplayListOps.in" diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp index 146bf283c58a..b046f45d9c57 100644 --- a/libs/hwui/hwui/Canvas.cpp +++ b/libs/hwui/hwui/Canvas.cpp @@ -110,16 +110,19 @@ public: bool darken = channelSum < (128 * 3); // outline + gDrawTextBlobMode = DrawTextBlobMode::HctOutline; Paint outlinePaint(paint); simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, &outlinePaint); outlinePaint.setStyle(SkPaint::kStrokeAndFill_Style); canvas->drawGlyphs(glyphFunc, glyphCount, outlinePaint, x, y, totalAdvance); // inner + gDrawTextBlobMode = DrawTextBlobMode::HctInner; Paint innerPaint(paint); simplifyPaint(darken ? SK_ColorBLACK : SK_ColorWHITE, &innerPaint); innerPaint.setStyle(SkPaint::kFill_Style); canvas->drawGlyphs(glyphFunc, glyphCount, innerPaint, x, y, totalAdvance); + gDrawTextBlobMode = DrawTextBlobMode::Normal; } else { // standard draw path canvas->drawGlyphs(glyphFunc, glyphCount, paint, x, y, totalAdvance); diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h index fdfa2883c33f..d1bdb712e911 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -61,6 +61,14 @@ class Bitmap; class Paint; struct Typeface; +enum class DrawTextBlobMode { + Normal, + HctOutline, + HctInner, +}; + +inline DrawTextBlobMode gDrawTextBlobMode = DrawTextBlobMode::Normal; + class ANDROID_API Canvas { public: virtual ~Canvas(){}; diff --git a/libs/hwui/tests/common/TestContext.cpp b/libs/hwui/tests/common/TestContext.cpp index 6a9a98d6743b..898c64bd4159 100644 --- a/libs/hwui/tests/common/TestContext.cpp +++ b/libs/hwui/tests/common/TestContext.cpp @@ -22,16 +22,16 @@ namespace android { namespace uirenderer { namespace test { -const DisplayInfo& getDisplayInfo() { - static DisplayInfo info = [] { - DisplayInfo info; +const ui::StaticDisplayInfo& getDisplayInfo() { + static ui::StaticDisplayInfo info = [] { + ui::StaticDisplayInfo info; #if HWUI_NULL_GPU info.density = 2.f; #else const sp<IBinder> token = SurfaceComposerClient::getInternalDisplayToken(); LOG_ALWAYS_FATAL_IF(!token, "%s: No internal display", __FUNCTION__); - const status_t status = SurfaceComposerClient::getDisplayInfo(token, &info); + const status_t status = SurfaceComposerClient::getStaticDisplayInfo(token, &info); LOG_ALWAYS_FATAL_IF(status, "%s: Failed to get display info", __FUNCTION__); #endif return info; diff --git a/libs/hwui/tests/common/TestContext.h b/libs/hwui/tests/common/TestContext.h index 7d2f6d8ea731..9d00366daffe 100644 --- a/libs/hwui/tests/common/TestContext.h +++ b/libs/hwui/tests/common/TestContext.h @@ -23,8 +23,8 @@ #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> #include <gui/SurfaceControl.h> -#include <ui/DisplayInfo.h> #include <ui/DisplayMode.h> +#include <ui/StaticDisplayInfo.h> #include <utils/Looper.h> #include <atomic> @@ -36,7 +36,7 @@ namespace android { namespace uirenderer { namespace test { -const DisplayInfo& getDisplayInfo(); +const ui::StaticDisplayInfo& getDisplayInfo(); const ui::DisplayMode& getActiveDisplayMode(); inline const ui::Size& getActiveDisplayResolution() { diff --git a/libs/input/MouseCursorController.h b/libs/input/MouseCursorController.h index e6dfc4c6f99a..c0ab58bd2e7e 100644 --- a/libs/input/MouseCursorController.h +++ b/libs/input/MouseCursorController.h @@ -20,7 +20,6 @@ #include <gui/DisplayEventReceiver.h> #include <input/DisplayViewport.h> #include <input/Input.h> -#include <ui/DisplayInfo.h> #include <utils/BitSet.h> #include <utils/Looper.h> #include <utils/RefBase.h> diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h index 827fcf1e1bc1..97567bab202b 100644 --- a/libs/input/PointerController.h +++ b/libs/input/PointerController.h @@ -21,7 +21,6 @@ #include <gui/DisplayEventReceiver.h> #include <input/DisplayViewport.h> #include <input/Input.h> -#include <ui/DisplayInfo.h> #include <utils/BitSet.h> #include <utils/Looper.h> #include <utils/RefBase.h> diff --git a/libs/input/PointerControllerContext.h b/libs/input/PointerControllerContext.h index 98073fea323e..26a65a47471d 100644 --- a/libs/input/PointerControllerContext.h +++ b/libs/input/PointerControllerContext.h @@ -21,7 +21,6 @@ #include <gui/DisplayEventReceiver.h> #include <input/DisplayViewport.h> #include <input/Input.h> -#include <ui/DisplayInfo.h> #include <utils/BitSet.h> #include <utils/Looper.h> #include <utils/RefBase.h> diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java index 205c1f4b4057..383c93d7716d 100644 --- a/media/java/android/media/AudioDeviceInfo.java +++ b/media/java/android/media/AudioDeviceInfo.java @@ -16,8 +16,10 @@ package android.media; +import android.Manifest; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.RequiresPermission; import android.util.SparseIntArray; import java.lang.annotation.Retention; @@ -161,6 +163,14 @@ public final class AudioDeviceInfo { */ public static final int TYPE_BLE_SPEAKER = 27; + /** + * A device type describing an Echo Canceller loopback Reference. + * This device is only used when capturing with MediaRecorder.AudioSource.ECHO_REFERENCE, + * which requires privileged permission + * {@link android.Manifest.permission#CAPTURE_AUDIO_OUTPUT}. + * @hide */ + @RequiresPermission(Manifest.permission.CAPTURE_AUDIO_OUTPUT) + public static final int TYPE_ECHO_REFERENCE = 28; /** @hide */ @IntDef(flag = false, prefix = "TYPE", value = { @@ -188,7 +198,8 @@ public final class AudioDeviceInfo { TYPE_FM_TUNER, TYPE_TV_TUNER, TYPE_BLE_HEADSET, - TYPE_BLE_SPEAKER} + TYPE_BLE_SPEAKER, + TYPE_ECHO_REFERENCE} ) @Retention(RetentionPolicy.SOURCE) public @interface AudioDeviceType {} @@ -211,7 +222,8 @@ public final class AudioDeviceInfo { TYPE_LINE_DIGITAL, TYPE_IP, TYPE_BUS, - TYPE_BLE_HEADSET} + TYPE_BLE_HEADSET, + TYPE_ECHO_REFERENCE} ) @Retention(RetentionPolicy.SOURCE) public @interface AudioDeviceTypeIn {} @@ -297,6 +309,7 @@ public final class AudioDeviceInfo { case TYPE_BUS: case TYPE_REMOTE_SUBMIX: case TYPE_BLE_HEADSET: + case TYPE_ECHO_REFERENCE: return true; default: return false; @@ -621,6 +634,8 @@ public final class AudioDeviceInfo { INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_BUS, TYPE_BUS); INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_REMOTE_SUBMIX, TYPE_REMOTE_SUBMIX); INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_BLE_HEADSET, TYPE_BLE_HEADSET); + INT_TO_EXT_DEVICE_MAPPING.put(AudioSystem.DEVICE_IN_ECHO_REFERENCE, TYPE_ECHO_REFERENCE); + // privileges mapping to output device EXT_TO_INT_DEVICE_MAPPING = new SparseIntArray(); @@ -678,6 +693,9 @@ public final class AudioDeviceInfo { EXT_TO_INT_INPUT_DEVICE_MAPPING.put( TYPE_REMOTE_SUBMIX, AudioSystem.DEVICE_IN_REMOTE_SUBMIX); EXT_TO_INT_INPUT_DEVICE_MAPPING.put(TYPE_BLE_HEADSET, AudioSystem.DEVICE_IN_BLE_HEADSET); + EXT_TO_INT_INPUT_DEVICE_MAPPING.put( + TYPE_ECHO_REFERENCE, AudioSystem.DEVICE_IN_ECHO_REFERENCE); + } } diff --git a/media/jni/tuner/DemuxClient.cpp b/media/jni/tuner/DemuxClient.cpp index 359ef364083c..78cb5b003a38 100644 --- a/media/jni/tuner/DemuxClient.cpp +++ b/media/jni/tuner/DemuxClient.cpp @@ -216,7 +216,7 @@ Result DemuxClient::disconnectCiCam() { Result DemuxClient::close() { if (mTunerDemux != NULL) { Status s = mTunerDemux->close(); - mDemux = NULL; + mTunerDemux = NULL; return ClientHelper::getServiceSpecificErrorCode(s); } diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp index e51add276647..e8cf63f64572 100644 --- a/native/android/surface_control.cpp +++ b/native/android/surface_control.cpp @@ -27,14 +27,13 @@ #include <gui/SurfaceComposerClient.h> #include <gui/SurfaceControl.h> -#include <ui/HdrCapabilities.h> +#include <ui/DynamicDisplayInfo.h> #include <utils/Timers.h> using namespace android::hardware::configstore; using namespace android::hardware::configstore::V1_0; using namespace android; -using android::hardware::configstore::V1_0::ISurfaceFlingerConfigs; using Transaction = SurfaceComposerClient::Transaction; @@ -72,14 +71,13 @@ static bool getHdrSupport(const sp<SurfaceControl>& surfaceControl) { return false; } - HdrCapabilities hdrCapabilities; - status_t err = client->getHdrCapabilities(display, &hdrCapabilities); - if (err) { + ui::DynamicDisplayInfo info; + if (status_t err = client->getDynamicDisplayInfo(display, &info); err != NO_ERROR) { ALOGE("unable to get hdr capabilities"); - return false; + return err; } - return !hdrCapabilities.getSupportedHdrTypes().empty(); + return !info.hdrCapabilities.getSupportedHdrTypes().empty(); } static bool isDataSpaceValid(const sp<SurfaceControl>& surfaceControl, ADataSpace dataSpace) { diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 3866151982a4..79fbcc376b3c 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -1007,6 +1007,9 @@ <!-- Settings item title to select the default behavior for transcoding if an encodig is not supported by an app. [CHAR LIMIT=85] --> <string name="transcode_default">Assume apps support modern formats</string> + <!-- Settings item title to select whether to show transcoding notifications. [CHAR LIMIT=85] --> + <string name="transcode_notification">Show transcoding notifications</string> + <!-- Services settings screen, setting option name for the user to go to the screen to view running services --> <string name="runningservices_settings_title">Running services</string> <!-- Services settings screen, setting option summary for the user to go to the screen to view running services --> diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 2faca8dbdcbf..fbe58c505662 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -334,6 +334,11 @@ </intent-filter> </receiver> + <activity android:name=".screenshot.LongScreenshotActivity" + android:theme="@android:style/Theme.DeviceDefault.NoActionBar" + android:process=":screenshot" + android:finishOnTaskLaunch="true" /> + <activity android:name=".screenrecord.ScreenRecordDialog" android:theme="@style/ScreenRecord" android:showForAllUsers="true" diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml index a8ef7c346b95..23f34251b812 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded.xml @@ -73,7 +73,7 @@ android:id="@+id/qs_edge_guideline" android:layout_width="wrap_content" android:layout_height="wrap_content" - systemui:layout_constraintGuide_percent="0.5" + systemui:layout_constraintGuide_percent="0.4" android:orientation="vertical"/> <com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java index d8ca63960b30..9686c91f2c31 100644 --- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java @@ -473,7 +473,8 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon @Override public void onOpNoted(int code, int uid, String packageName, - @AppOpsManager.OpFlags int flags, @AppOpsManager.Mode int result) { + String attributionTag, @AppOpsManager.OpFlags int flags, + @AppOpsManager.Mode int result) { if (DEBUG) { Log.w(TAG, "Noted op: " + code + " with result " + AppOpsManager.MODE_NAMES[result] + " for package " + packageName); diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java index 2b362b94d1f5..8d2639d4cdd0 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java @@ -22,6 +22,7 @@ import com.android.systemui.ForegroundServicesDialog; import com.android.systemui.keyguard.WorkLockActivity; import com.android.systemui.people.PeopleSpaceActivity; import com.android.systemui.screenrecord.ScreenRecordDialog; +import com.android.systemui.screenshot.LongScreenshotActivity; import com.android.systemui.settings.brightness.BrightnessDialog; import com.android.systemui.statusbar.tv.notifications.TvNotificationPanelActivity; import com.android.systemui.tuner.TunerActivity; @@ -99,4 +100,10 @@ public abstract class DefaultActivityBinder { @IntoMap @ClassKey(PeopleSpaceActivity.class) public abstract Activity bindPeopleSpaceActivity(PeopleSpaceActivity activity); + + /** Inject into LongScreenshotActivity. */ + @Binds + @IntoMap + @ClassKey(LongScreenshotActivity.class) + public abstract Activity bindLongScreenshotActivity(LongScreenshotActivity activity); } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java new file mode 100644 index 000000000000..a6433ae94b2b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2021 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.screenshot; + +import android.app.Activity; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.os.UserHandle; +import android.text.TextUtils; +import android.util.Log; +import android.view.View; +import android.widget.ImageView; + +import com.android.internal.logging.UiEventLogger; +import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; + +import java.util.concurrent.Executor; + +import javax.inject.Inject; + +/** + * LongScreenshotActivity acquires bitmap data for a long screenshot and lets the user trim the top + * and bottom before saving/sharing/editing. + */ +public class LongScreenshotActivity extends Activity { + private static final String TAG = "LongScreenshotActivity"; + + private final UiEventLogger mUiEventLogger; + private final ScrollCaptureController mScrollCaptureController; + + private ImageView mPreview; + private View mSave; + private View mCancel; + private View mEdit; + private View mShare; + private CropView mCropView; + private MagnifierView mMagnifierView; + + private enum PendingAction { + SHARE, + EDIT, + SAVE + } + + @Inject + public LongScreenshotActivity(UiEventLogger uiEventLogger, + ImageExporter imageExporter, + @Main Executor mainExecutor, + @Background Executor bgExecutor, + Context context) { + mUiEventLogger = uiEventLogger; + + mScrollCaptureController = new ScrollCaptureController(context, + ScreenshotController.sScrollConnection, mainExecutor, bgExecutor, imageExporter); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.long_screenshot); + + mPreview = findViewById(R.id.preview); + mSave = findViewById(R.id.save); + mCancel = findViewById(R.id.cancel); + mEdit = findViewById(R.id.edit); + mShare = findViewById(R.id.share); + mCropView = findViewById(R.id.crop_view); + mMagnifierView = findViewById(R.id.magnifier); + mCropView.setCropInteractionListener(mMagnifierView); + + mSave.setOnClickListener(this::onClicked); + mCancel.setOnClickListener(this::onClicked); + mEdit.setOnClickListener(this::onClicked); + mShare.setOnClickListener(this::onClicked); + } + + @Override + public void onStart() { + super.onStart(); + if (mPreview.getDrawable() == null) { + doCapture(); + } + } + + private void disableButtons() { + mSave.setEnabled(false); + mCancel.setEnabled(false); + mEdit.setEnabled(false); + mShare.setEnabled(false); + } + + private void doEdit(Uri uri) { + String editorPackage = getString(R.string.config_screenshotEditor); + Intent intent = new Intent(Intent.ACTION_EDIT); + if (!TextUtils.isEmpty(editorPackage)) { + intent.setComponent(ComponentName.unflattenFromString(editorPackage)); + } + intent.setType("image/png"); + intent.setData(uri); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK + | Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + + startActivityAsUser(intent, UserHandle.CURRENT); + finishAndRemoveTask(); + } + + private void doShare(Uri uri) { + Intent intent = new Intent(Intent.ACTION_SEND); + intent.setType("image/png"); + intent.setData(uri); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK + | Intent.FLAG_GRANT_READ_URI_PERMISSION); + Intent sharingChooserIntent = Intent.createChooser(intent, null) + .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_GRANT_READ_URI_PERMISSION); + + startActivityAsUser(sharingChooserIntent, UserHandle.CURRENT); + } + + private void onClicked(View v) { + int id = v.getId(); + v.setPressed(true); + disableButtons(); + if (id == R.id.save) { + startExport(PendingAction.SAVE); + } else if (id == R.id.cancel) { + finishAndRemoveTask(); + } else if (id == R.id.edit) { + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_EDIT); + startExport(PendingAction.EDIT); + } else if (id == R.id.share) { + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_SHARE); + startExport(PendingAction.SHARE); + } + } + + private void startExport(PendingAction action) { + mScrollCaptureController.startExport(mCropView.getTopBoundary(), + mCropView.getBottomBoundary(), new ScrollCaptureController.ExportCallback() { + @Override + public void onError() { + Log.e(TAG, "Error exporting image data."); + } + + @Override + public void onExportComplete(Uri outputUri) { + switch (action) { + case EDIT: + doEdit(outputUri); + break; + case SHARE: + doShare(outputUri); + break; + case SAVE: + // Nothing more to do + finishAndRemoveTask(); + break; + } + } + }); + } + + private void doCapture() { + mScrollCaptureController.start(new ScrollCaptureController.ScrollCaptureCallback() { + @Override + public void onError() { + Log.e(TAG, "Error!"); + finishAndRemoveTask(); + } + + @Override + public void onComplete(ImageTileSet imageTileSet) { + Log.i(TAG, "Got tiles " + imageTileSet.getWidth() + " x " + + imageTileSet.getHeight()); + mPreview.setImageDrawable(imageTileSet.getDrawable()); + mMagnifierView.setImageTileset(imageTileSet); + mCropView.animateBoundaryTo(CropView.CropBoundary.BOTTOM, 0.5f); + } + }); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index 953b40b6e17b..31c693bdde1f 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -41,6 +41,7 @@ import android.app.Notification; import android.app.WindowContext; import android.content.ComponentName; import android.content.Context; +import android.content.Intent; import android.content.pm.ActivityInfo; import android.graphics.Bitmap; import android.graphics.Insets; @@ -100,6 +101,8 @@ import javax.inject.Inject; public class ScreenshotController { private static final String TAG = logTag(ScreenshotController.class); + public static ScrollCaptureClient.Connection sScrollConnection; + /** * POD used in the AsyncTask which saves an image in the background. */ @@ -597,21 +600,12 @@ public class ScreenshotController { } private void runScrollCapture(ScrollCaptureClient.Connection connection) { - cancelTimeout(); - ScrollCaptureController controller = new ScrollCaptureController(mContext, connection, - mMainExecutor, mBgExecutor, mImageExporter, mUiEventLogger); - controller.attach(mWindow); - controller.start(new TakeScreenshotService.RequestCallback() { - @Override - public void reportError() { - } + sScrollConnection = connection; // For LongScreenshotActivity to pick up. - @Override - public void onFinish() { - Log.d(TAG, "onFinish from ScrollCaptureController"); - finishDismiss(); - } - }); + Intent intent = new Intent(mContext, LongScreenshotActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); + mContext.startActivity(intent); + dismissScreenshot(false); } /** diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java index ad5e637b189e..863116a22ee4 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java @@ -16,29 +16,16 @@ package com.android.systemui.screenshot; -import android.annotation.IdRes; import android.annotation.UiThread; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; import android.graphics.Rect; import android.net.Uri; -import android.os.UserHandle; import android.provider.Settings; -import android.text.TextUtils; import android.util.Log; -import android.view.View; -import android.view.ViewTreeObserver.InternalInsetsInfo; -import android.view.ViewTreeObserver.OnComputeInternalInsetsListener; -import android.view.Window; -import android.widget.ImageView; - -import com.android.internal.logging.UiEventLogger; -import com.android.systemui.R; + import com.android.systemui.screenshot.ScrollCaptureClient.CaptureResult; import com.android.systemui.screenshot.ScrollCaptureClient.Connection; import com.android.systemui.screenshot.ScrollCaptureClient.Session; -import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback; import com.google.common.util.concurrent.ListenableFuture; @@ -50,7 +37,7 @@ import java.util.concurrent.Executor; /** * Interaction controller between the UI and ScrollCaptureClient. */ -public class ScrollCaptureController implements OnComputeInternalInsetsListener { +public class ScrollCaptureController { private static final String TAG = "ScrollCaptureController"; private static final float MAX_PAGES_DEFAULT = 3f; @@ -64,13 +51,6 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener private boolean mAtTopEdge; private Session mSession; - // TODO: Support saving without additional action. - private enum PendingAction { - SHARE, - EDIT, - SAVE - } - public static final int MAX_HEIGHT = 12000; private final Connection mConnection; @@ -80,172 +60,59 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener private final Executor mBgExecutor; private final ImageExporter mImageExporter; private final ImageTileSet mImageTileSet; - private final UiEventLogger mUiEventLogger; private ZonedDateTime mCaptureTime; private UUID mRequestId; - private RequestCallback mCallback; - private Window mWindow; - private ImageView mPreview; - private View mSave; - private View mCancel; - private View mEdit; - private View mShare; - private CropView mCropView; - private MagnifierView mMagnifierView; + private ScrollCaptureCallback mCaptureCallback; public ScrollCaptureController(Context context, Connection connection, Executor uiExecutor, - Executor bgExecutor, ImageExporter exporter, UiEventLogger uiEventLogger) { + Executor bgExecutor, ImageExporter exporter) { mContext = context; mConnection = connection; mUiExecutor = uiExecutor; mBgExecutor = bgExecutor; mImageExporter = exporter; - mUiEventLogger = uiEventLogger; mImageTileSet = new ImageTileSet(context.getMainThreadHandler()); } /** - * @param window the window to display the preview - */ - public void attach(Window window) { - mWindow = window; - } - - /** * Run scroll capture! * * @param callback request callback to report back to the service */ - public void start(RequestCallback callback) { + public void start(ScrollCaptureCallback callback) { mCaptureTime = ZonedDateTime.now(); mRequestId = UUID.randomUUID(); - mCallback = callback; - - setContentView(R.layout.long_screenshot); - mWindow.getDecorView().getViewTreeObserver() - .addOnComputeInternalInsetsListener(this); - mPreview = findViewById(R.id.preview); - - mSave = findViewById(R.id.save); - mCancel = findViewById(R.id.cancel); - mEdit = findViewById(R.id.edit); - mShare = findViewById(R.id.share); - mCropView = findViewById(R.id.crop_view); - mMagnifierView = findViewById(R.id.magnifier); - mCropView.setCropInteractionListener(mMagnifierView); - - mSave.setOnClickListener(this::onClicked); - mCancel.setOnClickListener(this::onClicked); - mEdit.setOnClickListener(this::onClicked); - mShare.setOnClickListener(this::onClicked); + mCaptureCallback = callback; float maxPages = Settings.Secure.getFloat(mContext.getContentResolver(), SETTING_KEY_MAX_PAGES, MAX_PAGES_DEFAULT); mConnection.start(this::startCapture, maxPages); } - - /** Ensure the entire window is touchable */ - public void onComputeInternalInsets(InternalInsetsInfo inoutInfo) { - inoutInfo.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME); - } - - void disableButtons() { - mSave.setEnabled(false); - mCancel.setEnabled(false); - mEdit.setEnabled(false); - mShare.setEnabled(false); - } - - private void onClicked(View v) { - Log.d(TAG, "button clicked!"); - - int id = v.getId(); - v.setPressed(true); - disableButtons(); - if (id == R.id.save) { - startExport(PendingAction.SAVE); - } else if (id == R.id.cancel) { - doFinish(); - } else if (id == R.id.edit) { - mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_EDIT); - startExport(PendingAction.EDIT); - } else if (id == R.id.share) { - mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_SHARE); - startExport(PendingAction.SHARE); - } - } - - private void doFinish() { - mPreview.setImageDrawable(null); - mMagnifierView.setImageTileset(null); - mImageTileSet.clear(); - mCallback.onFinish(); - mWindow.getDecorView().getViewTreeObserver() - .removeOnComputeInternalInsetsListener(this); - } - - private void startExport(PendingAction action) { + /** + * @param topCrop [0,1) fraction of the top of the image to be cropped out. + * @param bottomCrop (0, 1] fraction to be cropped out, e.g. 0.7 will crop out the bottom 30%. + */ + public void startExport(float topCrop, float bottomCrop, ExportCallback callback) { Rect croppedPortion = new Rect( 0, - (int) (mImageTileSet.getHeight() * mCropView.getTopBoundary()), + (int) (mImageTileSet.getHeight() * topCrop), mImageTileSet.getWidth(), - (int) (mImageTileSet.getHeight() * mCropView.getBottomBoundary())); + (int) (mImageTileSet.getHeight() * bottomCrop)); ListenableFuture<ImageExporter.Result> exportFuture = mImageExporter.export( mBgExecutor, mRequestId, mImageTileSet.toBitmap(croppedPortion), mCaptureTime); exportFuture.addListener(() -> { try { ImageExporter.Result result = exportFuture.get(); - if (action == PendingAction.EDIT) { - doEdit(result.uri); - } else if (action == PendingAction.SHARE) { - doShare(result.uri); - } - doFinish(); + callback.onExportComplete(result.uri); } catch (InterruptedException | ExecutionException e) { Log.e(TAG, "failed to export", e); - mCallback.onFinish(); + callback.onError(); } }, mUiExecutor); } - private void doEdit(Uri uri) { - String editorPackage = mContext.getString(R.string.config_screenshotEditor); - Intent intent = new Intent(Intent.ACTION_EDIT); - if (!TextUtils.isEmpty(editorPackage)) { - intent.setComponent(ComponentName.unflattenFromString(editorPackage)); - } - intent.setType("image/png"); - intent.setData(uri); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK - | Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); - - mContext.startActivityAsUser(intent, UserHandle.CURRENT); - } - - private void doShare(Uri uri) { - Intent intent = new Intent(Intent.ACTION_SEND); - intent.setType("image/png"); - intent.setData(uri); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK - | Intent.FLAG_GRANT_READ_URI_PERMISSION); - Intent sharingChooserIntent = Intent.createChooser(intent, null) - .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_GRANT_READ_URI_PERMISSION); - - mContext.startActivityAsUser(sharingChooserIntent, UserHandle.CURRENT); - } - - private void setContentView(@IdRes int id) { - mWindow.setContentView(id); - } - - <T extends View> T findViewById(@IdRes int res) { - return mWindow.findViewById(res); - } - - private void onCaptureResult(CaptureResult result) { Log.d(TAG, "onCaptureResult: " + result); boolean emptyResult = result.captured.height() == 0; @@ -327,11 +194,26 @@ public class ScrollCaptureController implements OnComputeInternalInsetsListener Log.d(TAG, "afterCaptureComplete"); if (mImageTileSet.isEmpty()) { - session.end(mCallback::onFinish); + mCaptureCallback.onError(); } else { - mPreview.setImageDrawable(mImageTileSet.getDrawable()); - mMagnifierView.setImageTileset(mImageTileSet); - mCropView.animateBoundaryTo(CropView.CropBoundary.BOTTOM, 0.5f); + mCaptureCallback.onComplete(mImageTileSet); } } + + /** + * Callback for image capture completion or error. + */ + public interface ScrollCaptureCallback { + void onComplete(ImageTileSet imageTileSet); + void onError(); + } + + /** + * Callback for image export completion or error. + */ + public interface ExportCallback { + void onExportComplete(Uri outputUri); + void onError(); + } + } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java index 73c7fd1b64a3..f21771a89c9e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java @@ -128,15 +128,6 @@ public class NotificationFilter { // this is a foreground-service disclosure for a user that does not need to show one return true; } - if (getFsc().isSystemAlertNotification(sbn)) { - final String[] apps = sbn.getNotification().extras.getStringArray( - Notification.EXTRA_FOREGROUND_APPS); - if (apps != null && apps.length >= 1) { - if (!getFsc().isSystemAlertWarningNeeded(sbn.getUserId(), apps[0])) { - return true; - } - } - } if (mIsMediaFlagEnabled && isMediaNotification(sbn)) { return true; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java index 998ae9e55313..3a87f6853bcf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinator.java @@ -95,18 +95,6 @@ public class AppOpsCoordinator implements Coordinator { sbn.getUser().getIdentifier())) { return true; } - - // Filters out system alert notifications when unneeded - if (mForegroundServiceController.isSystemAlertNotification(sbn)) { - final String[] apps = sbn.getNotification().extras.getStringArray( - Notification.EXTRA_FOREGROUND_APPS); - if (apps != null && apps.length >= 1) { - if (!mForegroundServiceController.isSystemAlertWarningNeeded( - sbn.getUser().getIdentifier(), apps[0])) { - return true; - } - } - } return false; } }; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index f4ffb207e665..615ff79feb1e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -868,25 +868,16 @@ public class NotificationPanelViewController extends PanelViewController { public void updateResources() { int qsWidth = mResources.getDimensionPixelSize(R.dimen.qs_panel_width); - ViewGroup.LayoutParams lp = mQsFrame.getLayoutParams(); - if (lp.width != qsWidth) { - lp.width = qsWidth; - mQsFrame.setLayoutParams(lp); - } - int panelWidth = mResources.getDimensionPixelSize(R.dimen.notification_panel_width); - lp = mNotificationStackScrollLayoutController.getLayoutParams(); - if (lp.width != panelWidth) { - lp.width = panelWidth; - mNotificationStackScrollLayoutController.setLayoutParams(lp); - } - // In order to change the constraints at runtime, all children of the Constraint Layout - // must have ids. + // To change the constraints at runtime, all children of the ConstraintLayout must have ids ensureAllViewsHaveIds(mNotificationContainerParent); ConstraintSet constraintSet = new ConstraintSet(); constraintSet.clone(mNotificationContainerParent); if (Utils.shouldUseSplitNotificationShade(mFeatureFlags, mResources)) { + // width = 0 to take up all available space within constraints + qsWidth = 0; + panelWidth = 0; constraintSet.connect(R.id.qs_frame, END, R.id.qs_edge_guideline, END); constraintSet.connect( R.id.notification_stack_scroller, START, @@ -895,6 +886,8 @@ public class NotificationPanelViewController extends PanelViewController { constraintSet.connect(R.id.qs_frame, END, PARENT_ID, END); constraintSet.connect(R.id.notification_stack_scroller, START, PARENT_ID, START); } + constraintSet.getConstraint(R.id.notification_stack_scroller).layout.mWidth = panelWidth; + constraintSet.getConstraint(R.id.qs_frame).layout.mWidth = qsWidth; constraintSet.applyTo(mNotificationContainerParent); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java index e394ebc65a6b..0c9ed661925c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java @@ -18,14 +18,12 @@ package com.android.systemui.statusbar.phone; import android.app.Fragment; import android.content.Context; -import android.content.res.Configuration; import android.graphics.Canvas; import android.util.AttributeSet; import android.view.View; import android.view.WindowInsets; import android.widget.FrameLayout; -import androidx.annotation.DimenRes; import androidx.constraintlayout.widget.ConstraintLayout; import com.android.systemui.R; @@ -83,22 +81,6 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout } @Override - protected void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - reloadWidth(mQsFrame, R.dimen.qs_panel_width); - reloadWidth(mStackScroller, R.dimen.notification_panel_width); - } - - /** - * Loads the given width resource and sets it on the given View. - */ - private void reloadWidth(View view, @DimenRes int width) { - LayoutParams params = (LayoutParams) view.getLayoutParams(); - params.width = getResources().getDimensionPixelSize(width); - view.setLayoutParams(params); - } - - @Override public WindowInsets onApplyWindowInsets(WindowInsets insets) { mBottomPadding = insets.getStableInsetBottom(); setPadding(0, 0, 0, mBottomPadding); diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java index 6c3b37edbd15..ba21afdfab0f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java @@ -309,17 +309,6 @@ public class ForegroundServiceControllerTest extends SysuiTestCase { } @Test - public void testOverlayPredicate() { - StatusBarNotification sbn_user1_app1 = makeMockSBN(USERID_ONE, "com.example.app1", - 5000, "monkeys", Notification.FLAG_AUTO_CANCEL); - StatusBarNotification sbn_user1_overlay = makeMockSBN(USERID_ONE, "android", - 0, "AlertWindowNotification", Notification.FLAG_NO_CLEAR); - - assertTrue(mFsc.isSystemAlertNotification(sbn_user1_overlay)); - assertFalse(mFsc.isSystemAlertNotification(sbn_user1_app1)); - } - - @Test public void testNoNotifsNorAppOps_noSystemAlertWarningRequired() { // no notifications nor app op signals that this package/userId requires system alert // warning diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java index bc322f7f18fc..97cb8736f01c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java @@ -69,6 +69,7 @@ import java.util.List; @TestableLooper.RunWithLooper public class AppOpsControllerTest extends SysuiTestCase { private static final String TEST_PACKAGE_NAME = "test"; + private static final String TEST_ATTRIBUTION_NAME = "attribution"; private static final int TEST_UID = UserHandle.getUid(0, 0); private static final int TEST_UID_OTHER = UserHandle.getUid(1, 0); private static final int TEST_UID_NON_USER_SENSITIVE = UserHandle.getUid(2, 0); @@ -164,7 +165,7 @@ public class AppOpsControllerTest extends SysuiTestCase { mController.onOpActiveChanged( AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, - AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); + TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); mTestableLooper.processAllMessages(); verify(mCallback).onActiveStateChanged(AppOpsManager.OP_RECORD_AUDIO, TEST_UID, TEST_PACKAGE_NAME, true); @@ -218,8 +219,8 @@ public class AppOpsControllerTest extends SysuiTestCase { mController.onOpActiveChanged(AppOpsManager.OP_CAMERA, TEST_UID, TEST_PACKAGE_NAME, true); mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, - TEST_UID, TEST_PACKAGE_NAME, AppOpsManager.OP_FLAG_SELF, - AppOpsManager.MODE_ALLOWED); + TEST_UID, TEST_PACKAGE_NAME, TEST_ATTRIBUTION_NAME, + AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); assertEquals(3, mController.getActiveAppOps().size()); } @@ -230,8 +231,8 @@ public class AppOpsControllerTest extends SysuiTestCase { mController.onOpActiveChanged(AppOpsManager.OP_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true); mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, - TEST_UID, TEST_PACKAGE_NAME, AppOpsManager.OP_FLAG_SELF, - AppOpsManager.MODE_ALLOWED); + TEST_UID, TEST_PACKAGE_NAME, TEST_ATTRIBUTION_NAME, + AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); assertEquals(2, mController.getActiveAppOpsForUser(UserHandle.getUserId(TEST_UID)).size()); assertEquals(1, @@ -262,7 +263,7 @@ public class AppOpsControllerTest extends SysuiTestCase { public void opNotedScheduledForRemoval() { mController.setBGHandler(mMockHandler); mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, - AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); + TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); verify(mMockHandler).scheduleRemoval(any(AppOpItem.class), anyLong()); } @@ -274,7 +275,7 @@ public class AppOpsControllerTest extends SysuiTestCase { mController.onOpActiveChanged(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true); mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, - AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); + TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); assertFalse(mController.getActiveAppOps().isEmpty()); mController.setListening(false); @@ -288,9 +289,9 @@ public class AppOpsControllerTest extends SysuiTestCase { mController.setBGHandler(mMockHandler); mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, - AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); + TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, - AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); + TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); // Only one post to notify subscribers verify(mMockHandler, times(1)).post(any()); @@ -304,9 +305,9 @@ public class AppOpsControllerTest extends SysuiTestCase { mController.setBGHandler(mMockHandler); mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, - AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); + TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, - AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); + TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); // Only one post to notify subscribers verify(mMockHandler, times(2)).scheduleRemoval(any(), anyLong()); @@ -324,7 +325,7 @@ public class AppOpsControllerTest extends SysuiTestCase { AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true); mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, - AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); + TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); // Check that we "scheduled" the removal. Don't actually schedule until we are ready to // process messages at a later time. @@ -353,7 +354,7 @@ public class AppOpsControllerTest extends SysuiTestCase { mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback); mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, - AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); + TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); mController.onOpActiveChanged( AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true); @@ -382,7 +383,7 @@ public class AppOpsControllerTest extends SysuiTestCase { mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback); mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, - AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); + TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); mController.onOpActiveChanged( AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true); @@ -400,7 +401,7 @@ public class AppOpsControllerTest extends SysuiTestCase { AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true); mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, - AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); + TEST_ATTRIBUTION_NAME, AppOpsManager.OP_FLAG_SELF, AppOpsManager.MODE_ALLOWED); mTestableLooper.processAllMessages(); verify(mCallback).onActiveStateChanged( diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java index 05cf33a3729c..b493b9a6bd65 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationFilterTest.java @@ -171,62 +171,10 @@ public class NotificationFilterTest extends SysuiTestCase { } @Test - public void testSuppressSystemAlertNotification() { - when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false); - when(mFsc.isSystemAlertNotification(any())).thenReturn(true); - StatusBarNotification sbn = mRow.getEntry().getSbn(); - Bundle bundle = new Bundle(); - bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[]{"something"}); - sbn.getNotification().extras = bundle; - - assertTrue(mNotificationFilter.shouldFilterOut(mRow.getEntry())); - } - - @Test - public void testDoNotSuppressSystemAlertNotification() { - StatusBarNotification sbn = mRow.getEntry().getSbn(); - Bundle bundle = new Bundle(); - bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[]{"something"}); - sbn.getNotification().extras = bundle; - - when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true); - when(mFsc.isSystemAlertNotification(any())).thenReturn(true); - - assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry())); - - when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true); - when(mFsc.isSystemAlertNotification(any())).thenReturn(false); - - assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry())); - - when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false); - when(mFsc.isSystemAlertNotification(any())).thenReturn(false); - - assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry())); - } - - @Test - public void testDoNotSuppressMalformedSystemAlertNotification() { - when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(true); - - // missing extra - assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry())); - - StatusBarNotification sbn = mRow.getEntry().getSbn(); - Bundle bundle = new Bundle(); - bundle.putStringArray(Notification.EXTRA_FOREGROUND_APPS, new String[]{}); - sbn.getNotification().extras = bundle; - - // extra missing values - assertFalse(mNotificationFilter.shouldFilterOut(mRow.getEntry())); - } - - @Test public void testShouldFilterHiddenNotifications() { initStatusBarNotification(false); // setup when(mFsc.isSystemAlertWarningNeeded(anyInt(), anyString())).thenReturn(false); - when(mFsc.isSystemAlertNotification(any())).thenReturn(false); // test should filter out hidden notifications: // hidden diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java index 09546216183f..278456859735 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/AppOpsCoordinatorTest.java @@ -115,41 +115,6 @@ public class AppOpsCoordinatorTest extends SysuiTestCase { } @Test - public void filterTest_systemAlertNotificationUnnecessary() { - // GIVEN the alert notification isn't needed for this user - final Bundle extras = new Bundle(); - extras.putStringArray(Notification.EXTRA_FOREGROUND_APPS, - new String[]{TEST_PKG}); - mEntryBuilder.modifyNotification(mContext) - .setExtras(extras); - NotificationEntry entry = mEntryBuilder.build(); - StatusBarNotification sbn = entry.getSbn(); - when(mForegroundServiceController.isSystemAlertWarningNeeded(sbn.getUserId(), TEST_PKG)) - .thenReturn(false); - - // GIVEN the notification is a system alert notification + not a disclosure notification - when(mForegroundServiceController.isSystemAlertNotification(sbn)).thenReturn(true); - when(mForegroundServiceController.isDisclosureNotification(sbn)).thenReturn(false); - - - // THEN filter out the notification - assertTrue(mForegroundFilter.shouldFilterOut(entry, 0)); - } - - @Test - public void filterTest_doNotFilter() { - NotificationEntry entry = mEntryBuilder.build(); - StatusBarNotification sbn = entry.getSbn(); - - // GIVEN the notification isn't a system alert notification nor a disclosure notification - when(mForegroundServiceController.isSystemAlertNotification(sbn)).thenReturn(false); - when(mForegroundServiceController.isDisclosureNotification(sbn)).thenReturn(false); - - // THEN don't filter out the notification - assertFalse(mForegroundFilter.shouldFilterOut(entry, 0)); - } - - @Test public void testIncludeFGSInSection_importanceDefault() { // GIVEN the notification represents a colorized foreground service with > min importance mEntryBuilder diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java index 8cb898443fd2..dd31f522b94d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java @@ -33,6 +33,7 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.annotation.IdRes; import android.app.ActivityManager; import android.app.StatusBarManager; import android.content.res.Configuration; @@ -269,8 +270,9 @@ public class NotificationPanelViewTest extends SysuiTestCase { when(mView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame); when(mView.findViewById(R.id.keyguard_status_view)) .thenReturn(mock(KeyguardStatusView.class)); - when(mView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar); mNotificationContainerParent = new NotificationsQuickSettingsContainer(getContext(), null); + mNotificationContainerParent.addView(newViewWithId(R.id.qs_frame)); + mNotificationContainerParent.addView(newViewWithId(R.id.notification_stack_scroller)); when(mView.findViewById(R.id.notification_container_parent)) .thenReturn(mNotificationContainerParent); FlingAnimationUtils.Builder flingAnimationUtilsBuilder = new FlingAnimationUtils.Builder( @@ -300,10 +302,6 @@ public class NotificationPanelViewTest extends SysuiTestCase { .thenReturn(mKeyguardClockSwitchController); when(mKeyguardStatusViewComponent.getKeyguardStatusViewController()) .thenReturn(mKeyguardStatusViewController); - when(mQsFrame.getLayoutParams()).thenReturn( - new ViewGroup.LayoutParams(600, 400)); - when(mNotificationStackScrollLayoutController.getLayoutParams()).thenReturn( - new ViewGroup.LayoutParams(600, 400)); mNotificationPanelViewController = new NotificationPanelViewController(mView, mResources, @@ -451,7 +449,8 @@ public class NotificationPanelViewTest extends SysuiTestCase { @Test public void testAllChildrenOfNotificationContainer_haveIds() { - enableDualPaneShade(); + enableSplitShade(); + mNotificationContainerParent.removeAllViews(); mNotificationContainerParent.addView(newViewWithId(1)); mNotificationContainerParent.addView(newViewWithId(View.NO_ID)); @@ -464,35 +463,59 @@ public class NotificationPanelViewTest extends SysuiTestCase { @Test public void testSinglePaneShadeLayout_isAlignedToParent() { when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(false); - mNotificationContainerParent.addView(newViewWithId(R.id.qs_frame)); - mNotificationContainerParent.addView(newViewWithId(R.id.notification_stack_scroller)); mNotificationPanelViewController.updateResources(); - ConstraintSet constraintSet = new ConstraintSet(); - constraintSet.clone(mNotificationContainerParent); - ConstraintSet.Layout qsFrameLayout = constraintSet.getConstraint(R.id.qs_frame).layout; - ConstraintSet.Layout stackScrollerLayout = constraintSet.getConstraint( - R.id.notification_stack_scroller).layout; - assertThat(qsFrameLayout.endToEnd).isEqualTo(ConstraintSet.PARENT_ID); - assertThat(stackScrollerLayout.startToStart).isEqualTo(ConstraintSet.PARENT_ID); + assertThat(getConstraintSetLayout(R.id.qs_frame).endToEnd) + .isEqualTo(ConstraintSet.PARENT_ID); + assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).startToStart) + .isEqualTo(ConstraintSet.PARENT_ID); } @Test public void testSplitShadeLayout_isAlignedToGuideline() { - enableDualPaneShade(); - mNotificationContainerParent.addView(newViewWithId(R.id.qs_frame)); - mNotificationContainerParent.addView(newViewWithId(R.id.notification_stack_scroller)); + enableSplitShade(); mNotificationPanelViewController.updateResources(); - ConstraintSet constraintSet = new ConstraintSet(); - constraintSet.clone(mNotificationContainerParent); - ConstraintSet.Layout qsFrameLayout = constraintSet.getConstraint(R.id.qs_frame).layout; - ConstraintSet.Layout stackScrollerLayout = constraintSet.getConstraint( - R.id.notification_stack_scroller).layout; - assertThat(qsFrameLayout.endToEnd).isEqualTo(R.id.qs_edge_guideline); - assertThat(stackScrollerLayout.startToStart).isEqualTo(R.id.qs_edge_guideline); + assertThat(getConstraintSetLayout(R.id.qs_frame).endToEnd) + .isEqualTo(R.id.qs_edge_guideline); + assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).startToStart) + .isEqualTo(R.id.qs_edge_guideline); + } + + @Test + public void testSinglePaneShadeLayout_childrenHaveConstantWidth() { + when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(false); + + mNotificationPanelViewController.updateResources(); + + assertThat(getConstraintSetLayout(R.id.qs_frame).mWidth) + .isEqualTo(mResources.getDimensionPixelSize(R.dimen.qs_panel_width)); + assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).mWidth) + .isEqualTo(mResources.getDimensionPixelSize(R.dimen.notification_panel_width)); + } + + @Test + public void testSplitShadeLayout_childrenHaveZeroWidth() { + enableSplitShade(); + + mNotificationPanelViewController.updateResources(); + + assertThat(getConstraintSetLayout(R.id.qs_frame).mWidth).isEqualTo(0); + assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).mWidth).isEqualTo(0); + } + + @Test + public void testOnDragDownEvent_horizontalTranslationIsZeroForSplitShade() { + when(mNotificationStackScrollLayoutController.getWidth()).thenReturn(350f); + when(mView.getWidth()).thenReturn(800); + enableSplitShade(); + + onTouchEvent(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, + 200f /* x position */, 0f, 0)); + + verify(mQsFrame).setTranslationX(0); } @Test @@ -538,19 +561,13 @@ public class NotificationPanelViewTest extends SysuiTestCase { return view; } - @Test - public void testOnDragDownEvent_horizontalTranslationIsZeroForDualPaneShade() { - when(mNotificationStackScrollLayoutController.getWidth()).thenReturn(350f); - when(mView.getWidth()).thenReturn(800); - enableDualPaneShade(); - - onTouchEvent(MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, - 200f /* x position */, 0f, 0)); - - verify(mQsFrame).setTranslationX(0); + private ConstraintSet.Layout getConstraintSetLayout(@IdRes int id) { + ConstraintSet constraintSet = new ConstraintSet(); + constraintSet.clone(mNotificationContainerParent); + return constraintSet.getConstraint(id).layout; } - private void enableDualPaneShade() { + private void enableSplitShade() { when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true); when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true); } diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java index edaf6a90bd4a..84e429dc83e4 100644 --- a/services/core/java/com/android/server/SensorPrivacyService.java +++ b/services/core/java/com/android/server/SensorPrivacyService.java @@ -16,6 +16,7 @@ package com.android.server; +import static android.Manifest.permission.MANAGE_SENSOR_PRIVACY; import static android.app.ActivityManager.RunningServiceInfo; import static android.app.ActivityManager.RunningTaskInfo; import static android.app.AppOpsManager.MODE_ALLOWED; @@ -197,18 +198,20 @@ public final class SensorPrivacyService extends SystemService { Intent.EXTRA_USER)).getIdentifier(), intent.getIntExtra(EXTRA_SENSOR, UNKNOWN), false); } - }, new IntentFilter(ACTION_DISABLE_INDIVIDUAL_SENSOR_PRIVACY)); + }, new IntentFilter(ACTION_DISABLE_INDIVIDUAL_SENSOR_PRIVACY), + MANAGE_SENSOR_PRIVACY, null); } @Override - public void onOpStarted(int code, int uid, String packageName, + public void onOpStarted(int code, int uid, String packageName, String attributionTag, @AppOpsManager.OpFlags int flags, @AppOpsManager.Mode int result) { - onOpNoted(code, uid, packageName, flags, result); + onOpNoted(code, uid, packageName, attributionTag, flags, result); } @Override public void onOpNoted(int code, int uid, String packageName, - @AppOpsManager.OpFlags int flags, @AppOpsManager.Mode int result) { + String attributionTag, @AppOpsManager.OpFlags int flags, + @AppOpsManager.Mode int result) { if (result != MODE_ALLOWED || (flags & AppOpsManager.OP_FLAGS_ALL_TRUSTED) == 0) { return; } @@ -459,12 +462,12 @@ public final class SensorPrivacyService extends SystemService { */ private void enforceSensorPrivacyPermission() { if (mContext.checkCallingOrSelfPermission( - android.Manifest.permission.MANAGE_SENSOR_PRIVACY) == PERMISSION_GRANTED) { + MANAGE_SENSOR_PRIVACY) == PERMISSION_GRANTED) { return; } throw new SecurityException( "Changing sensor privacy requires the following permission: " - + android.Manifest.permission.MANAGE_SENSOR_PRIVACY); + + MANAGE_SENSOR_PRIVACY); } /** diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index a4ff230b0678..5550999b2405 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -2111,7 +2111,8 @@ public final class ActiveServices { private final AppOpsManager.OnOpNotedListener mOpNotedCallback = new AppOpsManager.OnOpNotedListener() { @Override - public void onOpNoted(int op, int uid, String pkgName, int flags, int result) { + public void onOpNoted(int op, int uid, String pkgName, + String attributionTag, int flags, int result) { incrementOpCountIfNeeded(op, uid, result); } }; @@ -2119,7 +2120,8 @@ public final class ActiveServices { private final AppOpsManager.OnOpStartedListener mOpStartedCallback = new AppOpsManager.OnOpStartedListener() { @Override - public void onOpStarted(int op, int uid, String pkgName, int flags, + public void onOpStarted(int op, int uid, String pkgName, + String attributionTag, int flags, int result) { incrementOpCountIfNeeded(op, uid, result); } diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index c92abd48fdc2..a776458d63c5 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -3124,7 +3124,7 @@ public class AppOpsService extends IAppOpsService.Stub { final Ops ops = getOpsLocked(uid, packageName, attributionTag, bypass, true /* edit */); if (ops == null) { - scheduleOpNotedIfNeededLocked(code, uid, packageName, flags, + scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags, AppOpsManager.MODE_IGNORED); if (DEBUG) Slog.d(TAG, "noteOperation: no op for code " + code + " uid " + uid + " package " + packageName); @@ -3142,7 +3142,7 @@ public class AppOpsService extends IAppOpsService.Stub { final UidState uidState = ops.uidState; if (isOpRestrictedLocked(uid, code, packageName, bypass)) { attributedOp.rejected(uidState.state, flags); - scheduleOpNotedIfNeededLocked(code, uid, packageName, flags, + scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags, AppOpsManager.MODE_IGNORED); return AppOpsManager.MODE_IGNORED; } @@ -3155,7 +3155,8 @@ public class AppOpsService extends IAppOpsService.Stub { + switchCode + " (" + code + ") uid " + uid + " package " + packageName); attributedOp.rejected(uidState.state, flags); - scheduleOpNotedIfNeededLocked(code, uid, packageName, flags, uidMode); + scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags, + uidMode); return uidMode; } } else { @@ -3167,7 +3168,8 @@ public class AppOpsService extends IAppOpsService.Stub { + switchCode + " (" + code + ") uid " + uid + " package " + packageName); attributedOp.rejected(uidState.state, flags); - scheduleOpNotedIfNeededLocked(code, uid, packageName, flags, mode); + scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags, + mode); return mode; } } @@ -3177,7 +3179,7 @@ public class AppOpsService extends IAppOpsService.Stub { + packageName + (attributionTag == null ? "" : "." + attributionTag)); } - scheduleOpNotedIfNeededLocked(code, uid, packageName, flags, + scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags, AppOpsManager.MODE_ALLOWED); attributedOp.accessed(proxyUid, proxyPackageName, proxyAttributionTag, uidState.state, flags); @@ -3580,7 +3582,7 @@ public class AppOpsService extends IAppOpsService.Stub { final Ops ops = getOpsLocked(uid, packageName, attributionTag, bypass, true /* edit */); if (ops == null) { if (!dryRun) { - scheduleOpStartedIfNeededLocked(code, uid, packageName, + scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag, flags, AppOpsManager.MODE_IGNORED); } if (DEBUG) Slog.d(TAG, "startOperation: no op for code " + code + " uid " + uid @@ -3590,7 +3592,7 @@ public class AppOpsService extends IAppOpsService.Stub { final Op op = getOpLocked(ops, code, uid, true); if (isOpRestrictedLocked(uid, code, packageName, bypass)) { if (!dryRun) { - scheduleOpStartedIfNeededLocked(code, uid, packageName, + scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag, flags, AppOpsManager.MODE_IGNORED); } return AppOpsManager.MODE_IGNORED; @@ -3611,7 +3613,8 @@ public class AppOpsService extends IAppOpsService.Stub { } if (!dryRun) { attributedOp.rejected(uidState.state, flags); - scheduleOpStartedIfNeededLocked(code, uid, packageName, flags, uidMode); + scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag, + flags, uidMode); } return uidMode; } @@ -3626,7 +3629,8 @@ public class AppOpsService extends IAppOpsService.Stub { + packageName); if (!dryRun) { attributedOp.rejected(uidState.state, flags); - scheduleOpStartedIfNeededLocked(code, uid, packageName, flags, mode); + scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag, + flags, mode); } return mode; } @@ -3634,7 +3638,7 @@ public class AppOpsService extends IAppOpsService.Stub { if (DEBUG) Slog.d(TAG, "startOperation: allowing code " + code + " uid " + uid + " package " + packageName); if (!dryRun) { - scheduleOpStartedIfNeededLocked(code, uid, packageName, flags, + scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag, flags, AppOpsManager.MODE_ALLOWED); try { attributedOp.started(clientId, proxyUid, proxyPackageName, proxyAttributionTag, @@ -3774,7 +3778,7 @@ public class AppOpsService extends IAppOpsService.Stub { } private void scheduleOpStartedIfNeededLocked(int code, int uid, String pkgName, - @OpFlags int flags, @Mode int result) { + String attributionTag, @OpFlags int flags, @Mode int result) { ArraySet<StartedCallback> dispatchedCallbacks = null; final int callbackListCount = mStartedWatchers.size(); for (int i = 0; i < callbackListCount; i++) { @@ -3799,18 +3803,21 @@ public class AppOpsService extends IAppOpsService.Stub { mHandler.sendMessage(PooledLambda.obtainMessage( AppOpsService::notifyOpStarted, - this, dispatchedCallbacks, code, uid, pkgName, flags, result)); + this, dispatchedCallbacks, code, uid, pkgName, attributionTag, flags, + result)); } private void notifyOpStarted(ArraySet<StartedCallback> callbacks, - int code, int uid, String packageName, @OpFlags int flags, @Mode int result) { + int code, int uid, String packageName, String attributionTag, @OpFlags int flags, + @Mode int result) { final long identity = Binder.clearCallingIdentity(); try { final int callbackCount = callbacks.size(); for (int i = 0; i < callbackCount; i++) { final StartedCallback callback = callbacks.valueAt(i); try { - callback.mCallback.opStarted(code, uid, packageName, flags, result); + callback.mCallback.opStarted(code, uid, packageName, attributionTag, flags, + result); } catch (RemoteException e) { /* do nothing */ } @@ -3821,7 +3828,7 @@ public class AppOpsService extends IAppOpsService.Stub { } private void scheduleOpNotedIfNeededLocked(int code, int uid, String packageName, - @OpFlags int flags, @Mode int result) { + String attributionTag, @OpFlags int flags, @Mode int result) { ArraySet<NotedCallback> dispatchedCallbacks = null; final int callbackListCount = mNotedWatchers.size(); for (int i = 0; i < callbackListCount; i++) { @@ -3842,11 +3849,13 @@ public class AppOpsService extends IAppOpsService.Stub { } mHandler.sendMessage(PooledLambda.obtainMessage( AppOpsService::notifyOpChecked, - this, dispatchedCallbacks, code, uid, packageName, flags, result)); + this, dispatchedCallbacks, code, uid, packageName, attributionTag, flags, + result)); } private void notifyOpChecked(ArraySet<NotedCallback> callbacks, - int code, int uid, String packageName, @OpFlags int flags, @Mode int result) { + int code, int uid, String packageName, String attributionTag, @OpFlags int flags, + @Mode int result) { // There are features watching for checks in our process. The callbacks in // these features may require permissions our remote caller does not have. final long identity = Binder.clearCallingIdentity(); @@ -3855,7 +3864,8 @@ public class AppOpsService extends IAppOpsService.Stub { for (int i = 0; i < callbackCount; i++) { final NotedCallback callback = callbacks.valueAt(i); try { - callback.mCallback.opNoted(code, uid, packageName, flags, result); + callback.mCallback.opNoted(code, uid, packageName, attributionTag, flags, + result); } catch (RemoteException e) { /* do nothing */ } diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java index 5e1df27167c2..198ea3d3f66a 100644 --- a/services/core/java/com/android/server/display/ColorFade.java +++ b/services/core/java/com/android/server/display/ColorFade.java @@ -165,7 +165,7 @@ final class ColorFade { "Failed to take screenshot because internal display is disconnected"); return false; } - boolean isWideColor = SurfaceControl.getActiveColorMode(token) + boolean isWideColor = SurfaceControl.getDynamicDisplayInfo(token).activeColorMode == Display.COLOR_MODE_DISPLAY_P3; // Set mPrepared here so if initialization fails, resources can be cleaned up. diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java index 501533d535d3..40cee66a6791 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java +++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java @@ -466,7 +466,7 @@ final class DisplayDeviceInfo { sb.append(", supportedModes ").append(Arrays.toString(supportedModes)); sb.append(", colorMode ").append(colorMode); sb.append(", supportedColorModes ").append(Arrays.toString(supportedColorModes)); - sb.append(", HdrCapabilities ").append(hdrCapabilities); + sb.append(", hdrCapabilities ").append(hdrCapabilities); sb.append(", allmSupported ").append(allmSupported); sb.append(", gameContentTypeSupported ").append(gameContentTypeSupported); sb.append(", density ").append(densityDpi); diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 3b66236c9f0f..7ce4f066b058 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -100,50 +100,48 @@ final class LocalDisplayAdapter extends DisplayAdapter { private void tryConnectDisplayLocked(long physicalDisplayId) { final IBinder displayToken = SurfaceControl.getPhysicalDisplayToken(physicalDisplayId); if (displayToken != null) { - SurfaceControl.DisplayInfo info = SurfaceControl.getDisplayInfo(displayToken); - if (info == null) { - Slog.w(TAG, "No valid info found for display device " + physicalDisplayId); + SurfaceControl.StaticDisplayInfo staticInfo = + SurfaceControl.getStaticDisplayInfo(displayToken); + if (staticInfo == null) { + Slog.w(TAG, "No valid static info found for display device " + physicalDisplayId); return; } - SurfaceControl.DisplayMode[] displayModes = - SurfaceControl.getDisplayModes(displayToken); - if (displayModes == null) { + SurfaceControl.DynamicDisplayInfo dynamicInfo = + SurfaceControl.getDynamicDisplayInfo(displayToken); + if (dynamicInfo == null) { + Slog.w(TAG, "No valid dynamic info found for display device " + physicalDisplayId); + return; + } + if (dynamicInfo.supportedDisplayModes == null) { // There are no valid modes for this device, so we can't use it Slog.w(TAG, "No valid modes found for display device " + physicalDisplayId); return; } - int activeDisplayMode = SurfaceControl.getActiveDisplayMode(displayToken); - if (activeDisplayMode < 0) { + if (dynamicInfo.activeDisplayModeId < 0) { // There is no active mode, and for now we don't have the // policy to set one. - Slog.w(TAG, "No active mode found for display device " + physicalDisplayId); + Slog.w(TAG, "No valid active mode found for display device " + physicalDisplayId); return; } - int activeColorMode = SurfaceControl.getActiveColorMode(displayToken); - if (activeColorMode < 0) { + if (dynamicInfo.activeColorMode < 0) { // We failed to get the active color mode. We don't bail out here since on the next // configuration pass we'll go ahead and set it to whatever it was set to last (or // COLOR_MODE_NATIVE if this is the first configuration). - Slog.w(TAG, "Unable to get active color mode for display device " + - physicalDisplayId); - activeColorMode = Display.COLOR_MODE_INVALID; + Slog.w(TAG, "No valid active color mode for display device " + physicalDisplayId); + dynamicInfo.activeColorMode = Display.COLOR_MODE_INVALID; } - SurfaceControl.DesiredDisplayModeSpecs modeSpecsSpecs = + SurfaceControl.DesiredDisplayModeSpecs modeSpecs = SurfaceControl.getDesiredDisplayModeSpecs(displayToken); - int[] colorModes = SurfaceControl.getDisplayColorModes(displayToken); - Display.HdrCapabilities hdrCapabilities = - SurfaceControl.getHdrCapabilities(displayToken); LocalDisplayDevice device = mDevices.get(physicalDisplayId); if (device == null) { // Display was added. final boolean isDefaultDisplay = mDevices.size() == 0; - device = new LocalDisplayDevice(displayToken, physicalDisplayId, info, displayModes, - activeDisplayMode, modeSpecsSpecs, colorModes, activeColorMode, - hdrCapabilities, isDefaultDisplay); + device = new LocalDisplayDevice(displayToken, physicalDisplayId, staticInfo, + dynamicInfo, modeSpecs, isDefaultDisplay); mDevices.put(physicalDisplayId, device); sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED); - } else if (device.updateDisplayPropertiesLocked(info, displayModes, activeDisplayMode, - modeSpecsSpecs, colorModes, activeColorMode, hdrCapabilities)) { + } else if (device.updateDisplayPropertiesLocked(staticInfo, dynamicInfo, + modeSpecs)) { sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED); } } else { @@ -195,7 +193,6 @@ final class LocalDisplayAdapter extends DisplayAdapter { private DisplayModeDirector.DesiredDisplayModeSpecs mDisplayModeSpecs = new DisplayModeDirector.DesiredDisplayModeSpecs(); private boolean mDisplayModeSpecsInvalid; - private int mActiveDisplayModeId; private int mActiveColorMode; private Display.HdrCapabilities mHdrCapabilities; private boolean mAllmSupported; @@ -204,8 +201,11 @@ final class LocalDisplayAdapter extends DisplayAdapter { private boolean mGameContentTypeRequested; private boolean mSidekickActive; private SidekickInternal mSidekickInternal; - private SurfaceControl.DisplayInfo mDisplayInfo; - private SurfaceControl.DisplayMode[] mDisplayModes; + private SurfaceControl.StaticDisplayInfo mStaticDisplayInfo; + // The supported display modes according in SurfaceFlinger + private SurfaceControl.DisplayMode[] mSfDisplayModes; + // The active display mode in SurfaceFlinger + private SurfaceControl.DisplayMode mActiveSfDisplayMode; private Spline mSystemBrightnessToNits; private Spline mNitsToHalBrightness; private DisplayDeviceConfig mDisplayDeviceConfig; @@ -214,15 +214,13 @@ final class LocalDisplayAdapter extends DisplayAdapter { new DisplayEventReceiver.FrameRateOverride[0]; LocalDisplayDevice(IBinder displayToken, long physicalDisplayId, - SurfaceControl.DisplayInfo info, SurfaceControl.DisplayMode[] displayModes, - int activeDisplayModeId, SurfaceControl.DesiredDisplayModeSpecs modeSpecs, - int[] colorModes, int activeColorMode, Display.HdrCapabilities hdrCapabilities, - boolean isDefaultDisplay) { + SurfaceControl.StaticDisplayInfo staticDisplayInfo, + SurfaceControl.DynamicDisplayInfo dynamicInfo, + SurfaceControl.DesiredDisplayModeSpecs modeSpecs, boolean isDefaultDisplay) { super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + physicalDisplayId); mPhysicalDisplayId = physicalDisplayId; mIsDefaultDisplay = isDefaultDisplay; - updateDisplayPropertiesLocked(info, displayModes, activeDisplayModeId, modeSpecs, - colorModes, activeColorMode, hdrCapabilities); + updateDisplayPropertiesLocked(staticDisplayInfo, dynamicInfo, modeSpecs); mSidekickInternal = LocalServices.getService(SidekickInternal.class); mBacklightAdapter = new BacklightAdapter(displayToken, isDefaultDisplay); mAllmSupported = SurfaceControl.getAutoLowLatencyModeSupport(displayToken); @@ -241,15 +239,15 @@ final class LocalDisplayAdapter extends DisplayAdapter { /** * Returns true if there is a change. **/ - public boolean updateDisplayPropertiesLocked(SurfaceControl.DisplayInfo info, - SurfaceControl.DisplayMode[] displayModes, - int activeDisplayModeId, SurfaceControl.DesiredDisplayModeSpecs modeSpecs, - int[] colorModes, int activeColorMode, Display.HdrCapabilities hdrCapabilities) { + public boolean updateDisplayPropertiesLocked(SurfaceControl.StaticDisplayInfo staticInfo, + SurfaceControl.DynamicDisplayInfo dynamicInfo, + SurfaceControl.DesiredDisplayModeSpecs modeSpecs) { boolean changed = updateDisplayModesLocked( - displayModes, activeDisplayModeId, modeSpecs); - changed |= updateDisplayInfo(info); - changed |= updateColorModesLocked(colorModes, activeColorMode); - changed |= updateHdrCapabilitiesLocked(hdrCapabilities); + dynamicInfo.supportedDisplayModes, dynamicInfo.activeDisplayModeId, modeSpecs); + changed |= updateStaticInfo(staticInfo); + changed |= updateColorModesLocked(dynamicInfo.supportedColorModes, + dynamicInfo.activeColorMode); + changed |= updateHdrCapabilitiesLocked(dynamicInfo.hdrCapabilities); if (changed) { mHavePendingChanges = true; @@ -260,8 +258,9 @@ final class LocalDisplayAdapter extends DisplayAdapter { public boolean updateDisplayModesLocked( SurfaceControl.DisplayMode[] displayModes, int activeDisplayModeId, SurfaceControl.DesiredDisplayModeSpecs modeSpecs) { - mDisplayModes = Arrays.copyOf(displayModes, displayModes.length); - mActiveDisplayModeId = activeDisplayModeId; + mSfDisplayModes = Arrays.copyOf(displayModes, displayModes.length); + mActiveSfDisplayMode = getModeById(displayModes, activeDisplayModeId); + // Build an updated list of all existing modes. ArrayList<DisplayModeRecord> records = new ArrayList<>(); boolean modesAdded = false; @@ -282,7 +281,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { // First, check to see if we've already added a matching mode. Since not all // configuration options are exposed via Display.Mode, it's possible that we have - // multiple DisplayModess that would generate the same Display.Mode. + // multiple DisplayModes that would generate the same Display.Mode. boolean existingMode = false; for (DisplayModeRecord record : records) { if (record.hasMatchingMode(mode) @@ -312,9 +311,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { // Get the currently active mode DisplayModeRecord activeRecord = null; - for (int i = 0; i < records.size(); i++) { - DisplayModeRecord record = records.get(i); - if (record.hasMatchingMode(displayModes[activeDisplayModeId])) { + for (DisplayModeRecord record : records) { + if (record.hasMatchingMode(mActiveSfDisplayMode)) { activeRecord = record; break; } @@ -370,17 +368,17 @@ final class LocalDisplayAdapter extends DisplayAdapter { // For a new display, we need to initialize the default mode ID. if (mDefaultModeId == NO_DISPLAY_MODE_ID) { mDefaultModeId = activeRecord.mMode.getModeId(); - mDefaultModeGroup = displayModes[activeDisplayModeId].group; + mDefaultModeGroup = mActiveSfDisplayMode.group; } else if (modesAdded && activeModeChanged) { Slog.d(TAG, "New display modes are added and the active mode has changed, " + "use active mode as default mode."); mDefaultModeId = activeRecord.mMode.getModeId(); - mDefaultModeGroup = displayModes[activeDisplayModeId].group; + mDefaultModeGroup = mActiveSfDisplayMode.group; } else if (findDisplayModeIdLocked(mDefaultModeId, mDefaultModeGroup) < 0) { Slog.w(TAG, "Default display mode no longer available, using currently" + " active mode as default."); mDefaultModeId = activeRecord.mMode.getModeId(); - mDefaultModeGroup = displayModes[activeDisplayModeId].group; + mDefaultModeGroup = mActiveSfDisplayMode.group; } // Determine whether the display mode specs' base mode is still there. @@ -454,11 +452,11 @@ final class LocalDisplayAdapter extends DisplayAdapter { mSystemBrightnessToNits = sysToNits; } - private boolean updateDisplayInfo(SurfaceControl.DisplayInfo info) { - if (Objects.equals(mDisplayInfo, info)) { + private boolean updateStaticInfo(SurfaceControl.StaticDisplayInfo info) { + if (Objects.equals(mStaticDisplayInfo, info)) { return false; } - mDisplayInfo = info; + mStaticDisplayInfo = info; return true; } @@ -520,6 +518,17 @@ final class LocalDisplayAdapter extends DisplayAdapter { return true; } + private SurfaceControl.DisplayMode getModeById(SurfaceControl.DisplayMode[] supportedModes, + int modeId) { + for (SurfaceControl.DisplayMode mode : supportedModes) { + if (mode.id == modeId) { + return mode; + } + } + Slog.e(TAG, "Can't find display mode with id " + modeId); + return null; + } + private DisplayModeRecord findDisplayModeRecord(SurfaceControl.DisplayMode mode, List<Float> alternativeRefreshRates) { for (int i = 0; i < mSupportedModes.size(); i++) { @@ -556,10 +565,9 @@ final class LocalDisplayAdapter extends DisplayAdapter { @Override public DisplayDeviceInfo getDisplayDeviceInfoLocked() { if (mInfo == null) { - SurfaceControl.DisplayMode mode = mDisplayModes[mActiveDisplayModeId]; mInfo = new DisplayDeviceInfo(); - mInfo.width = mode.width; - mInfo.height = mode.height; + mInfo.width = mActiveSfDisplayMode.width; + mInfo.height = mActiveSfDisplayMode.height; mInfo.modeId = mActiveModeId; mInfo.defaultModeId = mDefaultModeId; mInfo.supportedModes = getDisplayModes(mSupportedModes); @@ -572,21 +580,21 @@ final class LocalDisplayAdapter extends DisplayAdapter { mInfo.supportedColorModes[i] = mSupportedColorModes.get(i); } mInfo.hdrCapabilities = mHdrCapabilities; - mInfo.appVsyncOffsetNanos = mode.appVsyncOffsetNanos; - mInfo.presentationDeadlineNanos = mode.presentationDeadlineNanos; + mInfo.appVsyncOffsetNanos = mActiveSfDisplayMode.appVsyncOffsetNanos; + mInfo.presentationDeadlineNanos = mActiveSfDisplayMode.presentationDeadlineNanos; mInfo.state = mState; mInfo.uniqueId = getUniqueId(); final DisplayAddress.Physical physicalAddress = DisplayAddress.fromPhysicalDisplayId(mPhysicalDisplayId); mInfo.address = physicalAddress; - mInfo.densityDpi = (int) (mDisplayInfo.density * 160 + 0.5f); - mInfo.xDpi = mode.xDpi; - mInfo.yDpi = mode.yDpi; - mInfo.deviceProductInfo = mDisplayInfo.deviceProductInfo; + mInfo.densityDpi = (int) (mStaticDisplayInfo.density * 160 + 0.5f); + mInfo.xDpi = mActiveSfDisplayMode.xDpi; + mInfo.yDpi = mActiveSfDisplayMode.yDpi; + mInfo.deviceProductInfo = mStaticDisplayInfo.deviceProductInfo; // Assume that all built-in displays that have secure output (eg. HDCP) also // support compositing from gralloc protected buffers. - if (mDisplayInfo.secure) { + if (mStaticDisplayInfo.secure) { mInfo.flags = DisplayDeviceInfo.FLAG_SECURE | DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS; } @@ -620,7 +628,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { } } - if (mDisplayInfo.isInternal) { + if (mStaticDisplayInfo.isInternal) { mInfo.type = Display.TYPE_INTERNAL; mInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL; mInfo.flags |= DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT; @@ -878,9 +886,10 @@ final class LocalDisplayAdapter extends DisplayAdapter { // Do not lock when calling these SurfaceControl methods because they are sync // operations that may block for a while when setting display power mode. SurfaceControl.setDesiredDisplayModeSpecs(displayToken, modeSpecs); - final int activeMode = SurfaceControl.getActiveDisplayMode(displayToken); + final int sfActiveModeId = + SurfaceControl.getDynamicDisplayInfo(displayToken).activeDisplayModeId; synchronized (getSyncRoot()) { - if (updateActiveModeLocked(activeMode)) { + if (updateActiveModeLocked(sfActiveModeId)) { updateDeviceInfoLocked(); } } @@ -891,8 +900,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { updateDeviceInfoLocked(); } - public void onActiveDisplayModeChangedLocked(int modeId) { - if (updateActiveModeLocked(modeId)) { + public void onActiveDisplayModeChangedLocked(int sfModeId) { + if (updateActiveModeLocked(sfModeId)) { updateDeviceInfoLocked(); } } @@ -904,15 +913,15 @@ final class LocalDisplayAdapter extends DisplayAdapter { } } - public boolean updateActiveModeLocked(int activeModeId) { - if (mActiveDisplayModeId == activeModeId) { + public boolean updateActiveModeLocked(int activeSfModeId) { + if (mActiveSfDisplayMode.id == activeSfModeId) { return false; } - mActiveDisplayModeId = activeModeId; - mActiveModeId = findMatchingModeIdLocked(activeModeId); + mActiveSfDisplayMode = getModeById(mSfDisplayModes, activeSfModeId); + mActiveModeId = findMatchingModeIdLocked(activeSfModeId); if (mActiveModeId == NO_DISPLAY_MODE_ID) { Slog.w(TAG, "In unknown mode after setting allowed modes" - + ", activeModeId=" + mActiveDisplayModeId); + + ", activeModeId=" + activeSfModeId); } return true; } @@ -992,7 +1001,6 @@ final class LocalDisplayAdapter extends DisplayAdapter { pw.println("mPhysicalDisplayId=" + mPhysicalDisplayId); pw.println("mDisplayModeSpecs={" + mDisplayModeSpecs + "}"); pw.println("mDisplayModeSpecsInvalid=" + mDisplayModeSpecsInvalid); - pw.println("mActiveDisplayModeId=" + mActiveDisplayModeId); pw.println("mActiveModeId=" + mActiveModeId); pw.println("mActiveColorMode=" + mActiveColorMode); pw.println("mDefaultModeId=" + mDefaultModeId); @@ -1003,11 +1011,12 @@ final class LocalDisplayAdapter extends DisplayAdapter { pw.println("mAllmRequested=" + mAllmRequested); pw.println("mGameContentTypeSupported=" + mGameContentTypeSupported); pw.println("mGameContentTypeRequested=" + mGameContentTypeRequested); - pw.println("mDisplayInfo=" + mDisplayInfo); - pw.println("mDisplayModes="); - for (int i = 0; i < mDisplayModes.length; i++) { - pw.println(" " + mDisplayModes[i]); + pw.println("mStaticDisplayInfo=" + mStaticDisplayInfo); + pw.println("mSfDisplayModes="); + for (int i = 0; i < mSfDisplayModes.length; i++) { + pw.println(" " + mSfDisplayModes[i]); } + pw.println("mActiveSfDisplayMode=" + mActiveSfDisplayMode); pw.println("mSupportedModes="); for (int i = 0; i < mSupportedModes.size(); i++) { pw.println(" " + mSupportedModes.valueAt(i)); @@ -1020,17 +1029,16 @@ final class LocalDisplayAdapter extends DisplayAdapter { int matchingModeId = SurfaceControl.DisplayMode.INVALID_DISPLAY_MODE_ID; DisplayModeRecord record = mSupportedModes.get(modeId); if (record != null) { - for (int i = 0; i < mDisplayModes.length; i++) { - SurfaceControl.DisplayMode mode = mDisplayModes[i]; + for (SurfaceControl.DisplayMode mode : mSfDisplayModes) { if (record.hasMatchingMode(mode)) { if (matchingModeId == SurfaceControl.DisplayMode.INVALID_DISPLAY_MODE_ID) { - matchingModeId = i; + matchingModeId = mode.id; } // Prefer to return a mode that matches the modeGroup if (mode.group == modeGroup) { - return i; + return mode.id; } } } @@ -1038,12 +1046,12 @@ final class LocalDisplayAdapter extends DisplayAdapter { return matchingModeId; } - private int findMatchingModeIdLocked(int modeId) { - if (modeId < 0 || modeId >= mDisplayModes.length) { - Slog.e(TAG, "Invalid display config index " + modeId); + private int findMatchingModeIdLocked(int sfModeId) { + SurfaceControl.DisplayMode mode = getModeById(mSfDisplayModes, sfModeId); + if (mode == null) { + Slog.e(TAG, "Invalid display mode ID " + sfModeId); return NO_DISPLAY_MODE_ID; } - SurfaceControl.DisplayMode mode = mDisplayModes[modeId]; for (int i = 0; i < mSupportedModes.size(); i++) { DisplayModeRecord record = mSupportedModes.valueAt(i); if (record.hasMatchingMode(mode)) { @@ -1229,8 +1237,6 @@ final class LocalDisplayAdapter extends DisplayAdapter { /** * @param displayToken Token for display associated with this backlight. * @param isDefaultDisplay {@code true} if it is the default display. - * @param forceSurfaceControl {@code true} if brightness should always be - * set via SurfaceControl API. */ BacklightAdapter(IBinder displayToken, boolean isDefaultDisplay) { mDisplayToken = displayToken; diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 5cb9d8ff3f31..f09f33ea95ff 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -781,7 +781,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @GuardedBy("mLock") private File mInheritedFilesBase; @GuardedBy("mLock") - private boolean mVerityFound; + private boolean mVerityFoundForApks; /** * Both flags should be guarded with mLock whenever changes need to be in lockstep. @@ -2717,8 +2717,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, "Missing existing base package"); } - // Default to require only if existing base has fs-verity. - mVerityFound = PackageManagerServiceUtils.isApkVerityEnabled() + // Default to require only if existing base apk has fs-verity. + mVerityFoundForApks = PackageManagerServiceUtils.isApkVerityEnabled() && params.mode == SessionParams.MODE_INHERIT_EXISTING && VerityUtils.hasFsverity(pkgInfo.applicationInfo.getBaseCodePath()); @@ -3013,34 +3013,18 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } @GuardedBy("mLock") - private void maybeStageFsveritySignatureLocked(File origFile, File targetFile) - throws PackageManagerException { + private void maybeStageFsveritySignatureLocked(File origFile, File targetFile, + boolean fsVerityRequired) throws PackageManagerException { final File originalSignature = new File( VerityUtils.getFsveritySignatureFilePath(origFile.getPath())); - // Make sure .fsv_sig exists when it should, then resolve and stage it. if (originalSignature.exists()) { - // mVerityFound can only change from false to true here during the staging loop. Since - // all or none of files should have .fsv_sig, this should only happen in the first time - // (or never), otherwise bail out. - if (!mVerityFound) { - mVerityFound = true; - if (mResolvedStagedFiles.size() > 1) { - throw new PackageManagerException(INSTALL_FAILED_BAD_SIGNATURE, - "Some file is missing fs-verity signature"); - } - } - } else { - if (!mVerityFound) { - return; - } + final File stagedSignature = new File( + VerityUtils.getFsveritySignatureFilePath(targetFile.getPath())); + stageFileLocked(originalSignature, stagedSignature); + } else if (fsVerityRequired) { throw new PackageManagerException(INSTALL_FAILED_BAD_SIGNATURE, "Missing corresponding fs-verity signature to " + origFile); } - - final File stagedSignature = new File( - VerityUtils.getFsveritySignatureFilePath(targetFile.getPath())); - - stageFileLocked(originalSignature, stagedSignature); } @GuardedBy("mLock") @@ -3059,7 +3043,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { DexMetadataHelper.buildDexMetadataPathForApk(targetFile.getName())); stageFileLocked(dexMetadataFile, targetDexMetadataFile); - maybeStageFsveritySignatureLocked(dexMetadataFile, targetDexMetadataFile); + + // Also stage .dm.fsv_sig. .dm may be required to install with fs-verity signature on + // supported on older devices. + maybeStageFsveritySignatureLocked(dexMetadataFile, targetDexMetadataFile, + VerityUtils.isFsVeritySupported() && DexMetadataHelper.isFsVerityRequired()); } private void storeBytesToInstallationFile(final String localPath, final String absolutePath, @@ -3121,13 +3109,45 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } @GuardedBy("mLock") + private boolean isFsVerityRequiredForApk(File origFile, File targetFile) + throws PackageManagerException { + if (mVerityFoundForApks) { + return true; + } + + // We haven't seen .fsv_sig for any APKs. Treat it as not required until we see one. + final File originalSignature = new File( + VerityUtils.getFsveritySignatureFilePath(origFile.getPath())); + if (!originalSignature.exists()) { + return false; + } + mVerityFoundForApks = true; + + // When a signature is found, also check any previous staged APKs since they also need to + // have fs-verity signature consistently. + for (File file : mResolvedStagedFiles) { + if (!file.getName().endsWith(".apk")) { + continue; + } + // Ignore the current targeting file. + if (targetFile.getName().equals(file.getName())) { + continue; + } + throw new PackageManagerException(INSTALL_FAILED_BAD_SIGNATURE, + "Previously staged apk is missing fs-verity signature"); + } + return true; + } + + @GuardedBy("mLock") private void resolveAndStageFileLocked(File origFile, File targetFile, String splitName) throws PackageManagerException { stageFileLocked(origFile, targetFile); - // Stage fsverity signature if present. - maybeStageFsveritySignatureLocked(origFile, targetFile); - // Stage dex metadata (.dm) if present. + // Stage APK's fs-verity signature if present. + maybeStageFsveritySignatureLocked(origFile, targetFile, + isFsVerityRequiredForApk(origFile, targetFile)); + // Stage dex metadata (.dm) and corresponding fs-verity signature if present. maybeStageDexMetadataLocked(origFile, targetFile); // Stage checksums (.digests) if present. maybeStageDigestsLocked(origFile, targetFile, splitName); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 14d15ac49227..4fd0bba54df8 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -8959,6 +8959,10 @@ public class PackageManagerService extends IPackageManager.Stub @Override public List<String> getAllPackages() { + // Allow iorapd to call this method. + if (Binder.getCallingUid() != Process.IORAPD_UID) { + enforceSystemOrRootOrShell("getAllPackages is limited to privileged callers"); + } final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); synchronized (mLock) { diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index c0b820293649..508c73ec40c2 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -5121,7 +5121,6 @@ public final class PowerManagerService extends SystemService @Override // Binder call public int getPowerSaveModeTrigger() { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.POWER_SAVER, null); final long ident = Binder.clearCallingIdentity(); try { return Settings.Global.getInt(mContext.getContentResolver(), diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java index 904bbe83e020..151895f02ac4 100644 --- a/services/core/java/com/android/server/wm/ActivityClientController.java +++ b/services/core/java/com/android/server/wm/ActivityClientController.java @@ -235,7 +235,10 @@ class ActivityClientController extends IActivityClientController.Stub { public void activityRelaunched(IBinder token) { final long origId = Binder.clearCallingIdentity(); synchronized (mGlobalLock) { - mTaskSupervisor.activityRelaunchedLocked(token); + final ActivityRecord r = ActivityRecord.forTokenLocked(token); + if (r != null) { + r.finishRelaunching(); + } } Binder.restoreCallingIdentity(origId); } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 8c3722dbbe76..5d3b9c197267 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -567,6 +567,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A boolean mVoiceInteraction; private int mPendingRelaunchCount; + long mRelaunchStartTime; // True if we are current in the process of removing this app token from the display private boolean mRemovingFromDisplay = false; @@ -3357,7 +3358,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return task.isDragResizing(); } + @VisibleForTesting void startRelaunching() { + if (mPendingRelaunchCount == 0) { + mRelaunchStartTime = SystemClock.elapsedRealtime(); + } if (shouldFreezeBounds()) { freezeBounds(); } @@ -3396,6 +3401,14 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Update keyguard flags upon finishing relaunch. checkKeyguardFlagsChanged(); } + + final Task rootTask = getRootTask(); + if (rootTask != null && rootTask.shouldSleepOrShutDownActivities()) { + // Activity is always relaunched to either resumed or paused state. If it was + // relaunched while hidden (by keyguard or smth else), it should be stopped. + rootTask.ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, + false /* preserveWindows */); + } } void clearRelaunching() { @@ -3404,6 +3417,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } unfreezeBounds(); mPendingRelaunchCount = 0; + mRelaunchStartTime = 0; } /** diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index 1264d0c53e0c..bf2aae8867ba 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -2204,19 +2204,6 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { task.mTaskId, reason, topActivity.info.applicationInfo.packageName); } - void activityRelaunchedLocked(IBinder token) { - final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token); - if (r != null) { - r.finishRelaunching(); - if (r.getRootTask().shouldSleepOrShutDownActivities()) { - // Activity is always relaunched to either resumed or paused state. If it was - // relaunched while hidden (by keyguard or smth else), it should be stopped. - r.getRootTask().ensureActivitiesVisible(null /* starting */, 0 /* configChanges */, - false /* preserveWindows */); - } - } - } - void logRootTaskState() { mActivityMetricsLogger.logWindowState(); } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index d33799c15e8f..0143e70f53ca 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -673,10 +673,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // Used in updating override configurations private final Configuration mTempConfig = new Configuration(); - // Used in performing layout, to record the insets provided by other windows above the current - // window. - private InsetsState mTmpAboveInsetsState = new InsetsState(); - /** * Used to prevent recursions when calling * {@link #ensureActivitiesVisible(ActivityRecord, int, boolean, boolean)} @@ -778,13 +774,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp + " parentHidden=" + w.isParentWindowHidden()); } - // Sets mAboveInsets for each window. Windows behind the window providing the insets can - // receive the insets. - if (!w.mAboveInsetsState.equals(mTmpAboveInsetsState)) { - w.mAboveInsetsState.set(mTmpAboveInsetsState); - mWinInsetsChanged.add(w); - } - // If this view is GONE, then skip it -- keep the current frame, and let the caller know // so they can ignore it if they want. (We do the normal layout for INVISIBLE windows, // since that means "perform layout as normal, just don't display"). @@ -818,16 +807,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp + " mContainingFrame=" + w.getContainingFrame() + " mDisplayFrame=" + w.getDisplayFrame()); } - provideInsetsByWindow(w); }; - private void provideInsetsByWindow(WindowState w) { - for (int i = 0; i < w.mProvidedInsetsSources.size(); i++) { - final InsetsSource providedSource = w.mProvidedInsetsSources.valueAt(i); - mTmpAboveInsetsState.addSource(providedSource); - } - } - private final Consumer<WindowState> mPerformLayoutAttached = w -> { if (w.mLayoutAttached) { if (DEBUG_LAYOUT) Slog.v(TAG, "2ND PASS " + w + " mHaveFrame=" + w.mHaveFrame @@ -2511,8 +2492,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp void onDisplayInfoChanged() { final DisplayInfo info = mDisplayInfo; - mDisplayFrames.onDisplayInfoUpdated(info, calculateDisplayCutoutForRotation(info.rotation), - calculateRoundedCornersForRotation(info.rotation)); + if (mDisplayFrames.onDisplayInfoUpdated(info, + calculateDisplayCutoutForRotation(info.rotation), + calculateRoundedCornersForRotation(info.rotation))) { + // TODO(b/161810301): Set notifyInsetsChange to true while the server no longer performs + // layout. + mInsetsStateController.onDisplayInfoUpdated(false /* notifyInsetsChange */); + } mInputMonitor.layoutInputConsumers(info.logicalWidth, info.logicalHeight); mDisplayPolicy.onDisplayInfoChanged(info); } @@ -3767,8 +3753,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // 2. Assign window layers based on the IME surface parent to make sure it is on top of the // app. assignWindowLayers(true /* setLayoutNeeded */); - // 3. Update the IME control target to apply any inset change and animation. - // 4. Reparent the IME container surface to either the input target app, or the IME window + // 3. The z-order of IME might have been changed. Update the above insets state. + mInsetsStateController.updateAboveInsetsState( + mInputMethodWindow, true /* notifyInsetsChange */); + // 4. Update the IME control target to apply any inset change and animation. + // 5. Reparent the IME container surface to either the input target app, or the IME window // parent. updateImeControlTarget(); } @@ -4341,14 +4330,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp + " dh=" + mDisplayInfo.logicalHeight); } - // Used to indicate that we have processed the insets windows. This needs to be after - // beginLayoutLw to ensure the raw insets state display related info is initialized. - final InsetsState rawInsetsState = getInsetsStateController().getRawInsetsState(); - mTmpAboveInsetsState = new InsetsState(); - mTmpAboveInsetsState.setDisplayFrame(rawInsetsState.getDisplayFrame()); - mTmpAboveInsetsState.setDisplayCutout(rawInsetsState.getDisplayCutout()); - mTmpAboveInsetsState.mirrorAlwaysVisibleInsetsSources(rawInsetsState); - int seq = mLayoutSeq + 1; if (seq < 0) seq = 0; mLayoutSeq = seq; diff --git a/services/core/java/com/android/server/wm/DisplayFrames.java b/services/core/java/com/android/server/wm/DisplayFrames.java index e4230a2c8760..a37f3f254276 100644 --- a/services/core/java/com/android/server/wm/DisplayFrames.java +++ b/services/core/java/com/android/server/wm/DisplayFrames.java @@ -21,6 +21,7 @@ import static android.view.InsetsState.ITYPE_LEFT_DISPLAY_CUTOUT; import static android.view.InsetsState.ITYPE_RIGHT_DISPLAY_CUTOUT; import static android.view.InsetsState.ITYPE_TOP_DISPLAY_CUTOUT; +import android.annotation.NonNull; import android.graphics.Rect; import android.util.proto.ProtoOutputStream; import android.view.DisplayCutout; @@ -70,25 +71,28 @@ public class DisplayFrames { * @param info the updated {@link DisplayInfo}. * @param displayCutout the updated {@link DisplayCutout}. * @param roundedCorners the updated {@link RoundedCorners}. + * @return {@code true} if the insets state has been changed; {@code false} otherwise. */ - public void onDisplayInfoUpdated(DisplayInfo info, WmDisplayCutout displayCutout, - RoundedCorners roundedCorners) { - mDisplayWidth = info.logicalWidth; - mDisplayHeight = info.logicalHeight; + public boolean onDisplayInfoUpdated(DisplayInfo info, @NonNull WmDisplayCutout displayCutout, + @NonNull RoundedCorners roundedCorners) { mRotation = info.rotation; - final WmDisplayCutout wmDisplayCutout = - displayCutout != null ? displayCutout : WmDisplayCutout.NO_CUTOUT; final InsetsState state = mInsetsState; - final Rect unrestricted = mUnrestricted; final Rect safe = mDisplayCutoutSafe; - final DisplayCutout cutout = wmDisplayCutout.getDisplayCutout(); + final DisplayCutout cutout = displayCutout.getDisplayCutout(); + if (mDisplayWidth == info.logicalWidth && mDisplayHeight == info.logicalHeight + && state.getDisplayCutout().equals(cutout) + && state.getRoundedCorners().equals(roundedCorners)) { + return false; + } + mDisplayWidth = info.logicalWidth; + mDisplayHeight = info.logicalHeight; + final Rect unrestricted = mUnrestricted; unrestricted.set(0, 0, mDisplayWidth, mDisplayHeight); safe.set(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE); state.setDisplayFrame(unrestricted); state.setDisplayCutout(cutout); - state.setRoundedCorners(roundedCorners != null ? roundedCorners - : RoundedCorners.NO_ROUNDED_CORNERS); + state.setRoundedCorners(roundedCorners); if (!cutout.isEmpty()) { if (cutout.getSafeInsetLeft() > 0) { safe.left = unrestricted.left + cutout.getSafeInsetLeft(); @@ -116,6 +120,7 @@ public class DisplayFrames { state.removeSource(ITYPE_RIGHT_DISPLAY_CUTOUT); state.removeSource(ITYPE_BOTTOM_DISPLAY_CUTOUT); } + return true; } public void dumpDebug(ProtoOutputStream proto, long fieldId) { diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index 580d3285c6a5..75176df6aaf7 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -27,6 +27,9 @@ import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_INVALID; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR; +import static android.view.WindowInsets.Type.displayCutout; +import static android.view.WindowInsets.Type.mandatorySystemGestures; +import static android.view.WindowInsets.Type.systemGestures; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; @@ -293,6 +296,76 @@ class InsetsStateController { Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } + /** + * Updates {@link WindowState#mAboveInsetsState} for all windows in the display while the + * z-order of a window is changed. + * + * @param win The window whose z-order has changed. + * @param notifyInsetsChange {@code true} if the clients should be notified about the change. + */ + void updateAboveInsetsState(WindowState win, boolean notifyInsetsChange) { + if (win == null || win.getDisplayContent() != mDisplayContent) { + return; + } + final boolean[] aboveWin = { true }; + final InsetsState aboveInsetsState = new InsetsState(); + aboveInsetsState.set(mState, + displayCutout() | systemGestures() | mandatorySystemGestures()); + final SparseArray<InsetsSource> winProvidedSources = win.mProvidedInsetsSources; + final ArrayList<WindowState> insetsChangedWindows = new ArrayList<>(); + mDisplayContent.forAllWindows(w -> { + if (aboveWin[0]) { + if (w == win) { + aboveWin[0] = false; + if (!win.mAboveInsetsState.equals(aboveInsetsState)) { + win.mAboveInsetsState.set(aboveInsetsState); + insetsChangedWindows.add(win); + } + return winProvidedSources.size() == 0; + } else { + final SparseArray<InsetsSource> providedSources = w.mProvidedInsetsSources; + for (int i = providedSources.size() - 1; i >= 0; i--) { + aboveInsetsState.addSource(providedSources.valueAt(i)); + } + if (winProvidedSources.size() == 0) { + return false; + } + boolean changed = false; + for (int i = winProvidedSources.size() - 1; i >= 0; i--) { + changed |= w.mAboveInsetsState.removeSource(winProvidedSources.keyAt(i)); + } + if (changed) { + insetsChangedWindows.add(w); + } + } + } else { + for (int i = winProvidedSources.size() - 1; i >= 0; i--) { + w.mAboveInsetsState.addSource(winProvidedSources.valueAt(i)); + } + insetsChangedWindows.add(w); + } + return false; + }, true /* traverseTopToBottom */); + if (notifyInsetsChange) { + for (int i = insetsChangedWindows.size() - 1; i >= 0; i--) { + mDispatchInsetsChanged.accept(insetsChangedWindows.get(i)); + } + } + } + + void onDisplayInfoUpdated(boolean notifyInsetsChange) { + final ArrayList<WindowState> insetsChangedWindows = new ArrayList<>(); + mDisplayContent.forAllWindows(w -> { + w.mAboveInsetsState.set(mState, displayCutout()); + insetsChangedWindows.add(w); + }, true /* traverseTopToBottom */); + if (notifyInsetsChange) { + for (int i = insetsChangedWindows.size() - 1; i >= 0; i--) { + mDispatchInsetsChanged.accept(insetsChangedWindows.get(i)); + } + } + } + void onInsetsModified(InsetsControlTarget caller) { boolean changed = false; for (int i = mProviders.size() - 1; i >= 0; i--) { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 39b749ef54cb..89d30408fe25 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -1881,6 +1881,10 @@ public class WindowManagerService extends IWindowManager.Stub displayContent.sendNewConfiguration(); } + // This window doesn't have a frame yet. Don't let this window cause the insets change. + displayContent.getInsetsStateController().updateAboveInsetsState( + win, false /* notifyInsetsChanged */); + getInsetsSourceControls(win, outActiveControls); } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index a94b0aa9b72f..3dfdedfec0d1 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -211,11 +211,11 @@ import android.os.Trace; import android.os.WorkSource; import android.provider.Settings; import android.text.TextUtils; -import android.util.ArrayMap; import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.MergedConfiguration; import android.util.Slog; +import android.util.SparseArray; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; import android.view.Display; @@ -533,6 +533,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP */ private boolean mOrientationChanging; + /** The time when the window was last requested to redraw for orientation change. */ + private long mOrientationChangeRedrawRequestTime; + /** * Sometimes in addition to the mOrientationChanging * flag we report that the orientation is changing @@ -648,12 +651,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP /** * The insets state of sources provided by windows above the current window. */ - InsetsState mAboveInsetsState = new InsetsState(); + final InsetsState mAboveInsetsState = new InsetsState(); /** * The insets sources provided by this window. */ - ArrayMap<Integer, InsetsSource> mProvidedInsetsSources = new ArrayMap<>(); + final SparseArray<InsetsSource> mProvidedInsetsSources = new SparseArray<>(); /** * Surface insets from the previous call to relayout(), used to track @@ -1441,11 +1444,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // redrawn; to do that, we need to go through the process of getting informed by the // application when it has finished drawing. if (getOrientationChanging() || dragResizingChanged) { - if (getOrientationChanging()) { - Slog.v(TAG_WM, "Orientation start waiting for draw" - + ", mDrawState=DRAW_PENDING in " + this - + ", surfaceController " + winAnimator.mSurfaceController); - } if (dragResizingChanged) { ProtoLog.v(WM_DEBUG_RESIZE, "Resize start waiting for draw, " @@ -2204,7 +2202,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } - @Override + @Override void removeImmediately() { super.removeImmediately(); @@ -3651,7 +3649,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP ProtoLog.v(WM_DEBUG_RESIZE, "Reporting new frame to %s: %s", this, mWindowFrames.mCompatFrame); - if (mWinAnimator.mDrawState == DRAW_PENDING) { + final boolean drawPending = mWinAnimator.mDrawState == DRAW_PENDING; + if (drawPending) { ProtoLog.i(WM_DEBUG_ORIENTATION, "Resizing %s WITH DRAW PENDING", this); } @@ -3668,7 +3667,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mWindowFrames.clearReportResizeHints(); final MergedConfiguration mergedConfiguration = mLastReportedConfiguration; - final boolean reportDraw = mWinAnimator.mDrawState == DRAW_PENDING || useBLASTSync() || !mRedrawForSyncReported; + final boolean reportDraw = drawPending || useBLASTSync() || !mRedrawForSyncReported; final boolean forceRelayout = reportOrientation || isDragResizeChanged() || !mRedrawForSyncReported; final int displayId = getDisplayId(); fillClientWindowFrames(mClientWindowFrames); @@ -3679,6 +3678,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mClient.resized(mClientWindowFrames, reportDraw, mergedConfiguration, forceRelayout, getDisplayContent().getDisplayPolicy().areSystemBarsForcedShownLw(this), displayId); + if (drawPending && reportOrientation && mOrientationChanging) { + mOrientationChangeRedrawRequestTime = SystemClock.elapsedRealtime(); + ProtoLog.v(WM_DEBUG_ORIENTATION, + "Requested redraw for orientation change: %s", this); + } if (mWmService.mAccessibilityController != null) { mWmService.mAccessibilityController.onSomeWindowResizedOrMoved(displayId); @@ -5732,6 +5736,18 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } boolean finishDrawing(SurfaceControl.Transaction postDrawTransaction) { + if (mOrientationChangeRedrawRequestTime > 0) { + final long duration = + SystemClock.elapsedRealtime() - mOrientationChangeRedrawRequestTime; + Slog.i(TAG, "finishDrawing of orientation change: " + this + " " + duration + "ms"); + mOrientationChangeRedrawRequestTime = 0; + } else if (mActivityRecord != null && mActivityRecord.mRelaunchStartTime != 0 + && mActivityRecord.findMainWindow() == this) { + final long duration = + SystemClock.elapsedRealtime() - mActivityRecord.mRelaunchStartTime; + Slog.i(TAG, "finishDrawing of relaunch: " + this + " " + duration + "ms"); + mActivityRecord.mRelaunchStartTime = 0; + } if (!onSyncFinishedDrawing()) { return mWinAnimator.finishDrawingLocked(postDrawTransaction); } diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java index ca534927bd66..7f8784dc2599 100644 --- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java @@ -223,7 +223,7 @@ public class LocalDisplayAdapterTest { for (int i = 0; i < wrappedModes.length; i++) { modes[i] = wrappedModes[i].mode; } - display.modes = modes; + display.dynamicInfo.supportedDisplayModes = modes; setUpDisplay(display); mInjector.getTransmitter().sendHotplug(display, /* connected */ true); waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); @@ -252,53 +252,53 @@ public class LocalDisplayAdapterTest { testAlternativeRefreshRatesCommon(display, new DisplayModeWrapper[] { new DisplayModeWrapper( - createFakeDisplayMode(1920, 1080, 60f, 0), new float[]{24f, 50f}), + createFakeDisplayMode(0, 1920, 1080, 60f, 0), new float[]{24f, 50f}), new DisplayModeWrapper( - createFakeDisplayMode(1920, 1080, 50f, 0), new float[]{24f, 60f}), + createFakeDisplayMode(1, 1920, 1080, 50f, 0), new float[]{24f, 60f}), new DisplayModeWrapper( - createFakeDisplayMode(1920, 1080, 24f, 0), new float[]{50f, 60f}), + createFakeDisplayMode(2, 1920, 1080, 24f, 0), new float[]{50f, 60f}), new DisplayModeWrapper( - createFakeDisplayMode(3840, 2160, 60f, 0), new float[]{24f, 50f}), + createFakeDisplayMode(3, 3840, 2160, 60f, 0), new float[]{24f, 50f}), new DisplayModeWrapper( - createFakeDisplayMode(3840, 2160, 50f, 0), new float[]{24f, 60f}), + createFakeDisplayMode(4, 3840, 2160, 50f, 0), new float[]{24f, 60f}), new DisplayModeWrapper( - createFakeDisplayMode(3840, 2160, 24f, 0), new float[]{50f, 60f}), + createFakeDisplayMode(5, 3840, 2160, 24f, 0), new float[]{50f, 60f}), }); testAlternativeRefreshRatesCommon(display, new DisplayModeWrapper[] { new DisplayModeWrapper( - createFakeDisplayMode(1920, 1080, 60f, 0), new float[]{50f}), + createFakeDisplayMode(0, 1920, 1080, 60f, 0), new float[]{50f}), new DisplayModeWrapper( - createFakeDisplayMode(1920, 1080, 50f, 0), new float[]{60f}), + createFakeDisplayMode(1, 1920, 1080, 50f, 0), new float[]{60f}), new DisplayModeWrapper( - createFakeDisplayMode(1920, 1080, 24f, 1), new float[0]), + createFakeDisplayMode(2, 1920, 1080, 24f, 1), new float[0]), new DisplayModeWrapper( - createFakeDisplayMode(3840, 2160, 60f, 2), new float[0]), + createFakeDisplayMode(3, 3840, 2160, 60f, 2), new float[0]), new DisplayModeWrapper( - createFakeDisplayMode(3840, 2160, 50f, 3), new float[]{24f}), + createFakeDisplayMode(4, 3840, 2160, 50f, 3), new float[]{24f}), new DisplayModeWrapper( - createFakeDisplayMode(3840, 2160, 24f, 3), new float[]{50f}), + createFakeDisplayMode(5, 3840, 2160, 24f, 3), new float[]{50f}), }); testAlternativeRefreshRatesCommon(display, new DisplayModeWrapper[] { new DisplayModeWrapper( - createFakeDisplayMode(1920, 1080, 60f, 0), new float[0]), + createFakeDisplayMode(0, 1920, 1080, 60f, 0), new float[0]), new DisplayModeWrapper( - createFakeDisplayMode(1920, 1080, 50f, 1), new float[0]), + createFakeDisplayMode(1, 1920, 1080, 50f, 1), new float[0]), new DisplayModeWrapper( - createFakeDisplayMode(1920, 1080, 24f, 2), new float[0]), + createFakeDisplayMode(2, 1920, 1080, 24f, 2), new float[0]), new DisplayModeWrapper( - createFakeDisplayMode(3840, 2160, 60f, 3), new float[0]), + createFakeDisplayMode(3, 3840, 2160, 60f, 3), new float[0]), new DisplayModeWrapper( - createFakeDisplayMode(3840, 2160, 50f, 4), new float[0]), + createFakeDisplayMode(4, 3840, 2160, 50f, 4), new float[0]), new DisplayModeWrapper( - createFakeDisplayMode(3840, 2160, 24f, 5), new float[0]), + createFakeDisplayMode(5, 3840, 2160, 24f, 5), new float[0]), }); } @Test public void testAfterDisplayChange_DisplayModesAreUpdated() throws Exception { - SurfaceControl.DisplayMode displayMode = createFakeDisplayMode(1920, 1080, 60f); + SurfaceControl.DisplayMode displayMode = createFakeDisplayMode(0, 1920, 1080, 60f); SurfaceControl.DisplayMode[] modes = new SurfaceControl.DisplayMode[]{displayMode}; FakeDisplay display = new FakeDisplay(PORT_A, modes, 0); @@ -325,18 +325,14 @@ public class LocalDisplayAdapterTest { displayMode.refreshRate)).isTrue(); // Change the display - SurfaceControl.DisplayMode addedDisplayInfo = createFakeDisplayMode(3840, 2160, - 60f); + SurfaceControl.DisplayMode addedDisplayInfo = createFakeDisplayMode(1, 3840, 2160, 60f); modes = new SurfaceControl.DisplayMode[]{displayMode, addedDisplayInfo}; - display.modes = modes; - display.activeMode = 1; + display.dynamicInfo.supportedDisplayModes = modes; + display.dynamicInfo.activeDisplayModeId = 1; setUpDisplay(display); mInjector.getTransmitter().sendHotplug(display, /* connected */ true); waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); - assertThat(SurfaceControl.getActiveDisplayMode(display.token)).isEqualTo(1); - assertThat(SurfaceControl.getDisplayModes(display.token).length).isEqualTo(2); - assertThat(mListener.addedDisplays.size()).isEqualTo(1); assertThat(mListener.changedDisplays.size()).isEqualTo(1); @@ -360,8 +356,8 @@ public class LocalDisplayAdapterTest { @Test public void testAfterDisplayChange_ActiveModeIsUpdated() throws Exception { SurfaceControl.DisplayMode[] modes = new SurfaceControl.DisplayMode[]{ - createFakeDisplayMode(1920, 1080, 60f), - createFakeDisplayMode(1920, 1080, 50f) + createFakeDisplayMode(0, 1920, 1080, 60f), + createFakeDisplayMode(1, 1920, 1080, 50f) }; FakeDisplay display = new FakeDisplay(PORT_A, modes, /* activeMode */ 0); setUpDisplay(display); @@ -379,13 +375,11 @@ public class LocalDisplayAdapterTest { assertThat(activeMode.matches(1920, 1080, 60f)).isTrue(); // Change the display - display.activeMode = 1; + display.dynamicInfo.activeDisplayModeId = 1; setUpDisplay(display); mInjector.getTransmitter().sendHotplug(display, /* connected */ true); waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); - assertThat(SurfaceControl.getActiveDisplayMode(display.token)).isEqualTo(1); - assertThat(mListener.addedDisplays.size()).isEqualTo(1); assertThat(mListener.changedDisplays.size()).isEqualTo(1); @@ -402,7 +396,7 @@ public class LocalDisplayAdapterTest { FakeDisplay display = new FakeDisplay(PORT_A); Display.HdrCapabilities initialHdrCapabilities = new Display.HdrCapabilities(new int[0], 1000, 1000, 0); - display.hdrCapabilities = initialHdrCapabilities; + display.dynamicInfo.hdrCapabilities = initialHdrCapabilities; setUpDisplay(display); updateAvailableDisplays(); mAdapter.registerLocked(); @@ -419,7 +413,7 @@ public class LocalDisplayAdapterTest { // Change the display Display.HdrCapabilities changedHdrCapabilities = new Display.HdrCapabilities( new int[Display.HdrCapabilities.HDR_TYPE_HDR10_PLUS], 1000, 1000, 0); - display.hdrCapabilities = changedHdrCapabilities; + display.dynamicInfo.hdrCapabilities = changedHdrCapabilities; setUpDisplay(display); mInjector.getTransmitter().sendHotplug(display, /* connected */ true); waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); @@ -438,7 +432,7 @@ public class LocalDisplayAdapterTest { public void testAfterDisplayChange_ColorModesAreUpdated() throws Exception { FakeDisplay display = new FakeDisplay(PORT_A); final int[] initialColorModes = new int[]{Display.COLOR_MODE_BT709}; - display.colorModes = initialColorModes; + display.dynamicInfo.supportedColorModes = initialColorModes; setUpDisplay(display); updateAvailableDisplays(); mAdapter.registerLocked(); @@ -455,7 +449,7 @@ public class LocalDisplayAdapterTest { // Change the display final int[] changedColorModes = new int[]{Display.COLOR_MODE_DEFAULT}; - display.colorModes = changedColorModes; + display.dynamicInfo.supportedColorModes = changedColorModes; setUpDisplay(display); mInjector.getTransmitter().sendHotplug(display, /* connected */ true); waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); @@ -474,8 +468,8 @@ public class LocalDisplayAdapterTest { @Test public void testDisplayChange_withStaleDesiredDisplayModeSpecs() throws Exception { SurfaceControl.DisplayMode[] modes = new SurfaceControl.DisplayMode[]{ - createFakeDisplayMode(1920, 1080, 60f), - createFakeDisplayMode(1920, 1080, 50f) + createFakeDisplayMode(0, 1920, 1080, 60f), + createFakeDisplayMode(1, 1920, 1080, 50f) }; final int activeMode = 0; FakeDisplay display = new FakeDisplay(PORT_A, modes, activeMode); @@ -487,11 +481,11 @@ public class LocalDisplayAdapterTest { waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); // Change the display - display.modes = new SurfaceControl.DisplayMode[]{ - createFakeDisplayMode(1920, 1080, 60f) + display.dynamicInfo.supportedDisplayModes = new SurfaceControl.DisplayMode[]{ + createFakeDisplayMode(2, 1920, 1080, 60f) }; - // SurfaceFlinger can return a stale defaultMode. Make sure this doesn't - // trigger ArrayOutOfBoundsException. + display.dynamicInfo.activeDisplayModeId = 2; + // SurfaceFlinger can return a stale defaultMode. Make sure this doesn't crash. display.desiredDisplayModeSpecs.defaultMode = 1; setUpDisplay(display); @@ -588,12 +582,15 @@ public class LocalDisplayAdapterTest { private static class FakeDisplay { public final DisplayAddress.Physical address; public final IBinder token = new Binder(); - public final SurfaceControl.DisplayInfo info; - public SurfaceControl.DisplayMode[] modes; - public int activeMode; - public int[] colorModes = new int[]{ Display.COLOR_MODE_DEFAULT }; - public Display.HdrCapabilities hdrCapabilities = new Display.HdrCapabilities(new int[0], - 1000, 1000, 0); + public final SurfaceControl.StaticDisplayInfo info; + public SurfaceControl.DynamicDisplayInfo dynamicInfo = + new SurfaceControl.DynamicDisplayInfo(); + { + dynamicInfo.supportedColorModes = new int[]{ Display.COLOR_MODE_DEFAULT }; + dynamicInfo.hdrCapabilities = new Display.HdrCapabilities(new int[0], + 1000, 1000, 0); + } + public SurfaceControl.DesiredDisplayModeSpecs desiredDisplayModeSpecs = new SurfaceControl.DesiredDisplayModeSpecs(/* defaultMode */ 0, /* allowGroupSwitching */ false, @@ -603,19 +600,19 @@ public class LocalDisplayAdapterTest { /* appRefreshRateMax */60.f); private FakeDisplay(int port) { - this.address = createDisplayAddress(port); - this.info = createFakeDisplayInfo(); - this.modes = new SurfaceControl.DisplayMode[]{ - createFakeDisplayMode(800, 600, 60f) + address = createDisplayAddress(port); + info = createFakeDisplayInfo(); + dynamicInfo.supportedDisplayModes = new SurfaceControl.DisplayMode[]{ + createFakeDisplayMode(0, 800, 600, 60f) }; - this.activeMode = 0; + dynamicInfo.activeDisplayModeId = 0; } private FakeDisplay(int port, SurfaceControl.DisplayMode[] modes, int activeMode) { - this.address = createDisplayAddress(port); - this.info = createFakeDisplayInfo(); - this.modes = modes; - this.activeMode = activeMode; + address = createDisplayAddress(port); + info = createFakeDisplayInfo(); + dynamicInfo.supportedDisplayModes = modes; + dynamicInfo.activeDisplayModeId = activeMode; } } @@ -623,15 +620,9 @@ public class LocalDisplayAdapterTest { mAddresses.add(display.address); doReturn(display.token).when(() -> SurfaceControl.getPhysicalDisplayToken(display.address.getPhysicalDisplayId())); - doReturn(display.info).when(() -> SurfaceControl.getDisplayInfo(display.token)); - doReturn(display.modes).when( - () -> SurfaceControl.getDisplayModes(display.token)); - doReturn(display.activeMode).when(() -> SurfaceControl.getActiveDisplayMode(display.token)); - doReturn(0).when(() -> SurfaceControl.getActiveColorMode(display.token)); - doReturn(display.colorModes).when( - () -> SurfaceControl.getDisplayColorModes(display.token)); - doReturn(display.hdrCapabilities).when( - () -> SurfaceControl.getHdrCapabilities(display.token)); + doReturn(display.info).when(() -> SurfaceControl.getStaticDisplayInfo(display.token)); + doReturn(display.dynamicInfo).when( + () -> SurfaceControl.getDynamicDisplayInfo(display.token)); doReturn(display.desiredDisplayModeSpecs) .when(() -> SurfaceControl.getDesiredDisplayModeSpecs(display.token)); } @@ -650,20 +641,21 @@ public class LocalDisplayAdapterTest { return DisplayAddress.fromPortAndModel(port, DISPLAY_MODEL); } - private static SurfaceControl.DisplayInfo createFakeDisplayInfo() { - final SurfaceControl.DisplayInfo info = new SurfaceControl.DisplayInfo(); + private static SurfaceControl.StaticDisplayInfo createFakeDisplayInfo() { + final SurfaceControl.StaticDisplayInfo info = new SurfaceControl.StaticDisplayInfo(); info.density = 100; return info; } - private static SurfaceControl.DisplayMode createFakeDisplayMode(int width, int height, + private static SurfaceControl.DisplayMode createFakeDisplayMode(int id, int width, int height, float refreshRate) { - return createFakeDisplayMode(width, height, refreshRate, 0); + return createFakeDisplayMode(id, width, height, refreshRate, 0); } - private static SurfaceControl.DisplayMode createFakeDisplayMode(int width, int height, + private static SurfaceControl.DisplayMode createFakeDisplayMode(int id, int width, int height, float refreshRate, int group) { final SurfaceControl.DisplayMode mode = new SurfaceControl.DisplayMode(); + mode.id = id; mode.width = width; mode.height = height; mode.refreshRate = refreshRate; diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java index 6d40034c6000..91b3cb7dbdd9 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java @@ -25,6 +25,7 @@ import static com.android.server.job.JobSchedulerService.NEVER_INDEX; import static com.android.server.job.JobSchedulerService.RARE_INDEX; import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX; import static com.android.server.job.JobSchedulerService.WORKING_INDEX; +import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import static com.android.server.job.controllers.JobStatus.CONSTRAINT_BACKGROUND_NOT_RESTRICTED; import static com.android.server.job.controllers.JobStatus.CONSTRAINT_BATTERY_NOT_LOW; import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CHARGING; @@ -101,7 +102,7 @@ public class JobStatusTest { Clock.fixed(Clock.systemUTC().instant(), ZoneOffset.UTC); JobSchedulerService.sUptimeMillisClock = Clock.fixed(SystemClock.uptimeClock().instant(), ZoneOffset.UTC); - JobSchedulerService.sElapsedRealtimeClock = + sElapsedRealtimeClock = Clock.fixed(SystemClock.elapsedRealtimeClock().instant(), ZoneOffset.UTC); } @@ -204,7 +205,7 @@ public class JobStatusTest { @Test public void testFraction() throws Exception { - final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); + final long now = sElapsedRealtimeClock.millis(); assertEquals(1, createJobStatus(0, Long.MAX_VALUE).getFractionRunTime(), DELTA); @@ -261,15 +262,15 @@ public class JobStatusTest { final JobStatus job = createJobStatus(jobInfo); markImplicitConstraintsSatisfied(job, true); - job.setChargingConstraintSatisfied(false); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); - job.setChargingConstraintSatisfied(true); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); markImplicitConstraintsSatisfied(job, false); - job.setChargingConstraintSatisfied(false); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); - job.setChargingConstraintSatisfied(true); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); } @@ -282,15 +283,15 @@ public class JobStatusTest { final JobStatus job = createJobStatus(jobInfo); markImplicitConstraintsSatisfied(job, true); - job.setIdleConstraintSatisfied(false); + job.setIdleConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_IDLE)); - job.setIdleConstraintSatisfied(true); + job.setIdleConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_IDLE)); markImplicitConstraintsSatisfied(job, false); - job.setIdleConstraintSatisfied(false); + job.setIdleConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_IDLE)); - job.setIdleConstraintSatisfied(true); + job.setIdleConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_IDLE)); } @@ -303,15 +304,15 @@ public class JobStatusTest { final JobStatus job = createJobStatus(jobInfo); markImplicitConstraintsSatisfied(job, true); - job.setBatteryNotLowConstraintSatisfied(false); + job.setBatteryNotLowConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_BATTERY_NOT_LOW)); - job.setBatteryNotLowConstraintSatisfied(true); + job.setBatteryNotLowConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_BATTERY_NOT_LOW)); markImplicitConstraintsSatisfied(job, false); - job.setBatteryNotLowConstraintSatisfied(false); + job.setBatteryNotLowConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_BATTERY_NOT_LOW)); - job.setBatteryNotLowConstraintSatisfied(true); + job.setBatteryNotLowConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_BATTERY_NOT_LOW)); } @@ -324,15 +325,15 @@ public class JobStatusTest { final JobStatus job = createJobStatus(jobInfo); markImplicitConstraintsSatisfied(job, true); - job.setStorageNotLowConstraintSatisfied(false); + job.setStorageNotLowConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_STORAGE_NOT_LOW)); - job.setStorageNotLowConstraintSatisfied(true); + job.setStorageNotLowConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_STORAGE_NOT_LOW)); markImplicitConstraintsSatisfied(job, false); - job.setStorageNotLowConstraintSatisfied(false); + job.setStorageNotLowConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_STORAGE_NOT_LOW)); - job.setStorageNotLowConstraintSatisfied(true); + job.setStorageNotLowConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_STORAGE_NOT_LOW)); } @@ -345,15 +346,15 @@ public class JobStatusTest { final JobStatus job = createJobStatus(jobInfo); markImplicitConstraintsSatisfied(job, true); - job.setTimingDelayConstraintSatisfied(false); + job.setTimingDelayConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_TIMING_DELAY)); - job.setTimingDelayConstraintSatisfied(true); + job.setTimingDelayConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_TIMING_DELAY)); markImplicitConstraintsSatisfied(job, false); - job.setTimingDelayConstraintSatisfied(false); + job.setTimingDelayConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_TIMING_DELAY)); - job.setTimingDelayConstraintSatisfied(true); + job.setTimingDelayConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_TIMING_DELAY)); } @@ -366,15 +367,15 @@ public class JobStatusTest { final JobStatus job = createJobStatus(jobInfo); markImplicitConstraintsSatisfied(job, true); - job.setDeadlineConstraintSatisfied(false); + job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE)); - job.setDeadlineConstraintSatisfied(true); + job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE)); markImplicitConstraintsSatisfied(job, false); - job.setDeadlineConstraintSatisfied(false); + job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE)); - job.setDeadlineConstraintSatisfied(true); + job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE)); } @@ -387,15 +388,15 @@ public class JobStatusTest { final JobStatus job = createJobStatus(jobInfo); markImplicitConstraintsSatisfied(job, true); - job.setConnectivityConstraintSatisfied(false); + job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY)); - job.setConnectivityConstraintSatisfied(true); + job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY)); markImplicitConstraintsSatisfied(job, false); - job.setConnectivityConstraintSatisfied(false); + job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY)); - job.setConnectivityConstraintSatisfied(true); + job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY)); } @@ -410,15 +411,15 @@ public class JobStatusTest { final JobStatus job = createJobStatus(jobInfo); markImplicitConstraintsSatisfied(job, true); - job.setContentTriggerConstraintSatisfied(false); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); - job.setContentTriggerConstraintSatisfied(true); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); markImplicitConstraintsSatisfied(job, false); - job.setContentTriggerConstraintSatisfied(false); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); - job.setContentTriggerConstraintSatisfied(true); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); } @@ -436,15 +437,15 @@ public class JobStatusTest { markImplicitConstraintsSatisfied(job, false); - job.setChargingConstraintSatisfied(false); - job.setConnectivityConstraintSatisfied(false); - job.setContentTriggerConstraintSatisfied(false); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); - job.setChargingConstraintSatisfied(true); - job.setConnectivityConstraintSatisfied(true); - job.setContentTriggerConstraintSatisfied(true); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), true); // Still false because implicit constraints aren't satisfied. assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY)); @@ -452,61 +453,61 @@ public class JobStatusTest { markImplicitConstraintsSatisfied(job, true); - job.setChargingConstraintSatisfied(false); - job.setConnectivityConstraintSatisfied(false); - job.setContentTriggerConstraintSatisfied(false); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); // Turn on constraints one at a time. - job.setChargingConstraintSatisfied(true); - job.setConnectivityConstraintSatisfied(false); - job.setContentTriggerConstraintSatisfied(false); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); - job.setChargingConstraintSatisfied(false); - job.setConnectivityConstraintSatisfied(false); - job.setContentTriggerConstraintSatisfied(true); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); - job.setChargingConstraintSatisfied(false); - job.setConnectivityConstraintSatisfied(true); - job.setContentTriggerConstraintSatisfied(false); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); // With two of the 3 constraints satisfied (and implicit constraints also satisfied), only // the unsatisfied constraint should return true. - job.setChargingConstraintSatisfied(true); - job.setConnectivityConstraintSatisfied(false); - job.setContentTriggerConstraintSatisfied(true); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); - job.setChargingConstraintSatisfied(true); - job.setConnectivityConstraintSatisfied(true); - job.setContentTriggerConstraintSatisfied(false); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY)); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); - job.setChargingConstraintSatisfied(false); - job.setConnectivityConstraintSatisfied(true); - job.setContentTriggerConstraintSatisfied(true); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); - job.setChargingConstraintSatisfied(true); - job.setConnectivityConstraintSatisfied(true); - job.setContentTriggerConstraintSatisfied(true); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setConnectivityConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONNECTIVITY)); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); @@ -526,15 +527,15 @@ public class JobStatusTest { markImplicitConstraintsSatisfied(job, false); - job.setChargingConstraintSatisfied(false); - job.setContentTriggerConstraintSatisfied(false); - job.setDeadlineConstraintSatisfied(false); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE)); - job.setChargingConstraintSatisfied(true); - job.setContentTriggerConstraintSatisfied(true); - job.setDeadlineConstraintSatisfied(true); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), true); // Still false because implicit constraints aren't satisfied. assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); @@ -542,18 +543,18 @@ public class JobStatusTest { markImplicitConstraintsSatisfied(job, true); - job.setChargingConstraintSatisfied(false); - job.setContentTriggerConstraintSatisfied(false); - job.setDeadlineConstraintSatisfied(false); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); // Once implicit constraint are satisfied, deadline constraint should always return true. assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE)); // Turn on constraints one at a time. - job.setChargingConstraintSatisfied(true); - job.setContentTriggerConstraintSatisfied(false); - job.setDeadlineConstraintSatisfied(false); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); // Deadline should force isReady to be true, but isn't needed for the job to be // considered ready. @@ -561,17 +562,17 @@ public class JobStatusTest { // Once implicit constraint are satisfied, deadline constraint should always return true. assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE)); - job.setChargingConstraintSatisfied(false); - job.setContentTriggerConstraintSatisfied(true); - job.setDeadlineConstraintSatisfied(false); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); // Once implicit constraint are satisfied, deadline constraint should always return true. assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE)); - job.setChargingConstraintSatisfied(false); - job.setContentTriggerConstraintSatisfied(false); - job.setDeadlineConstraintSatisfied(true); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), true); // Since the deadline constraint is satisfied, none of the other explicit constraints are // needed. assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); @@ -581,33 +582,33 @@ public class JobStatusTest { // With two of the 3 constraints satisfied (and implicit constraints also satisfied), only // the unsatisfied constraint should return true. - job.setChargingConstraintSatisfied(true); - job.setContentTriggerConstraintSatisfied(true); - job.setDeadlineConstraintSatisfied(false); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); // Once implicit constraint are satisfied, deadline constraint should always return true. assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE)); - job.setChargingConstraintSatisfied(true); - job.setContentTriggerConstraintSatisfied(false); - job.setDeadlineConstraintSatisfied(true); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); // Once implicit constraint are satisfied, deadline constraint should always return true. assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE)); - job.setChargingConstraintSatisfied(false); - job.setContentTriggerConstraintSatisfied(true); - job.setDeadlineConstraintSatisfied(true); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), false); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); // Once implicit constraint are satisfied, deadline constraint should always return true. assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_DEADLINE)); - job.setChargingConstraintSatisfied(true); - job.setContentTriggerConstraintSatisfied(true); - job.setDeadlineConstraintSatisfied(true); + job.setChargingConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setContentTriggerConstraintSatisfied(sElapsedRealtimeClock.millis(), true); + job.setDeadlineConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CHARGING)); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_CONTENT_TRIGGER)); // Once implicit constraint are satisfied, deadline constraint should always return true. @@ -621,15 +622,15 @@ public class JobStatusTest { new JobInfo.Builder(101, new ComponentName("foo", "bar")).build()); markImplicitConstraintsSatisfied(job, false); - job.setDeviceNotDozingConstraintSatisfied(false, false); + job.setDeviceNotDozingConstraintSatisfied(sElapsedRealtimeClock.millis(), false, false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_DEVICE_NOT_DOZING)); - job.setDeviceNotDozingConstraintSatisfied(true, false); + job.setDeviceNotDozingConstraintSatisfied(sElapsedRealtimeClock.millis(), true, false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_DEVICE_NOT_DOZING)); markImplicitConstraintsSatisfied(job, true); - job.setDeviceNotDozingConstraintSatisfied(false, false); + job.setDeviceNotDozingConstraintSatisfied(sElapsedRealtimeClock.millis(), false, false); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_DEVICE_NOT_DOZING)); - job.setDeviceNotDozingConstraintSatisfied(true, false); + job.setDeviceNotDozingConstraintSatisfied(sElapsedRealtimeClock.millis(), true, false); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_DEVICE_NOT_DOZING)); } @@ -640,15 +641,15 @@ public class JobStatusTest { new JobInfo.Builder(101, new ComponentName("foo", "bar")).build()); markImplicitConstraintsSatisfied(job, false); - job.setQuotaConstraintSatisfied(false); + job.setQuotaConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_WITHIN_QUOTA)); - job.setQuotaConstraintSatisfied(true); + job.setQuotaConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_WITHIN_QUOTA)); markImplicitConstraintsSatisfied(job, true); - job.setQuotaConstraintSatisfied(false); + job.setQuotaConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_WITHIN_QUOTA)); - job.setQuotaConstraintSatisfied(true); + job.setQuotaConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_WITHIN_QUOTA)); } @@ -659,22 +660,24 @@ public class JobStatusTest { new JobInfo.Builder(101, new ComponentName("foo", "bar")).build()); markImplicitConstraintsSatisfied(job, false); - job.setBackgroundNotRestrictedConstraintSatisfied(false); + job.setBackgroundNotRestrictedConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_BACKGROUND_NOT_RESTRICTED)); - job.setBackgroundNotRestrictedConstraintSatisfied(true); + job.setBackgroundNotRestrictedConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertFalse(job.wouldBeReadyWithConstraint(CONSTRAINT_BACKGROUND_NOT_RESTRICTED)); markImplicitConstraintsSatisfied(job, true); - job.setBackgroundNotRestrictedConstraintSatisfied(false); + job.setBackgroundNotRestrictedConstraintSatisfied(sElapsedRealtimeClock.millis(), false); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_BACKGROUND_NOT_RESTRICTED)); - job.setBackgroundNotRestrictedConstraintSatisfied(true); + job.setBackgroundNotRestrictedConstraintSatisfied(sElapsedRealtimeClock.millis(), true); assertTrue(job.wouldBeReadyWithConstraint(CONSTRAINT_BACKGROUND_NOT_RESTRICTED)); } private void markImplicitConstraintsSatisfied(JobStatus job, boolean isSatisfied) { - job.setQuotaConstraintSatisfied(isSatisfied); - job.setDeviceNotDozingConstraintSatisfied(isSatisfied, false); - job.setBackgroundNotRestrictedConstraintSatisfied(isSatisfied); + job.setQuotaConstraintSatisfied(sElapsedRealtimeClock.millis(), isSatisfied); + job.setDeviceNotDozingConstraintSatisfied( + sElapsedRealtimeClock.millis(), isSatisfied, false); + job.setBackgroundNotRestrictedConstraintSatisfied( + sElapsedRealtimeClock.millis(), isSatisfied); } private static JobStatus createJobStatus(long earliestRunTimeElapsedMillis, diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java index b72121f096ba..88a691bbc209 100644 --- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java @@ -359,8 +359,9 @@ public class QuotaControllerTest { // Make sure tests aren't passing just because the default bucket is likely ACTIVE. js.setStandbyBucket(FREQUENT_INDEX); // Make sure Doze and background-not-restricted don't affect tests. - js.setDeviceNotDozingConstraintSatisfied(/* state */ true, /* allowlisted */false); - js.setBackgroundNotRestrictedConstraintSatisfied(true); + js.setDeviceNotDozingConstraintSatisfied(/* nowElapsed */ sElapsedRealtimeClock.millis(), + /* state */ true, /* allowlisted */false); + js.setBackgroundNotRestrictedConstraintSatisfied(sElapsedRealtimeClock.millis(), true); return js; } diff --git a/services/tests/servicestests/src/com/android/server/appop/AppOpsNotedWatcherTest.java b/services/tests/servicestests/src/com/android/server/appop/AppOpsNotedWatcherTest.java index 41e15631d258..663017890b0c 100644 --- a/services/tests/servicestests/src/com/android/server/appop/AppOpsNotedWatcherTest.java +++ b/services/tests/servicestests/src/com/android/server/appop/AppOpsNotedWatcherTest.java @@ -66,11 +66,13 @@ public class AppOpsNotedWatcherTest { inOrder.verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS) .times(1)).onOpNoted(eq(AppOpsManager.OP_FINE_LOCATION), eq(Process.myUid()), eq(getContext().getPackageName()), - eq(AppOpsManager.OP_FLAG_SELF), eq(AppOpsManager.MODE_ALLOWED)); + eq(getContext().getAttributionTag()), eq(AppOpsManager.OP_FLAG_SELF), + eq(AppOpsManager.MODE_ALLOWED)); inOrder.verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS) .times(1)).onOpNoted(eq(AppOpsManager.OP_CAMERA), eq(Process.myUid()), eq(getContext().getPackageName()), - eq(AppOpsManager.OP_FLAG_SELF), eq(AppOpsManager.MODE_ALLOWED)); + eq(getContext().getAttributionTag()), eq(AppOpsManager.OP_FLAG_SELF), + eq(AppOpsManager.MODE_ALLOWED)); // Stop watching appOpsManager.stopWatchingNoted(listener); @@ -94,7 +96,8 @@ public class AppOpsNotedWatcherTest { verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS) .times(2)).onOpNoted(eq(AppOpsManager.OP_FINE_LOCATION), eq(Process.myUid()), eq(getContext().getPackageName()), - eq(AppOpsManager.OP_FLAG_SELF), eq(AppOpsManager.MODE_ALLOWED)); + eq(getContext().getAttributionTag()), eq(AppOpsManager.OP_FLAG_SELF), + eq(AppOpsManager.MODE_ALLOWED)); // Finish up appOpsManager.stopWatchingNoted(listener); diff --git a/services/tests/servicestests/src/com/android/server/appop/AppOpsStartedWatcherTest.java b/services/tests/servicestests/src/com/android/server/appop/AppOpsStartedWatcherTest.java index fec8aa9ceaff..c12eb32a9143 100644 --- a/services/tests/servicestests/src/com/android/server/appop/AppOpsStartedWatcherTest.java +++ b/services/tests/servicestests/src/com/android/server/appop/AppOpsStartedWatcherTest.java @@ -63,11 +63,13 @@ public class AppOpsStartedWatcherTest { inOrder.verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS) .times(1)).onOpStarted(eq(AppOpsManager.OP_FINE_LOCATION), eq(Process.myUid()), eq(getContext().getPackageName()), - eq(AppOpsManager.OP_FLAG_SELF), eq(AppOpsManager.MODE_ALLOWED)); + eq(getContext().getAttributionTag()), eq(AppOpsManager.OP_FLAG_SELF), + eq(AppOpsManager.MODE_ALLOWED)); inOrder.verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS) .times(1)).onOpStarted(eq(AppOpsManager.OP_CAMERA), eq(Process.myUid()), eq(getContext().getPackageName()), - eq(AppOpsManager.OP_FLAG_SELF), eq(AppOpsManager.MODE_ALLOWED)); + eq(getContext().getAttributionTag()), eq(AppOpsManager.OP_FLAG_SELF), + eq(AppOpsManager.MODE_ALLOWED)); // Stop watching appOpsManager.stopWatchingStarted(listener); @@ -91,7 +93,8 @@ public class AppOpsStartedWatcherTest { verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS) .times(2)).onOpStarted(eq(AppOpsManager.OP_CAMERA), eq(Process.myUid()), eq(getContext().getPackageName()), - eq(AppOpsManager.OP_FLAG_SELF), eq(AppOpsManager.MODE_ALLOWED)); + eq(getContext().getAttributionTag()), eq(AppOpsManager.OP_FLAG_SELF), + eq(AppOpsManager.MODE_ALLOWED)); verifyNoMoreInteractions(listener); // Finish up diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java index 55748366c1ba..5f86d282406a 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java @@ -287,8 +287,7 @@ public class TimeDetectorServiceTest { } private static ExternalTimeSuggestion createExternalTimeSuggestion() { - TimestampedValue<Long> timeValue = new TimestampedValue<>(100L, 1_000_000L); - return new ExternalTimeSuggestion(timeValue); + return new ExternalTimeSuggestion(100L, 1_000_000L); } private static class StubbedTimeDetectorStrategy implements TimeDetectorStrategy { diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java index daa1b25de22f..f7a498bc9d73 100644 --- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorStrategyImplTest.java @@ -1483,11 +1483,8 @@ public class TimeDetectorStrategyImplTest { * reference time. */ ExternalTimeSuggestion generateExternalTimeSuggestion(Instant suggestedTime) { - TimestampedValue<Long> utcTime = - new TimestampedValue<>( - mFakeEnvironment.peekElapsedRealtimeMillis(), + return new ExternalTimeSuggestion(mFakeEnvironment.peekElapsedRealtimeMillis(), suggestedTime.toEpochMilli()); - return new ExternalTimeSuggestion(utcTime); } /** diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java index b1ea4a58d0f1..5a0466afa85b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java @@ -130,7 +130,7 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { // insets state with the global one. final InsetsState insetsState = win.getDisplayContent().getInsetsStateController().getRawInsetsState(); - win.mAboveInsetsState = insetsState; + win.mAboveInsetsState.set(insetsState); } public void setRotation(int rotation, boolean includingWindows) { diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java index 2163661b7506..47cf53b621d3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java @@ -18,6 +18,7 @@ package com.android.server.wm; import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; +import static android.view.RoundedCorners.NO_ROUNDED_CORNERS; import static android.view.Surface.ROTATION_0; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; @@ -37,6 +38,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM; import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_RIGHT; +import static com.android.server.wm.utils.WmDisplayCutout.NO_CUTOUT; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -300,9 +302,9 @@ public class DisplayPolicyTests extends WindowTestsBase { displayPolicy.addWindowLw(mNavBarWindow, mNavBarWindow.mAttrs); mNavBarWindow.getControllableInsetProvider().setServerVisible(true); final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState(); - mImeWindow.mAboveInsetsState = state; + mImeWindow.mAboveInsetsState.set(state); mDisplayContent.mDisplayFrames = new DisplayFrames(mDisplayContent.getDisplayId(), - state, displayInfo, null /* displayCutout */, null /* roundedCorners*/); + state, displayInfo, NO_CUTOUT, NO_ROUNDED_CORNERS); mDisplayContent.setInputMethodWindowLocked(mImeWindow); mImeWindow.mAttrs.setFitInsetsSides(Side.all() & ~Side.BOTTOM); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java index 20775e84fd8f..683ed889d283 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java @@ -117,7 +117,7 @@ public class DisplayPolicyTestsBase extends WindowTestsBase { static Pair<DisplayInfo, WmDisplayCutout> displayInfoAndCutoutForRotation(int rotation, boolean withDisplayCutout, boolean isLongEdgeCutout) { final DisplayInfo info = new DisplayInfo(); - WmDisplayCutout cutout = null; + WmDisplayCutout cutout = WmDisplayCutout.NO_CUTOUT; final boolean flippedDimensions = rotation == ROTATION_90 || rotation == ROTATION_270; info.logicalWidth = flippedDimensions ? DISPLAY_HEIGHT : DISPLAY_WIDTH; diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java index ee293fcf70e6..be036034542e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java @@ -32,13 +32,14 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.server.wm.DisplayContent.IME_TARGET_INPUT; +import static com.android.server.wm.WindowContainer.POSITION_TOP; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -208,7 +209,7 @@ public class InsetsStateControllerTest extends WindowTestsBase { app.mAboveInsetsState.getSource(ITYPE_IME).setFrame(mImeWindow.getFrame()); // Make sure app got notified. - verify(app, atLeast(1)).notifyInsetsChanged(); + verify(app, atLeastOnce()).notifyInsetsChanged(); // app will get visible IME insets while below IME. assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible()); @@ -336,6 +337,92 @@ public class InsetsStateControllerTest extends WindowTestsBase { assertFalse(app.getInsetsState().getSource(ITYPE_STATUS_BAR).isVisible()); } + @Test + public void testUpdateAboveInsetsState_provideInsets() { + final WindowState app = createTestWindow("app"); + final WindowState statusBar = createTestWindow("statusBar"); + final WindowState navBar = createTestWindow("navBar"); + + getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null); + + assertNull(app.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR)); + assertNull(statusBar.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR)); + assertNull(navBar.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR)); + + getController().updateAboveInsetsState(statusBar, true /* notifyInsetsChange */); + + assertNotNull(app.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR)); + assertNull(statusBar.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR)); + assertNull(navBar.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR)); + + verify(app, atLeastOnce()).notifyInsetsChanged(); + } + + @Test + public void testUpdateAboveInsetsState_receiveInsets() { + final WindowState app = createTestWindow("app"); + final WindowState statusBar = createTestWindow("statusBar"); + final WindowState navBar = createTestWindow("navBar"); + + getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null); + getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null); + + assertNull(app.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR)); + assertNull(app.mAboveInsetsState.peekSource(ITYPE_NAVIGATION_BAR)); + + getController().updateAboveInsetsState(app, true /* notifyInsetsChange */); + + assertNotNull(app.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR)); + assertNotNull(app.mAboveInsetsState.peekSource(ITYPE_NAVIGATION_BAR)); + + verify(app, atLeastOnce()).notifyInsetsChanged(); + } + + @Test + public void testUpdateAboveInsetsState_zOrderChanged() { + final WindowState ime = createTestWindow("ime"); + final WindowState app = createTestWindow("app"); + final WindowState statusBar = createTestWindow("statusBar"); + final WindowState navBar = createTestWindow("navBar"); + + getController().getSourceProvider(ITYPE_IME).setWindow(ime, null, null); + getController().getSourceProvider(ITYPE_IME).setClientVisible(true); + getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null); + getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindow(navBar, null, null); + getController().updateAboveInsetsState(ime, false /* notifyInsetsChange */); + getController().updateAboveInsetsState(statusBar, false /* notifyInsetsChange */); + getController().updateAboveInsetsState(navBar, false /* notifyInsetsChange */); + + // ime is below others. + assertNull(app.mAboveInsetsState.peekSource(ITYPE_IME)); + assertNull(statusBar.mAboveInsetsState.peekSource(ITYPE_IME)); + assertNull(navBar.mAboveInsetsState.peekSource(ITYPE_IME)); + assertNotNull(ime.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR)); + assertNotNull(ime.mAboveInsetsState.peekSource(ITYPE_NAVIGATION_BAR)); + + ime.getParent().positionChildAt(POSITION_TOP, ime, true /* includingParents */); + getController().updateAboveInsetsState(ime, true /* notifyInsetsChange */); + + // ime is above others. + assertNotNull(app.mAboveInsetsState.peekSource(ITYPE_IME)); + assertNotNull(statusBar.mAboveInsetsState.peekSource(ITYPE_IME)); + assertNotNull(navBar.mAboveInsetsState.peekSource(ITYPE_IME)); + assertNull(ime.mAboveInsetsState.peekSource(ITYPE_STATUS_BAR)); + assertNull(ime.mAboveInsetsState.peekSource(ITYPE_NAVIGATION_BAR)); + + verify(ime, atLeastOnce()).notifyInsetsChanged(); + verify(app, atLeastOnce()).notifyInsetsChanged(); + verify(statusBar, atLeastOnce()).notifyInsetsChanged(); + verify(navBar, atLeastOnce()).notifyInsetsChanged(); + } + + private WindowState createTestWindow(String name) { + final WindowState win = createWindow(null, TYPE_APPLICATION, name); + win.setHasSurface(true); + spyOn(win); + return win; + } + private InsetsStateController getController() { return mDisplayContent.getInsetsStateController(); } diff --git a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java index ab3572ba2173..d96005b8a71a 100644 --- a/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java +++ b/tests/ApkVerityTest/src/com/android/apkverity/ApkVerityTest.java @@ -95,10 +95,13 @@ public class ApkVerityTest extends BaseHostJUnit4Test { new AddFsVerityCertRule(this, CERT_PATH); private ITestDevice mDevice; + private boolean mDmRequireFsVerity; @Before public void setUp() throws DeviceNotAvailableException { mDevice = getDevice(); + mDmRequireFsVerity = "true".equals( + mDevice.getProperty("pm.dexopt.dm.require_fsverity")); uninstallPackage(TARGET_PACKAGE); } @@ -124,7 +127,7 @@ public class ApkVerityTest extends BaseHostJUnit4Test { verifyInstalledFiles( INSTALLED_BASE_APK, INSTALLED_BASE_APK_FSV_SIG); - verifyInstalledFilesHaveFsverity(); + verifyInstalledFilesHaveFsverity(INSTALLED_BASE_APK); } @Test @@ -151,7 +154,9 @@ public class ApkVerityTest extends BaseHostJUnit4Test { INSTALLED_BASE_APK_FSV_SIG, INSTALLED_SPLIT_APK, INSTALLED_SPLIT_APK_FSV_SIG); - verifyInstalledFilesHaveFsverity(); + verifyInstalledFilesHaveFsverity( + INSTALLED_BASE_APK, + INSTALLED_SPLIT_APK); } @Test @@ -167,7 +172,9 @@ public class ApkVerityTest extends BaseHostJUnit4Test { INSTALLED_BASE_APK_FSV_SIG, INSTALLED_BASE_DM, INSTALLED_BASE_DM_FSV_SIG); - verifyInstalledFilesHaveFsverity(); + verifyInstalledFilesHaveFsverity( + INSTALLED_BASE_APK, + INSTALLED_BASE_DM); } @Test @@ -189,7 +196,11 @@ public class ApkVerityTest extends BaseHostJUnit4Test { INSTALLED_SPLIT_APK_FSV_SIG, INSTALLED_SPLIT_DM, INSTALLED_SPLIT_DM_FSV_SIG); - verifyInstalledFilesHaveFsverity(); + verifyInstalledFilesHaveFsverity( + INSTALLED_BASE_APK, + INSTALLED_BASE_DM, + INSTALLED_SPLIT_APK, + INSTALLED_SPLIT_DM); } @Test @@ -213,7 +224,9 @@ public class ApkVerityTest extends BaseHostJUnit4Test { INSTALLED_BASE_APK_FSV_SIG, INSTALLED_SPLIT_APK, INSTALLED_SPLIT_APK_FSV_SIG); - verifyInstalledFilesHaveFsverity(); + verifyInstalledFilesHaveFsverity( + INSTALLED_BASE_APK, + INSTALLED_SPLIT_APK); } @Test @@ -250,39 +263,92 @@ public class ApkVerityTest extends BaseHostJUnit4Test { INSTALLED_BASE_APK, INSTALLED_SPLIT_APK, INSTALLED_SPLIT_APK_FSV_SIG); - } @Test - public void testInstallOnlyBaseHasFsvSig() + public void testInstallOnlyDmHasFsvSig() throws DeviceNotAvailableException, FileNotFoundException { new InstallMultiple() - .addFileAndSignature(BASE_APK) + .addFile(BASE_APK) + .addFileAndSignature(BASE_APK_DM) + .addFile(SPLIT_APK) + .addFileAndSignature(SPLIT_APK_DM) + .run(); + verifyInstalledFiles( + INSTALLED_BASE_APK, + INSTALLED_BASE_DM, + INSTALLED_BASE_DM_FSV_SIG, + INSTALLED_SPLIT_APK, + INSTALLED_SPLIT_DM, + INSTALLED_SPLIT_DM_FSV_SIG); + verifyInstalledFilesHaveFsverity( + INSTALLED_BASE_DM, + INSTALLED_SPLIT_DM); + } + + @Test + public void testInstallDmWithoutFsvSig_Base() + throws DeviceNotAvailableException, FileNotFoundException { + InstallMultiple installer = new InstallMultiple() + .addFile(BASE_APK) .addFile(BASE_APK_DM) .addFile(SPLIT_APK) - .addFile(SPLIT_APK_DM) - .runExpectingFailure(); + .addFileAndSignature(SPLIT_APK_DM); + if (mDmRequireFsVerity) { + installer.runExpectingFailure(); + } else { + installer.run(); + verifyInstalledFiles( + INSTALLED_BASE_APK, + INSTALLED_BASE_DM, + INSTALLED_SPLIT_APK, + INSTALLED_SPLIT_DM, + INSTALLED_SPLIT_DM_FSV_SIG); + verifyInstalledFilesHaveFsverity(INSTALLED_SPLIT_DM); + } } @Test - public void testInstallOnlyDmHasFsvSig() + public void testInstallDmWithoutFsvSig_Split() throws DeviceNotAvailableException, FileNotFoundException { - new InstallMultiple() + InstallMultiple installer = new InstallMultiple() .addFile(BASE_APK) .addFileAndSignature(BASE_APK_DM) .addFile(SPLIT_APK) - .addFile(SPLIT_APK_DM) + .addFile(SPLIT_APK_DM); + if (mDmRequireFsVerity) { + installer.runExpectingFailure(); + } else { + installer.run(); + verifyInstalledFiles( + INSTALLED_BASE_APK, + INSTALLED_BASE_DM, + INSTALLED_BASE_DM_FSV_SIG, + INSTALLED_SPLIT_APK, + INSTALLED_SPLIT_DM); + verifyInstalledFilesHaveFsverity(INSTALLED_BASE_DM); + } + } + + @Test + public void testInstallSomeApkIsMissingFsvSig_Base() + throws DeviceNotAvailableException, FileNotFoundException { + new InstallMultiple() + .addFileAndSignature(BASE_APK) + .addFileAndSignature(BASE_APK_DM) + .addFile(SPLIT_APK) + .addFileAndSignature(SPLIT_APK_DM) .runExpectingFailure(); } @Test - public void testInstallOnlySplitHasFsvSig() + public void testInstallSomeApkIsMissingFsvSig_Split() throws DeviceNotAvailableException, FileNotFoundException { new InstallMultiple() .addFile(BASE_APK) - .addFile(BASE_APK_DM) + .addFileAndSignature(BASE_APK_DM) .addFileAndSignature(SPLIT_APK) - .addFile(SPLIT_APK_DM) + .addFileAndSignature(SPLIT_APK_DM) .runExpectingFailure(); } @@ -383,37 +449,36 @@ public class ApkVerityTest extends BaseHostJUnit4Test { } } - private void verifyInstalledFilesHaveFsverity() throws DeviceNotAvailableException { + private void verifyInstalledFilesHaveFsverity(String... filenames) + throws DeviceNotAvailableException { // Verify that all files are protected by fs-verity String apkPath = getApkPath(TARGET_PACKAGE); String appDir = apkPath.substring(0, apkPath.lastIndexOf("/")); long kTargetOffset = 0; - for (String basename : expectRemoteCommandToSucceed("ls " + appDir).split("\n")) { - if (basename.endsWith(".apk") || basename.endsWith(".dm")) { - String path = appDir + "/" + basename; - damageFileAgainstBlockDevice(path, kTargetOffset); - - // Retry is sometimes needed to pass the test. Package manager may have FD leaks - // (see b/122744005 as example) that prevents the file in question to be evicted - // from filesystem cache. Forcing GC workarounds the problem. - int retry = 5; - for (; retry > 0; retry--) { - BlockDeviceWriter.dropCaches(mDevice); - if (!BlockDeviceWriter.canReadByte(mDevice, path, kTargetOffset)) { - break; - } - try { - CLog.d("lsof: " + expectRemoteCommandToSucceed("lsof " + apkPath)); - Thread.sleep(1000); - String pid = expectRemoteCommandToSucceed("pidof system_server"); - mDevice.executeShellV2Command("kill -10 " + pid); // force GC - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - return; - } + for (String basename : filenames) { + String path = appDir + "/" + basename; + damageFileAgainstBlockDevice(path, kTargetOffset); + + // Retry is sometimes needed to pass the test. Package manager may have FD leaks + // (see b/122744005 as example) that prevents the file in question to be evicted + // from filesystem cache. Forcing GC workarounds the problem. + int retry = 5; + for (; retry > 0; retry--) { + BlockDeviceWriter.dropCaches(mDevice); + if (!BlockDeviceWriter.canReadByte(mDevice, path, kTargetOffset)) { + break; + } + try { + CLog.d("lsof: " + expectRemoteCommandToSucceed("lsof " + apkPath)); + Thread.sleep(1000); + String pid = expectRemoteCommandToSucceed("pidof system_server"); + mDevice.executeShellV2Command("kill -10 " + pid); // force GC + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return; } - assertTrue("Read from " + path + " should fail", retry > 0); } + assertTrue("Read from " + path + " should fail", retry > 0); } } |