Merge "[Shell-transition]: More checking animating state methods" into tm-dev
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 2b0a833..ee0f9e8 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
@@ -37,7 +37,6 @@
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.IUidObserver;
-import android.app.job.JobInfo;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal;
import android.app.usage.UsageStatsManagerInternal.UsageEventListener;
@@ -166,28 +165,6 @@
public long inQuotaTimeElapsed;
/**
- * The time after which the app will be under the bucket quota and can start running
- * low priority jobs again. This is only valid if
- * {@link #executionTimeInWindowMs} >=
- * {@link #mAllowedTimePerPeriodMs} * (1 - {@link #mAllowedTimeSurplusPriorityLow}),
- * {@link #executionTimeInMaxPeriodMs} >= {@link #mMaxExecutionTimeMs},
- * {@link #bgJobCountInWindow} >= {@link #jobCountLimit}, or
- * {@link #sessionCountInWindow} >= {@link #sessionCountLimit}.
- */
- public long inQuotaTimeLowElapsed;
-
- /**
- * The time after which the app will be under the bucket quota and can start running
- * min priority jobs again. This is only valid if
- * {@link #executionTimeInWindowMs} >=
- * {@link #mAllowedTimePerPeriodMs} * (1 - {@link #mAllowedTimeSurplusPriorityMin}),
- * {@link #executionTimeInMaxPeriodMs} >= {@link #mMaxExecutionTimeMs},
- * {@link #bgJobCountInWindow} >= {@link #jobCountLimit}, or
- * {@link #sessionCountInWindow} >= {@link #sessionCountLimit}.
- */
- public long inQuotaTimeMinElapsed;
-
- /**
* The time after which {@link #jobCountInRateLimitingWindow} should be considered invalid,
* in the elapsed realtime timebase.
*/
@@ -227,8 +204,6 @@
+ "bgJobCountInMaxPeriod=" + bgJobCountInMaxPeriod + ", "
+ "sessionCountInWindow=" + sessionCountInWindow + ", "
+ "inQuotaTime=" + inQuotaTimeElapsed + ", "
- + "inQuotaTimeLow=" + inQuotaTimeLowElapsed + ", "
- + "inQuotaTimeMin=" + inQuotaTimeMinElapsed + ", "
+ "rateLimitJobCountExpirationTime=" + jobRateLimitExpirationTimeElapsed + ", "
+ "rateLimitJobCountWindow=" + jobCountInRateLimitingWindow + ", "
+ "rateLimitSessionCountExpirationTime="
@@ -385,24 +360,6 @@
*/
private long mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs;
- /**
- * The percentage of {@link #mAllowedTimePerPeriodMs} that should not be used by
- * {@link JobInfo#PRIORITY_LOW low priority} jobs. In other words, there must be a minimum
- * surplus of this amount of remaining allowed time before we start running low priority
- * jobs.
- */
- private float mAllowedTimeSurplusPriorityLow =
- QcConstants.DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_LOW;
-
- /**
- * The percentage of {@link #mAllowedTimePerPeriodMs} that should not be used by
- * {@link JobInfo#PRIORITY_MIN min priority} jobs. In other words, there must be a minimum
- * surplus of this amount of remaining allowed time before we start running low priority
- * jobs.
- */
- private float mAllowedTimeSurplusPriorityMin =
- QcConstants.DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_MIN;
-
/** The period of time used to rate limit recently run jobs. */
private long mRateLimitingWindowMs = QcConstants.DEFAULT_RATE_LIMITING_WINDOW_MS;
@@ -828,8 +785,7 @@
return mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS;
}
return getTimeUntilQuotaConsumedLocked(
- jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(),
- jobStatus.getEffectivePriority());
+ jobStatus.getSourceUserId(), jobStatus.getSourcePackageName());
}
// Expedited job.
@@ -919,8 +875,7 @@
return isTopStartedJobLocked(jobStatus)
|| isUidInForeground(jobStatus.getSourceUid())
|| isWithinQuotaLocked(
- jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), standbyBucket,
- jobStatus.getEffectivePriority());
+ jobStatus.getSourceUserId(), jobStatus.getSourcePackageName(), standbyBucket);
}
@GuardedBy("mLock")
@@ -937,7 +892,7 @@
@VisibleForTesting
@GuardedBy("mLock")
boolean isWithinQuotaLocked(final int userId, @NonNull final String packageName,
- final int standbyBucket, final int priority) {
+ final int standbyBucket) {
if (!mIsEnabled) {
return true;
}
@@ -945,18 +900,9 @@
if (isQuotaFreeLocked(standbyBucket)) return true;
- final long minSurplus;
- if (priority <= JobInfo.PRIORITY_MIN) {
- minSurplus = (long)
- (mAllowedTimePerPeriodMs[standbyBucket] * mAllowedTimeSurplusPriorityMin);
- } else if (priority <= JobInfo.PRIORITY_LOW) {
- minSurplus = (long)
- (mAllowedTimePerPeriodMs[standbyBucket] * mAllowedTimeSurplusPriorityLow);
- } else {
- minSurplus = 0;
- }
ExecutionStats stats = getExecutionStatsLocked(userId, packageName, standbyBucket);
- return getRemainingExecutionTimeLocked(stats) > minSurplus
+ // TODO: use a higher minimum remaining time for jobs with MINIMUM priority
+ return getRemainingExecutionTimeLocked(stats) > 0
&& isUnderJobCountQuotaLocked(stats, standbyBucket)
&& isUnderSessionCountQuotaLocked(stats, standbyBucket);
}
@@ -1074,8 +1020,7 @@
* job is running.
*/
@VisibleForTesting
- long getTimeUntilQuotaConsumedLocked(final int userId, @NonNull final String packageName,
- @JobInfo.Priority int jobPriority) {
+ long getTimeUntilQuotaConsumedLocked(final int userId, @NonNull final String packageName) {
final long nowElapsed = sElapsedRealtimeClock.millis();
final int standbyBucket = JobSchedulerService.standbyBucketForPackage(
packageName, userId, nowElapsed);
@@ -1096,15 +1041,11 @@
final long startWindowElapsed = nowElapsed - stats.windowSizeMs;
final long startMaxElapsed = nowElapsed - MAX_PERIOD_MS;
- final long allowedTimePerPeriodMs = getAllowedTimePerPeriodMs(standbyBucket, jobPriority);
+ final long allowedTimePerPeriodMs = mAllowedTimePerPeriodMs[standbyBucket];
final long allowedTimeRemainingMs = allowedTimePerPeriodMs - stats.executionTimeInWindowMs;
final long maxExecutionTimeRemainingMs =
mMaxExecutionTimeMs - stats.executionTimeInMaxPeriodMs;
- if (maxExecutionTimeRemainingMs < 0) {
- return 0;
- }
-
// Regular ACTIVE case. Since the bucket size equals the allowed time, the app jobs can
// essentially run until they reach the maximum limit.
if (stats.windowSizeMs == mAllowedTimePerPeriodMs[standbyBucket]) {
@@ -1112,10 +1053,6 @@
sessions, startMaxElapsed, maxExecutionTimeRemainingMs);
}
- if (allowedTimeRemainingMs < 0) {
- return 0;
- }
-
// Need to check both max time and period time in case one is less than the other.
// For example, max time remaining could be less than bucket time remaining, but sessions
// contributing to the max time remaining could phase out enough that we'd want to use the
@@ -1127,21 +1064,6 @@
sessions, startWindowElapsed, allowedTimeRemainingMs));
}
- private long getAllowedTimePerPeriodMs(int standbyBucket, @JobInfo.Priority int jobPriority) {
- return getAllowedTimePerPeriodMs(mAllowedTimePerPeriodMs[standbyBucket], jobPriority);
- }
-
- private long getAllowedTimePerPeriodMs(long initialAllowedTime,
- @JobInfo.Priority int jobPriority) {
- if (jobPriority <= JobInfo.PRIORITY_MIN) {
- return (long) (initialAllowedTime * (1 - mAllowedTimeSurplusPriorityMin));
- }
- if (jobPriority <= JobInfo.PRIORITY_LOW) {
- return (long) (initialAllowedTime * (1 - mAllowedTimeSurplusPriorityLow));
- }
- return initialAllowedTime;
- }
-
/**
* Calculates how much time it will take, in milliseconds, until the quota is fully consumed.
*
@@ -1299,15 +1221,10 @@
stats.sessionCountInWindow = 0;
if (stats.jobCountLimit == 0 || stats.sessionCountLimit == 0) {
// App won't be in quota until configuration changes.
- stats.inQuotaTimeElapsed = stats.inQuotaTimeLowElapsed = stats.inQuotaTimeMinElapsed =
- Long.MAX_VALUE;
+ stats.inQuotaTimeElapsed = Long.MAX_VALUE;
} else {
stats.inQuotaTimeElapsed = 0;
}
- final long allowedTimeMinMs =
- getAllowedTimePerPeriodMs(stats.allowedTimePerPeriodMs, JobInfo.PRIORITY_MIN);
- final long allowedTimeLowMs =
- getAllowedTimePerPeriodMs(stats.allowedTimePerPeriodMs, JobInfo.PRIORITY_LOW);
final long allowedTimeIntoQuotaMs = stats.allowedTimePerPeriodMs - mQuotaBufferMs;
Timer timer = mPkgTimers.get(userId, packageName);
@@ -1326,25 +1243,13 @@
stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed,
nowElapsed - allowedTimeIntoQuotaMs + stats.windowSizeMs);
}
- if (stats.executionTimeInWindowMs >= allowedTimeLowMs) {
- stats.inQuotaTimeLowElapsed = Math.max(stats.inQuotaTimeLowElapsed,
- nowElapsed - allowedTimeLowMs + stats.windowSizeMs);
- }
- if (stats.executionTimeInWindowMs >= allowedTimeMinMs) {
- stats.inQuotaTimeMinElapsed = Math.max(stats.inQuotaTimeMinElapsed,
- nowElapsed - allowedTimeMinMs + stats.windowSizeMs);
- }
if (stats.executionTimeInMaxPeriodMs >= mMaxExecutionTimeIntoQuotaMs) {
final long inQuotaTime = nowElapsed - mMaxExecutionTimeIntoQuotaMs + MAX_PERIOD_MS;
stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed, inQuotaTime);
- stats.inQuotaTimeLowElapsed = Math.max(stats.inQuotaTimeLowElapsed, inQuotaTime);
- stats.inQuotaTimeMinElapsed = Math.max(stats.inQuotaTimeMinElapsed, inQuotaTime);
}
if (stats.bgJobCountInWindow >= stats.jobCountLimit) {
final long inQuotaTime = nowElapsed + stats.windowSizeMs;
stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed, inQuotaTime);
- stats.inQuotaTimeLowElapsed = Math.max(stats.inQuotaTimeLowElapsed, inQuotaTime);
- stats.inQuotaTimeMinElapsed = Math.max(stats.inQuotaTimeMinElapsed, inQuotaTime);
}
}
@@ -1386,23 +1291,9 @@
start + stats.executionTimeInWindowMs - allowedTimeIntoQuotaMs
+ stats.windowSizeMs);
}
- if (stats.executionTimeInWindowMs >= allowedTimeLowMs) {
- stats.inQuotaTimeLowElapsed = Math.max(stats.inQuotaTimeLowElapsed,
- start + stats.executionTimeInWindowMs - allowedTimeLowMs
- + stats.windowSizeMs);
- }
- if (stats.executionTimeInWindowMs >= allowedTimeMinMs) {
- stats.inQuotaTimeMinElapsed = Math.max(stats.inQuotaTimeMinElapsed,
- start + stats.executionTimeInWindowMs - allowedTimeMinMs
- + stats.windowSizeMs);
- }
if (stats.bgJobCountInWindow >= stats.jobCountLimit) {
final long inQuotaTime = session.endTimeElapsed + stats.windowSizeMs;
stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed, inQuotaTime);
- stats.inQuotaTimeLowElapsed = Math.max(stats.inQuotaTimeLowElapsed,
- inQuotaTime);
- stats.inQuotaTimeMinElapsed = Math.max(stats.inQuotaTimeMinElapsed,
- inQuotaTime);
}
if (i == loopStart
|| (sessions.get(i + 1).startTimeElapsed - session.endTimeElapsed)
@@ -1413,10 +1304,6 @@
if (sessionCountInWindow >= stats.sessionCountLimit) {
final long inQuotaTime = session.endTimeElapsed + stats.windowSizeMs;
stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed, inQuotaTime);
- stats.inQuotaTimeLowElapsed = Math.max(stats.inQuotaTimeLowElapsed,
- inQuotaTime);
- stats.inQuotaTimeMinElapsed = Math.max(stats.inQuotaTimeMinElapsed,
- inQuotaTime);
}
}
}
@@ -1706,7 +1593,7 @@
/**
* Update the CONSTRAINT_WITHIN_QUOTA bit for all of the Jobs for a given package.
*
- * @return true if at least one job had its bit changed
+ * @return the set of jobs whose status changed
*/
@NonNull
private ArraySet<JobStatus> maybeUpdateConstraintForPkgLocked(final long nowElapsed,
@@ -1719,8 +1606,7 @@
// Quota is the same for all jobs within a package.
final int realStandbyBucket = jobs.valueAt(0).getStandbyBucket();
- final boolean realInQuota = isWithinQuotaLocked(
- userId, packageName, realStandbyBucket, JobInfo.PRIORITY_DEFAULT);
+ final boolean realInQuota = isWithinQuotaLocked(userId, packageName, realStandbyBucket);
boolean outOfEJQuota = false;
for (int i = jobs.size() - 1; i >= 0; --i) {
final JobStatus js = jobs.valueAt(i);
@@ -1733,8 +1619,7 @@
changedJobs.add(js);
}
} else if (realStandbyBucket != EXEMPTED_INDEX && realStandbyBucket != ACTIVE_INDEX
- && realStandbyBucket == js.getEffectiveStandbyBucket()
- && js.getEffectivePriority() >= JobInfo.PRIORITY_DEFAULT) {
+ && 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.
@@ -1798,8 +1683,9 @@
final String packageName = jobStatus.getSourcePackageName();
final int realStandbyBucket = jobStatus.getStandbyBucket();
if (isWithinEJQuota
- && isWithinQuotaLocked(userId, packageName, realStandbyBucket,
- JobInfo.PRIORITY_MIN)) {
+ && isWithinQuotaLocked(userId, packageName, realStandbyBucket)) {
+ // TODO(141645789): we probably shouldn't cancel the alarm until we've verified
+ // that all jobs for the userId-package are within quota.
mInQuotaAlarmQueue.removeAlarmForKey(new Package(userId, packageName));
} else {
mToScheduleStartAlarms.add(userId, packageName, realStandbyBucket);
@@ -1860,25 +1746,8 @@
standbyBucket);
final long remainingEJQuota = getRemainingEJExecutionTimeLocked(userId, packageName);
- int minPriority = JobInfo.PRIORITY_MAX;
- boolean hasDefPlus = false, hasLow = false, hasMin = false;
- for (int i = jobs.size() - 1; i >= 0; --i) {
- final int priority = jobs.valueAt(i).getEffectivePriority();
- minPriority = Math.min(minPriority, priority);
- if (priority <= JobInfo.PRIORITY_MIN) {
- hasMin = true;
- } else if (priority <= JobInfo.PRIORITY_LOW) {
- hasLow = true;
- } else {
- hasDefPlus = true;
- }
- if (hasMin && hasLow && hasDefPlus) {
- break;
- }
- }
final boolean inRegularQuota =
- stats.executionTimeInWindowMs
- < getAllowedTimePerPeriodMs(standbyBucket, minPriority)
+ stats.executionTimeInWindowMs < mAllowedTimePerPeriodMs[standbyBucket]
&& stats.executionTimeInMaxPeriodMs < mMaxExecutionTimeMs
&& isUnderJobCountQuota
&& isUnderTimingSessionCountQuota;
@@ -1900,24 +1769,7 @@
long inEJQuotaTimeElapsed = Long.MAX_VALUE;
if (!inRegularQuota) {
// The time this app will have quota again.
- long executionInQuotaTime = Long.MAX_VALUE;
- boolean hasExecutionInQuotaTime = false;
- if (hasMin && stats.inQuotaTimeMinElapsed > 0) {
- executionInQuotaTime = Math.min(executionInQuotaTime, stats.inQuotaTimeMinElapsed);
- hasExecutionInQuotaTime = true;
- }
- if (hasLow && stats.inQuotaTimeLowElapsed > 0) {
- executionInQuotaTime = Math.min(executionInQuotaTime, stats.inQuotaTimeLowElapsed);
- hasExecutionInQuotaTime = true;
- }
- if (hasDefPlus && stats.inQuotaTimeElapsed > 0) {
- executionInQuotaTime = Math.min(executionInQuotaTime, stats.inQuotaTimeElapsed);
- hasExecutionInQuotaTime = true;
- }
- long inQuotaTimeElapsed = 0;
- if (hasExecutionInQuotaTime) {
- inQuotaTimeElapsed = executionInQuotaTime;
- }
+ long inQuotaTimeElapsed = stats.inQuotaTimeElapsed;
if (!isUnderJobCountQuota && stats.bgJobCountInWindow < stats.jobCountLimit) {
// App hit the rate limit.
inQuotaTimeElapsed =
@@ -2130,7 +1982,6 @@
private final ArraySet<JobStatus> mRunningBgJobs = new ArraySet<>();
private long mStartTimeElapsed;
private int mBgJobCount;
- private int mLowestPriority = JobInfo.PRIORITY_MAX;
private long mDebitAdjustment;
Timer(int uid, int userId, String packageName, boolean regularJobTimer) {
@@ -2153,8 +2004,6 @@
Slog.v(TAG, "Starting to track " + jobStatus.toShortString());
}
// Always maintain list of running jobs, even when quota is free.
- final boolean priorityLowered = mLowestPriority > jobStatus.getEffectivePriority();
- mLowestPriority = Math.min(mLowestPriority, jobStatus.getEffectivePriority());
if (mRunningBgJobs.add(jobStatus) && shouldTrackLocked()) {
mBgJobCount++;
if (mRegularJobTimer) {
@@ -2170,8 +2019,6 @@
invalidateAllExecutionStatsLocked(mPkg.userId, mPkg.packageName);
}
scheduleCutoff();
- } else if (mRegularJobTimer && priorityLowered) {
- rescheduleCutoff();
}
}
}
@@ -2196,19 +2043,6 @@
&& !isQuotaFreeLocked(standbyBucket)) {
emitSessionLocked(nowElapsed);
cancelCutoff();
- mLowestPriority = JobInfo.PRIORITY_MAX;
- } else if (mRegularJobTimer
- && mLowestPriority == jobStatus.getEffectivePriority()) {
- // Lowest priority doesn't matter for EJ timers.
- final int oldPriority = mLowestPriority;
- mLowestPriority = JobInfo.PRIORITY_MAX;
- for (int i = mRunningBgJobs.size() - 1; i >= 0; --i) {
- mLowestPriority = Math.min(mLowestPriority,
- mRunningBgJobs.valueAt(i).getEffectivePriority());
- }
- if (mLowestPriority != oldPriority) {
- rescheduleCutoff();
- }
}
}
}
@@ -2335,14 +2169,9 @@
}
Message msg = mHandler.obtainMessage(
mRegularJobTimer ? MSG_REACHED_QUOTA : MSG_REACHED_EJ_QUOTA, mPkg);
- final long timeRemainingMs;
- if (mRegularJobTimer) {
- timeRemainingMs = getTimeUntilQuotaConsumedLocked(
- mPkg.userId, mPkg.packageName, mLowestPriority);
- } else {
- timeRemainingMs =
- getTimeUntilEJQuotaConsumedLocked(mPkg.userId, mPkg.packageName);
- }
+ final long timeRemainingMs = mRegularJobTimer
+ ? getTimeUntilQuotaConsumedLocked(mPkg.userId, mPkg.packageName)
+ : getTimeUntilEJQuotaConsumedLocked(mPkg.userId, mPkg.packageName);
if (DEBUG) {
Slog.i(TAG,
(mRegularJobTimer ? "Regular job" : "EJ") + " for " + mPkg + " has "
@@ -2695,19 +2524,26 @@
Slog.d(TAG, "Checking if " + pkg + " has reached its quota.");
}
- final ArraySet<JobStatus> changedJobs = maybeUpdateConstraintForPkgLocked(
- sElapsedRealtimeClock.millis(), pkg.userId, pkg.packageName);
- if (changedJobs.size() > 0) {
+ long timeRemainingMs = getRemainingExecutionTimeLocked(pkg.userId,
+ pkg.packageName);
+ if (timeRemainingMs <= 50) {
+ // Less than 50 milliseconds left. Start process of shutting down jobs.
if (DEBUG) Slog.d(TAG, pkg + " has reached its quota.");
- mStateChangedListener.onControllerStateChanged(changedJobs);
+ mStateChangedListener.onControllerStateChanged(
+ maybeUpdateConstraintForPkgLocked(
+ sElapsedRealtimeClock.millis(),
+ pkg.userId, pkg.packageName));
} else {
// This could potentially happen if an old session phases out while a
// job is currently running.
// Reschedule message
+ Message rescheduleMsg = obtainMessage(MSG_REACHED_QUOTA, pkg);
+ timeRemainingMs = getTimeUntilQuotaConsumedLocked(pkg.userId,
+ pkg.packageName);
if (DEBUG) {
- Slog.d(TAG, pkg + " had early REACHED_QUOTA message");
+ Slog.d(TAG, pkg + " has " + timeRemainingMs + "ms left.");
}
- mPkgTimers.get(pkg.userId, pkg.packageName).rescheduleCutoff();
+ sendMessageDelayed(rescheduleMsg, timeRemainingMs);
}
break;
}
@@ -2717,19 +2553,25 @@
Slog.d(TAG, "Checking if " + pkg + " has reached its EJ quota.");
}
- final ArraySet<JobStatus> changedJobs = maybeUpdateConstraintForPkgLocked(
- sElapsedRealtimeClock.millis(), pkg.userId, pkg.packageName);
- if (changedJobs.size() > 0) {
+ long timeRemainingMs = getRemainingEJExecutionTimeLocked(
+ pkg.userId, pkg.packageName);
+ if (timeRemainingMs <= 0) {
if (DEBUG) Slog.d(TAG, pkg + " has reached its EJ quota.");
- mStateChangedListener.onControllerStateChanged(changedJobs);
+ mStateChangedListener.onControllerStateChanged(
+ maybeUpdateConstraintForPkgLocked(
+ sElapsedRealtimeClock.millis(),
+ pkg.userId, pkg.packageName));
} else {
// This could potentially happen if an old session phases out while a
// job is currently running.
// Reschedule message
+ Message rescheduleMsg = obtainMessage(MSG_REACHED_EJ_QUOTA, pkg);
+ timeRemainingMs = getTimeUntilEJQuotaConsumedLocked(
+ pkg.userId, pkg.packageName);
if (DEBUG) {
- Slog.d(TAG, pkg + " had early REACHED_EJ_QUOTA message");
+ Slog.d(TAG, pkg + " has " + timeRemainingMs + "ms left for EJ");
}
- mEJPkgTimers.get(pkg.userId, pkg.packageName).rescheduleCutoff();
+ sendMessageDelayed(rescheduleMsg, timeRemainingMs);
}
break;
}
@@ -2993,12 +2835,6 @@
static final String KEY_IN_QUOTA_BUFFER_MS =
QC_CONSTANT_PREFIX + "in_quota_buffer_ms";
@VisibleForTesting
- static final String KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW =
- QC_CONSTANT_PREFIX + "allowed_time_surplus_priority_low";
- @VisibleForTesting
- static final String KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN =
- QC_CONSTANT_PREFIX + "allowed_time_surplus_priority_min";
- @VisibleForTesting
static final String KEY_WINDOW_SIZE_EXEMPTED_MS =
QC_CONSTANT_PREFIX + "window_size_exempted_ms";
@VisibleForTesting
@@ -3130,8 +2966,6 @@
10 * 60 * 1000L; // 10 minutes
private static final long DEFAULT_IN_QUOTA_BUFFER_MS =
30 * 1000L; // 30 seconds
- private static final float DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_LOW = .25f;
- private static final float DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_MIN = .5f;
private static final long DEFAULT_WINDOW_SIZE_EXEMPTED_MS =
DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS; // EXEMPT apps can run jobs at any time
private static final long DEFAULT_WINDOW_SIZE_ACTIVE_MS =
@@ -3230,22 +3064,6 @@
public long IN_QUOTA_BUFFER_MS = DEFAULT_IN_QUOTA_BUFFER_MS;
/**
- * The percentage of ALLOWED_TIME_PER_PERIOD_*_MS that should not be used by
- * {@link JobInfo#PRIORITY_LOW low priority} jobs. In other words, there must be a minimum
- * surplus of this amount of remaining allowed time before we start running low priority
- * jobs.
- */
- public float ALLOWED_TIME_SURPLUS_PRIORITY_LOW = DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_LOW;
-
- /**
- * The percentage of ALLOWED_TIME_PER_PERIOD_*_MS that should not be used by
- * {@link JobInfo#PRIORITY_MIN low priority} jobs. In other words, there must be a minimum
- * surplus of this amount of remaining allowed time before we start running min priority
- * jobs.
- */
- public float ALLOWED_TIME_SURPLUS_PRIORITY_MIN = DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_MIN;
-
- /**
* The quota window size of the particular standby bucket. Apps in this standby bucket are
* expected to run only {@link #ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS} within the past
* WINDOW_SIZE_MS.
@@ -3514,8 +3332,6 @@
case KEY_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS:
case KEY_ALLOWED_TIME_PER_PERIOD_RARE_MS:
case KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS:
- case KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW:
- case KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN:
case KEY_IN_QUOTA_BUFFER_MS:
case KEY_MAX_EXECUTION_TIME_MS:
case KEY_WINDOW_SIZE_ACTIVE_MS:
@@ -3757,7 +3573,6 @@
KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS, KEY_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS,
KEY_ALLOWED_TIME_PER_PERIOD_RARE_MS, KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
KEY_IN_QUOTA_BUFFER_MS,
- KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW, KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN,
KEY_MAX_EXECUTION_TIME_MS,
KEY_WINDOW_SIZE_EXEMPTED_MS, KEY_WINDOW_SIZE_ACTIVE_MS,
KEY_WINDOW_SIZE_WORKING_MS,
@@ -3781,12 +3596,6 @@
ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS =
properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
DEFAULT_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS);
- ALLOWED_TIME_SURPLUS_PRIORITY_LOW =
- properties.getFloat(KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW,
- DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_LOW);
- ALLOWED_TIME_SURPLUS_PRIORITY_MIN =
- properties.getFloat(KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN,
- DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_MIN);
IN_QUOTA_BUFFER_MS = properties.getLong(KEY_IN_QUOTA_BUFFER_MS,
DEFAULT_IN_QUOTA_BUFFER_MS);
MAX_EXECUTION_TIME_MS = properties.getLong(KEY_MAX_EXECUTION_TIME_MS,
@@ -3865,23 +3674,6 @@
mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs;
mShouldReevaluateConstraints = true;
}
- // Low priority surplus should be in the range [0, .9]. A value of 1 would essentially
- // mean never run low priority jobs.
- float newAllowedTimeSurplusPriorityLow =
- Math.max(0f, Math.min(.9f, ALLOWED_TIME_SURPLUS_PRIORITY_LOW));
- if (Float.compare(
- mAllowedTimeSurplusPriorityLow, newAllowedTimeSurplusPriorityLow) != 0) {
- mAllowedTimeSurplusPriorityLow = newAllowedTimeSurplusPriorityLow;
- mShouldReevaluateConstraints = true;
- }
- // Min priority surplus should be in the range [0, mAllowedTimeSurplusPriorityLow].
- float newAllowedTimeSurplusPriorityMin = Math.max(0f,
- Math.min(mAllowedTimeSurplusPriorityLow, ALLOWED_TIME_SURPLUS_PRIORITY_MIN));
- if (Float.compare(
- mAllowedTimeSurplusPriorityMin, newAllowedTimeSurplusPriorityMin) != 0) {
- mAllowedTimeSurplusPriorityMin = newAllowedTimeSurplusPriorityMin;
- mShouldReevaluateConstraints = true;
- }
long newExemptedPeriodMs = Math.max(mAllowedTimePerPeriodMs[EXEMPTED_INDEX],
Math.min(MAX_PERIOD_MS, WINDOW_SIZE_EXEMPTED_MS));
if (mBucketPeriodsMs[EXEMPTED_INDEX] != newExemptedPeriodMs) {
@@ -4081,10 +3873,6 @@
.println();
pw.print(KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS).println();
- pw.print(KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW, ALLOWED_TIME_SURPLUS_PRIORITY_LOW)
- .println();
- pw.print(KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN, ALLOWED_TIME_SURPLUS_PRIORITY_MIN)
- .println();
pw.print(KEY_IN_QUOTA_BUFFER_MS, IN_QUOTA_BUFFER_MS).println();
pw.print(KEY_WINDOW_SIZE_EXEMPTED_MS, WINDOW_SIZE_EXEMPTED_MS).println();
pw.print(KEY_WINDOW_SIZE_ACTIVE_MS, WINDOW_SIZE_ACTIVE_MS).println();
@@ -4210,16 +3998,6 @@
}
@VisibleForTesting
- float getAllowedTimeSurplusPriorityLow() {
- return mAllowedTimeSurplusPriorityLow;
- }
-
- @VisibleForTesting
- float getAllowedTimeSurplusPriorityMin() {
- return mAllowedTimeSurplusPriorityMin;
- }
-
- @VisibleForTesting
@NonNull
int[] getBucketMaxJobCounts() {
return mMaxBucketJobCounts;
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index cae6cdc..54c3db4 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -104,6 +104,7 @@
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import android.util.SparseLongArray;
import android.util.TimeUtils;
import android.view.Display;
import android.widget.Toast;
@@ -260,6 +261,13 @@
private final CountDownLatch mAdminDataAvailableLatch = new CountDownLatch(1);
+ /**
+ * Set of user IDs and the next time (in the elapsed realtime timebase) when we should check the
+ * apps' idle states.
+ */
+ @GuardedBy("mPendingIdleStateChecks")
+ private final SparseLongArray mPendingIdleStateChecks = new SparseLongArray();
+
// Cache the active network scorer queried from the network scorer service
private volatile String mCachedNetworkScorer = null;
// The last time the network scorer service was queried
@@ -722,7 +730,14 @@
@Override
public void postCheckIdleStates(int userId) {
- mHandler.sendMessage(mHandler.obtainMessage(MSG_CHECK_IDLE_STATES, userId, 0));
+ if (userId == UserHandle.USER_ALL) {
+ postOneTimeCheckIdleStates();
+ } else {
+ synchronized (mPendingIdleStateChecks) {
+ mPendingIdleStateChecks.put(userId, mInjector.elapsedRealtime());
+ }
+ mHandler.obtainMessage(MSG_CHECK_IDLE_STATES).sendToTarget();
+ }
}
@Override
@@ -2374,10 +2389,32 @@
break;
case MSG_CHECK_IDLE_STATES:
- if (checkIdleStates(msg.arg1) && mAppIdleEnabled) {
- mHandler.sendMessageDelayed(mHandler.obtainMessage(
- MSG_CHECK_IDLE_STATES, msg.arg1, 0),
- mCheckIdleIntervalMillis);
+ removeMessages(MSG_CHECK_IDLE_STATES);
+
+ long earliestCheck = Long.MAX_VALUE;
+ final long nowElapsed = mInjector.elapsedRealtime();
+ synchronized (mPendingIdleStateChecks) {
+ for (int i = mPendingIdleStateChecks.size() - 1; i >= 0; --i) {
+ long expirationTime = mPendingIdleStateChecks.valueAt(i);
+
+ if (expirationTime <= nowElapsed) {
+ final int userId = mPendingIdleStateChecks.keyAt(i);
+ if (checkIdleStates(userId) && mAppIdleEnabled) {
+ expirationTime = nowElapsed + mCheckIdleIntervalMillis;
+ mPendingIdleStateChecks.put(userId, expirationTime);
+ } else {
+ mPendingIdleStateChecks.removeAt(i);
+ continue;
+ }
+ }
+
+ earliestCheck = Math.min(earliestCheck, expirationTime);
+ }
+ }
+ if (earliestCheck != Long.MAX_VALUE) {
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(MSG_CHECK_IDLE_STATES),
+ earliestCheck - nowElapsed);
}
break;
diff --git a/cmds/incidentd/src/incidentd_util.cpp b/cmds/incidentd/src/incidentd_util.cpp
index 150ab99..ec0b79b 100644
--- a/cmds/incidentd/src/incidentd_util.cpp
+++ b/cmds/incidentd/src/incidentd_util.cpp
@@ -184,11 +184,26 @@
sigemptyset(&child_mask);
sigaddset(&child_mask, SIGCHLD);
+ // block SIGCHLD before we check if a process has exited
if (sigprocmask(SIG_BLOCK, &child_mask, &old_mask) == -1) {
- ALOGW("sigprocmask failed: %s", strerror(errno));
+ ALOGW("*** sigprocmask failed: %s\n", strerror(errno));
return false;
}
+ // if the child has exited already, handle and reset signals before leaving
+ pid_t child_pid = waitpid(pid, status, WNOHANG);
+ if (child_pid != pid) {
+ if (child_pid > 0) {
+ ALOGW("*** Waiting for pid %d, got pid %d instead\n", pid, child_pid);
+ sigprocmask(SIG_SETMASK, &old_mask, nullptr);
+ return false;
+ }
+ } else {
+ sigprocmask(SIG_SETMASK, &old_mask, nullptr);
+ return true;
+ }
+
+ // wait for a SIGCHLD
timespec ts;
ts.tv_sec = timeout_ms / 1000;
ts.tv_nsec = (timeout_ms % 1000) * 1000000;
@@ -197,7 +212,7 @@
// Set the signals back the way they were.
if (sigprocmask(SIG_SETMASK, &old_mask, nullptr) == -1) {
- ALOGW("sigprocmask failed: %s", strerror(errno));
+ ALOGW("*** sigprocmask failed: %s\n", strerror(errno));
if (ret == 0) {
return false;
}
@@ -207,21 +222,21 @@
if (errno == EAGAIN) {
errno = ETIMEDOUT;
} else {
- ALOGW("sigtimedwait failed: %s", strerror(errno));
+ ALOGW("*** sigtimedwait failed: %s\n", strerror(errno));
}
return false;
}
- pid_t child_pid = waitpid(pid, status, WNOHANG);
- if (child_pid == pid) {
- return true;
+ child_pid = waitpid(pid, status, WNOHANG);
+ if (child_pid != pid) {
+ if (child_pid != -1) {
+ ALOGW("*** Waiting for pid %d, got pid %d instead\n", pid, child_pid);
+ } else {
+ ALOGW("*** waitpid failed: %s\n", strerror(errno));
+ }
+ return false;
}
- if (child_pid == -1) {
- ALOGW("waitpid failed: %s", strerror(errno));
- } else {
- ALOGW("Waiting for pid %d, got pid %d instead", pid, child_pid);
- }
- return false;
+ return true;
}
status_t kill_child(pid_t pid) {
diff --git a/core/api/current.txt b/core/api/current.txt
index 00d7d80..27a45ac 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -10329,7 +10329,6 @@
field public static final String ACTION_VIEW_LOCUS = "android.intent.action.VIEW_LOCUS";
field @RequiresPermission(android.Manifest.permission.START_VIEW_PERMISSION_USAGE) public static final String ACTION_VIEW_PERMISSION_USAGE = "android.intent.action.VIEW_PERMISSION_USAGE";
field @RequiresPermission(android.Manifest.permission.START_VIEW_PERMISSION_USAGE) public static final String ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD = "android.intent.action.VIEW_PERMISSION_USAGE_FOR_PERIOD";
- field @RequiresPermission("android.permission.MANAGE_SENSOR_PRIVACY") public static final String ACTION_VIEW_SAFETY_HUB = "android.intent.action.VIEW_SAFETY_HUB";
field public static final String ACTION_VOICE_COMMAND = "android.intent.action.VOICE_COMMAND";
field @Deprecated public static final String ACTION_WALLPAPER_CHANGED = "android.intent.action.WALLPAPER_CHANGED";
field public static final String ACTION_WEB_SEARCH = "android.intent.action.WEB_SEARCH";
@@ -26831,7 +26830,7 @@
field public static final String EXTRA_ERROR_CLASS = "android.net.extra.ERROR_CLASS";
field public static final String EXTRA_ERROR_CODE = "android.net.extra.ERROR_CODE";
field public static final String EXTRA_SESSION_KEY = "android.net.extra.SESSION_KEY";
- field public static final String EXTRA_TIMESTAMP = "android.net.extra.TIMESTAMP";
+ field public static final String EXTRA_TIMESTAMP_MILLIS = "android.net.extra.TIMESTAMP_MILLIS";
field public static final String EXTRA_UNDERLYING_LINK_PROPERTIES = "android.net.extra.UNDERLYING_LINK_PROPERTIES";
field public static final String EXTRA_UNDERLYING_NETWORK = "android.net.extra.UNDERLYING_NETWORK";
field public static final String EXTRA_UNDERLYING_NETWORK_CAPABILITIES = "android.net.extra.UNDERLYING_NETWORK_CAPABILITIES";
@@ -31576,7 +31575,7 @@
method @Deprecated @Nullable public android.os.Parcelable.Creator<?> readParcelableCreator(@Nullable ClassLoader);
method @Nullable public <T> android.os.Parcelable.Creator<T> readParcelableCreator(@Nullable ClassLoader, @NonNull Class<T>);
method @Deprecated @NonNull public <T extends android.os.Parcelable> java.util.List<T> readParcelableList(@NonNull java.util.List<T>, @Nullable ClassLoader);
- method @NonNull public <T> java.util.List<T> readParcelableList(@NonNull java.util.List<T>, @Nullable ClassLoader, @NonNull Class<T>);
+ method @NonNull public <T> java.util.List<T> readParcelableList(@NonNull java.util.List<T>, @Nullable ClassLoader, @NonNull Class<? extends T>);
method @Nullable public android.os.PersistableBundle readPersistableBundle();
method @Nullable public android.os.PersistableBundle readPersistableBundle(@Nullable ClassLoader);
method @Deprecated @Nullable public java.io.Serializable readSerializable();
@@ -38081,6 +38080,7 @@
}
public final class Field {
+ method @Nullable public java.util.regex.Pattern getFilter();
method @Nullable public android.service.autofill.Presentations getPresentations();
method @Nullable public android.view.autofill.AutofillValue getValue();
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 83464f0..e611d6b 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2823,11 +2823,14 @@
public final class VirtualDeviceParams implements android.os.Parcelable {
method public int describeContents();
- method @Nullable public java.util.Set<android.content.ComponentName> getAllowedActivities();
- method @Nullable public java.util.Set<android.content.ComponentName> getBlockedActivities();
+ method @NonNull public java.util.Set<android.content.ComponentName> getAllowedActivities();
+ method @NonNull public java.util.Set<android.content.ComponentName> getBlockedActivities();
+ method public int getDefaultActivityPolicy();
method public int getLockState();
method @NonNull public java.util.Set<android.os.UserHandle> getUsersWithMatchingAccounts();
method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field public static final int ACTIVITY_POLICY_DEFAULT_ALLOWED = 0; // 0x0
+ field public static final int ACTIVITY_POLICY_DEFAULT_BLOCKED = 1; // 0x1
field @NonNull public static final android.os.Parcelable.Creator<android.companion.virtual.VirtualDeviceParams> CREATOR;
field public static final int LOCK_STATE_ALWAYS_UNLOCKED = 1; // 0x1
field public static final int LOCK_STATE_DEFAULT = 0; // 0x0
@@ -2836,8 +2839,8 @@
public static final class VirtualDeviceParams.Builder {
ctor public VirtualDeviceParams.Builder();
method @NonNull public android.companion.virtual.VirtualDeviceParams build();
- method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setAllowedActivities(@Nullable java.util.Set<android.content.ComponentName>);
- method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedActivities(@Nullable java.util.Set<android.content.ComponentName>);
+ method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setAllowedActivities(@NonNull java.util.Set<android.content.ComponentName>);
+ method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setBlockedActivities(@NonNull java.util.Set<android.content.ComponentName>);
method @NonNull @RequiresPermission(value=android.Manifest.permission.ADD_ALWAYS_UNLOCKED_DISPLAY, conditional=true) public android.companion.virtual.VirtualDeviceParams.Builder setLockState(int);
method @NonNull public android.companion.virtual.VirtualDeviceParams.Builder setUsersWithMatchingAccounts(@NonNull java.util.Set<android.os.UserHandle>);
}
@@ -3035,6 +3038,7 @@
field public static final String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED";
field public static final String ACTION_USER_SWITCHED = "android.intent.action.USER_SWITCHED";
field @RequiresPermission(android.Manifest.permission.START_VIEW_APP_FEATURES) public static final String ACTION_VIEW_APP_FEATURES = "android.intent.action.VIEW_APP_FEATURES";
+ field @RequiresPermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY) public static final String ACTION_VIEW_SAFETY_CENTER_QS = "android.intent.action.VIEW_SAFETY_CENTER_QS";
field public static final String ACTION_VOICE_ASSIST = "android.intent.action.VOICE_ASSIST";
field public static final String CATEGORY_LEANBACK_SETTINGS = "android.intent.category.LEANBACK_SETTINGS";
field public static final String EXTRA_CALLING_PACKAGE = "android.intent.extra.CALLING_PACKAGE";
@@ -5908,16 +5912,16 @@
method @IntRange(from=0, to=1023) public int getIssueOfDataClock();
method @IntRange(from=0, to=255) public int getIssueOfDataEphemeris();
method @Nullable public android.location.SatellitePvt.PositionEcef getPositionEcef();
- method @IntRange(from=0) public long getTimeOfClock();
- method @IntRange(from=0) public long getTimeOfEphemeris();
+ method @IntRange(from=0) public long getTimeOfClockSeconds();
+ method @IntRange(from=0) public long getTimeOfEphemerisSeconds();
method @FloatRange public double getTropoDelayMeters();
method @Nullable public android.location.SatellitePvt.VelocityEcef getVelocityEcef();
method public boolean hasIono();
method public boolean hasIssueOfDataClock();
method public boolean hasIssueOfDataEphemeris();
method public boolean hasPositionVelocityClockInfo();
- method public boolean hasTimeOfClock();
- method public boolean hasTimeOfEphemeris();
+ method public boolean hasTimeOfClockSeconds();
+ method public boolean hasTimeOfEphemerisSeconds();
method public boolean hasTropo();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.location.SatellitePvt> CREATOR;
@@ -5936,8 +5940,8 @@
method @NonNull public android.location.SatellitePvt.Builder setIssueOfDataClock(@IntRange(from=0, to=1023) int);
method @NonNull public android.location.SatellitePvt.Builder setIssueOfDataEphemeris(@IntRange(from=0, to=255) int);
method @NonNull public android.location.SatellitePvt.Builder setPositionEcef(@NonNull android.location.SatellitePvt.PositionEcef);
- method @NonNull public android.location.SatellitePvt.Builder setTimeOfClock(@IntRange(from=0) long);
- method @NonNull public android.location.SatellitePvt.Builder setTimeOfEphemeris(@IntRange(from=0) int);
+ method @NonNull public android.location.SatellitePvt.Builder setTimeOfClockSeconds(@IntRange(from=0) long);
+ method @NonNull public android.location.SatellitePvt.Builder setTimeOfEphemerisSeconds(@IntRange(from=0) long);
method @NonNull public android.location.SatellitePvt.Builder setTropoDelayMeters(@FloatRange(from=0.0f, to=100.0f) double);
method @NonNull public android.location.SatellitePvt.Builder setVelocityEcef(@NonNull android.location.SatellitePvt.VelocityEcef);
}
@@ -6104,7 +6108,7 @@
public class AudioManager {
method @Deprecated public int abandonAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, android.media.AudioAttributes);
- method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addAssistantServicesUids(@NonNull java.util.List<java.lang.Integer>);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addAssistantServicesUids(@NonNull int[]);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDeviceForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener) throws java.lang.SecurityException;
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDevicesForCapturePresetChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDevicesForCapturePresetChangedListener) throws java.lang.SecurityException;
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void addOnPreferredDevicesForStrategyChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.OnPreferredDevicesForStrategyChangedListener) throws java.lang.SecurityException;
@@ -6112,9 +6116,9 @@
method public void clearAudioServerStateCallback();
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean clearPreferredDevicesForCapturePreset(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int dispatchAudioFocusChange(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
- method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<java.lang.Integer> getActiveAssistantServicesUids();
+ method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int[] getActiveAssistantServicesUids();
method @IntRange(from=0) public long getAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo);
- method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public java.util.List<java.lang.Integer> getAssistantServicesUids();
+ method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int[] getAssistantServicesUids();
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioProductStrategy> getAudioProductStrategies();
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioVolumeGroup> getAudioVolumeGroups();
method @NonNull @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public android.media.AudioRecord getCallDownlinkExtractionAudioRecord(@NonNull android.media.AudioFormat);
@@ -6139,7 +6143,7 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int registerAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void registerMuteAwaitConnectionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.MuteAwaitConnectionCallback);
method public void registerVolumeGroupCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.VolumeGroupCallback);
- method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeAssistantServicesUids(@NonNull java.util.List<java.lang.Integer>);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeAssistantServicesUids(@NonNull int[]);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDeviceForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDeviceForStrategyChangedListener);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDevicesForCapturePresetChangedListener(@NonNull android.media.AudioManager.OnPreferredDevicesForCapturePresetChangedListener);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeOnPreferredDevicesForStrategyChangedListener(@NonNull android.media.AudioManager.OnPreferredDevicesForStrategyChangedListener);
@@ -6147,7 +6151,7 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int) throws java.lang.IllegalArgumentException;
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.MODIFY_AUDIO_ROUTING}) public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, @NonNull android.media.AudioAttributes, int, int, android.media.audiopolicy.AudioPolicy) throws java.lang.IllegalArgumentException;
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int requestAudioFocus(@NonNull android.media.AudioFocusRequest, @Nullable android.media.audiopolicy.AudioPolicy);
- method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setActiveAssistantServiceUids(@NonNull java.util.List<java.lang.Integer>);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setActiveAssistantServiceUids(@NonNull int[]);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setAdditionalOutputDeviceDelay(@NonNull android.media.AudioDeviceInfo, @IntRange(from=0) long);
method public void setAudioServerStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.AudioServerStateCallback);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes, int);
@@ -8528,14 +8532,21 @@
}
public final class EthernetNetworkUpdateRequest implements android.os.Parcelable {
- ctor public EthernetNetworkUpdateRequest(@NonNull android.net.StaticIpConfiguration, @NonNull android.net.NetworkCapabilities);
method public int describeContents();
- method @NonNull public android.net.StaticIpConfiguration getIpConfig();
+ method @NonNull public android.net.IpConfiguration getIpConfiguration();
method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.EthernetNetworkUpdateRequest> CREATOR;
}
+ public static final class EthernetNetworkUpdateRequest.Builder {
+ ctor public EthernetNetworkUpdateRequest.Builder();
+ ctor public EthernetNetworkUpdateRequest.Builder(@NonNull android.net.EthernetNetworkUpdateRequest);
+ method @NonNull public android.net.EthernetNetworkUpdateRequest build();
+ method @NonNull public android.net.EthernetNetworkUpdateRequest.Builder setIpConfiguration(@NonNull android.net.IpConfiguration);
+ method @NonNull public android.net.EthernetNetworkUpdateRequest.Builder setNetworkCapabilities(@NonNull android.net.NetworkCapabilities);
+ }
+
public final class MatchAllNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
ctor public MatchAllNetworkSpecifier();
method public int describeContents();
@@ -10318,6 +10329,7 @@
field public static final String NAMESPACE_RUNTIME_NATIVE = "runtime_native";
field public static final String NAMESPACE_RUNTIME_NATIVE_BOOT = "runtime_native_boot";
field public static final String NAMESPACE_SCHEDULER = "scheduler";
+ field public static final String NAMESPACE_SDK_SANDBOX = "sdk_sandbox";
field public static final String NAMESPACE_STATSD_JAVA = "statsd_java";
field public static final String NAMESPACE_STATSD_JAVA_BOOT = "statsd_java_boot";
field public static final String NAMESPACE_STATSD_NATIVE = "statsd_native";
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index b752c4d..22637ca 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1073,7 +1073,7 @@
field public static final int DIALOG = 3; // 0x3
field public static final int OTHER = 5; // 0x5
field public static final int QS_TILE = 1; // 0x1
- field public static final int SAFETY_HUB = 6; // 0x6
+ field public static final int SAFETY_CENTER = 6; // 0x6
field public static final int SETTINGS = 2; // 0x2
field public static final int SHELL = 4; // 0x4
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 11663a5..bc979fc 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -7416,7 +7416,6 @@
} else {
mDumpableContainer.listDumpables(prefix, writer);
}
- mDumpableContainer.listDumpables(prefix, writer);
return;
case DUMP_ARG_DUMP_DUMPABLE:
if (args.length == 1) {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 61d1865..64f0301 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4469,12 +4469,6 @@
// we are back active so skip it.
unscheduleGcIdler();
- // To investigate "duplciate Application objects" bug (b/185177290)
- if (UserHandle.myUserId() != UserHandle.getUserId(data.info.applicationInfo.uid)) {
- Slog.wtf(TAG, "handleCreateService called with wrong appinfo UID: myUserId="
- + UserHandle.myUserId() + " appinfo.uid=" + data.info.applicationInfo.uid);
- }
-
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
Service service = null;
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 99a523a..cf259e57 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -160,6 +160,13 @@
private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mUnboundServices
= new ArrayMap<>();
private AppComponentFactory mAppComponentFactory;
+
+ /**
+ * We cache the instantiated application object for each package on this process here.
+ */
+ @GuardedBy("sApplications")
+ private static final ArrayMap<String, Application> sApplications = new ArrayMap<>(4);
+
private final Object mLock = new Object();
Application getApplication() {
@@ -1345,14 +1352,6 @@
return mResources;
}
- /**
- * Used to investigate "duplicate app objects" bug (b/185177290).
- * makeApplication() should only be called on the main thread, so no synchronization should
- * be needed, but syncing anyway just in case.
- */
- @GuardedBy("sApplicationCache")
- private static final ArrayMap<String, Application> sApplicationCache = new ArrayMap<>(4);
-
@UnsupportedAppUsage
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
@@ -1361,15 +1360,8 @@
}
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "makeApplication");
- // For b/185177290.
- final boolean wrongUser =
- UserHandle.myUserId() != UserHandle.getUserId(mApplicationInfo.uid);
- if (wrongUser) {
- Slog.wtf(TAG, "makeApplication called with wrong appinfo UID: myUserId="
- + UserHandle.myUserId() + " appinfo.uid=" + mApplicationInfo.uid);
- }
- synchronized (sApplicationCache) {
- final Application cached = sApplicationCache.get(mPackageName);
+ synchronized (sApplications) {
+ final Application cached = sApplications.get(mPackageName);
if (cached != null) {
// Looks like this is always happening for the system server, because
// the LoadedApk created in systemMain() -> attach() isn't cached properly?
@@ -1377,8 +1369,8 @@
Slog.wtf(TAG, "App instance already created for package=" + mPackageName
+ " instance=" + cached);
}
- // TODO Return the cached one, unles it's for the wrong user?
- // For now, we just add WTF checks.
+ mApplication = cached;
+ return cached;
}
}
@@ -1429,8 +1421,8 @@
}
mActivityThread.mAllApplications.add(app);
mApplication = app;
- synchronized (sApplicationCache) {
- sApplicationCache.put(mPackageName, app);
+ synchronized (sApplications) {
+ sApplications.put(mPackageName, app);
}
if (instrumentation != null) {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 9dd206e..7e0cea89 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -9009,6 +9009,10 @@
* information that will replace the default values for the output switcher chip on the
* media control, as well as an intent to use when the output switcher chip is tapped,
* on devices where this is supported.
+ * <p>
+ * This method is intended for system applications to provide information and/or
+ * functionality that would otherwise be unavailable to the default output switcher because
+ * the media originated on a remote device.
*
* @param deviceName The name of the remote device to display
* @param iconResource Icon resource representing the device
diff --git a/core/java/android/app/admin/WifiSsidPolicy.aidl b/core/java/android/app/admin/WifiSsidPolicy.aidl
new file mode 100644
index 0000000..150705d
--- /dev/null
+++ b/core/java/android/app/admin/WifiSsidPolicy.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.admin;
+
+parcelable WifiSsidPolicy;
\ No newline at end of file
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
index 45d0ad5..41b1a1f 100644
--- a/core/java/android/companion/virtual/VirtualDeviceParams.java
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -20,9 +20,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.RequiresPermission;
-import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.content.ComponentName;
import android.os.Parcel;
@@ -64,20 +62,43 @@
*/
public static final int LOCK_STATE_ALWAYS_UNLOCKED = 1;
+ /** @hide */
+ @IntDef(prefix = "ACTIVITY_POLICY_",
+ value = {ACTIVITY_POLICY_DEFAULT_ALLOWED, ACTIVITY_POLICY_DEFAULT_BLOCKED})
+ @Retention(RetentionPolicy.SOURCE)
+ @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
+ public @interface ActivityPolicy {}
+
+ /**
+ * Indicates that activities are allowed by default on this virtual device, unless they are
+ * explicitly blocked by {@link Builder#setBlockedActivities}.
+ */
+ public static final int ACTIVITY_POLICY_DEFAULT_ALLOWED = 0;
+
+ /**
+ * Indicates that activities are blocked by default on this virtual device, unless they are
+ * allowed by {@link Builder#setAllowedActivities}.
+ */
+ public static final int ACTIVITY_POLICY_DEFAULT_BLOCKED = 1;
+
private final int mLockState;
private final ArraySet<UserHandle> mUsersWithMatchingAccounts;
- @Nullable private final ArraySet<ComponentName> mAllowedActivities;
- @Nullable private final ArraySet<ComponentName> mBlockedActivities;
+ @NonNull private final ArraySet<ComponentName> mAllowedActivities;
+ @NonNull private final ArraySet<ComponentName> mBlockedActivities;
+ @ActivityPolicy
+ private final int mDefaultActivityPolicy;
private VirtualDeviceParams(
@LockState int lockState,
@NonNull Set<UserHandle> usersWithMatchingAccounts,
- @Nullable Set<ComponentName> allowedActivities,
- @Nullable Set<ComponentName> blockedActivities) {
+ @NonNull Set<ComponentName> allowedActivities,
+ @NonNull Set<ComponentName> blockedActivities,
+ @ActivityPolicy int defaultActivityPolicy) {
mLockState = lockState;
mUsersWithMatchingAccounts = new ArraySet<>(usersWithMatchingAccounts);
mAllowedActivities = allowedActivities == null ? null : new ArraySet<>(allowedActivities);
mBlockedActivities = blockedActivities == null ? null : new ArraySet<>(blockedActivities);
+ mDefaultActivityPolicy = defaultActivityPolicy;
}
@SuppressWarnings("unchecked")
@@ -86,6 +107,7 @@
mUsersWithMatchingAccounts = (ArraySet<UserHandle>) parcel.readArraySet(null);
mAllowedActivities = (ArraySet<ComponentName>) parcel.readArraySet(null);
mBlockedActivities = (ArraySet<ComponentName>) parcel.readArraySet(null);
+ mDefaultActivityPolicy = parcel.readInt();
}
/**
@@ -113,12 +135,10 @@
*
* @see Builder#setAllowedActivities(Set)
*/
- // Null and empty have different semantics - Null allows all activities to be streamed
- @SuppressLint("NullableCollection")
- @Nullable
+ @NonNull
public Set<ComponentName> getAllowedActivities() {
if (mAllowedActivities == null) {
- return null;
+ return Collections.emptySet();
}
return Collections.unmodifiableSet(mAllowedActivities);
}
@@ -129,16 +149,27 @@
*
* @see Builder#setBlockedActivities(Set)
*/
- // Allowing null to enforce that at most one of allowed / blocked activities can be non-null
- @SuppressLint("NullableCollection")
- @Nullable
+ @NonNull
public Set<ComponentName> getBlockedActivities() {
if (mBlockedActivities == null) {
- return null;
+ return Collections.emptySet();
}
return Collections.unmodifiableSet(mBlockedActivities);
}
+ /**
+ * Returns {@link #ACTIVITY_POLICY_DEFAULT_ALLOWED} if activities are allowed to launch on this
+ * virtual device by default, or {@link #ACTIVITY_POLICY_DEFAULT_BLOCKED} if activities must be
+ * allowed by {@link Builder#setAllowedActivities} to launch here.
+ *
+ * @see Builder#setBlockedActivities
+ * @see Builder#setAllowedActivities
+ */
+ @ActivityPolicy
+ public int getDefaultActivityPolicy() {
+ return mDefaultActivityPolicy;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -150,6 +181,7 @@
dest.writeArraySet(mUsersWithMatchingAccounts);
dest.writeArraySet(mAllowedActivities);
dest.writeArraySet(mBlockedActivities);
+ dest.writeInt(mDefaultActivityPolicy);
}
@Override
@@ -164,12 +196,15 @@
return mLockState == that.mLockState
&& mUsersWithMatchingAccounts.equals(that.mUsersWithMatchingAccounts)
&& Objects.equals(mAllowedActivities, that.mAllowedActivities)
- && Objects.equals(mBlockedActivities, that.mBlockedActivities);
+ && Objects.equals(mBlockedActivities, that.mBlockedActivities)
+ && mDefaultActivityPolicy == that.mDefaultActivityPolicy;
}
@Override
public int hashCode() {
- return Objects.hash(mLockState, mUsersWithMatchingAccounts);
+ return Objects.hash(
+ mLockState, mUsersWithMatchingAccounts, mAllowedActivities, mBlockedActivities,
+ mDefaultActivityPolicy);
}
@Override
@@ -180,6 +215,7 @@
+ " mUsersWithMatchingAccounts=" + mUsersWithMatchingAccounts
+ " mAllowedActivities=" + mAllowedActivities
+ " mBlockedActivities=" + mBlockedActivities
+ + " mDefaultActivityPolicy=" + mDefaultActivityPolicy
+ ")";
}
@@ -202,8 +238,11 @@
private @LockState int mLockState = LOCK_STATE_DEFAULT;
private Set<UserHandle> mUsersWithMatchingAccounts;
- @Nullable private Set<ComponentName> mBlockedActivities;
- @Nullable private Set<ComponentName> mAllowedActivities;
+ @NonNull private Set<ComponentName> mBlockedActivities = Collections.emptySet();
+ @NonNull private Set<ComponentName> mAllowedActivities = Collections.emptySet();
+ @ActivityPolicy
+ private int mDefaultActivityPolicy = ACTIVITY_POLICY_DEFAULT_ALLOWED;
+ private boolean mDefaultActivityPolicyConfigured = false;
/**
* Sets the lock state of the device. The permission {@code ADD_ALWAYS_UNLOCKED_DISPLAY}
@@ -248,53 +287,53 @@
}
/**
- * Sets the activities allowed to be launched in the virtual device. If
- * {@code allowedActivities} is non-null, all activities other than the ones in the set will
- * be blocked from launching.
+ * Sets the activities allowed to be launched in the virtual device. Calling this method
+ * will cause {@link #getDefaultActivityPolicy()} to be
+ * {@link #ACTIVITY_POLICY_DEFAULT_BLOCKED}, meaning activities not in
+ * {@code allowedActivities} will be blocked from launching here.
*
- * <p>{@code allowedActivities} and the set in {@link #setBlockedActivities(Set)} cannot
- * both be non-null at the same time.
+ * <p>This method must not be called if {@link #setBlockedActivities(Set)} has been called.
*
- * @throws IllegalArgumentException if {@link #setBlockedActivities(Set)} has been set to a
- * non-null value.
+ * @throws IllegalArgumentException if {@link #setBlockedActivities(Set)} has been called.
*
* @param allowedActivities A set of activity {@link ComponentName} allowed to be launched
* in the virtual device.
*/
- // Null and empty have different semantics - Null allows all activities to be streamed
- @SuppressLint("NullableCollection")
@NonNull
- public Builder setAllowedActivities(@Nullable Set<ComponentName> allowedActivities) {
- if (mBlockedActivities != null && allowedActivities != null) {
+ public Builder setAllowedActivities(@NonNull Set<ComponentName> allowedActivities) {
+ if (mDefaultActivityPolicyConfigured
+ && mDefaultActivityPolicy != ACTIVITY_POLICY_DEFAULT_BLOCKED) {
throw new IllegalArgumentException(
"Allowed activities and Blocked activities cannot both be set.");
}
+ mDefaultActivityPolicy = ACTIVITY_POLICY_DEFAULT_BLOCKED;
+ mDefaultActivityPolicyConfigured = true;
mAllowedActivities = allowedActivities;
return this;
}
/**
- * Sets the activities blocked from launching in the virtual device. If the {@code
- * blockedActivities} is non-null, activities in the set are blocked from launching in the
- * virtual device.
+ * Sets the activities blocked from launching in the virtual device. Calling this method
+ * will cause {@link #getDefaultActivityPolicy()} to be
+ * {@link #ACTIVITY_POLICY_DEFAULT_ALLOWED}, meaning activities are allowed to launch here
+ * unless they are in {@code blockedActivities}.
*
- * {@code blockedActivities} and the set in {@link #setAllowedActivities(Set)} cannot both
- * be non-null at the same time.
+ * <p>This method must not be called if {@link #setAllowedActivities(Set)} has been called.
*
- * @throws IllegalArgumentException if {@link #setAllowedActivities(Set)} has been set to a
- * non-null value.
+ * @throws IllegalArgumentException if {@link #setAllowedActivities(Set)} has been called.
*
* @param blockedActivities A set of {@link ComponentName} to be blocked launching from
* virtual device.
*/
- // Allowing null to enforce that at most one of allowed / blocked activities can be non-null
- @SuppressLint("NullableCollection")
@NonNull
- public Builder setBlockedActivities(@Nullable Set<ComponentName> blockedActivities) {
- if (mAllowedActivities != null && blockedActivities != null) {
+ public Builder setBlockedActivities(@NonNull Set<ComponentName> blockedActivities) {
+ if (mDefaultActivityPolicyConfigured
+ && mDefaultActivityPolicy != ACTIVITY_POLICY_DEFAULT_ALLOWED) {
throw new IllegalArgumentException(
"Allowed activities and Blocked activities cannot both be set.");
}
+ mDefaultActivityPolicy = ACTIVITY_POLICY_DEFAULT_ALLOWED;
+ mDefaultActivityPolicyConfigured = true;
mBlockedActivities = blockedActivities;
return this;
}
@@ -307,13 +346,12 @@
if (mUsersWithMatchingAccounts == null) {
mUsersWithMatchingAccounts = Collections.emptySet();
}
- if (mAllowedActivities != null && mBlockedActivities != null) {
- // Should never reach here because the setters block this as well.
- throw new IllegalStateException(
- "Allowed activities and Blocked activities cannot both be set.");
- }
- return new VirtualDeviceParams(mLockState, mUsersWithMatchingAccounts,
- mAllowedActivities, mBlockedActivities);
+ return new VirtualDeviceParams(
+ mLockState,
+ mUsersWithMatchingAccounts,
+ mAllowedActivities,
+ mBlockedActivities,
+ mDefaultActivityPolicy);
}
}
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 0aa25ef..478befd 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2054,7 +2054,7 @@
"android.intent.action.VIEW_PERMISSION_USAGE_FOR_PERIOD";
/**
- * Activity action: Launch the Safety Hub UI.
+ * Activity action: Launch the Safety Center Quick Settings UI.
*
* <p>
* Input: Nothing.
@@ -2062,11 +2062,14 @@
* <p>
* Output: Nothing.
* </p>
+ *
+ * @hide
*/
+ @SystemApi
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
@RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
- public static final String ACTION_VIEW_SAFETY_HUB =
- "android.intent.action.VIEW_SAFETY_HUB";
+ public static final String ACTION_VIEW_SAFETY_CENTER_QS =
+ "android.intent.action.VIEW_SAFETY_CENTER_QS";
/**
* Activity action: Launch UI to manage a default app.
diff --git a/core/java/android/hardware/SensorPrivacyManager.java b/core/java/android/hardware/SensorPrivacyManager.java
index a3cc01c..0460e58 100644
--- a/core/java/android/hardware/SensorPrivacyManager.java
+++ b/core/java/android/hardware/SensorPrivacyManager.java
@@ -136,9 +136,9 @@
public static final int OTHER = SensorPrivacyToggleSourceProto.OTHER;
/**
- * Constant for SAFETY_HUB.
+ * Constant for SAFETY_CENTER.
*/
- public static final int SAFETY_HUB = SensorPrivacyToggleSourceProto.SAFETY_HUB;
+ public static final int SAFETY_CENTER = SensorPrivacyToggleSourceProto.SAFETY_CENTER;
/**
* Source for toggling sensors
@@ -151,7 +151,7 @@
DIALOG,
SHELL,
OTHER,
- SAFETY_HUB
+ SAFETY_CENTER
})
@Retention(RetentionPolicy.SOURCE)
public @interface Source {}
@@ -652,7 +652,7 @@
String packageName = mContext.getOpPackageName();
if (Objects.equals(packageName,
mContext.getPackageManager().getPermissionControllerPackageName())) {
- return Sources.SAFETY_HUB;
+ return Sources.SAFETY_CENTER;
}
return Sources.OTHER;
}
diff --git a/core/java/android/net/VpnManager.java b/core/java/android/net/VpnManager.java
index 779d931..c51444c 100644
--- a/core/java/android/net/VpnManager.java
+++ b/core/java/android/net/VpnManager.java
@@ -108,7 +108,7 @@
* <ul>
* <li>{@link #EXTRA_SESSION_KEY}, a {@code String} for the session key, as returned by
* {@link #startProvisionedVpnProfileSession}.
- * <li>{@link #EXTRA_TIMESTAMP}, a long for the timestamp at which the error occurred,
+ * <li>{@link #EXTRA_TIMESTAMP_MILLIS}, a long for the timestamp at which the error occurred,
* in milliseconds since the epoch, as returned by
* {@link java.lang.System#currentTimeMillis}.
* <li>{@link #EXTRA_UNDERLYING_NETWORK}, a {@link Network} containing the underlying
@@ -196,7 +196,7 @@
* This is a number of milliseconds since the epoch, suitable to be compared with
* {@link java.lang.System#currentTimeMillis}.
*/
- public static final String EXTRA_TIMESTAMP = "android.net.extra.TIMESTAMP";
+ public static final String EXTRA_TIMESTAMP_MILLIS = "android.net.extra.TIMESTAMP_MILLIS";
/**
* Extra for the error class, as an {@code int}.
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 09cfb6e..978e99d 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -201,7 +201,7 @@
* The methods to use are {@link #writeFileDescriptor(FileDescriptor)},
* {@link #readFileDescriptor()}.
*
- * <h3>Untyped Containers</h3>
+ * <h3>Parcelable Containers</h3>
*
* <p>A final class of methods are for writing and reading standard Java
* containers of arbitrary types. These all revolve around the
@@ -213,6 +213,19 @@
* {@link #writeMap(Map)}, {@link #readMap(Map, ClassLoader)},
* {@link #writeSparseArray(SparseArray)},
* {@link #readSparseArray(ClassLoader)}.
+ *
+ * <h3>Restricted Parcelable Containers</h3>
+ *
+ * <p>A final class of methods are for reading standard Java containers of restricted types.
+ * These methods replace methods for reading containers of arbitrary types from previous section
+ * starting from Android {@link Build.VERSION_CODES#TIRAMISU}. The pairing writing methods are
+ * still the same from previous section.
+ * These methods accepts additional {@code clazz} parameters as the required types.
+ * The Restricted Parcelable container methods are {@link #readArray(ClassLoader, Class)},
+ * {@link #readList(List, ClassLoader, Class)},
+ * {@link #readArrayList(ClassLoader, Class)},
+ * {@link #readMap(Map, ClassLoader, Class, Class)},
+ * {@link #readSparseArray(ClassLoader, Class)}.
*/
public final class Parcel {
@@ -3839,7 +3852,7 @@
*/
@NonNull
public <T> List<T> readParcelableList(@NonNull List<T> list,
- @Nullable ClassLoader cl, @NonNull Class<T> clazz) {
+ @Nullable ClassLoader cl, @NonNull Class<? extends T> clazz) {
Objects.requireNonNull(list);
Objects.requireNonNull(clazz);
return readParcelableListInternal(list, cl, clazz);
@@ -3850,7 +3863,7 @@
*/
@NonNull
private <T> List<T> readParcelableListInternal(@NonNull List<T> list,
- @Nullable ClassLoader cl, @Nullable Class<T> clazz) {
+ @Nullable ClassLoader cl, @Nullable Class<? extends T> clazz) {
final int n = readInt();
if (n == -1) {
list.clear();
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 52b6944..afd1283 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -407,6 +407,13 @@
public static final String NAMESPACE_SCHEDULER = "scheduler";
/**
+ * Namespace for all SdkSandbox related features.
+ * @hide
+ */
+ @SystemApi
+ public static final String NAMESPACE_SDK_SANDBOX = "sdk_sandbox";
+
+ /**
* Namespace for settings statistics features.
*
* @hide
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 528156f..8feff16 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9948,6 +9948,14 @@
public static final String LOCKSCREEN_SHOW_CONTROLS = "lockscreen_show_controls";
/**
+ * Whether trivial home controls can be used without authentication
+ *
+ * @hide
+ */
+ public static final String LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS =
+ "lockscreen_allow_trivial_controls";
+
+ /**
* Whether wallet should be accessible from the lockscreen
*
* @hide
@@ -9963,6 +9971,13 @@
"lockscreen_use_double_line_clock";
/**
+ * Whether to show the vibrate icon in the Status Bar (default off)
+ *
+ * @hide
+ */
+ public static final String STATUS_BAR_SHOW_VIBRATE_ICON = "status_bar_show_vibrate_icon";
+
+ /**
* Specifies whether the web action API is enabled.
*
* @hide
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index cfb6909..b701e07 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -905,7 +905,7 @@
if (field == null) {
setLifeTheUniverseAndEverything(id, null, null, null, null, null, null);
} else {
- final DatasetFieldFilter filter = field.getFilter();
+ final DatasetFieldFilter filter = field.getDatasetFieldFilter();
final Presentations presentations = field.getPresentations();
if (presentations == null) {
setLifeTheUniverseAndEverything(id, field.getValue(), null, null, null,
diff --git a/core/java/android/service/autofill/Field.java b/core/java/android/service/autofill/Field.java
index b7c0d82..8c905a6 100644
--- a/core/java/android/service/autofill/Field.java
+++ b/core/java/android/service/autofill/Field.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SuppressLint;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
@@ -86,11 +85,21 @@
* @see Dataset.DatasetFieldFilter
* @hide
*/
- public @Nullable Dataset.DatasetFieldFilter getFilter() {
+ public @Nullable Dataset.DatasetFieldFilter getDatasetFieldFilter() {
return mFilter;
}
/**
+ * Regex used to determine if the dataset should be shown in the autofill UI;
+ * when {@code null}, it disables filtering on that dataset (this is the recommended
+ * approach when {@code value} is not {@code null} and field contains sensitive data
+ * such as passwords).
+ */
+ public @Nullable Pattern getFilter() {
+ return mFilter == null ? null : mFilter.pattern;
+ }
+
+ /**
* The presentations used to visualize this field in Autofill UI.
*/
public @Nullable Presentations getPresentations() {
@@ -127,7 +136,6 @@
* approach when {@code value} is not {@code null} and field contains sensitive data
* such as passwords).
*/
- @SuppressLint("MissingGetterMatchingBuilder")
public @NonNull Builder setFilter(@Nullable Pattern value) {
checkNotUsed();
mFilter = new Dataset.DatasetFieldFilter(value);
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index db622d3..8670759 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -231,7 +231,7 @@
* The default value for whether to show complications on the overlay.
* @hide
*/
- public static final boolean DEFAULT_SHOW_COMPLICATIONS = true;
+ public static final boolean DEFAULT_SHOW_COMPLICATIONS = false;
private final IDreamManager mDreamManager;
private final Handler mHandler = new Handler(Looper.getMainLooper());
diff --git a/core/java/android/util/NtpTrustedTime.java b/core/java/android/util/NtpTrustedTime.java
index 4ac3178..8604078 100644
--- a/core/java/android/util/NtpTrustedTime.java
+++ b/core/java/android/util/NtpTrustedTime.java
@@ -81,7 +81,16 @@
/** Calculates and returns the age of this result. */
public long getAgeMillis() {
- return SystemClock.elapsedRealtime() - mElapsedRealtimeMillis;
+ return getAgeMillis(SystemClock.elapsedRealtime());
+ }
+
+ /**
+ * Calculates and returns the age of this result relative to currentElapsedRealtimeMillis.
+ *
+ * @param currentElapsedRealtimeMillis - reference elapsed real time
+ */
+ public long getAgeMillis(long currentElapsedRealtimeMillis) {
+ return currentElapsedRealtimeMillis - mElapsedRealtimeMillis;
}
@Override
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 53b842a..f8a1b45 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -955,4 +955,10 @@
* @hide
*/
Bitmap snapshotTaskForRecents(int taskId);
+
+ /**
+ * Informs the system whether the recents app is currently behind the system bars. If so,
+ * means the recents app can control the SystemUI flags, and vice-versa.
+ */
+ void setRecentsAppBehindSystemBars(boolean behindSystemBars);
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 55300b3..91d5b20 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1789,4 +1789,11 @@
t.apply();
}
}
+
+ /**
+ * @hide
+ */
+ public void syncNextFrame(Transaction t) {
+ mBlastBufferQueue.setSyncTransaction(t);
+ }
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 34a1386..553c537 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8226,9 +8226,25 @@
*/
public void setAccessibilityPaneTitle(@Nullable CharSequence accessibilityPaneTitle) {
if (!TextUtils.equals(accessibilityPaneTitle, mAccessibilityPaneTitle)) {
+ boolean currentPaneTitleEmpty = mAccessibilityPaneTitle == null;
+ boolean newPaneTitleEmpty = accessibilityPaneTitle == null;
mAccessibilityPaneTitle = accessibilityPaneTitle;
- notifyViewAccessibilityStateChangedIfNeeded(
- AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_TITLE);
+ // Make explicitly important as nulled titles need to be important for DISAPPEARED
+ // events.
+ if (mAccessibilityPaneTitle != null
+ && getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
+ setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+ }
+ if (currentPaneTitleEmpty) {
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_APPEARED);
+ } else if (newPaneTitleEmpty) {
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED);
+ } else {
+ notifyViewAccessibilityStateChangedIfNeeded(
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_TITLE);
+ }
}
}
@@ -14168,9 +14184,12 @@
}
// Changes to views with a pane title count as window state changes, as the pane title
- // marks them as significant parts of the UI.
+ // marks them as significant parts of the UI. A visible view with a nulled title may send
+ // a disappeared event.
if ((changeType != AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE)
- && isAccessibilityPane()) {
+ && (isAccessibilityPane()
+ || (changeType == AccessibilityEvent.CONTENT_CHANGE_TYPE_PANE_DISAPPEARED)
+ && isAggregatedVisible())) {
// If the pane isn't visible, content changed events are sufficient unless we're
// reporting that the view just disappeared
if ((isAggregatedVisible())
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index feb17f5..8bb7e67 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -195,6 +195,7 @@
import android.view.inputmethod.InputMethodManager;
import android.widget.Scroller;
import android.window.ClientWindowFrames;
+import android.window.SurfaceSyncer;
import android.window.WindowOnBackInvokedDispatcher;
import com.android.internal.R;
@@ -10873,4 +10874,71 @@
IWindowSession getWindowSession() {
return mWindowSession;
}
+
+ private void registerCallbacksForSync(
+ final SurfaceSyncer.SyncBufferCallback syncBufferCallback) {
+ if (!isHardwareEnabled()) {
+ // TODO: correctly handle when hardware disabled
+ syncBufferCallback.onBufferReady(null);
+ return;
+ }
+
+ mAttachInfo.mThreadedRenderer.registerRtFrameCallback(new FrameDrawingCallback() {
+ @Override
+ public void onFrameDraw(long frame) {
+ }
+
+ @Override
+ public HardwareRenderer.FrameCommitCallback onFrameDraw(int syncResult, long frame) {
+ if (DEBUG_BLAST) {
+ Log.d(mTag,
+ "Received frameDrawingCallback syncResult=" + syncResult + " frameNum="
+ + frame + ".");
+ }
+
+ final Transaction t = new Transaction();
+
+ // If the syncResults are SYNC_LOST_SURFACE_REWARD_IF_FOUND or
+ // SYNC_CONTEXT_IS_STOPPED it means nothing will draw. There's no need to set up
+ // any blast sync or commit callback, and the code should directly call
+ // pendingDrawFinished.
+ if ((syncResult
+ & (SYNC_LOST_SURFACE_REWARD_IF_FOUND | SYNC_CONTEXT_IS_STOPPED)) != 0) {
+ t.merge(mBlastBufferQueue.gatherPendingTransactions(frame));
+ syncBufferCallback.onBufferReady(t);
+ return null;
+ }
+
+ mBlastBufferQueue.setSyncTransaction(t);
+ if (DEBUG_BLAST) {
+ Log.d(mTag, "Setting up sync and frameCommitCallback");
+ }
+
+ return didProduceBuffer -> {
+ if (DEBUG_BLAST) {
+ Log.d(mTag, "Received frameCommittedCallback"
+ + " lastAttemptedDrawFrameNum=" + frame
+ + " didProduceBuffer=" + didProduceBuffer);
+ }
+
+ // If frame wasn't drawn, clear out the next transaction so it doesn't affect
+ // the next draw attempt. The next transaction and transaction complete callback
+ // were only set for the current draw attempt.
+ if (!didProduceBuffer) {
+ mBlastBufferQueue.setSyncTransaction(null);
+ // Gather the transactions that were sent to mergeWithNextTransaction
+ // since the frame didn't draw on this vsync. It's possible the frame will
+ // draw later, but it's better to not be sync than to block on a frame that
+ // may never come.
+ t.merge(mBlastBufferQueue.gatherPendingTransactions(frame));
+ }
+
+ syncBufferCallback.onBufferReady(t);
+ };
+ }
+ });
+ }
+
+ public final SurfaceSyncer.SyncTarget mSyncTarget =
+ syncBufferCallback -> registerCallbacksForSync(syncBufferCallback);
}
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 93cb0dd7..2dc5fbd 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -752,6 +752,14 @@
public void removeWindowlessRoot(ViewRootImpl impl) {
synchronized (mLock) {
mWindowlessRoots.remove(impl);
+ }
+ }
+
+ public void setRecentsAppBehindSystemBars(boolean behindSystemBars) {
+ try {
+ getWindowManagerService().setRecentsAppBehindSystemBars(behindSystemBars);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
}
}
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index cd8dd86..7e16531 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -592,6 +592,11 @@
/**
* Change type for {@link #TYPE_WINDOW_STATE_CHANGED} event:
* The node's pane title changed.
+ * <p>
+ * If this makes the pane appear, {@link #CONTENT_CHANGE_TYPE_PANE_APPEARED} is sent
+ * instead. If this makes the pane disappear, {@link #CONTENT_CHANGE_TYPE_PANE_DISAPPEARED}
+ * is sent.
+ *
*/
public static final int CONTENT_CHANGE_TYPE_PANE_TITLE = 0x00000008;
diff --git a/core/java/android/window/ProxyOnBackInvokedDispatcher.java b/core/java/android/window/ProxyOnBackInvokedDispatcher.java
index 4de977a..cdbd078 100644
--- a/core/java/android/window/ProxyOnBackInvokedDispatcher.java
+++ b/core/java/android/window/ProxyOnBackInvokedDispatcher.java
@@ -81,6 +81,10 @@
}
synchronized (mLock) {
mCallbacks.removeIf((p) -> p.first.equals(callback));
+ if (mActualDispatcherOwner != null) {
+ mActualDispatcherOwner.getOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(
+ callback);
+ }
}
}
diff --git a/core/java/android/window/SurfaceSyncer.java b/core/java/android/window/SurfaceSyncer.java
new file mode 100644
index 0000000..a593075
--- /dev/null
+++ b/core/java/android/window/SurfaceSyncer.java
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.window;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UiThread;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.SurfaceControl.Transaction;
+import android.view.SurfaceView;
+import android.view.View;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+/**
+ * Used to organize syncs for surfaces.
+ *
+ * The SurfaceSyncer allows callers to add desired syncs into a set and wait for them to all
+ * complete before getting a callback. The purpose of the Syncer is to be an accounting mechanism
+ * so each sync implementation doesn't need to handle it themselves. The Syncer class is used the
+ * following way.
+ *
+ * 1. {@link #setupSync(Runnable)} is called
+ * 2. {@link #addToSync(int, SyncTarget)} is called for every SyncTarget object that wants to be
+ * included in the sync. If the addSync is called for a View or SurfaceView it needs to be called
+ * on the UI thread. When addToSync is called, it's guaranteed that any UI updates that were
+ * requested before addToSync but after the last frame drew, will be included in the sync.
+ * 3. {@link #markSyncReady(int)} should be called when all the {@link SyncTarget}s have been added
+ * to the SyncSet. Now the SyncSet is closed and no more SyncTargets can be added to it.
+ * 4. The SyncSet will gather the data for each SyncTarget using the steps described below. When
+ * all the SyncTargets have finished, the syncRequestComplete will be invoked and the transaction
+ * will either be applied or sent to the caller. In most cases, only the SurfaceSyncer should be
+ * handling the Transaction object directly. However, there are some cases where the framework
+ * needs to send the Transaction elsewhere, like in ViewRootImpl, so that option is provided.
+ *
+ * The following is what happens within the {@link SyncSet}
+ * 1. Each SyncableTarget will get a {@link SyncTarget#onReadyToSync} callback that contains
+ * a {@link SyncBufferCallback}.
+ * 2. Each {@link SyncTarget} needs to invoke {@link SyncBufferCallback#onBufferReady(Transaction)}.
+ * This makes sure the SyncSet knows when the SyncTarget is complete, allowing the SyncSet to get
+ * the Transaction that contains the buffer.
+ * 3. When the final SyncBufferCallback finishes for the SyncSet, the syncRequestComplete Consumer
+ * will be invoked with the transaction that contains all information requested in the sync. This
+ * could include buffers and geometry changes. The buffer update will include the UI changes that
+ * were requested for the View.
+ *
+ * @hide
+ */
+public class SurfaceSyncer {
+ private static final String TAG = "SurfaceSyncer";
+ private static final boolean DEBUG = false;
+
+ private static Supplier<Transaction> sTransactionFactory = Transaction::new;
+
+ private final Object mSyncSetLock = new Object();
+ @GuardedBy("mSyncSetLock")
+ private final SparseArray<SyncSet> mSyncSets = new SparseArray<>();
+ @GuardedBy("mSyncSetLock")
+ private int mIdCounter = 0;
+
+ /**
+ * @hide
+ */
+ public static void setTransactionFactory(Supplier<Transaction> transactionFactory) {
+ sTransactionFactory = transactionFactory;
+ }
+
+ /**
+ * Starts a sync and will automatically apply the final, merged transaction.
+ *
+ * @param onComplete The runnable that is invoked when the sync has completed. This will run on
+ * the same thread that the sync was started on.
+ * @return The syncId for the newly created sync.
+ * @see #setupSync(Consumer)
+ */
+ public int setupSync(@Nullable Runnable onComplete) {
+ Handler handler = new Handler(Looper.myLooper());
+ return setupSync(transaction -> {
+ transaction.apply();
+ handler.post(onComplete);
+ });
+ }
+
+ /**
+ * Starts a sync.
+ *
+ * @param syncRequestComplete The complete callback that contains the syncId and transaction
+ * with all the sync data merged.
+ * @return The syncId for the newly created sync.
+ * @hide
+ * @see #setupSync(Runnable)
+ */
+ public int setupSync(@NonNull Consumer<Transaction> syncRequestComplete) {
+ synchronized (mSyncSetLock) {
+ mIdCounter++;
+ if (DEBUG) {
+ Log.d(TAG, "setupSync " + mIdCounter);
+ }
+ SyncSet syncSet = new SyncSet(mIdCounter, syncRequestComplete);
+ mSyncSets.put(mIdCounter, syncSet);
+ return mIdCounter;
+ }
+ }
+
+ /**
+ * Mark the sync set as ready to complete. No more data can be added to the specified syncId.
+ * Once the sync set is marked as ready, it will be able to complete once all Syncables in the
+ * set have completed their sync
+ *
+ * @param syncId The syncId to mark as ready.
+ */
+ public void markSyncReady(int syncId) {
+ SyncSet syncSet;
+ synchronized (mSyncSetLock) {
+ syncSet = mSyncSets.get(syncId);
+ mSyncSets.remove(syncId);
+ }
+ if (syncSet == null) {
+ Log.e(TAG, "Failed to find syncSet for syncId=" + syncId);
+ return;
+ }
+ syncSet.markSyncReady();
+ }
+
+ /**
+ * Add a SurfaceView to a sync set. This is different than {@link #addToSync(int, View)} because
+ * it requires the caller to notify the start and finish drawing in order to sync.
+ *
+ * @param syncId The syncId to add an entry to.
+ * @param surfaceView The SurfaceView to add to the sync.
+ * @param frameCallbackConsumer The callback that's invoked to allow the caller to notify the
+ * Syncer when the SurfaceView has started drawing and finished.
+ *
+ * @return true if the SurfaceView was successfully added to the SyncSet, false otherwise.
+ */
+ @UiThread
+ public boolean addToSync(int syncId, SurfaceView surfaceView,
+ Consumer<SurfaceViewFrameCallback> frameCallbackConsumer) {
+ return addToSync(syncId, new SurfaceViewSyncTarget(surfaceView, frameCallbackConsumer));
+ }
+
+ /**
+ * Add a View's rootView to a sync set.
+ *
+ * @param syncId The syncId to add an entry to.
+ * @param view The view where the root will be add to the sync set
+ *
+ * @return true if the View was successfully added to the SyncSet, false otherwise.
+ */
+ @UiThread
+ public boolean addToSync(int syncId, @NonNull View view) {
+ return addToSync(syncId, view.getViewRootImpl().mSyncTarget);
+ }
+
+ /**
+ * Add a {@link SyncTarget} to a sync set. The sync set will wait for all
+ * SyncableSurfaces to complete before notifying.
+ *
+ * @param syncId The syncId to add an entry to.
+ * @param syncTarget A SyncableSurface that implements how to handle syncing
+ * buffers.
+ *
+ * @return true if the SyncTarget was successfully added to the SyncSet, false otherwise.
+ */
+ public boolean addToSync(int syncId, @NonNull SyncTarget syncTarget) {
+ SyncSet syncSet = getAndValidateSyncSet(syncId);
+ if (syncSet == null) {
+ return false;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "addToSync id=" + syncId);
+ }
+ syncSet.addSyncableSurface(syncTarget);
+ return true;
+ }
+
+ /**
+ * Add a transaction to a specific sync so it can be merged along with the frames from the
+ * Syncables in the set. This is so the caller can add arbitrary transaction info that will be
+ * applied at the same time as the buffers
+ * @param syncId The syncId where the transaction will be merged to.
+ * @param t The transaction to merge in the sync set.
+ */
+ public void addTransactionToSync(int syncId, Transaction t) {
+ SyncSet syncSet = getAndValidateSyncSet(syncId);
+ if (syncSet != null) {
+ syncSet.addTransactionToSync(t);
+ }
+ }
+
+ private SyncSet getAndValidateSyncSet(int syncId) {
+ SyncSet syncSet;
+ synchronized (mSyncSetLock) {
+ syncSet = mSyncSets.get(syncId);
+ }
+ if (syncSet == null) {
+ Log.e(TAG, "Failed to find sync for id=" + syncId);
+ return null;
+ }
+ return syncSet;
+ }
+
+ /**
+ * A SyncTarget that can be added to a sync set.
+ */
+ public interface SyncTarget {
+ /**
+ * Called when the Syncable is ready to begin handing a sync request. When invoked, the
+ * implementor is required to call {@link SyncBufferCallback#onBufferReady(Transaction)}
+ * and {@link SyncBufferCallback#onBufferReady(Transaction)} in order for this Syncable
+ * to be marked as complete.
+ *
+ * @param syncBufferCallback A SyncBufferCallback that the caller must invoke onBufferReady
+ */
+ void onReadyToSync(SyncBufferCallback syncBufferCallback);
+ }
+
+ /**
+ * Interface so the SurfaceSyncer can know when it's safe to start and when everything has been
+ * completed. The caller should invoke the calls when the rendering has started and finished a
+ * frame.
+ */
+ public interface SyncBufferCallback {
+ /**
+ * Invoked when the transaction contains the buffer and is ready to sync.
+ *
+ * @param t The transaction that contains the buffer to be synced. This can be null if
+ * there's nothing to sync
+ */
+ void onBufferReady(@Nullable Transaction t);
+ }
+
+ /**
+ * Class that collects the {@link SyncTarget}s and notifies when all the surfaces have
+ * a frame ready.
+ */
+ private static class SyncSet {
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private final Set<Integer> mPendingSyncs = new HashSet<>();
+ @GuardedBy("mLock")
+ private final Transaction mTransaction = sTransactionFactory.get();
+ @GuardedBy("mLock")
+ private boolean mSyncReady;
+
+ private final int mSyncId;
+ private final Consumer<Transaction> mSyncRequestCompleteCallback;
+
+ private SyncSet(int syncId, Consumer<Transaction> syncRequestComplete) {
+ mSyncId = syncId;
+ mSyncRequestCompleteCallback = syncRequestComplete;
+ }
+
+ void addSyncableSurface(SyncTarget syncTarget) {
+ SyncBufferCallback syncBufferCallback = new SyncBufferCallback() {
+ @Override
+ public void onBufferReady(Transaction t) {
+ synchronized (mLock) {
+ if (t != null) {
+ mTransaction.merge(t);
+ }
+ mPendingSyncs.remove(hashCode());
+ checkIfSyncIsComplete();
+ }
+ }
+ };
+
+ synchronized (mLock) {
+ mPendingSyncs.add(syncBufferCallback.hashCode());
+ }
+ syncTarget.onReadyToSync(syncBufferCallback);
+ }
+
+ void markSyncReady() {
+ synchronized (mLock) {
+ mSyncReady = true;
+ checkIfSyncIsComplete();
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void checkIfSyncIsComplete() {
+ if (!mSyncReady || !mPendingSyncs.isEmpty()) {
+ if (DEBUG) {
+ Log.d(TAG, "Syncable is not complete. mSyncReady=" + mSyncReady
+ + " mPendingSyncs=" + mPendingSyncs.size());
+ }
+ return;
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "Successfully finished sync id=" + mSyncId);
+ }
+ mSyncRequestCompleteCallback.accept(mTransaction);
+ }
+
+ /**
+ * Add a Transaction to this sync set. This allows the caller to provide other info that
+ * should be synced with the buffers.
+ */
+ void addTransactionToSync(Transaction t) {
+ synchronized (mLock) {
+ mTransaction.merge(t);
+ }
+ }
+ }
+
+ /**
+ * Wrapper class to help synchronize SurfaceViews
+ */
+ private static class SurfaceViewSyncTarget implements SyncTarget {
+ private final SurfaceView mSurfaceView;
+ private final Consumer<SurfaceViewFrameCallback> mFrameCallbackConsumer;
+
+ SurfaceViewSyncTarget(SurfaceView surfaceView,
+ Consumer<SurfaceViewFrameCallback> frameCallbackConsumer) {
+ mSurfaceView = surfaceView;
+ mFrameCallbackConsumer = frameCallbackConsumer;
+ }
+
+ @Override
+ public void onReadyToSync(SyncBufferCallback syncBufferCallback) {
+ Transaction mTransaction = sTransactionFactory.get();
+ mFrameCallbackConsumer.accept(new SurfaceViewFrameCallback() {
+ @Override
+ public void onFrameStarted() {
+ mSurfaceView.syncNextFrame(mTransaction);
+ }
+
+ @Override
+ public void onFrameComplete() {
+ syncBufferCallback.onBufferReady(mTransaction);
+ }
+ });
+ }
+ }
+
+ /**
+ * A frame callback that is used to synchronize SurfaceViews. The owner of the SurfaceView must
+ * implement onFrameStarted and onFrameComplete when trying to sync the SurfaceView. This is to
+ * ensure the sync knows when the frame is ready to add to the sync.
+ */
+ public interface SurfaceViewFrameCallback {
+ /**
+ * Called when the SurfaceView is going to render a frame
+ */
+ void onFrameStarted();
+
+ /**
+ * Called when the SurfaceView has finished rendering a frame.
+ */
+ void onFrameComplete();
+ }
+}
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 0503c40..94e5ea9 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -76,9 +76,9 @@
/** Detaches the dispatcher instance from its window. */
public void detachFromWindow() {
+ clear();
mWindow = null;
mWindowSession = null;
- clear();
}
// TODO: Take an Executor for the callback to run on.
@@ -165,50 +165,13 @@
} else {
int priority = mAllCallbacks.get(callback);
mWindowSession.setOnBackInvokedCallback(
- mWindow, new OnBackInvokedCallbackWrapper(callback, priority), priority);
+ mWindow, new OnBackInvokedCallbackWrapper(callback), priority);
}
} catch (RemoteException e) {
Log.e(TAG, "Failed to set OnBackInvokedCallback to WM. Error: " + e);
}
}
- private class OnBackInvokedCallbackWrapper extends IOnBackInvokedCallback.Stub {
- private final OnBackInvokedCallback mCallback;
- private final @Priority int mPriority;
-
- OnBackInvokedCallbackWrapper(
- @NonNull OnBackInvokedCallback callback, @Priority int priority) {
- mCallback = callback;
- mPriority = priority;
- }
-
- @NonNull
- public OnBackInvokedCallback getCallback() {
- return mCallback;
- }
-
- @Override
- public void onBackStarted() throws RemoteException {
- Handler.getMain().post(() -> mCallback.onBackStarted());
- }
-
- @Override
- public void onBackProgressed(BackEvent backEvent)
- throws RemoteException {
- Handler.getMain().post(() -> mCallback.onBackProgressed(backEvent));
- }
-
- @Override
- public void onBackCancelled() throws RemoteException {
- Handler.getMain().post(() -> mCallback.onBackCancelled());
- }
-
- @Override
- public void onBackInvoked() throws RemoteException {
- Handler.getMain().post(() -> mCallback.onBackInvoked());
- }
- }
-
@Override
public OnBackInvokedCallback getTopCallback() {
if (mAllCallbacks.isEmpty()) {
@@ -223,6 +186,39 @@
return null;
}
+ private static class OnBackInvokedCallbackWrapper extends IOnBackInvokedCallback.Stub {
+ private final OnBackInvokedCallback mCallback;
+
+ OnBackInvokedCallbackWrapper(@NonNull OnBackInvokedCallback callback) {
+ mCallback = callback;
+ }
+
+ @NonNull
+ public OnBackInvokedCallback getCallback() {
+ return mCallback;
+ }
+
+ @Override
+ public void onBackStarted() {
+ Handler.getMain().post(() -> mCallback.onBackStarted());
+ }
+
+ @Override
+ public void onBackProgressed(BackEvent backEvent) {
+ Handler.getMain().post(() -> mCallback.onBackProgressed(backEvent));
+ }
+
+ @Override
+ public void onBackCancelled() {
+ Handler.getMain().post(() -> mCallback.onBackCancelled());
+ }
+
+ @Override
+ public void onBackInvoked() throws RemoteException {
+ Handler.getMain().post(() -> mCallback.onBackInvoked());
+ }
+ }
+
/**
* Returns if the legacy back behavior should be used.
*
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index dc55c05..90b272c 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -2712,10 +2712,10 @@
return AUDIO_JAVA_SUCCESS;
}
-static jint
-android_media_AudioSystem_getDevicesForAttributes(JNIEnv *env, jobject thiz,
- jobject jaa, jobjectArray jDeviceArray)
-{
+static jint android_media_AudioSystem_getDevicesForAttributes(JNIEnv *env, jobject thiz,
+ jobject jaa,
+ jobjectArray jDeviceArray,
+ jboolean forVolume) {
const jsize maxResultSize = env->GetArrayLength(jDeviceArray);
// the JNI is always expected to provide us with an array capable of holding enough
// devices i.e. the most we ever route a track to. This is preferred over receiving an ArrayList
@@ -2734,7 +2734,7 @@
AudioDeviceTypeAddrVector devices;
jStatus = check_AudioSystem_Command(
- AudioSystem::getDevicesForAttributes(*(paa.get()), &devices));
+ AudioSystem::getDevicesForAttributes(*(paa.get()), &devices, forVolume));
if (jStatus != NO_ERROR) {
return jStatus;
}
@@ -3045,7 +3045,7 @@
{"getDevicesForRoleAndCapturePreset", "(IILjava/util/List;)I",
(void *)android_media_AudioSystem_getDevicesForRoleAndCapturePreset},
{"getDevicesForAttributes",
- "(Landroid/media/AudioAttributes;[Landroid/media/AudioDeviceAttributes;)I",
+ "(Landroid/media/AudioAttributes;[Landroid/media/AudioDeviceAttributes;Z)I",
(void *)android_media_AudioSystem_getDevicesForAttributes},
{"setUserIdDeviceAffinities", "(I[I[Ljava/lang/String;)I",
(void *)android_media_AudioSystem_setUserIdDeviceAffinities},
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 0956418..68025a8 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -62,6 +62,7 @@
namespace android {
+using gui::CaptureArgs;
using gui::FocusRequest;
static void doThrowNPE(JNIEnv* env) {
diff --git a/core/proto/android/hardware/sensorprivacy.proto b/core/proto/android/hardware/sensorprivacy.proto
index 97870a1..9359528 100644
--- a/core/proto/android/hardware/sensorprivacy.proto
+++ b/core/proto/android/hardware/sensorprivacy.proto
@@ -128,7 +128,7 @@
DIALOG = 3;
SHELL = 4;
OTHER = 5;
- SAFETY_HUB = 6;
+ SAFETY_CENTER = 6;
}
// Source for which sensor privacy was toggled.
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 58a3bb4..73cdaba 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1903,14 +1903,16 @@
to improve wifi performance.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.MANAGE_WIFI_AUTO_JOIN"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|knownSigner"
+ android:knownCerts="@array/wifi_known_signers" />
<!-- Allows applications to get notified when a Wi-Fi interface request cannot
be satisfied without tearing down one or more other interfaces, and provide a decision
whether to approve the request or reject it.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.MANAGE_WIFI_INTERFACES"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|knownSigner"
+ android:knownCerts="@array/wifi_known_signers" />
<!-- @SystemApi @hide Allows apps to create and manage IPsec tunnels.
<p>Only granted to applications that are currently bound by the
@@ -1948,7 +1950,8 @@
modifications.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.OVERRIDE_WIFI_CONFIG"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|knownSigner"
+ android:knownCerts="@array/wifi_known_signers" />
<!-- @deprecated Allows applications to act as network scorers. @hide @SystemApi-->
<permission android:name="android.permission.SCORE_NETWORKS"
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index 8f2d6c3..dff7751 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -185,6 +185,12 @@
<item>@string/app_info</item>
</string-array>
+ <!-- Certificate digests for trusted apps that will be allowed to obtain the knownSigner Wi-Fi
+ permissions. The digest should be computed over the DER encoding of the trusted certificate
+ using the SHA-256 digest algorithm. -->
+ <string-array name="wifi_known_signers">
+ </string-array>
+
<!-- Device-specific array of SIM slot indexes which are are embedded eUICCs.
e.g. If a device has two physical slots with indexes 0, 1, and slot 1 is an
eUICC, then the value of this array should be:
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 5ac30de..4648f8a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1207,13 +1207,13 @@
<!-- Display low battery warning when battery level dips to this value.
Also, the battery stats are flushed to disk when we hit this level. -->
- <integer name="config_criticalBatteryWarningLevel">5</integer>
+ <integer name="config_criticalBatteryWarningLevel">10</integer>
<!-- Shutdown if the battery temperature exceeds (this value * 0.1) Celsius. -->
<integer name="config_shutdownBatteryTemperature">680</integer>
<!-- Display low battery warning when battery level dips to this value -->
- <integer name="config_lowBatteryWarningLevel">15</integer>
+ <integer name="config_lowBatteryWarningLevel">20</integer>
<!-- The default suggested battery % at which we enable battery saver automatically. -->
<integer name="config_lowBatteryAutoTriggerDefaultLevel">15</integer>
@@ -4130,9 +4130,9 @@
This service must be trusted, as it can be activated without explicit consent of the user.
If no service with the specified name exists on the device, cloudsearch will be disabled.
Example: "com.android.intelligence/.CloudSearchService"
- config_defaultCloudSearchService is for the single provider case.
+ config_defaultCloudSearchServices is for the multiple provider case.
-->
- <string name="config_defaultCloudSearchService" translatable="false"></string>
+ <string-array name="config_defaultCloudSearchServices"></string-array>
<!-- The package name for the system's translation service.
This service must be trusted, as it can be activated without explicit consent of the user.
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index bccd2b6..29eb0c0 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -62,8 +62,9 @@
<!-- View id for the action of text editor inside of an extracted text
{@link InputMethodService#onCreateExtractTextView IME extract view}. -->
<item type="id" name="inputExtractAction" />
- <!-- View id for the accessories of text editor inside of an extracted text
- {@link InputMethodService#onCreateExtractTextView IME extract view}. -->
+ <!-- View id for the accessories (such as the extracted input action button) of text editor
+ inside of an extracted text {@link InputMethodService#onCreateExtractTextView IME extract
+ view}. This layout must contain the {@link #inputExtractAction}. -->
<item type="id" name="inputExtractAccessories" />
<item type="id" name="selectAll" />
<item type="id" name="cut" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index e41aa45..e647561 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -197,7 +197,7 @@
<string name="ThreeWCMmi">Three way calling</string>
<string name="RuacMmi">Rejection of undesired annoying calls</string>
<string name="CndMmi">Calling number delivery</string>
- <string name="DndMmi" translatable="false">Priority mode</string>
+ <string name="DndMmi">Do not disturb</string>
<!-- Displayed to confirm to the user that caller ID will be restricted on the next call as usual. -->
<string name="CLIRDefaultOnNextCallOn">Caller ID defaults to restricted. Next call: Restricted</string>
@@ -2036,9 +2036,9 @@
<string name="permdesc_bindCarrierServices">Allows the holder to bind to carrier services. Should never be needed for normal apps.</string>
<!-- Title of an application permission, for applications that wish to access notification policy. -->
- <string name="permlab_access_notification_policy" translatable="false">access Priority mode</string>
+ <string name="permlab_access_notification_policy">access Do Not Disturb</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permdesc_access_notification_policy" translatable="false">Allows the app to read and write Priority mode configuration.</string>
+ <string name="permdesc_access_notification_policy">Allows the app to read and write Do Not Disturb configuration.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_startViewPermissionUsage">start view permission usage</string>
@@ -5302,7 +5302,7 @@
<string name="zen_mode_forever">Until you turn off</string>
<!-- Zen mode condition: no exit criteria, includes the name of the feature for emphasis. [CHAR LIMIT=NONE] -->
- <string name="zen_mode_forever_dnd" translatable="false">Until you turn off Priority mode</string>
+ <string name="zen_mode_forever_dnd">Until you turn off Do Not Disturb</string>
<!-- Zen mode active automatic rule name separator. [CHAR LIMIT=NONE] -->
<string name="zen_mode_rule_name_combination"><xliff:g id="first" example="Weeknights">%1$s</xliff:g> / <xliff:g id="rest" example="Meetings">%2$s</xliff:g></string>
@@ -5311,7 +5311,7 @@
<string name="toolbar_collapse_description">Collapse</string>
<!-- Zen mode - feature name. [CHAR LIMIT=40] -->
- <string name="zen_mode_feature_name" translatable="false">Priority mode</string>
+ <string name="zen_mode_feature_name">Do not disturb</string>
<!-- Zen mode - downtime legacy feature name. [CHAR LIMIT=40] -->
<string name="zen_mode_downtime_feature_name">Downtime</string>
@@ -5745,14 +5745,14 @@
<!-- Title for the notification channel notifying user of settings system changes. [CHAR LIMIT=NONE] -->
<string name="notification_channel_system_changes">System changes</string>
- <!-- Title for the notification channel notifying user of priority mode system changes (i.e. Priority mode has changed). [CHAR LIMIT=NONE] -->
- <string name="notification_channel_do_not_disturb" translatable="false">Priority mode</string>
- <!-- Title of notification indicating Priority mode visual interruption settings have changed when upgrading to P -->
- <string name="zen_upgrade_notification_visd_title" translatable="false">New: Priority mode is hiding notifications</string>
+ <!-- Title for the notification channel notifying user of do not disturb system changes (i.e. Do Not Disturb has changed). [CHAR LIMIT=NONE] -->
+ <string name="notification_channel_do_not_disturb">Do Not Disturb</string>
+ <!-- Title of notification indicating do not disturb visual interruption settings have changed when upgrading to P -->
+ <string name="zen_upgrade_notification_visd_title">New: Do Not Disturb is hiding notifications</string>
<!-- Content of notification indicating users can tap on the notification to go to dnd behavior settings -->
<string name="zen_upgrade_notification_visd_content">Tap to learn more and change.</string>
- <!-- Title of notification indicating priority mode settings have changed when upgrading to P -->
- <string name="zen_upgrade_notification_title" translatable="false">Priority mode has changed</string>
+ <!-- Title of notification indicating do not disturb settings have changed when upgrading to P -->
+ <string name="zen_upgrade_notification_title">Do Not Disturb has changed</string>
<!-- Content of notification indicating users can tap on the notification to go to dnd behavior settings -->
<string name="zen_upgrade_notification_content">Tap to check what\'s blocked.</string>
@@ -5793,7 +5793,7 @@
<!-- Label of notification action button to learn more about the enhanced notifications [CHAR LIMIT=20] -->
<string name="nas_upgrade_notification_learn_more_action">Learn more</string>
<!-- Content of notification learn more dialog about the enhanced notifications [CHAR LIMIT=NONE] -->
- <string name="nas_upgrade_notification_learn_more_content" translatable="false">Enhanced notifications replaced Android Adaptive Notifications in Android 12. This feature shows suggested actions and replies, and organizes your notifications.\n\nEnhanced notifications can access notification content, including personal information like contact names and messages. This feature can also dismiss or respond to notifications, such as answering phone calls, and control Priority mode.</string>
+ <string name="nas_upgrade_notification_learn_more_content">Enhanced notifications replaced Android Adaptive Notifications in Android 12. This feature shows suggested actions and replies, and organizes your notifications.\n\nEnhanced notifications can access notification content, including personal information like contact names and messages. This feature can also dismiss or respond to notifications, such as answering phone calls, and control Do Not Disturb.</string>
<!-- Dynamic mode battery saver strings -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 60a3398..ffd1f125 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1291,6 +1291,7 @@
<java-symbol type="array" name="vendor_required_apps_managed_profile" />
<java-symbol type="array" name="vendor_required_apps_managed_device" />
<java-symbol type="array" name="vendor_required_attestation_certificates" />
+ <java-symbol type="string" name="vendor_required_attestation_revocation_list_url" />
<java-symbol type="array" name="vendor_disallowed_apps_managed_user" />
<java-symbol type="array" name="vendor_disallowed_apps_managed_profile" />
<java-symbol type="array" name="vendor_disallowed_apps_managed_device" />
@@ -3674,7 +3675,7 @@
<java-symbol type="string" name="notification_channel_network_status" />
<java-symbol type="string" name="notification_channel_network_alerts" />
<java-symbol type="string" name="notification_channel_network_available" />
- <java-symbol type="string" name="config_defaultCloudSearchService" />
+ <java-symbol type="array" name="config_defaultCloudSearchServices" />
<java-symbol type="string" name="notification_channel_vpn" />
<java-symbol type="string" name="notification_channel_device_admin" />
<java-symbol type="string" name="notification_channel_alerts" />
diff --git a/core/res/res/values/vendor_required_attestation_certificates.xml b/core/res/res/values/vendor_required_attestation_certificates.xml
index ce5660f..ff7313e 100644
--- a/core/res/res/values/vendor_required_attestation_certificates.xml
+++ b/core/res/res/values/vendor_required_attestation_certificates.xml
@@ -29,4 +29,10 @@
-->
<string-array translatable="false" name="vendor_required_attestation_certificates">
</string-array>
+
+ <!-- Url to mapping of revoked certificates' hex encoded serial numbers. Example format
+ can be found at:
+ https://developer.android.com/training/articles/security-key-attestation#certificate_status
+ -->
+ <string translatable="false" name="vendor_required_attestation_revocation_list_url"></string>
</resources>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 2d2c03b..3c64cf5 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -20,13 +20,6 @@
applications that come with the platform
-->
<permissions>
- <privapp-permissions package="android.ext.services">
- <permission name="android.permission.PROVIDE_RESOLVER_RANKER_SERVICE" />
- <permission name="android.permission.MONITOR_DEFAULT_SMS_PACKAGE" />
- <permission name="android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE" />
- <permission name="android.permission.INTERACT_ACROSS_USERS" />
- </privapp-permissions>
-
<!-- Needed for Build.getSerial(), which is used to send a unique number for serial, per HUIG. -->
<privapp-permissions package="android.car.usb.handler">
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 91615fe..f01457b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -84,6 +84,7 @@
private final Optional<SplitScreenController> mSplitScreenOptional;
private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
private Transitions.TransitionFinishCallback mFinishCallback;
+ private SurfaceControl.Transaction mFinishTransaction;
private final Rect mExitDestinationBounds = new Rect();
@Nullable
private IBinder mExitTransition;
@@ -166,6 +167,7 @@
if (mFinishCallback != null) {
mFinishCallback.onTransitionFinished(null, null);
mFinishCallback = null;
+ mFinishTransaction = null;
throw new RuntimeException("Previous callback not called, aborting exit PIP.");
}
@@ -267,7 +269,8 @@
public void onFinishResize(TaskInfo taskInfo, Rect destinationBounds,
@PipAnimationController.TransitionDirection int direction,
@Nullable SurfaceControl.Transaction tx) {
- if (isInPipDirection(direction)) {
+ final boolean enteringPip = isInPipDirection(direction);
+ if (enteringPip) {
mPipTransitionState.setTransitionState(ENTERED_PIP);
}
// If there is an expected exit transition, then the exit will be "merged" into this
@@ -279,6 +282,20 @@
if (tx != null) {
wct.setBoundsChangeTransaction(taskInfo.token, tx);
}
+ final SurfaceControl leash = mPipOrganizer.getSurfaceControl();
+ final int displayRotation = taskInfo.getConfiguration().windowConfiguration
+ .getDisplayRotation();
+ if (enteringPip && mInFixedRotation && mEndFixedRotation != displayRotation
+ && leash != null && leash.isValid()) {
+ // Launcher may update the Shelf height during the animation, which will update the
+ // destination bounds. Because this is in fixed rotation, We need to make sure the
+ // finishTransaction is using the updated bounds in the display rotation.
+ final Rect displayBounds = mPipBoundsState.getDisplayBounds();
+ final Rect finishBounds = new Rect(destinationBounds);
+ rotateBounds(finishBounds, displayBounds, mEndFixedRotation, displayRotation);
+ mSurfaceTransactionHelper.crop(mFinishTransaction, leash, finishBounds);
+ }
+ mFinishTransaction = null;
mFinishCallback.onTransitionFinished(wct, null /* callback */);
mFinishCallback = null;
}
@@ -290,6 +307,7 @@
if (mFinishCallback == null) return;
mFinishCallback.onTransitionFinished(null /* wct */, null /* callback */);
mFinishCallback = null;
+ mFinishTransaction = null;
}
@Override
@@ -336,6 +354,7 @@
mPipOrganizer.onExitPipFinished(pipChange.getTaskInfo());
finishCallback.onTransitionFinished(wct, wctCB);
};
+ mFinishTransaction = finishTransaction;
// Check if it is Shell rotation.
if (Transitions.SHELL_TRANSITIONS_ROTATION) {
@@ -526,6 +545,7 @@
if (mFinishCallback != null) {
mFinishCallback.onTransitionFinished(null /* wct */, null /* callback */);
mFinishCallback = null;
+ mFinishTransaction = null;
throw new RuntimeException("Previous callback not called, aborting entering PIP.");
}
@@ -549,6 +569,7 @@
mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP);
mFinishCallback = finishCallback;
+ mFinishTransaction = finishTransaction;
final int endRotation = mInFixedRotation ? mEndFixedRotation : enterPip.getEndRotation();
return startEnterAnimation(enterPip.getTaskInfo(), enterPip.getLeash(),
startTransaction, finishTransaction, enterPip.getStartRotation(),
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index 3442699..61cbf6e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -364,6 +364,12 @@
final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
final SplashScreenView contentView = viewSupplier.get();
record.mBGColor = contentView.getInitBackgroundColor();
+ } else {
+ // release the icon view host
+ final SplashScreenView contentView = viewSupplier.get();
+ if (contentView.getSurfaceHost() != null) {
+ SplashScreenView.releaseIconHost(contentView.getSurfaceHost());
+ }
}
} catch (RuntimeException e) {
// don't crash if something else bad happens, for example a
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 4bc5850..56d5168 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -348,12 +348,13 @@
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
final boolean isTask = change.getTaskInfo() != null;
+ boolean isSeamlessDisplayChange = false;
if (change.getMode() == TRANSIT_CHANGE && (change.getFlags() & FLAG_IS_DISPLAY) != 0) {
if (info.getType() == TRANSIT_CHANGE) {
- boolean isSeamless = isRotationSeamless(info, mDisplayController);
+ isSeamlessDisplayChange = isRotationSeamless(info, mDisplayController);
final int anim = getRotationAnimation(info);
- if (!(isSeamless || anim == ROTATION_ANIMATION_JUMPCUT)) {
+ if (!(isSeamlessDisplayChange || anim == ROTATION_ANIMATION_JUMPCUT)) {
mRotationAnimation = new ScreenRotationAnimation(mContext, mSurfaceSession,
mTransactionPool, startTransaction, change, info.getRootLeash(),
anim);
@@ -387,6 +388,8 @@
startTransaction.setPosition(change.getLeash(),
change.getEndAbsBounds().left - info.getRootOffset().x,
change.getEndAbsBounds().top - info.getRootOffset().y);
+ // Seamless display transition doesn't need to animate.
+ if (isSeamlessDisplayChange) continue;
if (isTask) {
// Skip non-tasks since those usually have null bounds.
startTransaction.setWindowCrop(change.getLeash(),
diff --git a/location/java/android/location/SatellitePvt.java b/location/java/android/location/SatellitePvt.java
index f140c68..f3e1508 100644
--- a/location/java/android/location/SatellitePvt.java
+++ b/location/java/android/location/SatellitePvt.java
@@ -144,8 +144,8 @@
private final ClockInfo mClockInfo;
private final double mIonoDelayMeters;
private final double mTropoDelayMeters;
- private final long mTimeOfClock;
- private final long mTimeOfEphemeris;
+ private final long mTimeOfClockSeconds;
+ private final long mTimeOfEphemerisSeconds;
private final int mIssueOfDataClock;
private final int mIssueOfDataEphemeris;
@EphemerisSource
@@ -457,8 +457,8 @@
@Nullable ClockInfo clockInfo,
double ionoDelayMeters,
double tropoDelayMeters,
- long timeOfClock,
- long timeOfEphemeris,
+ long timeOfClockSeconds,
+ long timeOfEphemerisSeconds,
int issueOfDataClock,
int issueOfDataEphemeris,
@EphemerisSource int ephemerisSource) {
@@ -468,8 +468,8 @@
mClockInfo = clockInfo;
mIonoDelayMeters = ionoDelayMeters;
mTropoDelayMeters = tropoDelayMeters;
- mTimeOfClock = timeOfClock;
- mTimeOfEphemeris = timeOfEphemeris;
+ mTimeOfClockSeconds = timeOfClockSeconds;
+ mTimeOfEphemerisSeconds = timeOfEphemerisSeconds;
mIssueOfDataClock = issueOfDataClock;
mIssueOfDataEphemeris = issueOfDataEphemeris;
mEphemerisSource = ephemerisSource;
@@ -545,31 +545,31 @@
}
/**
- * Time of Clock.
+ * Time of Clock in seconds.
*
* <p>The value is in seconds since GPS epoch, regardless of the constellation.
*
* <p>The value is not encoded as in GPS ICD200 documentation.
*
- * <p>This field is valid if {@link #hasTimeOfClock()} is true.
+ * <p>This field is valid if {@link #hasTimeOfClockSeconds()} is true.
*/
@IntRange(from = 0)
- public long getTimeOfClock() {
- return mTimeOfClock;
+ public long getTimeOfClockSeconds() {
+ return mTimeOfClockSeconds;
}
/**
- * Time of ephemeris.
+ * Time of ephemeris in seconds.
*
* <p>The value is in seconds since GPS epoch, regardless of the constellation.
*
* <p>The value is not encoded as in GPS ICD200 documentation.
*
- * <p>This field is valid if {@link #hasTimeOfEphemeris()} is true.
+ * <p>This field is valid if {@link #hasTimeOfEphemerisSeconds()} is true.
*/
@IntRange(from = 0)
- public long getTimeOfEphemeris() {
- return mTimeOfEphemeris;
+ public long getTimeOfEphemerisSeconds() {
+ return mTimeOfEphemerisSeconds;
}
/**
@@ -607,13 +607,13 @@
return (mFlags & HAS_ISSUE_OF_DATA_EPHEMERIS) != 0;
}
- /** Returns {@code true} if {@link #getTimeOfClock()} ()} is valid. */
- public boolean hasTimeOfClock() {
+ /** Returns {@code true} if {@link #getTimeOfClockSeconds()} ()} is valid. */
+ public boolean hasTimeOfClockSeconds() {
return (mFlags & HAS_TIME_OF_CLOCK) != 0;
}
- /** Returns {@code true} if {@link #getTimeOfEphemeris()} is valid. */
- public boolean hasTimeOfEphemeris() {
+ /** Returns {@code true} if {@link #getTimeOfEphemerisSeconds()} is valid. */
+ public boolean hasTimeOfEphemerisSeconds() {
return (mFlags & HAS_TIME_OF_EPHEMERIS) != 0;
}
@@ -671,8 +671,8 @@
parcel.writeParcelable(mClockInfo, flags);
parcel.writeDouble(mIonoDelayMeters);
parcel.writeDouble(mTropoDelayMeters);
- parcel.writeLong(mTimeOfClock);
- parcel.writeLong(mTimeOfEphemeris);
+ parcel.writeLong(mTimeOfClockSeconds);
+ parcel.writeLong(mTimeOfEphemerisSeconds);
parcel.writeInt(mIssueOfDataClock);
parcel.writeInt(mIssueOfDataEphemeris);
parcel.writeInt(mEphemerisSource);
@@ -687,8 +687,8 @@
+ ", ClockInfo=" + mClockInfo
+ ", IonoDelayMeters=" + mIonoDelayMeters
+ ", TropoDelayMeters=" + mTropoDelayMeters
- + ", TimeOfClock=" + mTimeOfClock
- + ", TimeOfEphemeris=" + mTimeOfEphemeris
+ + ", TimeOfClockSeconds=" + mTimeOfClockSeconds
+ + ", TimeOfEphemerisSeconds=" + mTimeOfEphemerisSeconds
+ ", IssueOfDataClock=" + mIssueOfDataClock
+ ", IssueOfDataEphemeris=" + mIssueOfDataEphemeris
+ ", EphemerisSource=" + mEphemerisSource
@@ -709,8 +709,8 @@
@Nullable private ClockInfo mClockInfo;
private double mIonoDelayMeters;
private double mTropoDelayMeters;
- private long mTimeOfClock;
- private long mTimeOfEphemeris;
+ private long mTimeOfClockSeconds;
+ private long mTimeOfEphemerisSeconds;
private int mIssueOfDataClock;
private int mIssueOfDataEphemeris;
@EphemerisSource
@@ -796,13 +796,13 @@
*
* <p>The value is not encoded as in GPS ICD200 documentation.
*
- * @param timeOfClock time of clock (seconds)
+ * @param timeOfClockSeconds time of clock (seconds)
* @return builder object
*/
@NonNull
- public Builder setTimeOfClock(@IntRange(from = 0) long timeOfClock) {
- Preconditions.checkArgumentNonnegative(timeOfClock);
- mTimeOfClock = timeOfClock;
+ public Builder setTimeOfClockSeconds(@IntRange(from = 0) long timeOfClockSeconds) {
+ Preconditions.checkArgumentNonnegative(timeOfClockSeconds);
+ mTimeOfClockSeconds = timeOfClockSeconds;
mFlags = (byte) (mFlags | HAS_TIME_OF_CLOCK);
return this;
}
@@ -814,13 +814,13 @@
*
* <p>The value is not encoded as in GPS ICD200 documentation.
*
- * @param timeOfEphemeris time of ephemeris (seconds)
+ * @param timeOfEphemerisSeconds time of ephemeris (seconds)
* @return builder object
*/
@NonNull
- public Builder setTimeOfEphemeris(@IntRange(from = 0) int timeOfEphemeris) {
- Preconditions.checkArgumentNonnegative(timeOfEphemeris);
- mTimeOfEphemeris = timeOfEphemeris;
+ public Builder setTimeOfEphemerisSeconds(@IntRange(from = 0) long timeOfEphemerisSeconds) {
+ Preconditions.checkArgumentNonnegative(timeOfEphemerisSeconds);
+ mTimeOfEphemerisSeconds = timeOfEphemerisSeconds;
mFlags = (byte) (mFlags | HAS_TIME_OF_EPHEMERIS);
return this;
}
@@ -879,7 +879,8 @@
@NonNull
public SatellitePvt build() {
return new SatellitePvt(mFlags, mPositionEcef, mVelocityEcef, mClockInfo,
- mIonoDelayMeters, mTropoDelayMeters, mTimeOfClock, mTimeOfEphemeris,
+ mIonoDelayMeters, mTropoDelayMeters, mTimeOfClockSeconds,
+ mTimeOfEphemerisSeconds,
mIssueOfDataClock, mIssueOfDataEphemeris,
mEphemerisSource);
}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 6e695e6..6c99a07 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -91,7 +91,6 @@
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
/**
* AudioManager provides access to volume and ringer mode control.
@@ -8349,100 +8348,97 @@
}
/**
- * Add UID's that can be considered as assistant.
+ * Add UIDs that can be considered as assistant.
*
- * @param assistantUids UID's of the services that can be considered as assistant.
+ * @param assistantUids UIDs of the services that can be considered as assistant.
*
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
- public void addAssistantServicesUids(@NonNull List<Integer> assistantUids) {
+ public void addAssistantServicesUids(@NonNull int[] assistantUids) {
try {
- getService().addAssistantServicesUids(assistantUids.stream()
- .mapToInt(Integer::intValue).toArray());
+ getService().addAssistantServicesUids(assistantUids);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Remove UID's that can be considered as assistant.
+ * Remove UIDs that can be considered as assistant.
*
- * @param assistantUids UID'S of the services that should be remove.
+ * @param assistantUids UIDs of the services that should be remove.
*
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
- public void removeAssistantServicesUids(@NonNull List<Integer> assistantUids) {
+ public void removeAssistantServicesUids(@NonNull int[] assistantUids) {
try {
- getService().removeAssistantServicesUids(assistantUids.stream()
- .mapToInt(Integer::intValue).toArray());
+ getService().removeAssistantServicesUids(assistantUids);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Get the list of assistants UIDs that been added with the
- * {@link #addAssistantServicesUids(List)} (List)} and not yet removed with
- * {@link #removeAssistantServicesUids(List)}
+ * Get the assistants UIDs that been added with the
+ * {@link #addAssistantServicesUids(int[])} and not yet removed with
+ * {@link #removeAssistantServicesUids(int[])}
*
- * @return list of assistants UID's
+ * @return array of assistants UIDs
*
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
- public @NonNull List<Integer> getAssistantServicesUids() {
+ public @NonNull int[] getAssistantServicesUids() {
try {
int[] uids = getService().getAssistantServicesUids();
- return Arrays.stream(uids).boxed().collect(Collectors.toList());
+ return Arrays.copyOf(uids, uids.length);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Sets UID's that can be considered as active assistant. Calling the API with a new list will
- * overwrite previous list. If the list of UIDs is empty then no UID will be considered active.
- * In this manner calling the API with an empty list will remove all UID's previously set.
+ * Sets UIDs that can be considered as active assistant. Calling the API with a new array will
+ * overwrite previous UIDs. If the array of UIDs is empty then no UID will be considered active.
+ * In this manner calling the API with an empty array will remove all UIDs previously set.
*
- * @param assistantUids UID'S of the services that can be considered active assistant. Can be
- * an empty list, for this no UID will be considered active.
+ * @param assistantUids UIDs of the services that can be considered active assistant. Can be
+ * an empty array, for this no UID will be considered active.
*
* <p> Note that during audio service crash reset and after boot up the list of active assistant
- * UID's will be reset to an empty list (i.e. no UID will be considered as an active assistant).
+ * UIDs will be reset to an empty list (i.e. no UID will be considered as an active assistant).
* Just after user switch the list of active assistant will also reset to empty.
*
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
- public void setActiveAssistantServiceUids(@NonNull List<Integer> assistantUids) {
+ public void setActiveAssistantServiceUids(@NonNull int[] assistantUids) {
try {
- getService().setActiveAssistantServiceUids(assistantUids.stream()
- .mapToInt(Integer::intValue).toArray());
+ getService().setActiveAssistantServiceUids(assistantUids);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Get the list of active assistant UIDs last set with the
- * {@link #setActiveAssistantServiceUids(List)}
+ * Get active assistant UIDs last set with the
+ * {@link #setActiveAssistantServiceUids(int[])}
*
- * @return list of active assistants UID's
+ * @return array of active assistants UIDs
*
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
- public @NonNull List<Integer> getActiveAssistantServicesUids() {
+ public @NonNull int[] getActiveAssistantServicesUids() {
try {
int[] uids = getService().getActiveAssistantServiceUids();
- return Arrays.stream(uids).boxed().collect(Collectors.toList());
+ return Arrays.copyOf(uids, uids.length);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 6cacebb..210f3e5 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -1668,13 +1668,14 @@
* otherwise (typically one device, except for duplicated paths).
*/
public static @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributes(
- @NonNull AudioAttributes attributes) {
+ @NonNull AudioAttributes attributes, boolean forVolume) {
Objects.requireNonNull(attributes);
final AudioDeviceAttributes[] devices = new AudioDeviceAttributes[MAX_DEVICE_ROUTING];
- final int res = getDevicesForAttributes(attributes, devices);
+ final int res = getDevicesForAttributes(attributes, devices, forVolume);
final ArrayList<AudioDeviceAttributes> routeDevices = new ArrayList<>();
if (res != SUCCESS) {
- Log.e(TAG, "error " + res + " in getDevicesForAttributes for " + attributes);
+ Log.e(TAG, "error " + res + " in getDevicesForAttributes attributes: " + attributes
+ + " forVolume: " + forVolume);
return routeDevices;
}
@@ -1693,7 +1694,8 @@
private static final int MAX_DEVICE_ROUTING = 4;
private static native int getDevicesForAttributes(@NonNull AudioAttributes aa,
- @NonNull AudioDeviceAttributes[] devices);
+ @NonNull AudioDeviceAttributes[] devices,
+ boolean forVolume);
/** @hide returns true if master mono is enabled. */
public static native boolean getMasterMono();
diff --git a/obex/Android.bp b/obex/Android.bp
index 37e7f76..d89d41d 100644
--- a/obex/Android.bp
+++ b/obex/Android.bp
@@ -44,6 +44,8 @@
],
}
+// No longer used. Only kept because the ObexPacket class is a public API.
+// The library has been migrated to platform/external/obex.
java_sdk_library {
name: "javax.obex",
srcs: ["javax/**/*.java"],
diff --git a/obex/javax/obex/ApplicationParameter.java b/obex/javax/obex/ApplicationParameter.java
deleted file mode 100644
index 16770a1a..0000000
--- a/obex/javax/obex/ApplicationParameter.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright (c) 2008-2009, Motorola, Inc.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * - Neither the name of the Motorola, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package javax.obex;
-
-/**
- * @hide
- */
-public final class ApplicationParameter {
-
- private byte[] mArray;
-
- private int mLength;
-
- private int mMaxLength = 1000;
-
- public static class TRIPLET_TAGID {
- public static final byte ORDER_TAGID = 0x01;
-
- public static final byte SEARCH_VALUE_TAGID = 0x02;
-
- public static final byte SEARCH_ATTRIBUTE_TAGID = 0x03;
-
- // if equals to "0", PSE only reply number of contacts
- public static final byte MAXLISTCOUNT_TAGID = 0x04;
-
- public static final byte LISTSTARTOFFSET_TAGID = 0x05;
-
- public static final byte PROPERTY_SELECTOR_TAGID = 0x06;
-
- public static final byte FORMAT_TAGID = 0x07;
-
- // only used if max list count = 0
- public static final byte PHONEBOOKSIZE_TAGID = 0x08;
-
- // only used in "mch" in response
- public static final byte NEWMISSEDCALLS_TAGID = 0x09;
-
- public static final byte SUPPORTEDFEATURE_TAGID = 0x10;
-
- public static final byte PRIMARYVERSIONCOUNTER_TAGID = 0x0A;
-
- public static final byte SECONDARYVERSIONCOUNTER_TAGID = 0x0B;
-
- public static final byte VCARDSELECTOR_TAGID = 0x0C;
-
- public static final byte DATABASEIDENTIFIER_TAGID = 0x0D;
-
- public static final byte VCARDSELECTOROPERATOR_TAGID = 0x0E;
-
- public static final byte RESET_NEW_MISSED_CALLS_TAGID = 0x0F;
- }
-
- public static class TRIPLET_VALUE {
- public static class ORDER {
- public static final byte ORDER_BY_INDEX = 0x00;
-
- public static final byte ORDER_BY_ALPHANUMERIC = 0x01;
-
- public static final byte ORDER_BY_PHONETIC = 0x02;
- }
-
- public static class SEARCHATTRIBUTE {
- public static final byte SEARCH_BY_NAME = 0x00;
-
- public static final byte SEARCH_BY_NUMBER = 0x01;
-
- public static final byte SEARCH_BY_SOUND = 0x02;
- }
-
- public static class FORMAT {
- public static final byte VCARD_VERSION_21 = 0x00;
-
- public static final byte VCARD_VERSION_30 = 0x01;
- }
- }
-
- public static class TRIPLET_LENGTH {
- public static final byte ORDER_LENGTH = 1;
-
- public static final byte SEARCH_ATTRIBUTE_LENGTH = 1;
-
- public static final byte MAXLISTCOUNT_LENGTH = 2;
-
- public static final byte LISTSTARTOFFSET_LENGTH = 2;
-
- public static final byte PROPERTY_SELECTOR_LENGTH = 8;
-
- public static final byte FORMAT_LENGTH = 1;
-
- public static final byte PHONEBOOKSIZE_LENGTH = 2;
-
- public static final byte NEWMISSEDCALLS_LENGTH = 1;
-
- public static final byte SUPPORTEDFEATURE_LENGTH = 4;
-
- public static final byte PRIMARYVERSIONCOUNTER_LENGTH = 16;
-
- public static final byte SECONDARYVERSIONCOUNTER_LENGTH = 16;
-
- public static final byte VCARDSELECTOR_LENGTH = 8;
-
- public static final byte DATABASEIDENTIFIER_LENGTH = 16;
-
- public static final byte VCARDSELECTOROPERATOR_LENGTH = 1;
-
- public static final byte RESETNEWMISSEDCALLS_LENGTH = 1;
- }
-
- public ApplicationParameter() {
- mArray = new byte[mMaxLength];
- mLength = 0;
- }
-
- public void addAPPHeader(byte tag, byte len, byte[] value) {
- if ((mLength + len + 2) > mMaxLength) {
- byte[] array_tmp = new byte[mLength + 4 * len];
- System.arraycopy(mArray, 0, array_tmp, 0, mLength);
- mArray = array_tmp;
- mMaxLength = mLength + 4 * len;
- }
- mArray[mLength++] = tag;
- mArray[mLength++] = len;
- System.arraycopy(value, 0, mArray, mLength, len);
- mLength += len;
- }
-
- public byte[] getAPPparam() {
- byte[] para = new byte[mLength];
- System.arraycopy(mArray, 0, para, 0, mLength);
- return para;
- }
-}
diff --git a/obex/javax/obex/Authenticator.java b/obex/javax/obex/Authenticator.java
deleted file mode 100644
index ec226fb..0000000
--- a/obex/javax/obex/Authenticator.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (c) 2008-2009, Motorola, Inc.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * - Neither the name of the Motorola, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package javax.obex;
-
-/**
- * This interface provides a way to respond to authentication challenge and
- * authentication response headers. When a client or server receives an
- * authentication challenge or authentication response header, the
- * <code>onAuthenticationChallenge()</code> or
- * <code>onAuthenticationResponse()</code> will be called, respectively, by the
- * implementation.
- * <P>
- * For more information on how the authentication procedure works in OBEX,
- * please review the IrOBEX specification at <A
- * HREF="http://www.irda.org">http://www.irda.org</A>.
- * <P>
- * <STRONG>Authentication Challenges</STRONG>
- * <P>
- * When a client or server receives an authentication challenge header, the
- * <code>onAuthenticationChallenge()</code> method will be invoked by the OBEX
- * API implementation. The application will then return the user name (if
- * needed) and password via a <code>PasswordAuthentication</code> object. The
- * password in this object is not sent in the authentication response. Instead,
- * the 16-byte challenge received in the authentication challenge is combined
- * with the password returned from the <code>onAuthenticationChallenge()</code>
- * method and passed through the MD5 hash algorithm. The resulting value is sent
- * in the authentication response along with the user name if it was provided.
- * <P>
- * <STRONG>Authentication Responses</STRONG>
- * <P>
- * When a client or server receives an authentication response header, the
- * <code>onAuthenticationResponse()</code> method is invoked by the API
- * implementation with the user name received in the authentication response
- * header. (The user name will be <code>null</code> if no user name was provided
- * in the authentication response header.) The application must determine the
- * correct password. This value should be returned from the
- * <code>onAuthenticationResponse()</code> method. If the authentication request
- * should fail without the implementation checking the password,
- * <code>null</code> should be returned by the application. (This is needed for
- * reasons like not recognizing the user name, etc.) If the returned value is
- * not <code>null</code>, the OBEX API implementation will combine the password
- * returned from the <code>onAuthenticationResponse()</code> method and
- * challenge sent via the authentication challenge, apply the MD5 hash
- * algorithm, and compare the result to the response hash received in the
- * authentication response header. If the values are not equal, an
- * <code>IOException</code> will be thrown if the client requested
- * authentication. If the server requested authentication, the
- * <code>onAuthenticationFailure()</code> method will be called on the
- * <code>ServerRequestHandler</code> that failed authentication. The connection
- * is <B>not</B> closed if authentication failed.
- * @hide
- */
-public interface Authenticator {
-
- /**
- * Called when a client or a server receives an authentication challenge
- * header. It should respond to the challenge with a
- * <code>PasswordAuthentication</code> that contains the correct user name
- * and password for the challenge.
- * @param description the description of which user name and password should
- * be used; if no description is provided in the authentication
- * challenge or the description is encoded in an encoding scheme that
- * is not supported, an empty string will be provided
- * @param isUserIdRequired <code>true</code> if the user ID is required;
- * <code>false</code> if the user ID is not required
- * @param isFullAccess <code>true</code> if full access to the server will
- * be granted; <code>false</code> if read only access will be granted
- * @return a <code>PasswordAuthentication</code> object containing the user
- * name and password used for authentication
- */
- PasswordAuthentication onAuthenticationChallenge(String description, boolean isUserIdRequired,
- boolean isFullAccess);
-
- /**
- * Called when a client or server receives an authentication response
- * header. This method will provide the user name and expect the correct
- * password to be returned.
- * @param userName the user name provided in the authentication response; may
- * be <code>null</code>
- * @return the correct password for the user name provided; if
- * <code>null</code> is returned then the authentication request
- * failed
- */
- byte[] onAuthenticationResponse(byte[] userName);
-}
diff --git a/obex/javax/obex/BaseStream.java b/obex/javax/obex/BaseStream.java
deleted file mode 100644
index 022ad4f..0000000
--- a/obex/javax/obex/BaseStream.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (c) 2008-2009, Motorola, Inc.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * - Neither the name of the Motorola, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package javax.obex;
-
-import java.io.IOException;
-
-/**
- * This interface defines the methods needed by a parent that uses the
- * PrivateInputStream and PrivateOutputStream objects defined in this package.
- * @hide
- */
-public interface BaseStream {
-
- /**
- * Verifies that this object is still open.
- * @throws IOException if the object is closed
- */
- void ensureOpen() throws IOException;
-
- /**
- * Verifies that additional information may be sent. In other words, the
- * operation is not done.
- * @throws IOException if the operation is completed
- */
- void ensureNotDone() throws IOException;
-
- /**
- * Continues the operation since there is no data to read.
- * @param sendEmpty <code>true</code> if the operation should send an empty
- * packet or not send anything if there is no data to send
- * @param inStream <code>true</code> if the stream is input stream or is
- * output stream
- * @return <code>true</code> if the operation was completed;
- * <code>false</code> if no operation took place
- * @throws IOException if an IO error occurs
- */
- boolean continueOperation(boolean sendEmpty, boolean inStream) throws IOException;
-
- /**
- * Called when the output or input stream is closed.
- * @param inStream <code>true</code> if the input stream is closed;
- * <code>false</code> if the output stream is closed
- * @throws IOException if an IO error occurs
- */
- void streamClosed(boolean inStream) throws IOException;
-}
diff --git a/obex/javax/obex/ClientOperation.java b/obex/javax/obex/ClientOperation.java
deleted file mode 100644
index c627dfb..0000000
--- a/obex/javax/obex/ClientOperation.java
+++ /dev/null
@@ -1,851 +0,0 @@
-/*
- * Copyright (c) 2015 The Android Open Source Project
- * Copyright (C) 2015 Samsung LSI
- * Copyright (c) 2008-2009, Motorola, Inc.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * - Neither the name of the Motorola, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package javax.obex;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.ByteArrayOutputStream;
-
-import android.util.Log;
-
-/**
- * This class implements the <code>Operation</code> interface. It will read and
- * write data via puts and gets.
- * @hide
- */
-public final class ClientOperation implements Operation, BaseStream {
-
- private static final String TAG = "ClientOperation";
-
- private static final boolean V = ObexHelper.VDBG;
-
- private ClientSession mParent;
-
- private boolean mInputOpen;
-
- private PrivateInputStream mPrivateInput;
-
- private boolean mPrivateInputOpen;
-
- private PrivateOutputStream mPrivateOutput;
-
- private boolean mPrivateOutputOpen;
-
- private String mExceptionMessage;
-
- private int mMaxPacketSize;
-
- private boolean mOperationDone;
-
- private boolean mGetOperation;
-
- private boolean mGetFinalFlag;
-
- private HeaderSet mRequestHeader;
-
- private HeaderSet mReplyHeader;
-
- private boolean mEndOfBodySent;
-
- private boolean mSendBodyHeader = true;
- // A latch - when triggered, there is not way back ;-)
- private boolean mSrmActive = false;
-
- // Assume SRM disabled - until support is confirmed
- // by the server
- private boolean mSrmEnabled = false;
- // keep waiting until final-bit is received in request
- // to handle the case where the SRM enable header is in
- // a different OBEX packet than the SRMP header.
- private boolean mSrmWaitingForRemote = true;
-
-
- /**
- * Creates new OperationImpl to read and write data to a server
- * @param maxSize the maximum packet size
- * @param p the parent to this object
- * @param type <code>true</code> if this is a get request;
- * <code>false</code. if this is a put request
- * @param header the header to set in the initial request
- * @throws IOException if the an IO error occurred
- */
- public ClientOperation(int maxSize, ClientSession p, HeaderSet header, boolean type)
- throws IOException {
-
- mParent = p;
- mEndOfBodySent = false;
- mInputOpen = true;
- mOperationDone = false;
- mMaxPacketSize = maxSize;
- mGetOperation = type;
- mGetFinalFlag = false;
-
- mPrivateInputOpen = false;
- mPrivateOutputOpen = false;
- mPrivateInput = null;
- mPrivateOutput = null;
-
- mReplyHeader = new HeaderSet();
-
- mRequestHeader = new HeaderSet();
-
- int[] headerList = header.getHeaderList();
-
- if (headerList != null) {
-
- for (int i = 0; i < headerList.length; i++) {
- mRequestHeader.setHeader(headerList[i], header.getHeader(headerList[i]));
- }
- }
-
- if ((header).mAuthChall != null) {
- mRequestHeader.mAuthChall = new byte[(header).mAuthChall.length];
- System.arraycopy((header).mAuthChall, 0, mRequestHeader.mAuthChall, 0,
- (header).mAuthChall.length);
- }
-
- if ((header).mAuthResp != null) {
- mRequestHeader.mAuthResp = new byte[(header).mAuthResp.length];
- System.arraycopy((header).mAuthResp, 0, mRequestHeader.mAuthResp, 0,
- (header).mAuthResp.length);
-
- }
-
- if ((header).mConnectionID != null) {
- mRequestHeader.mConnectionID = new byte[4];
- System.arraycopy((header).mConnectionID, 0, mRequestHeader.mConnectionID, 0,
- 4);
-
- }
- }
-
- /**
- * Allows to set flag which will force GET to be always sent as single packet request with
- * final flag set. This is to improve compatibility with some profiles, i.e. PBAP which
- * require requests to be sent this way.
- */
- public void setGetFinalFlag(boolean flag) {
- mGetFinalFlag = flag;
- }
-
- /**
- * Sends an ABORT message to the server. By calling this method, the
- * corresponding input and output streams will be closed along with this
- * object.
- * @throws IOException if the transaction has already ended or if an OBEX
- * server called this method
- */
- public synchronized void abort() throws IOException {
- ensureOpen();
- //no compatible with sun-ri
- if ((mOperationDone) && (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE)) {
- throw new IOException("Operation has already ended");
- }
-
- mExceptionMessage = "Operation aborted";
- if ((!mOperationDone) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
- mOperationDone = true;
- /*
- * Since we are not sending any headers or returning any headers then
- * we just need to write and read the same bytes
- */
- mParent.sendRequest(ObexHelper.OBEX_OPCODE_ABORT, null, mReplyHeader, null, false);
-
- if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_OK) {
- throw new IOException("Invalid response code from server");
- }
-
- mExceptionMessage = null;
- }
-
- close();
- }
-
- /**
- * Retrieves the response code retrieved from the server. Response codes are
- * defined in the <code>ResponseCodes</code> interface.
- * @return the response code retrieved from the server
- * @throws IOException if an error occurred in the transport layer during
- * the transaction; if this method is called on a
- * <code>HeaderSet</code> object created by calling
- * <code>createHeaderSet</code> in a <code>ClientSession</code>
- * object
- */
- public synchronized int getResponseCode() throws IOException {
- if ((mReplyHeader.responseCode == -1)
- || (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
- validateConnection();
- }
-
- return mReplyHeader.responseCode;
- }
-
- /**
- * This method will always return <code>null</code>
- * @return <code>null</code>
- */
- public String getEncoding() {
- return null;
- }
-
- /**
- * Returns the type of content that the resource connected to is providing.
- * E.g. if the connection is via HTTP, then the value of the content-type
- * header field is returned.
- * @return the content type of the resource that the URL references, or
- * <code>null</code> if not known
- */
- public String getType() {
- try {
- return (String)mReplyHeader.getHeader(HeaderSet.TYPE);
- } catch (IOException e) {
- if(V) Log.d(TAG, "Exception occured - returning null",e);
- return null;
- }
- }
-
- /**
- * Returns the length of the content which is being provided. E.g. if the
- * connection is via HTTP, then the value of the content-length header field
- * is returned.
- * @return the content length of the resource that this connection's URL
- * references, or -1 if the content length is not known
- */
- public long getLength() {
- try {
- Long temp = (Long)mReplyHeader.getHeader(HeaderSet.LENGTH);
-
- if (temp == null) {
- return -1;
- } else {
- return temp.longValue();
- }
- } catch (IOException e) {
- if(V) Log.d(TAG,"Exception occured - returning -1",e);
- return -1;
- }
- }
-
- /**
- * Open and return an input stream for a connection.
- * @return an input stream
- * @throws IOException if an I/O error occurs
- */
- public InputStream openInputStream() throws IOException {
-
- ensureOpen();
-
- if (mPrivateInputOpen)
- throw new IOException("no more input streams available");
- if (mGetOperation) {
- // send the GET request here
- validateConnection();
- } else {
- if (mPrivateInput == null) {
- mPrivateInput = new PrivateInputStream(this);
- }
- }
-
- mPrivateInputOpen = true;
-
- return mPrivateInput;
- }
-
- /**
- * Open and return a data input stream for a connection.
- * @return an input stream
- * @throws IOException if an I/O error occurs
- */
- public DataInputStream openDataInputStream() throws IOException {
- return new DataInputStream(openInputStream());
- }
-
- /**
- * Open and return an output stream for a connection.
- * @return an output stream
- * @throws IOException if an I/O error occurs
- */
- public OutputStream openOutputStream() throws IOException {
-
- ensureOpen();
- ensureNotDone();
-
- if (mPrivateOutputOpen)
- throw new IOException("no more output streams available");
-
- if (mPrivateOutput == null) {
- // there are 3 bytes operation headers and 3 bytes body headers //
- mPrivateOutput = new PrivateOutputStream(this, getMaxPacketSize());
- }
-
- mPrivateOutputOpen = true;
-
- return mPrivateOutput;
- }
-
- public int getMaxPacketSize() {
- return mMaxPacketSize - 6 - getHeaderLength();
- }
-
- public int getHeaderLength() {
- // OPP may need it
- byte[] headerArray = ObexHelper.createHeader(mRequestHeader, false);
- return headerArray.length;
- }
-
- /**
- * Open and return a data output stream for a connection.
- * @return an output stream
- * @throws IOException if an I/O error occurs
- */
- public DataOutputStream openDataOutputStream() throws IOException {
- return new DataOutputStream(openOutputStream());
- }
-
- /**
- * Closes the connection and ends the transaction
- * @throws IOException if the operation has already ended or is closed
- */
- public void close() throws IOException {
- mInputOpen = false;
- mPrivateInputOpen = false;
- mPrivateOutputOpen = false;
- mParent.setRequestInactive();
- }
-
- /**
- * Returns the headers that have been received during the operation.
- * Modifying the object returned has no effect on the headers that are sent
- * or retrieved.
- * @return the headers received during this <code>Operation</code>
- * @throws IOException if this <code>Operation</code> has been closed
- */
- public HeaderSet getReceivedHeader() throws IOException {
- ensureOpen();
-
- return mReplyHeader;
- }
-
- /**
- * Specifies the headers that should be sent in the next OBEX message that
- * is sent.
- * @param headers the headers to send in the next message
- * @throws IOException if this <code>Operation</code> has been closed or the
- * transaction has ended and no further messages will be exchanged
- * @throws IllegalArgumentException if <code>headers</code> was not created
- * by a call to <code>ServerRequestHandler.createHeaderSet()</code>
- * @throws NullPointerException if <code>headers</code> is <code>null</code>
- */
- public void sendHeaders(HeaderSet headers) throws IOException {
- ensureOpen();
- if (mOperationDone) {
- throw new IOException("Operation has already exchanged all data");
- }
-
- if (headers == null) {
- throw new IOException("Headers may not be null");
- }
-
- int[] headerList = headers.getHeaderList();
- if (headerList != null) {
- for (int i = 0; i < headerList.length; i++) {
- mRequestHeader.setHeader(headerList[i], headers.getHeader(headerList[i]));
- }
- }
- }
-
- /**
- * Verifies that additional information may be sent. In other words, the
- * operation is not done.
- * @throws IOException if the operation is completed
- */
- public void ensureNotDone() throws IOException {
- if (mOperationDone) {
- throw new IOException("Operation has completed");
- }
- }
-
- /**
- * Verifies that the connection is open and no exceptions should be thrown.
- * @throws IOException if an exception needs to be thrown
- */
- public void ensureOpen() throws IOException {
- mParent.ensureOpen();
-
- if (mExceptionMessage != null) {
- throw new IOException(mExceptionMessage);
- }
- if (!mInputOpen) {
- throw new IOException("Operation has already ended");
- }
- }
-
- /**
- * Verifies that the connection is open and the proper data has been read.
- * @throws IOException if an IO error occurs
- */
- private void validateConnection() throws IOException {
- ensureOpen();
-
- // Make sure that a response has been recieved from remote
- // before continuing
- if (mPrivateInput == null || mReplyHeader.responseCode == -1) {
- startProcessing();
- }
- }
-
- /**
- * Sends a request to the client of the specified type.
- * This function will enable SRM and set SRM active if the server
- * response allows this.
- * @param opCode the request code to send to the client
- * @return <code>true</code> if there is more data to send;
- * <code>false</code> if there is no more data to send
- * @throws IOException if an IO error occurs
- */
- private boolean sendRequest(int opCode) throws IOException {
- boolean returnValue = false;
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- int bodyLength = -1;
- byte[] headerArray = ObexHelper.createHeader(mRequestHeader, true);
- if (mPrivateOutput != null) {
- bodyLength = mPrivateOutput.size();
- }
-
- /*
- * Determine if there is space to add a body request. At present
- * this method checks to see if there is room for at least a 17
- * byte body header. This number needs to be at least 6 so that
- * there is room for the header ID and length and the reply ID and
- * length, but it is a waste of resources if we can't send much of
- * the body.
- */
- final int MINIMUM_BODY_LENGTH = 3;
- if ((ObexHelper.BASE_PACKET_LENGTH + headerArray.length + MINIMUM_BODY_LENGTH)
- > mMaxPacketSize) {
- int end = 0;
- int start = 0;
- // split & send the headerArray in multiple packets.
-
- while (end != headerArray.length) {
- //split the headerArray
-
- end = ObexHelper.findHeaderEnd(headerArray, start, mMaxPacketSize
- - ObexHelper.BASE_PACKET_LENGTH);
- // can not split
- if (end == -1) {
- mOperationDone = true;
- abort();
- mExceptionMessage = "Header larger then can be sent in a packet";
- mInputOpen = false;
-
- if (mPrivateInput != null) {
- mPrivateInput.close();
- }
-
- if (mPrivateOutput != null) {
- mPrivateOutput.close();
- }
- throw new IOException("OBEX Packet exceeds max packet size");
- }
-
- byte[] sendHeader = new byte[end - start];
- System.arraycopy(headerArray, start, sendHeader, 0, sendHeader.length);
- if (!mParent.sendRequest(opCode, sendHeader, mReplyHeader, mPrivateInput, false)) {
- return false;
- }
-
- if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
- return false;
- }
-
- start = end;
- }
-
- // Enable SRM if it should be enabled
- checkForSrm();
-
- if (bodyLength > 0) {
- return true;
- } else {
- return false;
- }
- } else {
- /* All headers will fit into a single package */
- if(mSendBodyHeader == false) {
- /* As we are not to send any body data, set the FINAL_BIT */
- opCode |= ObexHelper.OBEX_OPCODE_FINAL_BIT_MASK;
- }
- out.write(headerArray);
- }
-
- if (bodyLength > 0) {
- /*
- * Determine if we can send the whole body or just part of
- * the body. Remember that there is the 3 bytes for the
- * response message and 3 bytes for the header ID and length
- */
- if (bodyLength > (mMaxPacketSize - headerArray.length - 6)) {
- returnValue = true;
-
- bodyLength = mMaxPacketSize - headerArray.length - 6;
- }
-
- byte[] body = mPrivateOutput.readBytes(bodyLength);
-
- /*
- * Since this is a put request if the final bit is set or
- * the output stream is closed we need to send the 0x49
- * (End of Body) otherwise, we need to send 0x48 (Body)
- */
- if ((mPrivateOutput.isClosed()) && (!returnValue) && (!mEndOfBodySent)
- && ((opCode & ObexHelper.OBEX_OPCODE_FINAL_BIT_MASK) != 0)) {
- out.write(HeaderSet.END_OF_BODY);
- mEndOfBodySent = true;
- } else {
- out.write(HeaderSet.BODY);
- }
-
- bodyLength += 3;
- out.write((byte)(bodyLength >> 8));
- out.write((byte)bodyLength);
-
- if (body != null) {
- out.write(body);
- }
- }
-
- if (mPrivateOutputOpen && bodyLength <= 0 && !mEndOfBodySent) {
- // only 0x82 or 0x83 can send 0x49
- if ((opCode & ObexHelper.OBEX_OPCODE_FINAL_BIT_MASK) == 0) {
- out.write(HeaderSet.BODY);
- } else {
- out.write(HeaderSet.END_OF_BODY);
- mEndOfBodySent = true;
- }
-
- bodyLength = 3;
- out.write((byte)(bodyLength >> 8));
- out.write((byte)bodyLength);
- }
-
- if (out.size() == 0) {
- if (!mParent.sendRequest(opCode, null, mReplyHeader, mPrivateInput, mSrmActive)) {
- return false;
- }
- // Enable SRM if it should be enabled
- checkForSrm();
- return returnValue;
- }
- if ((out.size() > 0)
- && (!mParent.sendRequest(opCode, out.toByteArray(),
- mReplyHeader, mPrivateInput, mSrmActive))) {
- return false;
- }
- // Enable SRM if it should be enabled
- checkForSrm();
-
- // send all of the output data in 0x48,
- // send 0x49 with empty body
- if ((mPrivateOutput != null) && (mPrivateOutput.size() > 0))
- returnValue = true;
-
- return returnValue;
- }
-
- private void checkForSrm() throws IOException {
- Byte srmMode = (Byte)mReplyHeader.getHeader(HeaderSet.SINGLE_RESPONSE_MODE);
- if(mParent.isSrmSupported() == true && srmMode != null
- && srmMode == ObexHelper.OBEX_SRM_ENABLE) {
- mSrmEnabled = true;
- }
- /**
- * Call this only when a complete obex packet have been received.
- * (This is not optimal, but the current design is not really suited to
- * the way SRM is specified.)
- * The BT usage of SRM is not really safe - it assumes that the SRMP will fit
- * into every OBEX packet, hence if another header occupies the entire packet,
- * the scheme will not work - unlikely though.
- */
- if(mSrmEnabled) {
- mSrmWaitingForRemote = false;
- Byte srmp = (Byte)mReplyHeader.getHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER);
- if(srmp != null && srmp == ObexHelper.OBEX_SRMP_WAIT) {
- mSrmWaitingForRemote = true;
- // Clear the wait header, as the absence of the header in the next packet
- // indicates don't wait anymore.
- mReplyHeader.setHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER, null);
- }
- }
- if((mSrmWaitingForRemote == false) && (mSrmEnabled == true)) {
- mSrmActive = true;
- }
- }
-
- /**
- * This method starts the processing thread results. It will send the
- * initial request. If the response takes more then one packet, a thread
- * will be started to handle additional requests
- * @throws IOException if an IO error occurs
- */
- private synchronized void startProcessing() throws IOException {
-
- if (mPrivateInput == null) {
- mPrivateInput = new PrivateInputStream(this);
- }
- boolean more = true;
-
- if (mGetOperation) {
- if (!mOperationDone) {
- if (!mGetFinalFlag) {
- mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
- while ((more) && (mReplyHeader.responseCode ==
- ResponseCodes.OBEX_HTTP_CONTINUE)) {
- more = sendRequest(ObexHelper.OBEX_OPCODE_GET);
- }
- // For GET we need to loop until all headers have been sent,
- // And then we wait for the first continue package with the
- // reply.
- if (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
- mParent.sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL,
- null, mReplyHeader, mPrivateInput, mSrmActive);
- }
- if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
- mOperationDone = true;
- } else {
- checkForSrm();
- }
- } else {
- more = sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL);
-
- if (more) {
- throw new IOException("FINAL_GET forced, data didn't fit into one packet");
- }
-
- mOperationDone = true;
- }
- }
- } else {
- // PUT operation
- if (!mOperationDone) {
- mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
- while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
- more = sendRequest(ObexHelper.OBEX_OPCODE_PUT);
- }
- }
-
- if (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
- mParent.sendRequest(ObexHelper.OBEX_OPCODE_PUT_FINAL,
- null, mReplyHeader, mPrivateInput, mSrmActive);
- }
-
- if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
- mOperationDone = true;
- }
- }
- }
-
- /**
- * Continues the operation since there is no data to read.
- * @param sendEmpty <code>true</code> if the operation should send an empty
- * packet or not send anything if there is no data to send
- * @param inStream <code>true</code> if the stream is input stream or is
- * output stream
- * @throws IOException if an IO error occurs
- */
- public synchronized boolean continueOperation(boolean sendEmpty, boolean inStream)
- throws IOException {
-
- // One path to the first put operation - the other one does not need to
- // handle SRM, as all will fit into one packet.
-
- if (mGetOperation) {
- if ((inStream) && (!mOperationDone)) {
- // to deal with inputstream in get operation
- mParent.sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL,
- null, mReplyHeader, mPrivateInput, mSrmActive);
- /*
- * Determine if that was not the last packet in the operation
- */
- if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
- mOperationDone = true;
- } else {
- checkForSrm();
- }
-
- return true;
-
- } else if ((!inStream) && (!mOperationDone)) {
- // to deal with outputstream in get operation
-
- if (mPrivateInput == null) {
- mPrivateInput = new PrivateInputStream(this);
- }
-
- if (!mGetFinalFlag) {
- sendRequest(ObexHelper.OBEX_OPCODE_GET);
- } else {
- sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL);
- }
- if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
- mOperationDone = true;
- }
- return true;
-
- } else if (mOperationDone) {
- return false;
- }
-
- } else {
- // PUT operation
- if ((!inStream) && (!mOperationDone)) {
- // to deal with outputstream in put operation
- if (mReplyHeader.responseCode == -1) {
- mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
- }
- sendRequest(ObexHelper.OBEX_OPCODE_PUT);
- return true;
- } else if ((inStream) && (!mOperationDone)) {
- // How to deal with inputstream in put operation ?
- return false;
-
- } else if (mOperationDone) {
- return false;
- }
-
- }
- return false;
- }
-
- /**
- * Called when the output or input stream is closed.
- * @param inStream <code>true</code> if the input stream is closed;
- * <code>false</code> if the output stream is closed
- * @throws IOException if an IO error occurs
- */
- public void streamClosed(boolean inStream) throws IOException {
- if (!mGetOperation) {
- if ((!inStream) && (!mOperationDone)) {
- // to deal with outputstream in put operation
-
- boolean more = true;
-
- if ((mPrivateOutput != null) && (mPrivateOutput.size() <= 0)) {
- byte[] headerArray = ObexHelper.createHeader(mRequestHeader, false);
- if (headerArray.length <= 0)
- more = false;
- }
- // If have not sent any data so send all now
- if (mReplyHeader.responseCode == -1) {
- mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
- }
-
- while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
- more = sendRequest(ObexHelper.OBEX_OPCODE_PUT);
- }
-
- /*
- * According to the IrOBEX specification, after the final put, you
- * only have a single reply to send. so we don't need the while
- * loop.
- */
- while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
-
- sendRequest(ObexHelper.OBEX_OPCODE_PUT_FINAL);
- }
- mOperationDone = true;
- } else if ((inStream) && (mOperationDone)) {
- // how to deal with input stream in put stream ?
- mOperationDone = true;
- }
- } else {
- if ((inStream) && (!mOperationDone)) {
-
- // to deal with inputstream in get operation
- // Have not sent any data so send it all now
-
- if (mReplyHeader.responseCode == -1) {
- mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
- }
-
- while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE && !mOperationDone) {
- if (!sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL)) {
- break;
- }
- }
- while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE && !mOperationDone) {
- mParent.sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL, null,
- mReplyHeader, mPrivateInput, false);
- // Regardless of the SRM state, wait for the response.
- }
- mOperationDone = true;
- } else if ((!inStream) && (!mOperationDone)) {
- // to deal with outputstream in get operation
- // part of the data may have been sent in continueOperation.
-
- boolean more = true;
-
- if ((mPrivateOutput != null) && (mPrivateOutput.size() <= 0)) {
- byte[] headerArray = ObexHelper.createHeader(mRequestHeader, false);
- if (headerArray.length <= 0)
- more = false;
- }
-
- if (mPrivateInput == null) {
- mPrivateInput = new PrivateInputStream(this);
- }
- if ((mPrivateOutput != null) && (mPrivateOutput.size() <= 0))
- more = false;
-
- mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
- while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
- more = sendRequest(ObexHelper.OBEX_OPCODE_GET);
- }
- sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL);
- // parent.sendRequest(0x83, null, replyHeaders, privateInput);
- if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
- mOperationDone = true;
- }
- }
- }
- }
-
- public void noBodyHeader(){
- mSendBodyHeader = false;
- }
-}
diff --git a/obex/javax/obex/ClientSession.java b/obex/javax/obex/ClientSession.java
deleted file mode 100644
index 272a920..0000000
--- a/obex/javax/obex/ClientSession.java
+++ /dev/null
@@ -1,616 +0,0 @@
-/*
- * Copyright (c) 2015 The Android Open Source Project
- * Copyright (C) 2015 Samsung LSI
- * Copyright (c) 2008-2009, Motorola, Inc.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * - Neither the name of the Motorola, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package javax.obex;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-import android.util.Log;
-
-/**
- * This class in an implementation of the OBEX ClientSession.
- * @hide
- */
-public final class ClientSession extends ObexSession {
-
- private static final String TAG = "ClientSession";
-
- private boolean mOpen;
-
- // Determines if an OBEX layer connection has been established
- private boolean mObexConnected;
-
- private byte[] mConnectionId = null;
-
- /*
- * The max Packet size must be at least 255 according to the OBEX
- * specification.
- */
- private int mMaxTxPacketSize = ObexHelper.LOWER_LIMIT_MAX_PACKET_SIZE;
-
- private boolean mRequestActive;
-
- private final InputStream mInput;
-
- private final OutputStream mOutput;
-
- private final boolean mLocalSrmSupported;
-
- private final ObexTransport mTransport;
-
- public ClientSession(final ObexTransport trans) throws IOException {
- mInput = trans.openInputStream();
- mOutput = trans.openOutputStream();
- mOpen = true;
- mRequestActive = false;
- mLocalSrmSupported = trans.isSrmSupported();
- mTransport = trans;
- }
-
- /**
- * Create a ClientSession
- * @param trans The transport to use for OBEX transactions
- * @param supportsSrm True if Single Response Mode should be used e.g. if the
- * supplied transport is a TCP or l2cap channel.
- * @throws IOException if it occurs while opening the transport streams.
- */
- public ClientSession(final ObexTransport trans, final boolean supportsSrm) throws IOException {
- mInput = trans.openInputStream();
- mOutput = trans.openOutputStream();
- mOpen = true;
- mRequestActive = false;
- mLocalSrmSupported = supportsSrm;
- mTransport = trans;
- }
-
- public HeaderSet connect(final HeaderSet header) throws IOException {
- ensureOpen();
- if (mObexConnected) {
- throw new IOException("Already connected to server");
- }
- setRequestActive();
-
- int totalLength = 4;
- byte[] head = null;
-
- // Determine the header byte array
- if (header != null) {
- if (header.nonce != null) {
- mChallengeDigest = new byte[16];
- System.arraycopy(header.nonce, 0, mChallengeDigest, 0, 16);
- }
- head = ObexHelper.createHeader(header, false);
- totalLength += head.length;
- }
- /*
- * Write the OBEX CONNECT packet to the server.
- * Byte 0: 0x80
- * Byte 1&2: Connect Packet Length
- * Byte 3: OBEX Version Number (Presently, 0x10)
- * Byte 4: Flags (For TCP 0x00)
- * Byte 5&6: Max OBEX Packet Length (Defined in MAX_PACKET_SIZE)
- * Byte 7 to n: headers
- */
- byte[] requestPacket = new byte[totalLength];
- int maxRxPacketSize = ObexHelper.getMaxRxPacketSize(mTransport);
- // We just need to start at byte 3 since the sendRequest() method will
- // handle the length and 0x80.
- requestPacket[0] = (byte)0x10;
- requestPacket[1] = (byte)0x00;
- requestPacket[2] = (byte)(maxRxPacketSize >> 8);
- requestPacket[3] = (byte)(maxRxPacketSize & 0xFF);
- if (head != null) {
- System.arraycopy(head, 0, requestPacket, 4, head.length);
- }
-
- // Since we are not yet connected, the peer max packet size is unknown,
- // hence we are only guaranteed the server will use the first 7 bytes.
- if ((requestPacket.length + 3) > ObexHelper.MAX_PACKET_SIZE_INT) {
- throw new IOException("Packet size exceeds max packet size for connect");
- }
-
- HeaderSet returnHeaderSet = new HeaderSet();
- sendRequest(ObexHelper.OBEX_OPCODE_CONNECT, requestPacket, returnHeaderSet, null, false);
-
- /*
- * Read the response from the OBEX server.
- * Byte 0: Response Code (If successful then OBEX_HTTP_OK)
- * Byte 1&2: Packet Length
- * Byte 3: OBEX Version Number
- * Byte 4: Flags3
- * Byte 5&6: Max OBEX packet Length
- * Byte 7 to n: Optional HeaderSet
- */
- if (returnHeaderSet.responseCode == ResponseCodes.OBEX_HTTP_OK) {
- mObexConnected = true;
- }
- setRequestInactive();
-
- return returnHeaderSet;
- }
-
- public Operation get(HeaderSet header) throws IOException {
-
- if (!mObexConnected) {
- throw new IOException("Not connected to the server");
- }
- setRequestActive();
-
- ensureOpen();
-
- HeaderSet head;
- if (header == null) {
- head = new HeaderSet();
- } else {
- head = header;
- if (head.nonce != null) {
- mChallengeDigest = new byte[16];
- System.arraycopy(head.nonce, 0, mChallengeDigest, 0, 16);
- }
- }
- // Add the connection ID if one exists
- if (mConnectionId != null) {
- head.mConnectionID = new byte[4];
- System.arraycopy(mConnectionId, 0, head.mConnectionID, 0, 4);
- }
-
- if(mLocalSrmSupported) {
- head.setHeader(HeaderSet.SINGLE_RESPONSE_MODE, ObexHelper.OBEX_SRM_ENABLE);
- /* TODO: Consider creating an interface to get the wait state.
- * On an android system, I cannot see when this is to be used.
- * except perhaps if we are to wait for user accept on a push message.
- if(getLocalWaitState()) {
- head.setHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER, ObexHelper.OBEX_SRMP_WAIT);
- }
- */
- }
-
- return new ClientOperation(mMaxTxPacketSize, this, head, true);
- }
-
- /**
- * 0xCB Connection Id an identifier used for OBEX connection multiplexing
- */
- public void setConnectionID(long id) {
- if ((id < 0) || (id > 0xFFFFFFFFL)) {
- throw new IllegalArgumentException("Connection ID is not in a valid range");
- }
- mConnectionId = ObexHelper.convertToByteArray(id);
- }
-
- public HeaderSet delete(HeaderSet header) throws IOException {
-
- Operation op = put(header);
- op.getResponseCode();
- HeaderSet returnValue = op.getReceivedHeader();
- op.close();
-
- return returnValue;
- }
-
- public HeaderSet disconnect(HeaderSet header) throws IOException {
- if (!mObexConnected) {
- throw new IOException("Not connected to the server");
- }
- setRequestActive();
-
- ensureOpen();
- // Determine the header byte array
- byte[] head = null;
- if (header != null) {
- if (header.nonce != null) {
- mChallengeDigest = new byte[16];
- System.arraycopy(header.nonce, 0, mChallengeDigest, 0, 16);
- }
- // Add the connection ID if one exists
- if (mConnectionId != null) {
- header.mConnectionID = new byte[4];
- System.arraycopy(mConnectionId, 0, header.mConnectionID, 0, 4);
- }
- head = ObexHelper.createHeader(header, false);
-
- if ((head.length + 3) > mMaxTxPacketSize) {
- throw new IOException("Packet size exceeds max packet size");
- }
- } else {
- // Add the connection ID if one exists
- if (mConnectionId != null) {
- head = new byte[5];
- head[0] = (byte)HeaderSet.CONNECTION_ID;
- System.arraycopy(mConnectionId, 0, head, 1, 4);
- }
- }
-
- HeaderSet returnHeaderSet = new HeaderSet();
- sendRequest(ObexHelper.OBEX_OPCODE_DISCONNECT, head, returnHeaderSet, null, false);
-
- /*
- * An OBEX DISCONNECT reply from the server:
- * Byte 1: Response code
- * Bytes 2 & 3: packet size
- * Bytes 4 & up: headers
- */
-
- /* response code , and header are ignored
- * */
-
- synchronized (this) {
- mObexConnected = false;
- setRequestInactive();
- }
-
- return returnHeaderSet;
- }
-
- public long getConnectionID() {
-
- if (mConnectionId == null) {
- return -1;
- }
- return ObexHelper.convertToLong(mConnectionId);
- }
-
- public Operation put(HeaderSet header) throws IOException {
- if (!mObexConnected) {
- throw new IOException("Not connected to the server");
- }
- setRequestActive();
-
- ensureOpen();
- HeaderSet head;
- if (header == null) {
- head = new HeaderSet();
- } else {
- head = header;
- // when auth is initiated by client ,save the digest
- if (head.nonce != null) {
- mChallengeDigest = new byte[16];
- System.arraycopy(head.nonce, 0, mChallengeDigest, 0, 16);
- }
- }
-
- // Add the connection ID if one exists
- if (mConnectionId != null) {
-
- head.mConnectionID = new byte[4];
- System.arraycopy(mConnectionId, 0, head.mConnectionID, 0, 4);
- }
-
- if(mLocalSrmSupported) {
- head.setHeader(HeaderSet.SINGLE_RESPONSE_MODE, ObexHelper.OBEX_SRM_ENABLE);
- /* TODO: Consider creating an interface to get the wait state.
- * On an android system, I cannot see when this is to be used.
- if(getLocalWaitState()) {
- head.setHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER, ObexHelper.OBEX_SRMP_WAIT);
- }
- */
- }
- return new ClientOperation(mMaxTxPacketSize, this, head, false);
- }
-
- public void setAuthenticator(Authenticator auth) throws IOException {
- if (auth == null) {
- throw new IOException("Authenticator may not be null");
- }
- mAuthenticator = auth;
- }
-
- public HeaderSet setPath(HeaderSet header, boolean backup, boolean create) throws IOException {
- if (!mObexConnected) {
- throw new IOException("Not connected to the server");
- }
- setRequestActive();
- ensureOpen();
-
- int totalLength = 2;
- byte[] head = null;
- HeaderSet headset;
- if (header == null) {
- headset = new HeaderSet();
- } else {
- headset = header;
- if (headset.nonce != null) {
- mChallengeDigest = new byte[16];
- System.arraycopy(headset.nonce, 0, mChallengeDigest, 0, 16);
- }
- }
-
- // when auth is initiated by client ,save the digest
- if (headset.nonce != null) {
- mChallengeDigest = new byte[16];
- System.arraycopy(headset.nonce, 0, mChallengeDigest, 0, 16);
- }
-
- // Add the connection ID if one exists
- if (mConnectionId != null) {
- headset.mConnectionID = new byte[4];
- System.arraycopy(mConnectionId, 0, headset.mConnectionID, 0, 4);
- }
-
- head = ObexHelper.createHeader(headset, false);
- totalLength += head.length;
-
- if (totalLength > mMaxTxPacketSize) {
- throw new IOException("Packet size exceeds max packet size");
- }
-
- int flags = 0;
- /*
- * The backup flag bit is bit 0 so if we add 1, this will set that bit
- */
- if (backup) {
- flags++;
- }
- /*
- * The create bit is bit 1 so if we or with 2 the bit will be set.
- */
- if (!create) {
- flags |= 2;
- }
-
- /*
- * An OBEX SETPATH packet to the server:
- * Byte 1: 0x85
- * Byte 2 & 3: packet size
- * Byte 4: flags
- * Byte 5: constants
- * Byte 6 & up: headers
- */
- byte[] packet = new byte[totalLength];
- packet[0] = (byte)flags;
- packet[1] = (byte)0x00;
- if (headset != null) {
- System.arraycopy(head, 0, packet, 2, head.length);
- }
-
- HeaderSet returnHeaderSet = new HeaderSet();
- sendRequest(ObexHelper.OBEX_OPCODE_SETPATH, packet, returnHeaderSet, null, false);
-
- /*
- * An OBEX SETPATH reply from the server:
- * Byte 1: Response code
- * Bytes 2 & 3: packet size
- * Bytes 4 & up: headers
- */
-
- setRequestInactive();
-
- return returnHeaderSet;
- }
-
- /**
- * Verifies that the connection is open.
- * @throws IOException if the connection is closed
- */
- public synchronized void ensureOpen() throws IOException {
- if (!mOpen) {
- throw new IOException("Connection closed");
- }
- }
-
- /**
- * Set request inactive. Allows Put and get operation objects to tell this
- * object when they are done.
- */
- /*package*/synchronized void setRequestInactive() {
- mRequestActive = false;
- }
-
- /**
- * Set request to active.
- * @throws IOException if already active
- */
- private synchronized void setRequestActive() throws IOException {
- if (mRequestActive) {
- throw new IOException("OBEX request is already being performed");
- }
- mRequestActive = true;
- }
-
- /**
- * Sends a standard request to the client. It will then wait for the reply
- * and update the header set object provided. If any authentication headers
- * (i.e. authentication challenge or authentication response) are received,
- * they will be processed.
- * @param opCode the type of request to send to the client
- * @param head the headers to send to the client
- * @param header the header object to update with the response
- * @param privateInput the input stream used by the Operation object; null
- * if this is called on a CONNECT, SETPATH or DISCONNECT
- * @return
- * <code>true</code> if the operation completed successfully;
- * <code>false</code> if an authentication response failed to pass
- * @throws IOException if an IO error occurs
- */
- public boolean sendRequest(int opCode, byte[] head, HeaderSet header,
- PrivateInputStream privateInput, boolean srmActive) throws IOException {
- //check header length with local max size
- if (head != null) {
- if ((head.length + 3) > ObexHelper.MAX_PACKET_SIZE_INT) {
- // TODO: This is an implementation limit - not a specification requirement.
- throw new IOException("header too large ");
- }
- }
-
- boolean skipSend = false;
- boolean skipReceive = false;
- if (srmActive == true) {
- if (opCode == ObexHelper.OBEX_OPCODE_PUT) {
- // we are in the middle of a SRM PUT operation, don't expect a continue.
- skipReceive = true;
- } else if (opCode == ObexHelper.OBEX_OPCODE_GET) {
- // We are still sending the get request, send, but don't expect continue
- // until the request is transfered (the final bit is set)
- skipReceive = true;
- } else if (opCode == ObexHelper.OBEX_OPCODE_GET_FINAL) {
- // All done sending the request, expect data from the server, without
- // sending continue.
- skipSend = true;
- }
-
- }
-
- int bytesReceived;
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- out.write((byte)opCode);
-
- // Determine if there are any headers to send
- if (head == null) {
- out.write(0x00);
- out.write(0x03);
- } else {
- out.write((byte)((head.length + 3) >> 8));
- out.write((byte)(head.length + 3));
- out.write(head);
- }
-
- if (!skipSend) {
- // Write the request to the output stream and flush the stream
- mOutput.write(out.toByteArray());
- // TODO: is this really needed? if this flush is implemented
- // correctly, we will get a gap between each obex packet.
- // which is kind of the idea behind SRM to avoid.
- // Consider offloading to another thread (async action)
- mOutput.flush();
- }
-
- if (!skipReceive) {
- header.responseCode = mInput.read();
-
- int length = ((mInput.read() << 8) | (mInput.read()));
-
- if (length > ObexHelper.getMaxRxPacketSize(mTransport)) {
- throw new IOException("Packet received exceeds packet size limit");
- }
- if (length > ObexHelper.BASE_PACKET_LENGTH) {
- byte[] data = null;
- if (opCode == ObexHelper.OBEX_OPCODE_CONNECT) {
- @SuppressWarnings("unused")
- int version = mInput.read();
- @SuppressWarnings("unused")
- int flags = mInput.read();
- mMaxTxPacketSize = (mInput.read() << 8) + mInput.read();
-
- //check with local max size
- if (mMaxTxPacketSize > ObexHelper.MAX_CLIENT_PACKET_SIZE) {
- mMaxTxPacketSize = ObexHelper.MAX_CLIENT_PACKET_SIZE;
- }
-
- // check with transport maximum size
- if(mMaxTxPacketSize > ObexHelper.getMaxTxPacketSize(mTransport)) {
- // To increase this size, increase the buffer size in L2CAP layer
- // in Bluedroid.
- Log.w(TAG, "An OBEX packet size of " + mMaxTxPacketSize + "was"
- + " requested. Transport only allows: "
- + ObexHelper.getMaxTxPacketSize(mTransport)
- + " Lowering limit to this value.");
- mMaxTxPacketSize = ObexHelper.getMaxTxPacketSize(mTransport);
- }
-
- if (length > 7) {
- data = new byte[length - 7];
-
- bytesReceived = mInput.read(data);
- while (bytesReceived != (length - 7)) {
- bytesReceived += mInput.read(data, bytesReceived, data.length
- - bytesReceived);
- }
- } else {
- return true;
- }
- } else {
- data = new byte[length - 3];
- bytesReceived = mInput.read(data);
-
- while (bytesReceived != (length - 3)) {
- bytesReceived += mInput.read(data, bytesReceived, data.length - bytesReceived);
- }
- if (opCode == ObexHelper.OBEX_OPCODE_ABORT) {
- return true;
- }
- }
-
- byte[] body = ObexHelper.updateHeaderSet(header, data);
- if ((privateInput != null) && (body != null)) {
- privateInput.writeBytes(body, 1);
- }
-
- if (header.mConnectionID != null) {
- mConnectionId = new byte[4];
- System.arraycopy(header.mConnectionID, 0, mConnectionId, 0, 4);
- }
-
- if (header.mAuthResp != null) {
- if (!handleAuthResp(header.mAuthResp)) {
- setRequestInactive();
- throw new IOException("Authentication Failed");
- }
- }
-
- if ((header.responseCode == ResponseCodes.OBEX_HTTP_UNAUTHORIZED)
- && (header.mAuthChall != null)) {
-
- if (handleAuthChall(header)) {
- out.write((byte)HeaderSet.AUTH_RESPONSE);
- out.write((byte)((header.mAuthResp.length + 3) >> 8));
- out.write((byte)(header.mAuthResp.length + 3));
- out.write(header.mAuthResp);
- header.mAuthChall = null;
- header.mAuthResp = null;
-
- byte[] sendHeaders = new byte[out.size() - 3];
- System.arraycopy(out.toByteArray(), 3, sendHeaders, 0, sendHeaders.length);
-
- return sendRequest(opCode, sendHeaders, header, privateInput, false);
- }
- }
- }
- }
-
- return true;
- }
-
- public void close() throws IOException {
- mOpen = false;
- mInput.close();
- mOutput.close();
- }
-
- public boolean isSrmSupported() {
- return mLocalSrmSupported;
- }
-}
diff --git a/obex/javax/obex/HeaderSet.java b/obex/javax/obex/HeaderSet.java
deleted file mode 100644
index 35fe186..0000000
--- a/obex/javax/obex/HeaderSet.java
+++ /dev/null
@@ -1,710 +0,0 @@
-/*
- * Copyright (c) 2014 The Android Open Source Project
- * Copyright (c) 2008-2009, Motorola, Inc.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * - Neither the name of the Motorola, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package javax.obex;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.util.Calendar;
-import java.security.SecureRandom;
-
-/**
- * This class implements the javax.obex.HeaderSet interface for OBEX over
- * RFCOMM or OBEX over l2cap.
- * @hide
- */
-public final class HeaderSet {
-
- /**
- * Represents the OBEX Count header. This allows the connection statement to
- * tell the server how many objects it plans to send or retrieve.
- * <P>
- * The value of <code>COUNT</code> is 0xC0 (192).
- */
- public static final int COUNT = 0xC0;
-
- /**
- * Represents the OBEX Name header. This specifies the name of the object.
- * <P>
- * The value of <code>NAME</code> is 0x01 (1).
- */
- public static final int NAME = 0x01;
-
- /**
- * Represents the OBEX Type header. This allows a request to specify the
- * type of the object (e.g. text, html, binary, etc.).
- * <P>
- * The value of <code>TYPE</code> is 0x42 (66).
- */
- public static final int TYPE = 0x42;
-
- /**
- * Represents the OBEX Length header. This is the length of the object in
- * bytes.
- * <P>
- * The value of <code>LENGTH</code> is 0xC3 (195).
- */
- public static final int LENGTH = 0xC3;
-
- /**
- * Represents the OBEX Time header using the ISO 8601 standards. This is the
- * preferred time header.
- * <P>
- * The value of <code>TIME_ISO_8601</code> is 0x44 (68).
- */
- public static final int TIME_ISO_8601 = 0x44;
-
- /**
- * Represents the OBEX Time header using the 4 byte representation. This is
- * only included for backwards compatibility. It represents the number of
- * seconds since January 1, 1970.
- * <P>
- * The value of <code>TIME_4_BYTE</code> is 0xC4 (196).
- */
- public static final int TIME_4_BYTE = 0xC4;
-
- /**
- * Represents the OBEX Description header. This is a text description of the
- * object.
- * <P>
- * The value of <code>DESCRIPTION</code> is 0x05 (5).
- */
- public static final int DESCRIPTION = 0x05;
-
- /**
- * Represents the OBEX Target header. This is the name of the service an
- * operation is targeted to.
- * <P>
- * The value of <code>TARGET</code> is 0x46 (70).
- */
- public static final int TARGET = 0x46;
-
- /**
- * Represents the OBEX HTTP header. This allows an HTTP 1.X header to be
- * included in a request or reply.
- * <P>
- * The value of <code>HTTP</code> is 0x47 (71).
- */
- public static final int HTTP = 0x47;
-
- /**
- * Represents the OBEX BODY header.
- * <P>
- * The value of <code>BODY</code> is 0x48 (72).
- */
- public static final int BODY = 0x48;
-
- /**
- * Represents the OBEX End of BODY header.
- * <P>
- * The value of <code>BODY</code> is 0x49 (73).
- */
- public static final int END_OF_BODY = 0x49;
-
- /**
- * Represents the OBEX Who header. Identifies the OBEX application to
- * determine if the two peers are talking to each other.
- * <P>
- * The value of <code>WHO</code> is 0x4A (74).
- */
- public static final int WHO = 0x4A;
-
- /**
- * Represents the OBEX Connection ID header. Identifies used for OBEX
- * connection multiplexing.
- * <P>
- * The value of <code>CONNECTION_ID</code> is 0xCB (203).
- */
-
- public static final int CONNECTION_ID = 0xCB;
-
- /**
- * Represents the OBEX Application Parameter header. This header specifies
- * additional application request and response information.
- * <P>
- * The value of <code>APPLICATION_PARAMETER</code> is 0x4C (76).
- */
- public static final int APPLICATION_PARAMETER = 0x4C;
-
- /**
- * Represents the OBEX authentication digest-challenge.
- * <P>
- * The value of <code>AUTH_CHALLENGE</code> is 0x4D (77).
- */
- public static final int AUTH_CHALLENGE = 0x4D;
-
- /**
- * Represents the OBEX authentication digest-response.
- * <P>
- * The value of <code>AUTH_RESPONSE</code> is 0x4E (78).
- */
- public static final int AUTH_RESPONSE = 0x4E;
-
- /**
- * Represents the OBEX Object Class header. This header specifies the OBEX
- * object class of the object.
- * <P>
- * The value of <code>OBJECT_CLASS</code> is 0x4F (79).
- */
- public static final int OBJECT_CLASS = 0x4F;
-
- /**
- * Represents the OBEX Single Response Mode (SRM). This header is used
- * for Single response mode, introduced in OBEX 1.5.
- * <P>
- * The value of <code>SINGLE_RESPONSE_MODE</code> is 0x97 (151).
- */
- public static final int SINGLE_RESPONSE_MODE = 0x97;
-
- /**
- * Represents the OBEX Single Response Mode Parameters. This header is used
- * for Single response mode, introduced in OBEX 1.5.
- * <P>
- * The value of <code>SINGLE_RESPONSE_MODE_PARAMETER</code> is 0x98 (152).
- */
- public static final int SINGLE_RESPONSE_MODE_PARAMETER = 0x98;
-
- private Long mCount; // 4 byte unsigned integer
-
- private String mName; // null terminated Unicode text string
-
- private boolean mEmptyName;
-
- private String mType; // null terminated ASCII text string
-
- private Long mLength; // 4 byte unsigend integer
-
- private Calendar mIsoTime; // String of the form YYYYMMDDTHHMMSSZ
-
- private Calendar mByteTime; // 4 byte unsigned integer
-
- private String mDescription; // null terminated Unicode text String
-
- private byte[] mTarget; // byte sequence
-
- private byte[] mHttpHeader; // byte sequence
-
- private byte[] mWho; // length prefixed byte sequence
-
- private byte[] mAppParam; // byte sequence of the form tag length value
-
- private byte[] mObjectClass; // byte sequence
-
- private String[] mUnicodeUserDefined; // null terminated unicode string
-
- private byte[][] mSequenceUserDefined; // byte sequence user defined
-
- private Byte[] mByteUserDefined; // 1 byte
-
- private Long[] mIntegerUserDefined; // 4 byte unsigned integer
-
- private SecureRandom mRandom = null;
-
- private Byte mSingleResponseMode; // byte to indicate enable/disable/support for SRM
-
- private Byte mSrmParam; // byte representing the SRM parameters - only "wait"
- // is supported by Bluetooth
-
- /*package*/ byte[] nonce;
-
- public byte[] mAuthChall; // The authentication challenge header
-
- public byte[] mAuthResp; // The authentication response header
-
- public byte[] mConnectionID; // THe connection ID
-
- public int responseCode;
-
- /**
- * Creates new <code>HeaderSet</code> object.
- * @param size the max packet size for this connection
- */
- public HeaderSet() {
- mUnicodeUserDefined = new String[16];
- mSequenceUserDefined = new byte[16][];
- mByteUserDefined = new Byte[16];
- mIntegerUserDefined = new Long[16];
- responseCode = -1;
- }
-
- /**
- * Sets flag for special "value" of NAME header which should be empty. This
- * is not the same as NAME header with empty string in which case it will
- * have length of 5 bytes. It should be 3 bytes with only header id and
- * length field.
- */
- public void setEmptyNameHeader() {
- mName = null;
- mEmptyName = true;
- }
-
- /**
- * Gets flag for special "value" of NAME header which should be empty. See
- * above.
- */
- public boolean getEmptyNameHeader() {
- return mEmptyName;
- }
-
- /**
- * Sets the value of the header identifier to the value provided. The type
- * of object must correspond to the Java type defined in the description of
- * this interface. If <code>null</code> is passed as the
- * <code>headerValue</code> then the header will be removed from the set of
- * headers to include in the next request.
- * @param headerID the identifier to include in the message
- * @param headerValue the value of the header identifier
- * @throws IllegalArgumentException if the header identifier provided is not
- * one defined in this interface or a user-defined header; if the
- * type of <code>headerValue</code> is not the correct Java type as
- * defined in the description of this interface\
- */
- public void setHeader(int headerID, Object headerValue) {
- long temp = -1;
-
- switch (headerID) {
- case COUNT:
- if (!(headerValue instanceof Long)) {
- if (headerValue == null) {
- mCount = null;
- break;
- }
- throw new IllegalArgumentException("Count must be a Long");
- }
- temp = ((Long)headerValue).longValue();
- if ((temp < 0L) || (temp > 0xFFFFFFFFL)) {
- throw new IllegalArgumentException("Count must be between 0 and 0xFFFFFFFF");
- }
- mCount = (Long)headerValue;
- break;
- case NAME:
- if ((headerValue != null) && (!(headerValue instanceof String))) {
- throw new IllegalArgumentException("Name must be a String");
- }
- mEmptyName = false;
- mName = (String)headerValue;
- break;
- case TYPE:
- if ((headerValue != null) && (!(headerValue instanceof String))) {
- throw new IllegalArgumentException("Type must be a String");
- }
- mType = (String)headerValue;
- break;
- case LENGTH:
- if (!(headerValue instanceof Long)) {
- if (headerValue == null) {
- mLength = null;
- break;
- }
- throw new IllegalArgumentException("Length must be a Long");
- }
- temp = ((Long)headerValue).longValue();
- if ((temp < 0L) || (temp > 0xFFFFFFFFL)) {
- throw new IllegalArgumentException("Length must be between 0 and 0xFFFFFFFF");
- }
- mLength = (Long)headerValue;
- break;
- case TIME_ISO_8601:
- if ((headerValue != null) && (!(headerValue instanceof Calendar))) {
- throw new IllegalArgumentException("Time ISO 8601 must be a Calendar");
- }
- mIsoTime = (Calendar)headerValue;
- break;
- case TIME_4_BYTE:
- if ((headerValue != null) && (!(headerValue instanceof Calendar))) {
- throw new IllegalArgumentException("Time 4 Byte must be a Calendar");
- }
- mByteTime = (Calendar)headerValue;
- break;
- case DESCRIPTION:
- if ((headerValue != null) && (!(headerValue instanceof String))) {
- throw new IllegalArgumentException("Description must be a String");
- }
- mDescription = (String)headerValue;
- break;
- case TARGET:
- if (headerValue == null) {
- mTarget = null;
- } else {
- if (!(headerValue instanceof byte[])) {
- throw new IllegalArgumentException("Target must be a byte array");
- } else {
- mTarget = new byte[((byte[])headerValue).length];
- System.arraycopy(headerValue, 0, mTarget, 0, mTarget.length);
- }
- }
- break;
- case HTTP:
- if (headerValue == null) {
- mHttpHeader = null;
- } else {
- if (!(headerValue instanceof byte[])) {
- throw new IllegalArgumentException("HTTP must be a byte array");
- } else {
- mHttpHeader = new byte[((byte[])headerValue).length];
- System.arraycopy(headerValue, 0, mHttpHeader, 0, mHttpHeader.length);
- }
- }
- break;
- case WHO:
- if (headerValue == null) {
- mWho = null;
- } else {
- if (!(headerValue instanceof byte[])) {
- throw new IllegalArgumentException("WHO must be a byte array");
- } else {
- mWho = new byte[((byte[])headerValue).length];
- System.arraycopy(headerValue, 0, mWho, 0, mWho.length);
- }
- }
- break;
- case OBJECT_CLASS:
- if (headerValue == null) {
- mObjectClass = null;
- } else {
- if (!(headerValue instanceof byte[])) {
- throw new IllegalArgumentException("Object Class must be a byte array");
- } else {
- mObjectClass = new byte[((byte[])headerValue).length];
- System.arraycopy(headerValue, 0, mObjectClass, 0, mObjectClass.length);
- }
- }
- break;
- case APPLICATION_PARAMETER:
- if (headerValue == null) {
- mAppParam = null;
- } else {
- if (!(headerValue instanceof byte[])) {
- throw new IllegalArgumentException(
- "Application Parameter must be a byte array");
- } else {
- mAppParam = new byte[((byte[])headerValue).length];
- System.arraycopy(headerValue, 0, mAppParam, 0, mAppParam.length);
- }
- }
- break;
- case SINGLE_RESPONSE_MODE:
- if (headerValue == null) {
- mSingleResponseMode = null;
- } else {
- if (!(headerValue instanceof Byte)) {
- throw new IllegalArgumentException(
- "Single Response Mode must be a Byte");
- } else {
- mSingleResponseMode = (Byte)headerValue;
- }
- }
- break;
- case SINGLE_RESPONSE_MODE_PARAMETER:
- if (headerValue == null) {
- mSrmParam = null;
- } else {
- if (!(headerValue instanceof Byte)) {
- throw new IllegalArgumentException(
- "Single Response Mode Parameter must be a Byte");
- } else {
- mSrmParam = (Byte)headerValue;
- }
- }
- break;
- default:
- // Verify that it was not a Unicode String user Defined
- if ((headerID >= 0x30) && (headerID <= 0x3F)) {
- if ((headerValue != null) && (!(headerValue instanceof String))) {
- throw new IllegalArgumentException(
- "Unicode String User Defined must be a String");
- }
- mUnicodeUserDefined[headerID - 0x30] = (String)headerValue;
-
- break;
- }
- // Verify that it was not a byte sequence user defined value
- if ((headerID >= 0x70) && (headerID <= 0x7F)) {
-
- if (headerValue == null) {
- mSequenceUserDefined[headerID - 0x70] = null;
- } else {
- if (!(headerValue instanceof byte[])) {
- throw new IllegalArgumentException(
- "Byte Sequence User Defined must be a byte array");
- } else {
- mSequenceUserDefined[headerID - 0x70] = new byte[((byte[])headerValue).length];
- System.arraycopy(headerValue, 0, mSequenceUserDefined[headerID - 0x70],
- 0, mSequenceUserDefined[headerID - 0x70].length);
- }
- }
- break;
- }
- // Verify that it was not a Byte user Defined
- if ((headerID >= 0xB0) && (headerID <= 0xBF)) {
- if ((headerValue != null) && (!(headerValue instanceof Byte))) {
- throw new IllegalArgumentException("ByteUser Defined must be a Byte");
- }
- mByteUserDefined[headerID - 0xB0] = (Byte)headerValue;
-
- break;
- }
- // Verify that is was not the 4 byte unsigned integer user
- // defined header
- if ((headerID >= 0xF0) && (headerID <= 0xFF)) {
- if (!(headerValue instanceof Long)) {
- if (headerValue == null) {
- mIntegerUserDefined[headerID - 0xF0] = null;
- break;
- }
- throw new IllegalArgumentException("Integer User Defined must be a Long");
- }
- temp = ((Long)headerValue).longValue();
- if ((temp < 0L) || (temp > 0xFFFFFFFFL)) {
- throw new IllegalArgumentException(
- "Integer User Defined must be between 0 and 0xFFFFFFFF");
- }
- mIntegerUserDefined[headerID - 0xF0] = (Long)headerValue;
- break;
- }
- throw new IllegalArgumentException("Invalid Header Identifier");
- }
- }
-
- /**
- * Retrieves the value of the header identifier provided. The type of the
- * Object returned is defined in the description of this interface.
- * @param headerID the header identifier whose value is to be returned
- * @return the value of the header provided or <code>null</code> if the
- * header identifier specified is not part of this
- * <code>HeaderSet</code> object
- * @throws IllegalArgumentException if the <code>headerID</code> is not one
- * defined in this interface or any of the user-defined headers
- * @throws IOException if an error occurred in the transport layer during
- * the operation or if the connection has been closed
- */
- public Object getHeader(int headerID) throws IOException {
-
- switch (headerID) {
- case COUNT:
- return mCount;
- case NAME:
- return mName;
- case TYPE:
- return mType;
- case LENGTH:
- return mLength;
- case TIME_ISO_8601:
- return mIsoTime;
- case TIME_4_BYTE:
- return mByteTime;
- case DESCRIPTION:
- return mDescription;
- case TARGET:
- return mTarget;
- case HTTP:
- return mHttpHeader;
- case WHO:
- return mWho;
- case CONNECTION_ID:
- return mConnectionID;
- case OBJECT_CLASS:
- return mObjectClass;
- case APPLICATION_PARAMETER:
- return mAppParam;
- case SINGLE_RESPONSE_MODE:
- return mSingleResponseMode;
- case SINGLE_RESPONSE_MODE_PARAMETER:
- return mSrmParam;
- default:
- // Verify that it was not a Unicode String user Defined
- if ((headerID >= 0x30) && (headerID <= 0x3F)) {
- return mUnicodeUserDefined[headerID - 0x30];
- }
- // Verify that it was not a byte sequence user defined header
- if ((headerID >= 0x70) && (headerID <= 0x7F)) {
- return mSequenceUserDefined[headerID - 0x70];
- }
- // Verify that it was not a byte user defined header
- if ((headerID >= 0xB0) && (headerID <= 0xBF)) {
- return mByteUserDefined[headerID - 0xB0];
- }
- // Verify that it was not a integer user defined header
- if ((headerID >= 0xF0) && (headerID <= 0xFF)) {
- return mIntegerUserDefined[headerID - 0xF0];
- }
- throw new IllegalArgumentException("Invalid Header Identifier");
- }
- }
-
- /**
- * Retrieves the list of headers that may be retrieved via the
- * <code>getHeader</code> method that will not return <code>null</code>. In
- * other words, this method returns all the headers that are available in
- * this object.
- * @see #getHeader
- * @return the array of headers that are set in this object or
- * <code>null</code> if no headers are available
- * @throws IOException if an error occurred in the transport layer during
- * the operation or the connection has been closed
- */
- public int[] getHeaderList() throws IOException {
- ByteArrayOutputStream out = new ByteArrayOutputStream();
-
- if (mCount != null) {
- out.write(COUNT);
- }
- if (mName != null) {
- out.write(NAME);
- }
- if (mType != null) {
- out.write(TYPE);
- }
- if (mLength != null) {
- out.write(LENGTH);
- }
- if (mIsoTime != null) {
- out.write(TIME_ISO_8601);
- }
- if (mByteTime != null) {
- out.write(TIME_4_BYTE);
- }
- if (mDescription != null) {
- out.write(DESCRIPTION);
- }
- if (mTarget != null) {
- out.write(TARGET);
- }
- if (mHttpHeader != null) {
- out.write(HTTP);
- }
- if (mWho != null) {
- out.write(WHO);
- }
- if (mAppParam != null) {
- out.write(APPLICATION_PARAMETER);
- }
- if (mObjectClass != null) {
- out.write(OBJECT_CLASS);
- }
- if(mSingleResponseMode != null) {
- out.write(SINGLE_RESPONSE_MODE);
- }
- if(mSrmParam != null) {
- out.write(SINGLE_RESPONSE_MODE_PARAMETER);
- }
-
- for (int i = 0x30; i < 0x40; i++) {
- if (mUnicodeUserDefined[i - 0x30] != null) {
- out.write(i);
- }
- }
-
- for (int i = 0x70; i < 0x80; i++) {
- if (mSequenceUserDefined[i - 0x70] != null) {
- out.write(i);
- }
- }
-
- for (int i = 0xB0; i < 0xC0; i++) {
- if (mByteUserDefined[i - 0xB0] != null) {
- out.write(i);
- }
- }
-
- for (int i = 0xF0; i < 0x100; i++) {
- if (mIntegerUserDefined[i - 0xF0] != null) {
- out.write(i);
- }
- }
-
- byte[] headers = out.toByteArray();
- out.close();
-
- if ((headers == null) || (headers.length == 0)) {
- return null;
- }
-
- int[] result = new int[headers.length];
- for (int i = 0; i < headers.length; i++) {
- // Convert the byte to a positive integer. That is, an integer
- // between 0 and 256.
- result[i] = headers[i] & 0xFF;
- }
-
- return result;
- }
-
- /**
- * Sets the authentication challenge header. The <code>realm</code> will be
- * encoded based upon the default encoding scheme used by the implementation
- * to encode strings. Therefore, the encoding scheme used to encode the
- * <code>realm</code> is application dependent.
- * @param realm a short description that describes what password to use; if
- * <code>null</code> no realm will be sent in the authentication
- * challenge header
- * @param userID if <code>true</code>, a user ID is required in the reply;
- * if <code>false</code>, no user ID is required
- * @param access if <code>true</code> then full access will be granted if
- * successful; if <code>false</code> then read-only access will be
- * granted if successful
- * @throws IOException
- */
- public void createAuthenticationChallenge(String realm, boolean userID, boolean access)
- throws IOException {
-
- nonce = new byte[16];
- if(mRandom == null) {
- mRandom = new SecureRandom();
- }
- for (int i = 0; i < 16; i++) {
- nonce[i] = (byte)mRandom.nextInt();
- }
-
- mAuthChall = ObexHelper.computeAuthenticationChallenge(nonce, realm, access, userID);
- }
-
- /**
- * Returns the response code received from the server. Response codes are
- * defined in the <code>ResponseCodes</code> class.
- * @see ResponseCodes
- * @return the response code retrieved from the server
- * @throws IOException if an error occurred in the transport layer during
- * the transaction; if this method is called on a
- * <code>HeaderSet</code> object created by calling
- * <code>createHeaderSet()</code> in a <code>ClientSession</code>
- * object; if this object was created by an OBEX server
- */
- public int getResponseCode() throws IOException {
- if (responseCode == -1) {
- throw new IOException("May not be called on a server");
- } else {
- return responseCode;
- }
- }
-}
diff --git a/obex/javax/obex/ObexHelper.java b/obex/javax/obex/ObexHelper.java
deleted file mode 100644
index 843793a..0000000
--- a/obex/javax/obex/ObexHelper.java
+++ /dev/null
@@ -1,1100 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- * Copyright (C) 2015 Samsung LSI
- * Copyright (c) 2008-2009, Motorola, Inc.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * - Neither the name of the Motorola, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package javax.obex;
-
-import android.util.Log;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.TimeZone;
-
-
-/**
- * This class defines a set of helper methods for the implementation of Obex.
- * @hide
- */
-public final class ObexHelper {
-
- private static final String TAG = "ObexHelper";
- public static final boolean VDBG = false;
- /**
- * Defines the basic packet length used by OBEX. Every OBEX packet has the
- * same basic format:<BR>
- * Byte 0: Request or Response Code Byte 1&2: Length of the packet.
- */
- public static final int BASE_PACKET_LENGTH = 3;
-
- /** Prevent object construction of helper class */
- private ObexHelper() {
- }
-
- /**
- * The maximum packet size for OBEX packets that this client can handle. At
- * present, this must be changed for each port. TODO: The max packet size
- * should be the Max incoming MTU minus TODO: L2CAP package headers and
- * RFCOMM package headers. TODO: Retrieve the max incoming MTU from TODO:
- * LocalDevice.getProperty().
- * NOTE: This value must be larger than or equal to the L2CAP SDU
- */
- /*
- * android note set as 0xFFFE to match remote MPS
- */
- public static final int MAX_PACKET_SIZE_INT = 0xFFFE;
-
- // The minimum allowed max packet size is 255 according to the OBEX specification
- public static final int LOWER_LIMIT_MAX_PACKET_SIZE = 255;
-
- // The length of OBEX Byte Sequency Header Id according to the OBEX specification
- public static final int OBEX_BYTE_SEQ_HEADER_LEN = 0x03;
-
- /**
- * Temporary workaround to be able to push files to Windows 7.
- * TODO: Should be removed as soon as Microsoft updates their driver.
- */
- public static final int MAX_CLIENT_PACKET_SIZE = 0xFC00;
-
- public static final int OBEX_OPCODE_FINAL_BIT_MASK = 0x80;
-
- public static final int OBEX_OPCODE_CONNECT = 0x80;
-
- public static final int OBEX_OPCODE_DISCONNECT = 0x81;
-
- public static final int OBEX_OPCODE_PUT = 0x02;
-
- public static final int OBEX_OPCODE_PUT_FINAL = 0x82;
-
- public static final int OBEX_OPCODE_GET = 0x03;
-
- public static final int OBEX_OPCODE_GET_FINAL = 0x83;
-
- public static final int OBEX_OPCODE_RESERVED = 0x04;
-
- public static final int OBEX_OPCODE_RESERVED_FINAL = 0x84;
-
- public static final int OBEX_OPCODE_SETPATH = 0x85;
-
- public static final int OBEX_OPCODE_ABORT = 0xFF;
-
- public static final int OBEX_AUTH_REALM_CHARSET_ASCII = 0x00;
-
- public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_1 = 0x01;
-
- public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_2 = 0x02;
-
- public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_3 = 0x03;
-
- public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_4 = 0x04;
-
- public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_5 = 0x05;
-
- public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_6 = 0x06;
-
- public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_7 = 0x07;
-
- public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_8 = 0x08;
-
- public static final int OBEX_AUTH_REALM_CHARSET_ISO_8859_9 = 0x09;
-
- public static final int OBEX_AUTH_REALM_CHARSET_UNICODE = 0xFF;
-
- public static final byte OBEX_SRM_ENABLE = 0x01; // For BT we only need enable/disable
- public static final byte OBEX_SRM_DISABLE = 0x00;
- public static final byte OBEX_SRM_SUPPORT = 0x02; // Unused for now
-
- public static final byte OBEX_SRMP_WAIT = 0x01; // Only SRMP value used by BT
-
- /**
- * Updates the HeaderSet with the headers received in the byte array
- * provided. Invalid headers are ignored.
- * <P>
- * The first two bits of an OBEX Header specifies the type of object that is
- * being sent. The table below specifies the meaning of the high bits.
- * <TABLE>
- * <TR>
- * <TH>Bits 8 and 7</TH>
- * <TH>Value</TH>
- * <TH>Description</TH>
- * </TR>
- * <TR>
- * <TD>00</TD>
- * <TD>0x00</TD>
- * <TD>Null Terminated Unicode text, prefixed with 2 byte unsigned integer</TD>
- * </TR>
- * <TR>
- * <TD>01</TD>
- * <TD>0x40</TD>
- * <TD>Byte Sequence, length prefixed with 2 byte unsigned integer</TD>
- * </TR>
- * <TR>
- * <TD>10</TD>
- * <TD>0x80</TD>
- * <TD>1 byte quantity</TD>
- * </TR>
- * <TR>
- * <TD>11</TD>
- * <TD>0xC0</TD>
- * <TD>4 byte quantity - transmitted in network byte order (high byte first</TD>
- * </TR>
- * </TABLE>
- * This method uses the information in this table to determine the type of
- * Java object to create and passes that object with the full header to
- * setHeader() to update the HeaderSet object. Invalid headers will cause an
- * exception to be thrown. When it is thrown, it is ignored.
- * @param header the HeaderSet to update
- * @param headerArray the byte array containing headers
- * @return the result of the last start body or end body header provided;
- * the first byte in the result will specify if a body or end of
- * body is received
- * @throws IOException if an invalid header was found
- */
- public static byte[] updateHeaderSet(HeaderSet header, byte[] headerArray) throws IOException {
- int index = 0;
- int length = 0;
- int headerID;
- byte[] value = null;
- byte[] body = null;
- HeaderSet headerImpl = header;
- try {
- while (index < headerArray.length) {
- headerID = 0xFF & headerArray[index];
- switch (headerID & (0xC0)) {
-
- /*
- * 0x00 is a unicode null terminate string with the first
- * two bytes after the header identifier being the length
- */
- case 0x00:
- // Fall through
- /*
- * 0x40 is a byte sequence with the first
- * two bytes after the header identifier being the length
- */
- case 0x40:
- boolean trimTail = true;
- index++;
- length = ((0xFF & headerArray[index]) << 8) +
- (0xFF & headerArray[index + 1]);
- index += 2;
- if (length <= OBEX_BYTE_SEQ_HEADER_LEN) {
- Log.e(TAG, "Remote sent an OBEX packet with " +
- "incorrect header length = " + length);
- break;
- }
- length -= OBEX_BYTE_SEQ_HEADER_LEN;
- value = new byte[length];
- System.arraycopy(headerArray, index, value, 0, length);
- if (length == 0 || (length > 0 && (value[length - 1] != 0))) {
- trimTail = false;
- }
- switch (headerID) {
- case HeaderSet.TYPE:
- try {
- // Remove trailing null
- if (trimTail == false) {
- headerImpl.setHeader(headerID, new String(value, 0,
- value.length, "ISO8859_1"));
- } else {
- headerImpl.setHeader(headerID, new String(value, 0,
- value.length - 1, "ISO8859_1"));
- }
- } catch (UnsupportedEncodingException e) {
- throw e;
- }
- break;
-
- case HeaderSet.AUTH_CHALLENGE:
- headerImpl.mAuthChall = new byte[length];
- System.arraycopy(headerArray, index, headerImpl.mAuthChall, 0,
- length);
- break;
-
- case HeaderSet.AUTH_RESPONSE:
- headerImpl.mAuthResp = new byte[length];
- System.arraycopy(headerArray, index, headerImpl.mAuthResp, 0,
- length);
- break;
-
- case HeaderSet.BODY:
- /* Fall Through */
- case HeaderSet.END_OF_BODY:
- body = new byte[length + 1];
- body[0] = (byte)headerID;
- System.arraycopy(headerArray, index, body, 1, length);
- break;
-
- case HeaderSet.TIME_ISO_8601:
- try {
- String dateString = new String(value, "ISO8859_1");
- Calendar temp = Calendar.getInstance();
- if ((dateString.length() == 16)
- && (dateString.charAt(15) == 'Z')) {
- temp.setTimeZone(TimeZone.getTimeZone("UTC"));
- }
- temp.set(Calendar.YEAR, Integer.parseInt(dateString.substring(
- 0, 4)));
- temp.set(Calendar.MONTH, Integer.parseInt(dateString.substring(
- 4, 6)));
- temp.set(Calendar.DAY_OF_MONTH, Integer.parseInt(dateString
- .substring(6, 8)));
- temp.set(Calendar.HOUR_OF_DAY, Integer.parseInt(dateString
- .substring(9, 11)));
- temp.set(Calendar.MINUTE, Integer.parseInt(dateString
- .substring(11, 13)));
- temp.set(Calendar.SECOND, Integer.parseInt(dateString
- .substring(13, 15)));
- headerImpl.setHeader(HeaderSet.TIME_ISO_8601, temp);
- } catch (UnsupportedEncodingException e) {
- throw e;
- }
- break;
-
- default:
- if ((headerID & 0xC0) == 0x00) {
- headerImpl.setHeader(headerID, ObexHelper.convertToUnicode(
- value, true));
- } else {
- headerImpl.setHeader(headerID, value);
- }
- }
-
- index += length;
- break;
-
- /*
- * 0x80 is a byte header. The only valid byte headers are
- * the 16 user defined byte headers.
- */
- case 0x80:
- index++;
- try {
- headerImpl.setHeader(headerID, Byte.valueOf(headerArray[index]));
- } catch (Exception e) {
- // Not a valid header so ignore
- }
- index++;
- break;
-
- /*
- * 0xC0 is a 4 byte unsigned integer header and with the
- * exception of TIME_4_BYTE will be converted to a Long
- * and added.
- */
- case 0xC0:
- index++;
- value = new byte[4];
- System.arraycopy(headerArray, index, value, 0, 4);
- try {
- if (headerID != HeaderSet.TIME_4_BYTE) {
- // Determine if it is a connection ID. These
- // need to be handled differently
- if (headerID == HeaderSet.CONNECTION_ID) {
- headerImpl.mConnectionID = new byte[4];
- System.arraycopy(value, 0, headerImpl.mConnectionID, 0, 4);
- } else {
- headerImpl.setHeader(headerID, Long
- .valueOf(convertToLong(value)));
- }
- } else {
- Calendar temp = Calendar.getInstance();
- temp.setTime(new Date(convertToLong(value) * 1000L));
- headerImpl.setHeader(HeaderSet.TIME_4_BYTE, temp);
- }
- } catch (Exception e) {
- // Not a valid header so ignore
- throw new IOException("Header was not formatted properly", e);
- }
- index += 4;
- break;
- }
-
- }
- } catch (IOException e) {
- throw new IOException("Header was not formatted properly", e);
- }
-
- return body;
- }
-
- /**
- * Creates the header part of OBEX packet based on the header provided.
- * TODO: Could use getHeaderList() to get the array of headers to include
- * and then use the high two bits to determine the the type of the object
- * and construct the byte array from that. This will make the size smaller.
- * @param head the header used to construct the byte array
- * @param nullOut <code>true</code> if the header should be set to
- * <code>null</code> once it is added to the array or
- * <code>false</code> if it should not be nulled out
- * @return the header of an OBEX packet
- */
- public static byte[] createHeader(HeaderSet head, boolean nullOut) {
- Long intHeader = null;
- String stringHeader = null;
- Calendar dateHeader = null;
- Byte byteHeader = null;
- StringBuffer buffer = null;
- byte[] value = null;
- byte[] result = null;
- byte[] lengthArray = new byte[2];
- int length;
- HeaderSet headImpl = null;
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- headImpl = head;
-
- try {
- /*
- * Determine if there is a connection ID to send. If there is,
- * then it should be the first header in the packet.
- */
- if ((headImpl.mConnectionID != null) && (headImpl.getHeader(HeaderSet.TARGET) == null)) {
-
- out.write((byte)HeaderSet.CONNECTION_ID);
- out.write(headImpl.mConnectionID);
- }
-
- // Count Header
- intHeader = (Long)headImpl.getHeader(HeaderSet.COUNT);
- if (intHeader != null) {
- out.write((byte)HeaderSet.COUNT);
- value = ObexHelper.convertToByteArray(intHeader.longValue());
- out.write(value);
- if (nullOut) {
- headImpl.setHeader(HeaderSet.COUNT, null);
- }
- }
-
- // Name Header
- stringHeader = (String)headImpl.getHeader(HeaderSet.NAME);
- if (stringHeader != null) {
- out.write((byte)HeaderSet.NAME);
- value = ObexHelper.convertToUnicodeByteArray(stringHeader);
- length = value.length + 3;
- lengthArray[0] = (byte)(0xFF & (length >> 8));
- lengthArray[1] = (byte)(0xFF & length);
- out.write(lengthArray);
- out.write(value);
- if (nullOut) {
- headImpl.setHeader(HeaderSet.NAME, null);
- }
- } else if (headImpl.getEmptyNameHeader()) {
- out.write((byte) HeaderSet.NAME);
- lengthArray[0] = (byte) 0x00;
- lengthArray[1] = (byte) 0x03;
- out.write(lengthArray);
- }
-
- // Type Header
- stringHeader = (String)headImpl.getHeader(HeaderSet.TYPE);
- if (stringHeader != null) {
- out.write((byte)HeaderSet.TYPE);
- try {
- value = stringHeader.getBytes("ISO8859_1");
- } catch (UnsupportedEncodingException e) {
- throw e;
- }
-
- length = value.length + 4;
- lengthArray[0] = (byte)(255 & (length >> 8));
- lengthArray[1] = (byte)(255 & length);
- out.write(lengthArray);
- out.write(value);
- out.write(0x00);
- if (nullOut) {
- headImpl.setHeader(HeaderSet.TYPE, null);
- }
- }
-
- // Length Header
- intHeader = (Long)headImpl.getHeader(HeaderSet.LENGTH);
- if (intHeader != null) {
- out.write((byte)HeaderSet.LENGTH);
- value = ObexHelper.convertToByteArray(intHeader.longValue());
- out.write(value);
- if (nullOut) {
- headImpl.setHeader(HeaderSet.LENGTH, null);
- }
- }
-
- // Time ISO Header
- dateHeader = (Calendar)headImpl.getHeader(HeaderSet.TIME_ISO_8601);
- if (dateHeader != null) {
-
- /*
- * The ISO Header should take the form YYYYMMDDTHHMMSSZ. The
- * 'Z' will only be included if it is a UTC time.
- */
- buffer = new StringBuffer();
- int temp = dateHeader.get(Calendar.YEAR);
- for (int i = temp; i < 1000; i = i * 10) {
- buffer.append("0");
- }
- buffer.append(temp);
- temp = dateHeader.get(Calendar.MONTH);
- if (temp < 10) {
- buffer.append("0");
- }
- buffer.append(temp);
- temp = dateHeader.get(Calendar.DAY_OF_MONTH);
- if (temp < 10) {
- buffer.append("0");
- }
- buffer.append(temp);
- buffer.append("T");
- temp = dateHeader.get(Calendar.HOUR_OF_DAY);
- if (temp < 10) {
- buffer.append("0");
- }
- buffer.append(temp);
- temp = dateHeader.get(Calendar.MINUTE);
- if (temp < 10) {
- buffer.append("0");
- }
- buffer.append(temp);
- temp = dateHeader.get(Calendar.SECOND);
- if (temp < 10) {
- buffer.append("0");
- }
- buffer.append(temp);
-
- if (dateHeader.getTimeZone().getID().equals("UTC")) {
- buffer.append("Z");
- }
-
- try {
- value = buffer.toString().getBytes("ISO8859_1");
- } catch (UnsupportedEncodingException e) {
- throw e;
- }
-
- length = value.length + 3;
- lengthArray[0] = (byte)(255 & (length >> 8));
- lengthArray[1] = (byte)(255 & length);
- out.write(HeaderSet.TIME_ISO_8601);
- out.write(lengthArray);
- out.write(value);
- if (nullOut) {
- headImpl.setHeader(HeaderSet.TIME_ISO_8601, null);
- }
- }
-
- // Time 4 Byte Header
- dateHeader = (Calendar)headImpl.getHeader(HeaderSet.TIME_4_BYTE);
- if (dateHeader != null) {
- out.write(HeaderSet.TIME_4_BYTE);
-
- /*
- * Need to call getTime() twice. The first call will return
- * a java.util.Date object. The second call returns the number
- * of milliseconds since January 1, 1970. We need to convert
- * it to seconds since the TIME_4_BYTE expects the number of
- * seconds since January 1, 1970.
- */
- value = ObexHelper.convertToByteArray(dateHeader.getTime().getTime() / 1000L);
- out.write(value);
- if (nullOut) {
- headImpl.setHeader(HeaderSet.TIME_4_BYTE, null);
- }
- }
-
- // Description Header
- stringHeader = (String)headImpl.getHeader(HeaderSet.DESCRIPTION);
- if (stringHeader != null) {
- out.write((byte)HeaderSet.DESCRIPTION);
- value = ObexHelper.convertToUnicodeByteArray(stringHeader);
- length = value.length + 3;
- lengthArray[0] = (byte)(255 & (length >> 8));
- lengthArray[1] = (byte)(255 & length);
- out.write(lengthArray);
- out.write(value);
- if (nullOut) {
- headImpl.setHeader(HeaderSet.DESCRIPTION, null);
- }
- }
-
- // Target Header
- value = (byte[])headImpl.getHeader(HeaderSet.TARGET);
- if (value != null) {
- out.write((byte)HeaderSet.TARGET);
- length = value.length + 3;
- lengthArray[0] = (byte)(255 & (length >> 8));
- lengthArray[1] = (byte)(255 & length);
- out.write(lengthArray);
- out.write(value);
- if (nullOut) {
- headImpl.setHeader(HeaderSet.TARGET, null);
- }
- }
-
- // HTTP Header
- value = (byte[])headImpl.getHeader(HeaderSet.HTTP);
- if (value != null) {
- out.write((byte)HeaderSet.HTTP);
- length = value.length + 3;
- lengthArray[0] = (byte)(255 & (length >> 8));
- lengthArray[1] = (byte)(255 & length);
- out.write(lengthArray);
- out.write(value);
- if (nullOut) {
- headImpl.setHeader(HeaderSet.HTTP, null);
- }
- }
-
- // Who Header
- value = (byte[])headImpl.getHeader(HeaderSet.WHO);
- if (value != null) {
- out.write((byte)HeaderSet.WHO);
- length = value.length + 3;
- lengthArray[0] = (byte)(255 & (length >> 8));
- lengthArray[1] = (byte)(255 & length);
- out.write(lengthArray);
- out.write(value);
- if (nullOut) {
- headImpl.setHeader(HeaderSet.WHO, null);
- }
- }
-
- // Connection ID Header
- value = (byte[])headImpl.getHeader(HeaderSet.APPLICATION_PARAMETER);
- if (value != null) {
- out.write((byte)HeaderSet.APPLICATION_PARAMETER);
- length = value.length + 3;
- lengthArray[0] = (byte)(255 & (length >> 8));
- lengthArray[1] = (byte)(255 & length);
- out.write(lengthArray);
- out.write(value);
- if (nullOut) {
- headImpl.setHeader(HeaderSet.APPLICATION_PARAMETER, null);
- }
- }
-
- // Object Class Header
- value = (byte[])headImpl.getHeader(HeaderSet.OBJECT_CLASS);
- if (value != null) {
- out.write((byte)HeaderSet.OBJECT_CLASS);
- length = value.length + 3;
- lengthArray[0] = (byte)(255 & (length >> 8));
- lengthArray[1] = (byte)(255 & length);
- out.write(lengthArray);
- out.write(value);
- if (nullOut) {
- headImpl.setHeader(HeaderSet.OBJECT_CLASS, null);
- }
- }
-
- // Check User Defined Headers
- for (int i = 0; i < 16; i++) {
-
- //Unicode String Header
- stringHeader = (String)headImpl.getHeader(i + 0x30);
- if (stringHeader != null) {
- out.write((byte)i + 0x30);
- value = ObexHelper.convertToUnicodeByteArray(stringHeader);
- length = value.length + 3;
- lengthArray[0] = (byte)(255 & (length >> 8));
- lengthArray[1] = (byte)(255 & length);
- out.write(lengthArray);
- out.write(value);
- if (nullOut) {
- headImpl.setHeader(i + 0x30, null);
- }
- }
-
- // Byte Sequence Header
- value = (byte[])headImpl.getHeader(i + 0x70);
- if (value != null) {
- out.write((byte)i + 0x70);
- length = value.length + 3;
- lengthArray[0] = (byte)(255 & (length >> 8));
- lengthArray[1] = (byte)(255 & length);
- out.write(lengthArray);
- out.write(value);
- if (nullOut) {
- headImpl.setHeader(i + 0x70, null);
- }
- }
-
- // Byte Header
- byteHeader = (Byte)headImpl.getHeader(i + 0xB0);
- if (byteHeader != null) {
- out.write((byte)i + 0xB0);
- out.write(byteHeader.byteValue());
- if (nullOut) {
- headImpl.setHeader(i + 0xB0, null);
- }
- }
-
- // Integer header
- intHeader = (Long)headImpl.getHeader(i + 0xF0);
- if (intHeader != null) {
- out.write((byte)i + 0xF0);
- out.write(ObexHelper.convertToByteArray(intHeader.longValue()));
- if (nullOut) {
- headImpl.setHeader(i + 0xF0, null);
- }
- }
- }
-
- // Add the authentication challenge header
- if (headImpl.mAuthChall != null) {
- out.write((byte)HeaderSet.AUTH_CHALLENGE);
- length = headImpl.mAuthChall.length + 3;
- lengthArray[0] = (byte)(255 & (length >> 8));
- lengthArray[1] = (byte)(255 & length);
- out.write(lengthArray);
- out.write(headImpl.mAuthChall);
- if (nullOut) {
- headImpl.mAuthChall = null;
- }
- }
-
- // Add the authentication response header
- if (headImpl.mAuthResp != null) {
- out.write((byte)HeaderSet.AUTH_RESPONSE);
- length = headImpl.mAuthResp.length + 3;
- lengthArray[0] = (byte)(255 & (length >> 8));
- lengthArray[1] = (byte)(255 & length);
- out.write(lengthArray);
- out.write(headImpl.mAuthResp);
- if (nullOut) {
- headImpl.mAuthResp = null;
- }
- }
-
- // TODO:
- // If the SRM and SRMP header is in use, they must be send in the same OBEX packet
- // But the current structure of the obex code cannot handle this, and therefore
- // it makes sense to put them in the tail of the headers, since we then reduce the
- // chance of enabling SRM to soon. The down side is that SRM cannot be used while
- // transferring non-body headers
-
- // Add the SRM header
- byteHeader = (Byte)headImpl.getHeader(HeaderSet.SINGLE_RESPONSE_MODE);
- if (byteHeader != null) {
- out.write((byte)HeaderSet.SINGLE_RESPONSE_MODE);
- out.write(byteHeader.byteValue());
- if (nullOut) {
- headImpl.setHeader(HeaderSet.SINGLE_RESPONSE_MODE, null);
- }
- }
-
- // Add the SRM parameter header
- byteHeader = (Byte)headImpl.getHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER);
- if (byteHeader != null) {
- out.write((byte)HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER);
- out.write(byteHeader.byteValue());
- if (nullOut) {
- headImpl.setHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER, null);
- }
- }
-
- } catch (IOException e) {
- } finally {
- result = out.toByteArray();
- try {
- out.close();
- } catch (Exception ex) {
- }
- }
-
- return result;
-
- }
-
- /**
- * Determines where the maximum divide is between headers. This method is
- * used by put and get operations to separate headers to a size that meets
- * the max packet size allowed.
- * @param headerArray the headers to separate
- * @param start the starting index to search
- * @param maxSize the maximum size of a packet
- * @return the index of the end of the header block to send or -1 if the
- * header could not be divided because the header is too large
- */
- public static int findHeaderEnd(byte[] headerArray, int start, int maxSize) {
-
- int fullLength = 0;
- int lastLength = -1;
- int index = start;
- int length = 0;
-
- // TODO: Ensure SRM and SRMP headers are not split into two OBEX packets
-
- while ((fullLength < maxSize) && (index < headerArray.length)) {
- int headerID = (headerArray[index] < 0 ? headerArray[index] + 256 : headerArray[index]);
- lastLength = fullLength;
-
- switch (headerID & (0xC0)) {
-
- case 0x00:
- // Fall through
- case 0x40:
-
- index++;
- length = (headerArray[index] < 0 ? headerArray[index] + 256
- : headerArray[index]);
- length = length << 8;
- index++;
- length += (headerArray[index] < 0 ? headerArray[index] + 256
- : headerArray[index]);
- length -= 3;
- index++;
- index += length;
- fullLength += length + 3;
- break;
-
- case 0x80:
-
- index++;
- index++;
- fullLength += 2;
- break;
-
- case 0xC0:
-
- index += 5;
- fullLength += 5;
- break;
-
- }
-
- }
-
- /*
- * Determine if this is the last header or not
- */
- if (lastLength == 0) {
- /*
- * Since this is the last header, check to see if the size of this
- * header is less then maxSize. If it is, return the length of the
- * header, otherwise return -1. The length of the header is
- * returned since it would be the start of the next header
- */
- if (fullLength < maxSize) {
- return headerArray.length;
- } else {
- return -1;
- }
- } else {
- return lastLength + start;
- }
- }
-
- /**
- * Converts the byte array to a long.
- * @param b the byte array to convert to a long
- * @return the byte array as a long
- */
- public static long convertToLong(byte[] b) {
- long result = 0;
- long value = 0;
- long power = 0;
-
- for (int i = (b.length - 1); i >= 0; i--) {
- value = b[i];
- if (value < 0) {
- value += 256;
- }
-
- result = result | (value << power);
- power += 8;
- }
-
- return result;
- }
-
- /**
- * Converts the long to a 4 byte array. The long must be non negative.
- * @param l the long to convert
- * @return a byte array that is the same as the long
- */
- public static byte[] convertToByteArray(long l) {
- byte[] b = new byte[4];
-
- b[0] = (byte)(255 & (l >> 24));
- b[1] = (byte)(255 & (l >> 16));
- b[2] = (byte)(255 & (l >> 8));
- b[3] = (byte)(255 & l);
-
- return b;
- }
-
- /**
- * Converts the String to a UNICODE byte array. It will also add the ending
- * null characters to the end of the string.
- * @param s the string to convert
- * @return the unicode byte array of the string
- */
- public static byte[] convertToUnicodeByteArray(String s) {
- if (s == null) {
- return null;
- }
-
- char c[] = s.toCharArray();
- byte[] result = new byte[(c.length * 2) + 2];
- for (int i = 0; i < c.length; i++) {
- result[(i * 2)] = (byte)(c[i] >> 8);
- result[((i * 2) + 1)] = (byte)c[i];
- }
-
- // Add the UNICODE null character
- result[result.length - 2] = 0;
- result[result.length - 1] = 0;
-
- return result;
- }
-
- /**
- * Retrieves the value from the byte array for the tag value specified. The
- * array should be of the form Tag - Length - Value triplet.
- * @param tag the tag to retrieve from the byte array
- * @param triplet the byte sequence containing the tag length value form
- * @return the value of the specified tag
- */
- public static byte[] getTagValue(byte tag, byte[] triplet) {
-
- int index = findTag(tag, triplet);
- if (index == -1) {
- return null;
- }
-
- index++;
- int length = triplet[index] & 0xFF;
-
- byte[] result = new byte[length];
- index++;
- System.arraycopy(triplet, index, result, 0, length);
-
- return result;
- }
-
- /**
- * Finds the index that starts the tag value pair in the byte array provide.
- * @param tag the tag to look for
- * @param value the byte array to search
- * @return the starting index of the tag or -1 if the tag could not be found
- */
- public static int findTag(byte tag, byte[] value) {
- int length = 0;
-
- if (value == null) {
- return -1;
- }
-
- int index = 0;
-
- while ((index < value.length) && (value[index] != tag)) {
- length = value[index + 1] & 0xFF;
- index += length + 2;
- }
-
- if (index >= value.length) {
- return -1;
- }
-
- return index;
- }
-
- /**
- * Converts the byte array provided to a unicode string.
- * @param b the byte array to convert to a string
- * @param includesNull determine if the byte string provided contains the
- * UNICODE null character at the end or not; if it does, it will be
- * removed
- * @return a Unicode string
- * @throws IllegalArgumentException if the byte array has an odd length
- */
- public static String convertToUnicode(byte[] b, boolean includesNull) {
- if (b == null || b.length == 0) {
- return null;
- }
- int arrayLength = b.length;
- if (!((arrayLength % 2) == 0)) {
- throw new IllegalArgumentException("Byte array not of a valid form");
- }
- arrayLength = (arrayLength >> 1);
- if (includesNull) {
- arrayLength -= 1;
- }
-
- char[] c = new char[arrayLength];
- for (int i = 0; i < arrayLength; i++) {
- int upper = b[2 * i];
- int lower = b[(2 * i) + 1];
- if (upper < 0) {
- upper += 256;
- }
- if (lower < 0) {
- lower += 256;
- }
- // If upper and lower both equal 0, it should be the end of string.
- // Ignore left bytes from array to avoid potential issues
- if (upper == 0 && lower == 0) {
- return new String(c, 0, i);
- }
-
- c[i] = (char)((upper << 8) | lower);
- }
-
- return new String(c);
- }
-
- /**
- * Compute the MD5 hash of the byte array provided. Does not accumulate
- * input.
- * @param in the byte array to hash
- * @return the MD5 hash of the byte array
- */
- public static byte[] computeMd5Hash(byte[] in) {
- try {
- MessageDigest md5 = MessageDigest.getInstance("MD5");
- return md5.digest(in);
- } catch (NoSuchAlgorithmException e) {
- throw new RuntimeException(e);
- }
- }
-
- /**
- * Computes an authentication challenge header.
- * @param nonce the challenge that will be provided to the peer; the
- * challenge must be 16 bytes long
- * @param realm a short description that describes what password to use
- * @param access if <code>true</code> then full access will be granted if
- * successful; if <code>false</code> then read only access will be
- * granted if successful
- * @param userID if <code>true</code>, a user ID is required in the reply;
- * if <code>false</code>, no user ID is required
- * @throws IllegalArgumentException if the challenge is not 16 bytes long;
- * if the realm can not be encoded in less then 255 bytes
- * @throws IOException if the encoding scheme ISO 8859-1 is not supported
- */
- public static byte[] computeAuthenticationChallenge(byte[] nonce, String realm, boolean access,
- boolean userID) throws IOException {
- byte[] authChall = null;
-
- if (nonce.length != 16) {
- throw new IllegalArgumentException("Nonce must be 16 bytes long");
- }
-
- /*
- * The authentication challenge is a byte sequence of the following form
- * byte 0: 0x00 - the tag for the challenge
- * byte 1: 0x10 - the length of the challenge; must be 16
- * byte 2-17: the authentication challenge
- * byte 18: 0x01 - the options tag; this is optional in the spec, but
- * we are going to include it in every message
- * byte 19: 0x01 - length of the options; must be 1
- * byte 20: the value of the options; bit 0 is set if user ID is
- * required; bit 1 is set if access mode is read only
- * byte 21: 0x02 - the tag for authentication realm; only included if
- * an authentication realm is specified
- * byte 22: the length of the authentication realm; only included if
- * the authentication realm is specified
- * byte 23: the encoding scheme of the authentication realm; we will use
- * the ISO 8859-1 encoding scheme since it is part of the KVM
- * byte 24 & up: the realm if one is specified.
- */
- if (realm == null) {
- authChall = new byte[21];
- } else {
- if (realm.length() >= 255) {
- throw new IllegalArgumentException("Realm must be less then 255 bytes");
- }
- authChall = new byte[24 + realm.length()];
- authChall[21] = 0x02;
- authChall[22] = (byte)(realm.length() + 1);
- authChall[23] = 0x01; // ISO 8859-1 Encoding
- System.arraycopy(realm.getBytes("ISO8859_1"), 0, authChall, 24, realm.length());
- }
-
- // Include the nonce field in the header
- authChall[0] = 0x00;
- authChall[1] = 0x10;
- System.arraycopy(nonce, 0, authChall, 2, 16);
-
- // Include the options header
- authChall[18] = 0x01;
- authChall[19] = 0x01;
- authChall[20] = 0x00;
-
- if (!access) {
- authChall[20] = (byte)(authChall[20] | 0x02);
- }
- if (userID) {
- authChall[20] = (byte)(authChall[20] | 0x01);
- }
-
- return authChall;
- }
-
- /**
- * Return the maximum allowed OBEX packet to transmit.
- * OBEX packets transmitted must be smaller than this value.
- * @param transport Reference to the ObexTransport in use.
- * @return the maximum allowed OBEX packet to transmit
- */
- public static int getMaxTxPacketSize(ObexTransport transport) {
- int size = transport.getMaxTransmitPacketSize();
- return validateMaxPacketSize(size);
- }
-
- /**
- * Return the maximum allowed OBEX packet to receive - used in OBEX connect.
- * @param transport
- * @return he maximum allowed OBEX packet to receive
- */
- public static int getMaxRxPacketSize(ObexTransport transport) {
- int size = transport.getMaxReceivePacketSize();
- return validateMaxPacketSize(size);
- }
-
- private static int validateMaxPacketSize(int size) {
- if (VDBG && (size > MAX_PACKET_SIZE_INT)) {
- Log.w(TAG, "The packet size supported for the connection (" + size + ") is larger"
- + " than the configured OBEX packet size: " + MAX_PACKET_SIZE_INT);
- }
- if (size != -1 && size < MAX_PACKET_SIZE_INT) {
- if (size < LOWER_LIMIT_MAX_PACKET_SIZE) {
- throw new IllegalArgumentException(size + " is less that the lower limit: "
- + LOWER_LIMIT_MAX_PACKET_SIZE);
- }
- return size;
- }
- return MAX_PACKET_SIZE_INT;
- }
-}
diff --git a/obex/javax/obex/ObexSession.java b/obex/javax/obex/ObexSession.java
deleted file mode 100644
index 542b9c8..0000000
--- a/obex/javax/obex/ObexSession.java
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright (c) 2008-2009, Motorola, Inc.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * - Neither the name of the Motorola, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package javax.obex;
-
-import java.io.IOException;
-
-import android.util.Log;
-
-/**
- * The <code>ObexSession</code> interface characterizes the term
- * "OBEX Connection" as defined in the IrDA Object Exchange Protocol v1.2, which
- * could be the server-side view of an OBEX connection, or the client-side view
- * of the same connection, which is established by server's accepting of a
- * client issued "CONNECT".
- * <P>
- * This interface serves as the common super class for
- * <CODE>ClientSession</CODE> and <CODE>ServerSession</CODE>.
- * @hide
- */
-public class ObexSession {
-
- private static final String TAG = "ObexSession";
- private static final boolean V = ObexHelper.VDBG;
-
- protected Authenticator mAuthenticator;
-
- protected byte[] mChallengeDigest;
-
- /**
- * Called when the server received an authentication challenge header. This
- * will cause the authenticator to handle the authentication challenge.
- * @param header the header with the authentication challenge
- * @return <code>true</code> if the last request should be resent;
- * <code>false</code> if the last request should not be resent
- * @throws IOException
- */
- public boolean handleAuthChall(HeaderSet header) throws IOException {
- if (mAuthenticator == null) {
- return false;
- }
-
- /*
- * An authentication challenge is made up of one required and two
- * optional tag length value triplets. The tag 0x00 is required to be in
- * the authentication challenge and it represents the challenge digest
- * that was received. The tag 0x01 is the options tag. This tag tracks
- * if user ID is required and if full access will be granted. The tag
- * 0x02 is the realm, which provides a description of which user name
- * and password to use.
- */
- byte[] challenge = ObexHelper.getTagValue((byte)0x00, header.mAuthChall);
- byte[] option = ObexHelper.getTagValue((byte)0x01, header.mAuthChall);
- byte[] description = ObexHelper.getTagValue((byte)0x02, header.mAuthChall);
-
- String realm = null;
- if (description != null) {
- byte[] realmString = new byte[description.length - 1];
- System.arraycopy(description, 1, realmString, 0, realmString.length);
-
- switch (description[0] & 0xFF) {
-
- case ObexHelper.OBEX_AUTH_REALM_CHARSET_ASCII:
- // ASCII encoding
- // Fall through
- case ObexHelper.OBEX_AUTH_REALM_CHARSET_ISO_8859_1:
- // ISO-8859-1 encoding
- try {
- realm = new String(realmString, "ISO8859_1");
- } catch (Exception e) {
- throw new IOException("Unsupported Encoding Scheme");
- }
- break;
-
- case ObexHelper.OBEX_AUTH_REALM_CHARSET_UNICODE:
- // UNICODE Encoding
- realm = ObexHelper.convertToUnicode(realmString, false);
- break;
-
- default:
- throw new IOException("Unsupported Encoding Scheme");
- }
- }
-
- boolean isUserIDRequired = false;
- boolean isFullAccess = true;
- if (option != null) {
- if ((option[0] & 0x01) != 0) {
- isUserIDRequired = true;
- }
-
- if ((option[0] & 0x02) != 0) {
- isFullAccess = false;
- }
- }
-
- PasswordAuthentication result = null;
- header.mAuthChall = null;
-
- try {
- result = mAuthenticator
- .onAuthenticationChallenge(realm, isUserIDRequired, isFullAccess);
- } catch (Exception e) {
- if (V) Log.d(TAG, "Exception occured - returning false", e);
- return false;
- }
-
- /*
- * If no password is provided then we not resent the request
- */
- if (result == null) {
- return false;
- }
-
- byte[] password = result.getPassword();
- if (password == null) {
- return false;
- }
-
- byte[] userName = result.getUserName();
-
- /*
- * Create the authentication response header. It includes 1 required and
- * 2 option tag length value triples. The required triple has a tag of
- * 0x00 and is the response digest. The first optional tag is 0x01 and
- * represents the user ID. If no user ID is provided, then no user ID
- * will be sent. The second optional tag is 0x02 and is the challenge
- * that was received. This will always be sent
- */
- if (userName != null) {
- header.mAuthResp = new byte[38 + userName.length];
- header.mAuthResp[36] = (byte)0x01;
- header.mAuthResp[37] = (byte)userName.length;
- System.arraycopy(userName, 0, header.mAuthResp, 38, userName.length);
- } else {
- header.mAuthResp = new byte[36];
- }
-
- // Create the secret String
- byte[] digest = new byte[challenge.length + password.length + 1];
- System.arraycopy(challenge, 0, digest, 0, challenge.length);
- // Insert colon between challenge and password
- digest[challenge.length] = (byte)0x3A;
- System.arraycopy(password, 0, digest, challenge.length + 1, password.length);
-
- // Add the Response Digest
- header.mAuthResp[0] = (byte)0x00;
- header.mAuthResp[1] = (byte)0x10;
-
- System.arraycopy(ObexHelper.computeMd5Hash(digest), 0, header.mAuthResp, 2, 16);
-
- // Add the challenge
- header.mAuthResp[18] = (byte)0x02;
- header.mAuthResp[19] = (byte)0x10;
- System.arraycopy(challenge, 0, header.mAuthResp, 20, 16);
-
- return true;
- }
-
- /**
- * Called when the server received an authentication response header. This
- * will cause the authenticator to handle the authentication response.
- * @param authResp the authentication response
- * @return <code>true</code> if the response passed; <code>false</code> if
- * the response failed
- */
- public boolean handleAuthResp(byte[] authResp) {
- if (mAuthenticator == null) {
- return false;
- }
- // get the correct password from the application
- byte[] correctPassword = mAuthenticator.onAuthenticationResponse(ObexHelper.getTagValue(
- (byte)0x01, authResp));
- if (correctPassword == null) {
- return false;
- }
-
- byte[] temp = new byte[correctPassword.length + 16];
-
- System.arraycopy(mChallengeDigest, 0, temp, 0, 16);
- System.arraycopy(correctPassword, 0, temp, 16, correctPassword.length);
-
- byte[] correctResponse = ObexHelper.computeMd5Hash(temp);
- byte[] actualResponse = ObexHelper.getTagValue((byte)0x00, authResp);
-
- // compare the MD5 hash array .
- for (int i = 0; i < 16; i++) {
- if (correctResponse[i] != actualResponse[i]) {
- return false;
- }
- }
-
- return true;
- }
-}
diff --git a/obex/javax/obex/ObexTransport.java b/obex/javax/obex/ObexTransport.java
deleted file mode 100644
index 4cef0b3..0000000
--- a/obex/javax/obex/ObexTransport.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (c) 2008-2009, Motorola, Inc.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * - Neither the name of the Motorola, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package javax.obex;
-
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-/**
- * The <code>ObexTransport</code> interface defines the underlying transport
- * connection which carries the OBEX protocol( such as TCP, RFCOMM device file
- * exposed by Bluetooth or USB in kernel, RFCOMM socket emulated in Android
- * platform, Irda). This interface provides an abstract layer to be used by the
- * <code>ObexConnection</code>. Each kind of medium shall have its own
- * implementation to wrap and follow the same interface.
- * <P>
- * See section 1.2.2 of IrDA Object Exchange Protocol specification.
- * <P>
- * Different kind of medium may have different construction - for example, the
- * RFCOMM device file medium may be constructed from a file descriptor or simply
- * a string while the TCP medium usually from a socket.
- * @hide
- */
-public interface ObexTransport {
-
- void create() throws IOException;
-
- void listen() throws IOException;
-
- void close() throws IOException;
-
- void connect() throws IOException;
-
- void disconnect() throws IOException;
-
- InputStream openInputStream() throws IOException;
-
- OutputStream openOutputStream() throws IOException;
-
- DataInputStream openDataInputStream() throws IOException;
-
- DataOutputStream openDataOutputStream() throws IOException;
-
- /**
- * Must return the maximum allowed OBEX packet that can be sent over
- * the transport. For L2CAP this will be the Max SDU reported by the
- * peer device.
- * The returned value will be used to set the outgoing OBEX packet
- * size. Therefore this value shall not change.
- * For RFCOMM or other transport types where the OBEX packets size
- * is unrelated to the transport packet size, return -1;
- * Exception can be made (like PBAP transport) with a smaller value
- * to avoid bad effect on other profiles using the RFCOMM;
- * @return the maximum allowed OBEX packet that can be send over
- * the transport. Or -1 in case of don't care.
- */
- int getMaxTransmitPacketSize();
-
- /**
- * Must return the maximum allowed OBEX packet that can be received over
- * the transport. For L2CAP this will be the Max SDU configured for the
- * L2CAP channel.
- * The returned value will be used to validate the incoming packet size
- * values.
- * For RFCOMM or other transport types where the OBEX packets size
- * is unrelated to the transport packet size, return -1;
- * @return the maximum allowed OBEX packet that can be send over
- * the transport. Or -1 in case of don't care.
- */
- int getMaxReceivePacketSize();
-
- /**
- * Shall return true if the transport in use supports SRM.
- * @return
- * <code>true</code> if SRM operation is supported, and is to be enabled.
- * <code>false</code> if SRM operations are not supported, or should not be used.
- */
- boolean isSrmSupported();
-
-
-}
diff --git a/obex/javax/obex/Operation.java b/obex/javax/obex/Operation.java
deleted file mode 100644
index 5b4d5ac..0000000
--- a/obex/javax/obex/Operation.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright (c) 2008-2009, Motorola, Inc.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * - Neither the name of the Motorola, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package javax.obex;
-
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-/**
- * The <code>Operation</code> interface provides ways to manipulate a single
- * OBEX PUT or GET operation. The implementation of this interface sends OBEX
- * packets as they are built. If during the operation the peer in the operation
- * ends the operation, an <code>IOException</code> is thrown on the next read
- * from the input stream, write to the output stream, or call to
- * <code>sendHeaders()</code>.
- * <P>
- * <STRONG>Definition of methods inherited from <code>ContentConnection</code>
- * </STRONG>
- * <P>
- * <code>getEncoding()</code> will always return <code>null</code>. <BR>
- * <code>getLength()</code> will return the length specified by the OBEX Length
- * header or -1 if the OBEX Length header was not included. <BR>
- * <code>getType()</code> will return the value specified in the OBEX Type
- * header or <code>null</code> if the OBEX Type header was not included.<BR>
- * <P>
- * <STRONG>How Headers are Handled</STRONG>
- * <P>
- * As headers are received, they may be retrieved through the
- * <code>getReceivedHeaders()</code> method. If new headers are set during the
- * operation, the new headers will be sent during the next packet exchange.
- * <P>
- * <STRONG>PUT example</STRONG>
- * <P>
- * <PRE>
- * void putObjectViaOBEX(ClientSession conn, HeaderSet head, byte[] obj) throws IOException {
- * // Include the length header
- * head.setHeader(head.LENGTH, new Long(obj.length));
- * // Initiate the PUT request
- * Operation op = conn.put(head);
- * // Open the output stream to put the object to it
- * DataOutputStream out = op.openDataOutputStream();
- * // Send the object to the server
- * out.write(obj);
- * // End the transaction
- * out.close();
- * op.close();
- * }
- * </PRE>
- * <P>
- * <STRONG>GET example</STRONG>
- * <P>
- * <PRE>
- * byte[] getObjectViaOBEX(ClientSession conn, HeaderSet head) throws IOException {
- * // Send the initial GET request to the server
- * Operation op = conn.get(head);
- * // Retrieve the length of the object being sent back
- * int length = op.getLength();
- * // Create space for the object
- * byte[] obj = new byte[length];
- * // Get the object from the input stream
- * DataInputStream in = trans.openDataInputStream();
- * in.read(obj);
- * // End the transaction
- * in.close();
- * op.close();
- * return obj;
- * }
- * </PRE>
- *
- * <H3>Client PUT Operation Flow</H3> For PUT operations, a call to
- * <code>close()</code> the <code>OutputStream</code> returned from
- * <code>openOutputStream()</code> or <code>openDataOutputStream()</code> will
- * signal that the request is done. (In OBEX terms, the End-Of-Body header
- * should be sent and the final bit in the request will be set.) At this point,
- * the reply from the server may begin to be processed. A call to
- * <code>getResponseCode()</code> will do an implicit close on the
- * <code>OutputStream</code> and therefore signal that the request is done.
- * <H3>Client GET Operation Flow</H3> For GET operation, a call to
- * <code>openInputStream()</code> or <code>openDataInputStream()</code> will
- * signal that the request is done. (In OBEX terms, the final bit in the request
- * will be set.) A call to <code>getResponseCode()</code> will cause an implicit
- * close on the <code>InputStream</code>. No further data may be read at this
- * point.
- * @hide
- */
-public interface Operation {
-
- /**
- * Sends an ABORT message to the server. By calling this method, the
- * corresponding input and output streams will be closed along with this
- * object. No headers are sent in the abort request. This will end the
- * operation since <code>close()</code> will be called by this method.
- * @throws IOException if the transaction has already ended or if an OBEX
- * server calls this method
- */
- void abort() throws IOException;
-
- /**
- * Returns the headers that have been received during the operation.
- * Modifying the object returned has no effect on the headers that are sent
- * or retrieved.
- * @return the headers received during this <code>Operation</code>
- * @throws IOException if this <code>Operation</code> has been closed
- */
- HeaderSet getReceivedHeader() throws IOException;
-
- /**
- * Specifies the headers that should be sent in the next OBEX message that
- * is sent.
- * @param headers the headers to send in the next message
- * @throws IOException if this <code>Operation</code> has been closed or the
- * transaction has ended and no further messages will be exchanged
- * @throws IllegalArgumentException if <code>headers</code> was not created
- * by a call to <code>ServerRequestHandler.createHeaderSet()</code>
- * or <code>ClientSession.createHeaderSet()</code>
- * @throws NullPointerException if <code>headers</code> if <code>null</code>
- */
- void sendHeaders(HeaderSet headers) throws IOException;
-
- /**
- * Returns the response code received from the server. Response codes are
- * defined in the <code>ResponseCodes</code> class.
- * @see ResponseCodes
- * @return the response code retrieved from the server
- * @throws IOException if an error occurred in the transport layer during
- * the transaction; if this object was created by an OBEX server
- */
- int getResponseCode() throws IOException;
-
- String getEncoding();
-
- long getLength();
-
- int getHeaderLength();
-
- String getType();
-
- InputStream openInputStream() throws IOException;
-
- DataInputStream openDataInputStream() throws IOException;
-
- OutputStream openOutputStream() throws IOException;
-
- DataOutputStream openDataOutputStream() throws IOException;
-
- void close() throws IOException;
-
- int getMaxPacketSize();
-
- public void noBodyHeader();
-}
diff --git a/obex/javax/obex/PasswordAuthentication.java b/obex/javax/obex/PasswordAuthentication.java
deleted file mode 100644
index 326b1ff..0000000
--- a/obex/javax/obex/PasswordAuthentication.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (c) 2008-2009, Motorola, Inc.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * - Neither the name of the Motorola, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package javax.obex;
-
-/**
- * This class holds user name and password combinations.
- * @hide
- */
-public final class PasswordAuthentication {
-
- private byte[] mUserName;
-
- private final byte[] mPassword;
-
- /**
- * Creates a new <code>PasswordAuthentication</code> with the user name and
- * password provided.
- * @param userName the user name to include; this may be <code>null</code>
- * @param password the password to include in the response
- * @throws NullPointerException if <code>password</code> is
- * <code>null</code>
- */
- public PasswordAuthentication(final byte[] userName, final byte[] password) {
- if (userName != null) {
- mUserName = new byte[userName.length];
- System.arraycopy(userName, 0, mUserName, 0, userName.length);
- }
-
- mPassword = new byte[password.length];
- System.arraycopy(password, 0, mPassword, 0, password.length);
- }
-
- /**
- * Retrieves the user name that was specified in the constructor. The user
- * name may be <code>null</code>.
- * @return the user name
- */
- public byte[] getUserName() {
- return mUserName;
- }
-
- /**
- * Retrieves the password.
- * @return the password
- */
- public byte[] getPassword() {
- return mPassword;
- }
-}
diff --git a/obex/javax/obex/PrivateInputStream.java b/obex/javax/obex/PrivateInputStream.java
deleted file mode 100644
index 5daee72..0000000
--- a/obex/javax/obex/PrivateInputStream.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (c) 2008-2009, Motorola, Inc.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * - Neither the name of the Motorola, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package javax.obex;
-
-import java.io.InputStream;
-import java.io.IOException;
-
-/**
- * This object provides an input stream to the Operation objects used in this
- * package.
- * @hide
- */
-public final class PrivateInputStream extends InputStream {
-
- private BaseStream mParent;
-
- private byte[] mData;
-
- private int mIndex;
-
- private boolean mOpen;
-
- /**
- * Creates an input stream for the <code>Operation</code> to read from
- * @param p the connection this input stream is for
- */
- public PrivateInputStream(BaseStream p) {
- mParent = p;
- mData = new byte[0];
- mIndex = 0;
- mOpen = true;
- }
-
- /**
- * Returns the number of bytes that can be read (or skipped over) from this
- * input stream without blocking by the next caller of a method for this
- * input stream. The next caller might be the same thread or or another
- * thread.
- * @return the number of bytes that can be read from this input stream
- * without blocking
- * @throws IOException if an I/O error occurs
- */
- @Override
- public synchronized int available() throws IOException {
- ensureOpen();
- return mData.length - mIndex;
- }
-
- /**
- * Reads the next byte of data from the input stream. The value byte is
- * returned as an int in the range 0 to 255. If no byte is available because
- * the end of the stream has been reached, the value -1 is returned. This
- * method blocks until input data is available, the end of the stream is
- * detected, or an exception is thrown.
- * @return the byte read from the input stream or -1 if it reaches the end of
- * stream
- * @throws IOException if an I/O error occurs
- */
- @Override
- public synchronized int read() throws IOException {
- ensureOpen();
- while (mData.length == mIndex) {
- if (!mParent.continueOperation(true, true)) {
- return -1;
- }
- }
- return (mData[mIndex++] & 0xFF);
- }
-
- @Override
- public int read(byte[] b) throws IOException {
- return read(b, 0, b.length);
- }
-
- @Override
- public synchronized int read(byte[] b, int offset, int length) throws IOException {
-
- if (b == null) {
- throw new IOException("buffer is null");
- }
- if ((offset | length) < 0 || length > b.length - offset) {
- throw new ArrayIndexOutOfBoundsException("index outof bound");
- }
- ensureOpen();
-
- int currentDataLength = mData.length - mIndex;
- int remainReadLength = length;
- int offset1 = offset;
- int result = 0;
-
- while (currentDataLength <= remainReadLength) {
- System.arraycopy(mData, mIndex, b, offset1, currentDataLength);
- mIndex += currentDataLength;
- offset1 += currentDataLength;
- result += currentDataLength;
- remainReadLength -= currentDataLength;
-
- if (!mParent.continueOperation(true, true)) {
- return result == 0 ? -1 : result;
- }
- currentDataLength = mData.length - mIndex;
- }
- if (remainReadLength > 0) {
- System.arraycopy(mData, mIndex, b, offset1, remainReadLength);
- mIndex += remainReadLength;
- result += remainReadLength;
- }
- return result;
- }
-
- /**
- * Allows the <code>OperationImpl</code> thread to add body data to the
- * input stream.
- * @param body the data to add to the stream
- * @param start the start of the body to array to copy
- */
- public synchronized void writeBytes(byte[] body, int start) {
-
- int length = (body.length - start) + (mData.length - mIndex);
- byte[] temp = new byte[length];
-
- System.arraycopy(mData, mIndex, temp, 0, mData.length - mIndex);
- System.arraycopy(body, start, temp, mData.length - mIndex, body.length - start);
-
- mData = temp;
- mIndex = 0;
- notifyAll();
- }
-
- /**
- * Verifies that this stream is open
- * @throws IOException if the stream is not open
- */
- private void ensureOpen() throws IOException {
- mParent.ensureOpen();
- if (!mOpen) {
- throw new IOException("Input stream is closed");
- }
- }
-
- /**
- * Closes the input stream. If the input stream is already closed, do
- * nothing.
- * @throws IOException this will never happen
- */
- @Override
- public void close() throws IOException {
- mOpen = false;
- mParent.streamClosed(true);
- }
-}
diff --git a/obex/javax/obex/PrivateOutputStream.java b/obex/javax/obex/PrivateOutputStream.java
deleted file mode 100644
index 713f4ae..0000000
--- a/obex/javax/obex/PrivateOutputStream.java
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * Copyright (c) 2008-2009, Motorola, Inc.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * - Neither the name of the Motorola, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package javax.obex;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.ByteArrayOutputStream;
-
-/**
- * This object provides an output stream to the Operation objects used in this
- * package.
- * @hide
- */
-public final class PrivateOutputStream extends OutputStream {
-
- private BaseStream mParent;
-
- private ByteArrayOutputStream mArray;
-
- private boolean mOpen;
-
- private int mMaxPacketSize;
-
- /**
- * Creates an empty <code>PrivateOutputStream</code> to write to.
- * @param p the connection that this stream runs over
- */
- public PrivateOutputStream(BaseStream p, int maxSize) {
- mParent = p;
- mArray = new ByteArrayOutputStream();
- mMaxPacketSize = maxSize;
- mOpen = true;
- }
-
- /**
- * Determines how many bytes have been written to the output stream.
- * @return the number of bytes written to the output stream
- */
- public int size() {
- return mArray.size();
- }
-
- /**
- * Writes the specified byte to this output stream. The general contract for
- * write is that one byte is written to the output stream. The byte to be
- * written is the eight low-order bits of the argument b. The 24 high-order
- * bits of b are ignored.
- * @param b the byte to write
- * @throws IOException if an I/O error occurs
- */
- @Override
- public synchronized void write(int b) throws IOException {
- ensureOpen();
- mParent.ensureNotDone();
- mArray.write(b);
- if (mArray.size() == mMaxPacketSize) {
- mParent.continueOperation(true, false);
- }
- }
-
- @Override
- public void write(byte[] buffer) throws IOException {
- write(buffer, 0, buffer.length);
- }
-
- @Override
- public synchronized void write(byte[] buffer, int offset, int count) throws IOException {
- int offset1 = offset;
- int remainLength = count;
-
- if (buffer == null) {
- throw new IOException("buffer is null");
- }
- if ((offset | count) < 0 || count > buffer.length - offset) {
- throw new IndexOutOfBoundsException("index outof bound");
- }
-
- ensureOpen();
- mParent.ensureNotDone();
- while ((mArray.size() + remainLength) >= mMaxPacketSize) {
- int bufferLeft = mMaxPacketSize - mArray.size();
- mArray.write(buffer, offset1, bufferLeft);
- offset1 += bufferLeft;
- remainLength -= bufferLeft;
- mParent.continueOperation(true, false);
- }
- if (remainLength > 0) {
- mArray.write(buffer, offset1, remainLength);
- }
- }
-
- /**
- * Reads the bytes that have been written to this stream.
- * @param size the size of the array to return
- * @return the byte array that is written
- */
- public synchronized byte[] readBytes(int size) {
- if (mArray.size() > 0) {
- byte[] temp = mArray.toByteArray();
- mArray.reset();
- byte[] result = new byte[size];
- System.arraycopy(temp, 0, result, 0, size);
- if (temp.length != size) {
- mArray.write(temp, size, temp.length - size);
- }
- return result;
- } else {
- return null;
- }
- }
-
- /**
- * Verifies that this stream is open
- * @throws IOException if the stream is not open
- */
- private void ensureOpen() throws IOException {
- mParent.ensureOpen();
- if (!mOpen) {
- throw new IOException("Output stream is closed");
- }
- }
-
- /**
- * Closes the output stream. If the input stream is already closed, do
- * nothing.
- * @throws IOException this will never happen
- */
- @Override
- public void close() throws IOException {
- mOpen = false;
- mParent.streamClosed(false);
- }
-
- /**
- * Determines if the connection is closed
- * @return <code>true</code> if the connection is closed; <code>false</code>
- * if the connection is open
- */
- public boolean isClosed() {
- return !mOpen;
- }
-}
diff --git a/obex/javax/obex/ResponseCodes.java b/obex/javax/obex/ResponseCodes.java
deleted file mode 100644
index a2b9a37..0000000
--- a/obex/javax/obex/ResponseCodes.java
+++ /dev/null
@@ -1,326 +0,0 @@
-/*
- * Copyright (c) 2008-2009, Motorola, Inc.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * - Neither the name of the Motorola, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package javax.obex;
-
-/**
- * The <code>ResponseCodes</code> class contains the list of valid response
- * codes a server may send to a client.
- * <P>
- * <STRONG>IMPORTANT NOTE</STRONG>
- * <P>
- * The values in this interface represent the values defined in the IrOBEX
- * specification, which is different with the HTTP specification.
- * <P>
- * <code>OBEX_DATABASE_FULL</code> and <code>OBEX_DATABASE_LOCKED</code> require
- * further description since they are not defined in HTTP. The server will send
- * an <code>OBEX_DATABASE_FULL</code> message when the client requests that
- * something be placed into a database but the database is full (cannot take
- * more data). <code>OBEX_DATABASE_LOCKED</code> will be returned when the
- * client wishes to access a database, database table, or database record that
- * has been locked.
- * @hide
- */
-public final class ResponseCodes {
-
- /**
- * Defines the OBEX CONTINUE response code.
- * <P>
- * The value of <code>OBEX_HTTP_CONTINUE</code> is 0x90 (144).
- */
- public static final int OBEX_HTTP_CONTINUE = 0x90;
-
- /**
- * Defines the OBEX SUCCESS response code.
- * <P>
- * The value of <code>OBEX_HTTP_OK</code> is 0xA0 (160).
- */
- public static final int OBEX_HTTP_OK = 0xA0;
-
- /**
- * Defines the OBEX CREATED response code.
- * <P>
- * The value of <code>OBEX_HTTP_CREATED</code> is 0xA1 (161).
- */
- public static final int OBEX_HTTP_CREATED = 0xA1;
-
- /**
- * Defines the OBEX ACCEPTED response code.
- * <P>
- * The value of <code>OBEX_HTTP_ACCEPTED</code> is 0xA2 (162).
- */
- public static final int OBEX_HTTP_ACCEPTED = 0xA2;
-
- /**
- * Defines the OBEX NON-AUTHORITATIVE INFORMATION response code.
- * <P>
- * The value of <code>OBEX_HTTP_NOT_AUTHORITATIVE</code> is 0xA3 (163).
- */
- public static final int OBEX_HTTP_NOT_AUTHORITATIVE = 0xA3;
-
- /**
- * Defines the OBEX NO CONTENT response code.
- * <P>
- * The value of <code>OBEX_HTTP_NO_CONTENT</code> is 0xA4 (164).
- */
- public static final int OBEX_HTTP_NO_CONTENT = 0xA4;
-
- /**
- * Defines the OBEX RESET CONTENT response code.
- * <P>
- * The value of <code>OBEX_HTTP_RESET</code> is 0xA5 (165).
- */
- public static final int OBEX_HTTP_RESET = 0xA5;
-
- /**
- * Defines the OBEX PARTIAL CONTENT response code.
- * <P>
- * The value of <code>OBEX_HTTP_PARTIAL</code> is 0xA6 (166).
- */
- public static final int OBEX_HTTP_PARTIAL = 0xA6;
-
- /**
- * Defines the OBEX MULTIPLE_CHOICES response code.
- * <P>
- * The value of <code>OBEX_HTTP_MULT_CHOICE</code> is 0xB0 (176).
- */
- public static final int OBEX_HTTP_MULT_CHOICE = 0xB0;
-
- /**
- * Defines the OBEX MOVED PERMANENTLY response code.
- * <P>
- * The value of <code>OBEX_HTTP_MOVED_PERM</code> is 0xB1 (177).
- */
- public static final int OBEX_HTTP_MOVED_PERM = 0xB1;
-
- /**
- * Defines the OBEX MOVED TEMPORARILY response code.
- * <P>
- * The value of <code>OBEX_HTTP_MOVED_TEMP</code> is 0xB2 (178).
- */
- public static final int OBEX_HTTP_MOVED_TEMP = 0xB2;
-
- /**
- * Defines the OBEX SEE OTHER response code.
- * <P>
- * The value of <code>OBEX_HTTP_SEE_OTHER</code> is 0xB3 (179).
- */
- public static final int OBEX_HTTP_SEE_OTHER = 0xB3;
-
- /**
- * Defines the OBEX NOT MODIFIED response code.
- * <P>
- * The value of <code>OBEX_HTTP_NOT_MODIFIED</code> is 0xB4 (180).
- */
- public static final int OBEX_HTTP_NOT_MODIFIED = 0xB4;
-
- /**
- * Defines the OBEX USE PROXY response code.
- * <P>
- * The value of <code>OBEX_HTTP_USE_PROXY</code> is 0xB5 (181).
- */
- public static final int OBEX_HTTP_USE_PROXY = 0xB5;
-
- /**
- * Defines the OBEX BAD REQUEST response code.
- * <P>
- * The value of <code>OBEX_HTTP_BAD_REQUEST</code> is 0xC0 (192).
- */
- public static final int OBEX_HTTP_BAD_REQUEST = 0xC0;
-
- /**
- * Defines the OBEX UNAUTHORIZED response code.
- * <P>
- * The value of <code>OBEX_HTTP_UNAUTHORIZED</code> is 0xC1 (193).
- */
- public static final int OBEX_HTTP_UNAUTHORIZED = 0xC1;
-
- /**
- * Defines the OBEX PAYMENT REQUIRED response code.
- * <P>
- * The value of <code>OBEX_HTTP_PAYMENT_REQUIRED</code> is 0xC2 (194).
- */
- public static final int OBEX_HTTP_PAYMENT_REQUIRED = 0xC2;
-
- /**
- * Defines the OBEX FORBIDDEN response code.
- * <P>
- * The value of <code>OBEX_HTTP_FORBIDDEN</code> is 0xC3 (195).
- */
- public static final int OBEX_HTTP_FORBIDDEN = 0xC3;
-
- /**
- * Defines the OBEX NOT FOUND response code.
- * <P>
- * The value of <code>OBEX_HTTP_NOT_FOUND</code> is 0xC4 (196).
- */
- public static final int OBEX_HTTP_NOT_FOUND = 0xC4;
-
- /**
- * Defines the OBEX METHOD NOT ALLOWED response code.
- * <P>
- * The value of <code>OBEX_HTTP_BAD_METHOD</code> is 0xC5 (197).
- */
- public static final int OBEX_HTTP_BAD_METHOD = 0xC5;
-
- /**
- * Defines the OBEX NOT ACCEPTABLE response code.
- * <P>
- * The value of <code>OBEX_HTTP_NOT_ACCEPTABLE</code> is 0xC6 (198).
- */
- public static final int OBEX_HTTP_NOT_ACCEPTABLE = 0xC6;
-
- /**
- * Defines the OBEX PROXY AUTHENTICATION REQUIRED response code.
- * <P>
- * The value of <code>OBEX_HTTP_PROXY_AUTH</code> is 0xC7 (199).
- */
- public static final int OBEX_HTTP_PROXY_AUTH = 0xC7;
-
- /**
- * Defines the OBEX REQUEST TIME OUT response code.
- * <P>
- * The value of <code>OBEX_HTTP_TIMEOUT</code> is 0xC8 (200).
- */
- public static final int OBEX_HTTP_TIMEOUT = 0xC8;
-
- /**
- * Defines the OBEX METHOD CONFLICT response code.
- * <P>
- * The value of <code>OBEX_HTTP_CONFLICT</code> is 0xC9 (201).
- */
- public static final int OBEX_HTTP_CONFLICT = 0xC9;
-
- /**
- * Defines the OBEX METHOD GONE response code.
- * <P>
- * The value of <code>OBEX_HTTP_GONE</code> is 0xCA (202).
- */
- public static final int OBEX_HTTP_GONE = 0xCA;
-
- /**
- * Defines the OBEX METHOD LENGTH REQUIRED response code.
- * <P>
- * The value of <code>OBEX_HTTP_LENGTH_REQUIRED</code> is 0xCB (203).
- */
- public static final int OBEX_HTTP_LENGTH_REQUIRED = 0xCB;
-
- /**
- * Defines the OBEX PRECONDITION FAILED response code.
- * <P>
- * The value of <code>OBEX_HTTP_PRECON_FAILED</code> is 0xCC (204).
- */
- public static final int OBEX_HTTP_PRECON_FAILED = 0xCC;
-
- /**
- * Defines the OBEX REQUESTED ENTITY TOO LARGE response code.
- * <P>
- * The value of <code>OBEX_HTTP_ENTITY_TOO_LARGE</code> is 0xCD (205).
- */
- public static final int OBEX_HTTP_ENTITY_TOO_LARGE = 0xCD;
-
- /**
- * Defines the OBEX REQUESTED URL TOO LARGE response code.
- * <P>
- * The value of <code>OBEX_HTTP_REQ_TOO_LARGE</code> is 0xCE (206).
- */
- public static final int OBEX_HTTP_REQ_TOO_LARGE = 0xCE;
-
- /**
- * Defines the OBEX UNSUPPORTED MEDIA TYPE response code.
- * <P>
- * The value of <code>OBEX_HTTP_UNSUPPORTED_TYPE</code> is 0xCF (207).
- */
- public static final int OBEX_HTTP_UNSUPPORTED_TYPE = 0xCF;
-
- /**
- * Defines the OBEX INTERNAL SERVER ERROR response code.
- * <P>
- * The value of <code>OBEX_HTTP_INTERNAL_ERROR</code> is 0xD0 (208).
- */
- public static final int OBEX_HTTP_INTERNAL_ERROR = 0xD0;
-
- /**
- * Defines the OBEX NOT IMPLEMENTED response code.
- * <P>
- * The value of <code>OBEX_HTTP_NOT_IMPLEMENTED</code> is 0xD1 (209).
- */
- public static final int OBEX_HTTP_NOT_IMPLEMENTED = 0xD1;
-
- /**
- * Defines the OBEX BAD GATEWAY response code.
- * <P>
- * The value of <code>OBEX_HTTP_BAD_GATEWAY</code> is 0xD2 (210).
- */
- public static final int OBEX_HTTP_BAD_GATEWAY = 0xD2;
-
- /**
- * Defines the OBEX SERVICE UNAVAILABLE response code.
- * <P>
- * The value of <code>OBEX_HTTP_UNAVAILABLE</code> is 0xD3 (211).
- */
- public static final int OBEX_HTTP_UNAVAILABLE = 0xD3;
-
- /**
- * Defines the OBEX GATEWAY TIMEOUT response code.
- * <P>
- * The value of <code>OBEX_HTTP_GATEWAY_TIMEOUT</code> is 0xD4 (212).
- */
- public static final int OBEX_HTTP_GATEWAY_TIMEOUT = 0xD4;
-
- /**
- * Defines the OBEX HTTP VERSION NOT SUPPORTED response code.
- * <P>
- * The value of <code>OBEX_HTTP_VERSION</code> is 0xD5 (213).
- */
- public static final int OBEX_HTTP_VERSION = 0xD5;
-
- /**
- * Defines the OBEX DATABASE FULL response code.
- * <P>
- * The value of <code>OBEX_DATABASE_FULL</code> is 0xE0 (224).
- */
- public static final int OBEX_DATABASE_FULL = 0xE0;
-
- /**
- * Defines the OBEX DATABASE LOCKED response code.
- * <P>
- * The value of <code>OBEX_DATABASE_LOCKED</code> is 0xE1 (225).
- */
- public static final int OBEX_DATABASE_LOCKED = 0xE1;
-
- /**
- * Constructor does nothing.
- */
- private ResponseCodes() {
- }
-}
diff --git a/obex/javax/obex/ServerOperation.java b/obex/javax/obex/ServerOperation.java
deleted file mode 100644
index 15ea367..0000000
--- a/obex/javax/obex/ServerOperation.java
+++ /dev/null
@@ -1,861 +0,0 @@
-/* Copyright (c) 2015 The Android Open Source Project
- * Copyright (C) 2015 Samsung LSI
- * Copyright (c) 2008-2009, Motorola, Inc.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * - Neither the name of the Motorola, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package javax.obex;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.DataInputStream;
-import java.io.OutputStream;
-import java.io.DataOutputStream;
-import java.io.ByteArrayOutputStream;
-
-import android.util.Log;
-
-/**
- * This class implements the Operation interface for server side connections.
- * <P>
- * <STRONG>Request Codes</STRONG> There are four different request codes that
- * are in this class. 0x02 is a PUT request that signals that the request is not
- * complete and requires an additional OBEX packet. 0x82 is a PUT request that
- * says that request is complete. In this case, the server can begin sending the
- * response. The 0x03 is a GET request that signals that the request is not
- * finished. When the server receives a 0x83, the client is signaling the server
- * that it is done with its request. TODO: Extend the ClientOperation and reuse
- * the methods defined TODO: in that class.
- * @hide
- */
-public final class ServerOperation implements Operation, BaseStream {
-
- private static final String TAG = "ServerOperation";
-
- private static final boolean V = ObexHelper.VDBG; // Verbose debugging
-
- public boolean isAborted;
-
- public HeaderSet requestHeader;
-
- public HeaderSet replyHeader;
-
- public boolean finalBitSet;
-
- private InputStream mInput;
-
- private ServerSession mParent;
-
- private int mMaxPacketLength;
-
- private int mResponseSize;
-
- private boolean mClosed;
-
- private boolean mGetOperation;
-
- private PrivateInputStream mPrivateInput;
-
- private PrivateOutputStream mPrivateOutput;
-
- private ObexTransport mTransport;
-
- private boolean mPrivateOutputOpen;
-
- private String mExceptionString;
-
- private ServerRequestHandler mListener;
-
- private boolean mRequestFinished;
-
- private boolean mHasBody;
-
- private boolean mSendBodyHeader = true;
- // Assume SRM disabled - needs to be explicit
- // enabled by client
- private boolean mSrmEnabled = false;
- // A latch - when triggered, there is not way back ;-)
- private boolean mSrmActive = false;
- // Set to true when a SRM enable response have been send
- private boolean mSrmResponseSent = false;
- // keep waiting until final-bit is received in request
- // to handle the case where the SRM enable header is in
- // a different OBEX packet than the SRMP header.
- private boolean mSrmWaitingForRemote = true;
- // Why should we wait? - currently not exposed to apps.
- private boolean mSrmLocalWait = false;
-
- /**
- * Creates new ServerOperation
- * @param p the parent that created this object
- * @param in the input stream to read from
- * @param out the output stream to write to
- * @param request the initial request that was received from the client
- * @param maxSize the max packet size that the client will accept
- * @param listen the listener that is responding to the request
- * @throws IOException if an IO error occurs
- */
- public ServerOperation(ServerSession p, InputStream in, int request, int maxSize,
- ServerRequestHandler listen) throws IOException {
-
- isAborted = false;
- mParent = p;
- mInput = in;
- mMaxPacketLength = maxSize;
- mClosed = false;
- requestHeader = new HeaderSet();
- replyHeader = new HeaderSet();
- mPrivateInput = new PrivateInputStream(this);
- mResponseSize = 3;
- mListener = listen;
- mRequestFinished = false;
- mPrivateOutputOpen = false;
- mHasBody = false;
- ObexPacket packet;
- mTransport = p.getTransport();
-
- /*
- * Determine if this is a PUT request
- */
- if ((request == ObexHelper.OBEX_OPCODE_PUT) ||
- (request == ObexHelper.OBEX_OPCODE_PUT_FINAL)) {
- /*
- * It is a PUT request.
- */
- mGetOperation = false;
-
- /*
- * Determine if the final bit is set
- */
- if ((request & ObexHelper.OBEX_OPCODE_FINAL_BIT_MASK) == 0) {
- finalBitSet = false;
- } else {
- finalBitSet = true;
- mRequestFinished = true;
- }
- } else if ((request == ObexHelper.OBEX_OPCODE_GET) ||
- (request == ObexHelper.OBEX_OPCODE_GET_FINAL)) {
- /*
- * It is a GET request.
- */
- mGetOperation = true;
-
- // For Get request, final bit set is decided by server side logic
- finalBitSet = false;
-
- if (request == ObexHelper.OBEX_OPCODE_GET_FINAL) {
- mRequestFinished = true;
- }
- } else {
- throw new IOException("ServerOperation can not handle such request");
- }
-
- packet = ObexPacket.read(request, mInput);
-
- /*
- * Determine if the packet length is larger than this device can receive
- */
- if (packet.mLength > ObexHelper.getMaxRxPacketSize(mTransport)) {
- mParent.sendResponse(ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE, null);
- throw new IOException("Packet received was too large. Length: "
- + packet.mLength + " maxLength: " + ObexHelper.getMaxRxPacketSize(mTransport));
- }
-
- /*
- * Determine if any headers were sent in the initial request
- */
- if (packet.mLength > 3) {
- if(!handleObexPacket(packet)) {
- return;
- }
- /* Don't Pre-Send continue when Remote requested for SRM
- * Let the Application confirm.
- */
- if (V) Log.v(TAG, "Get App confirmation if SRM ENABLED case: " + mSrmEnabled
- + " not hasBody case: " + mHasBody);
- if (!mHasBody && !mSrmEnabled) {
- while ((!mGetOperation) && (!finalBitSet)) {
- sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
- if (mPrivateInput.available() > 0) {
- break;
- }
- }
- }
- }
- /* Don't Pre-Send continue when Remote requested for SRM
- * Let the Application confirm.
- */
- if (V) Log.v(TAG, "Get App confirmation if SRM ENABLED case: " + mSrmEnabled
- + " not finalPacket: " + finalBitSet + " not GETOp Case: " + mGetOperation);
- while ((!mSrmEnabled) && (!mGetOperation) && (!finalBitSet)
- && (mPrivateInput.available() == 0)) {
- sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
- if (mPrivateInput.available() > 0) {
- break;
- }
- }
-
- // wait for get request finished !!!!
- while (mGetOperation && !mRequestFinished) {
- sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
- }
- }
-
- /**
- * Parse headers and update member variables
- * @param packet the received obex packet
- * @return false for failing authentication - and a OBEX_HTTP_UNAUTHORIZED
- * response have been send. Else true.
- * @throws IOException
- */
- private boolean handleObexPacket(ObexPacket packet) throws IOException {
- byte[] body = updateRequestHeaders(packet);
-
- if (body != null) {
- mHasBody = true;
- }
- if (mListener.getConnectionId() != -1 && requestHeader.mConnectionID != null) {
- mListener.setConnectionId(ObexHelper
- .convertToLong(requestHeader.mConnectionID));
- } else {
- mListener.setConnectionId(1);
- }
-
- if (requestHeader.mAuthResp != null) {
- if (!mParent.handleAuthResp(requestHeader.mAuthResp)) {
- mExceptionString = "Authentication Failed";
- mParent.sendResponse(ResponseCodes.OBEX_HTTP_UNAUTHORIZED, null);
- mClosed = true;
- requestHeader.mAuthResp = null;
- return false;
- }
- requestHeader.mAuthResp = null;
- }
-
- if (requestHeader.mAuthChall != null) {
- mParent.handleAuthChall(requestHeader);
- // send the auhtResp to the client
- replyHeader.mAuthResp = new byte[requestHeader.mAuthResp.length];
- System.arraycopy(requestHeader.mAuthResp, 0, replyHeader.mAuthResp, 0,
- replyHeader.mAuthResp.length);
- requestHeader.mAuthResp = null;
- requestHeader.mAuthChall = null;
- }
-
- if (body != null) {
- mPrivateInput.writeBytes(body, 1);
- }
- return true;
- }
-
- /**
- * Update the request header set, and sniff on SRM headers to update local state.
- * @param data the OBEX packet data
- * @return any bytes in a body/end-of-body header returned by {@link ObexHelper.updateHeaderSet}
- * @throws IOException
- */
- private byte[] updateRequestHeaders(ObexPacket packet) throws IOException {
- byte[] body = null;
- if (packet.mPayload != null) {
- body = ObexHelper.updateHeaderSet(requestHeader, packet.mPayload);
- }
- Byte srmMode = (Byte)requestHeader.getHeader(HeaderSet.SINGLE_RESPONSE_MODE);
- if(mTransport.isSrmSupported() && srmMode != null
- && srmMode == ObexHelper.OBEX_SRM_ENABLE) {
- mSrmEnabled = true;
- if(V) Log.d(TAG,"SRM is now ENABLED (but not active) for this operation");
- }
- checkForSrmWait(packet.mHeaderId);
- if((!mSrmWaitingForRemote) && (mSrmEnabled)) {
- if(V) Log.d(TAG,"SRM is now ACTIVE for this operation");
- mSrmActive = true;
- }
- return body;
- }
-
- /**
- * Call this only when a complete request have been received.
- * (This is not optimal, but the current design is not really suited to
- * the way SRM is specified.)
- */
- private void checkForSrmWait(int headerId){
- if (mSrmEnabled && (headerId == ObexHelper.OBEX_OPCODE_GET
- || headerId == ObexHelper.OBEX_OPCODE_GET_FINAL
- || headerId == ObexHelper.OBEX_OPCODE_PUT)) {
- try {
- mSrmWaitingForRemote = false;
- Byte srmp = (Byte)requestHeader.getHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER);
- if(srmp != null && srmp == ObexHelper.OBEX_SRMP_WAIT) {
- mSrmWaitingForRemote = true;
- // Clear the wait header, as the absents of the header when the final bit is set
- // indicates don't wait.
- requestHeader.setHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER, null);
- }
- } catch (IOException e) {if(V){Log.w(TAG,"Exception while extracting header",e);}}
- }
- }
-
- public boolean isValidBody() {
- return mHasBody;
- }
-
- /**
- * Determines if the operation should continue or should wait. If it should
- * continue, this method will continue the operation.
- * @param sendEmpty if <code>true</code> then this will continue the
- * operation even if no headers will be sent; if <code>false</code>
- * then this method will only continue the operation if there are
- * headers to send
- * @param inStream if<code>true</code> the stream is input stream, otherwise
- * output stream
- * @return <code>true</code> if the operation was completed;
- * <code>false</code> if no operation took place
- */
- public synchronized boolean continueOperation(boolean sendEmpty, boolean inStream)
- throws IOException {
- if (!mGetOperation) {
- if (!finalBitSet) {
- if (sendEmpty) {
- sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
- return true;
- } else {
- if ((mResponseSize > 3) || (mPrivateOutput.size() > 0)) {
- sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
- return true;
- } else {
- return false;
- }
- }
- } else {
- return false;
- }
- } else {
- sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
- return true;
- }
- }
-
- /**
- * Sends a reply to the client. If the reply is a OBEX_HTTP_CONTINUE, it
- * will wait for a response from the client before ending unless SRM is active.
- * @param type the response code to send back to the client
- * @return <code>true</code> if the final bit was not set on the reply;
- * <code>false</code> if no reply was received because the operation
- * ended, an abort was received, the final bit was set in the
- * reply or SRM is active.
- * @throws IOException if an IO error occurs
- */
- public synchronized boolean sendReply(int type) throws IOException {
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- boolean skipSend = false;
- boolean skipReceive = false;
- boolean srmRespSendPending = false;
-
- long id = mListener.getConnectionId();
- if (id == -1) {
- replyHeader.mConnectionID = null;
- } else {
- replyHeader.mConnectionID = ObexHelper.convertToByteArray(id);
- }
-
- if(mSrmEnabled && !mSrmResponseSent) {
- // As we are not ensured that the SRM enable is in the first OBEX packet
- // We must check for each reply.
- if(V)Log.v(TAG, "mSrmEnabled==true, sending SRM enable response.");
- replyHeader.setHeader(HeaderSet.SINGLE_RESPONSE_MODE, (byte)ObexHelper.OBEX_SRM_ENABLE);
- srmRespSendPending = true;
- }
-
- if(mSrmEnabled && !mGetOperation && mSrmLocalWait) {
- replyHeader.setHeader(HeaderSet.SINGLE_RESPONSE_MODE, (byte)ObexHelper.OBEX_SRMP_WAIT);
- }
-
- byte[] headerArray = ObexHelper.createHeader(replyHeader, true); // This clears the headers
- int bodyLength = -1;
- int orginalBodyLength = -1;
-
- if (mPrivateOutput != null) {
- bodyLength = mPrivateOutput.size();
- orginalBodyLength = bodyLength;
- }
-
- if ((ObexHelper.BASE_PACKET_LENGTH + headerArray.length) > mMaxPacketLength) {
-
- int end = 0;
- int start = 0;
-
- while (end != headerArray.length) {
- end = ObexHelper.findHeaderEnd(headerArray, start, mMaxPacketLength
- - ObexHelper.BASE_PACKET_LENGTH);
- if (end == -1) {
-
- mClosed = true;
-
- if (mPrivateInput != null) {
- mPrivateInput.close();
- }
-
- if (mPrivateOutput != null) {
- mPrivateOutput.close();
- }
- mParent.sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
- throw new IOException("OBEX Packet exceeds max packet size");
- }
- byte[] sendHeader = new byte[end - start];
- System.arraycopy(headerArray, start, sendHeader, 0, sendHeader.length);
-
- mParent.sendResponse(type, sendHeader);
- start = end;
- }
-
- if (bodyLength > 0) {
- return true;
- } else {
- return false;
- }
-
- } else {
- out.write(headerArray);
- }
-
- // For Get operation: if response code is OBEX_HTTP_OK, then this is the
- // last packet; so set finalBitSet to true.
- if (mGetOperation && type == ResponseCodes.OBEX_HTTP_OK) {
- finalBitSet = true;
- }
-
- if(mSrmActive) {
- if(!mGetOperation && type == ResponseCodes.OBEX_HTTP_CONTINUE &&
- mSrmResponseSent == true) {
- // we are in the middle of a SRM PUT operation, don't send a continue.
- skipSend = true;
- } else if(mGetOperation && mRequestFinished == false && mSrmResponseSent == true) {
- // We are still receiving the get request, receive, but don't send continue.
- skipSend = true;
- } else if(mGetOperation && mRequestFinished == true) {
- // All done receiving the GET request, send data to the client, without
- // expecting a continue.
- skipReceive = true;
- }
- if(V)Log.v(TAG, "type==" + type + " skipSend==" + skipSend
- + " skipReceive==" + skipReceive);
- }
- if(srmRespSendPending) {
- if(V)Log.v(TAG,
- "SRM Enabled (srmRespSendPending == true)- sending SRM Enable response");
- mSrmResponseSent = true;
- }
-
- if ((finalBitSet) || (headerArray.length < (mMaxPacketLength - 20))) {
- if (bodyLength > 0) {
- /*
- * Determine if I can send the whole body or just part of
- * the body. Remember that there is the 3 bytes for the
- * response message and 3 bytes for the header ID and length
- */
- if (bodyLength > (mMaxPacketLength - headerArray.length - 6)) {
- bodyLength = mMaxPacketLength - headerArray.length - 6;
- }
-
- byte[] body = mPrivateOutput.readBytes(bodyLength);
-
- /*
- * Since this is a put request if the final bit is set or
- * the output stream is closed we need to send the 0x49
- * (End of Body) otherwise, we need to send 0x48 (Body)
- */
- if ((finalBitSet) || (mPrivateOutput.isClosed())) {
- if(mSendBodyHeader == true) {
- out.write(0x49);
- bodyLength += 3;
- out.write((byte)(bodyLength >> 8));
- out.write((byte)bodyLength);
- out.write(body);
- }
- } else {
- if(mSendBodyHeader == true) {
- out.write(0x48);
- bodyLength += 3;
- out.write((byte)(bodyLength >> 8));
- out.write((byte)bodyLength);
- out.write(body);
- }
- }
-
- }
- }
-
- if ((finalBitSet) && (type == ResponseCodes.OBEX_HTTP_OK) && (orginalBodyLength <= 0)) {
- if(mSendBodyHeader) {
- out.write(0x49);
- orginalBodyLength = 3;
- out.write((byte)(orginalBodyLength >> 8));
- out.write((byte)orginalBodyLength);
- }
- }
-
- if(skipSend == false) {
- mResponseSize = 3;
- mParent.sendResponse(type, out.toByteArray());
- }
-
- if (type == ResponseCodes.OBEX_HTTP_CONTINUE) {
-
- if(mGetOperation && skipReceive) {
- // Here we need to check for and handle abort (throw an exception).
- // Any other signal received should be discarded silently (only on server side)
- checkSrmRemoteAbort();
- } else {
- // Receive and handle data (only send reply if !skipSend)
- // Read a complete OBEX Packet
- ObexPacket packet = ObexPacket.read(mInput);
-
- int headerId = packet.mHeaderId;
- if ((headerId != ObexHelper.OBEX_OPCODE_PUT)
- && (headerId != ObexHelper.OBEX_OPCODE_PUT_FINAL)
- && (headerId != ObexHelper.OBEX_OPCODE_GET)
- && (headerId != ObexHelper.OBEX_OPCODE_GET_FINAL)) {
-
- /*
- * Determine if an ABORT was sent as the reply
- */
- if (headerId == ObexHelper.OBEX_OPCODE_ABORT) {
- handleRemoteAbort();
- } else {
- // TODO:shall we send this if it occurs during SRM? Errata on the subject
- mParent.sendResponse(ResponseCodes.OBEX_HTTP_BAD_REQUEST, null);
- mClosed = true;
- mExceptionString = "Bad Request Received";
- throw new IOException("Bad Request Received");
- }
- } else {
-
- if ((headerId == ObexHelper.OBEX_OPCODE_PUT_FINAL)) {
- finalBitSet = true;
- } else if (headerId == ObexHelper.OBEX_OPCODE_GET_FINAL) {
- mRequestFinished = true;
- }
-
- /*
- * Determine if the packet length is larger than the negotiated packet size
- */
- if (packet.mLength > ObexHelper.getMaxRxPacketSize(mTransport)) {
- mParent.sendResponse(ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE, null);
- throw new IOException("Packet received was too large");
- }
-
- /*
- * Determine if any headers were sent in the initial request
- */
- if (packet.mLength > 3 || (mSrmEnabled && packet.mLength == 3)) {
- if(handleObexPacket(packet) == false) {
- return false;
- }
- }
- }
-
- }
- return true;
- } else {
- return false;
- }
- }
-
- /**
- * This method will look for an abort from the peer during a SRM transfer.
- * The function will not block if no data has been received from the remote device.
- * If data have been received, the function will block while reading the incoming
- * OBEX package.
- * An Abort request will be handled, and cause an IOException("Abort Received").
- * Other messages will be discarded silently as per GOEP specification.
- * @throws IOException if an abort request have been received.
- * TODO: I think this is an error in the specification. If we discard other messages,
- * the peer device will most likely stall, as it will not receive the expected
- * response for the message...
- * I'm not sure how to understand "Receipt of invalid or unexpected SRM or SRMP
- * header values shall be ignored by the receiving device."
- * If any signal is received during an active SRM transfer it is unexpected regardless
- * whether or not it contains SRM/SRMP headers...
- */
- private void checkSrmRemoteAbort() throws IOException {
- if(mInput.available() > 0) {
- ObexPacket packet = ObexPacket.read(mInput);
- /*
- * Determine if an ABORT was sent as the reply
- */
- if (packet.mHeaderId == ObexHelper.OBEX_OPCODE_ABORT) {
- handleRemoteAbort();
- } else {
- // TODO: should we throw an exception here anyway? - don't see how to
- // ignore SRM/SRMP headers without ignoring the complete signal
- // (in this particular case).
- Log.w(TAG, "Received unexpected request from client - discarding...\n"
- + " headerId: " + packet.mHeaderId + " length: " + packet.mLength);
- }
- }
- }
-
- private void handleRemoteAbort() throws IOException {
- /* TODO: To increase the speed of the abort operation in SRM, we need
- * to be able to flush the L2CAP queue for the PSM in use.
- * This could be implemented by introducing a control
- * message to be send over the socket, that in the abort case
- * could carry a flush command. */
- mParent.sendResponse(ResponseCodes.OBEX_HTTP_OK, null);
- mClosed = true;
- isAborted = true;
- mExceptionString = "Abort Received";
- throw new IOException("Abort Received");
- }
-
- /**
- * Sends an ABORT message to the server. By calling this method, the
- * corresponding input and output streams will be closed along with this
- * object.
- * @throws IOException if the transaction has already ended or if an OBEX
- * server called this method
- */
- public void abort() throws IOException {
- throw new IOException("Called from a server");
- }
-
- /**
- * Returns the headers that have been received during the operation.
- * Modifying the object returned has no effect on the headers that are sent
- * or retrieved.
- * @return the headers received during this <code>Operation</code>
- * @throws IOException if this <code>Operation</code> has been closed
- */
- public HeaderSet getReceivedHeader() throws IOException {
- ensureOpen();
- return requestHeader;
- }
-
- /**
- * Specifies the headers that should be sent in the next OBEX message that
- * is sent.
- * @param headers the headers to send in the next message
- * @throws IOException if this <code>Operation</code> has been closed or the
- * transaction has ended and no further messages will be exchanged
- * @throws IllegalArgumentException if <code>headers</code> was not created
- * by a call to <code>ServerRequestHandler.createHeaderSet()</code>
- */
- public void sendHeaders(HeaderSet headers) throws IOException {
- ensureOpen();
-
- if (headers == null) {
- throw new IOException("Headers may not be null");
- }
-
- int[] headerList = headers.getHeaderList();
- if (headerList != null) {
- for (int i = 0; i < headerList.length; i++) {
- replyHeader.setHeader(headerList[i], headers.getHeader(headerList[i]));
- }
-
- }
- }
-
- /**
- * Retrieves the response code retrieved from the server. Response codes are
- * defined in the <code>ResponseCodes</code> interface.
- * @return the response code retrieved from the server
- * @throws IOException if an error occurred in the transport layer during
- * the transaction; if this method is called on a
- * <code>HeaderSet</code> object created by calling
- * <code>createHeaderSet</code> in a <code>ClientSession</code>
- * object; if this is called from a server
- */
- public int getResponseCode() throws IOException {
- throw new IOException("Called from a server");
- }
-
- /**
- * Always returns <code>null</code>
- * @return <code>null</code>
- */
- public String getEncoding() {
- return null;
- }
-
- /**
- * Returns the type of content that the resource connected to is providing.
- * E.g. if the connection is via HTTP, then the value of the content-type
- * header field is returned.
- * @return the content type of the resource that the URL references, or
- * <code>null</code> if not known
- */
- public String getType() {
- try {
- return (String)requestHeader.getHeader(HeaderSet.TYPE);
- } catch (IOException e) {
- return null;
- }
- }
-
- /**
- * Returns the length of the content which is being provided. E.g. if the
- * connection is via HTTP, then the value of the content-length header field
- * is returned.
- * @return the content length of the resource that this connection's URL
- * references, or -1 if the content length is not known
- */
- public long getLength() {
- try {
- Long temp = (Long)requestHeader.getHeader(HeaderSet.LENGTH);
-
- if (temp == null) {
- return -1;
- } else {
- return temp.longValue();
- }
- } catch (IOException e) {
- return -1;
- }
- }
-
- public int getMaxPacketSize() {
- return mMaxPacketLength - 6 - getHeaderLength();
- }
-
- public int getHeaderLength() {
- long id = mListener.getConnectionId();
- if (id == -1) {
- replyHeader.mConnectionID = null;
- } else {
- replyHeader.mConnectionID = ObexHelper.convertToByteArray(id);
- }
-
- byte[] headerArray = ObexHelper.createHeader(replyHeader, false);
-
- return headerArray.length;
- }
-
- /**
- * Open and return an input stream for a connection.
- * @return an input stream
- * @throws IOException if an I/O error occurs
- */
- public InputStream openInputStream() throws IOException {
- ensureOpen();
- return mPrivateInput;
- }
-
- /**
- * Open and return a data input stream for a connection.
- * @return an input stream
- * @throws IOException if an I/O error occurs
- */
- public DataInputStream openDataInputStream() throws IOException {
- return new DataInputStream(openInputStream());
- }
-
- /**
- * Open and return an output stream for a connection.
- * @return an output stream
- * @throws IOException if an I/O error occurs
- */
- public OutputStream openOutputStream() throws IOException {
- ensureOpen();
-
- if (mPrivateOutputOpen) {
- throw new IOException("no more input streams available, stream already opened");
- }
-
- if (!mRequestFinished) {
- throw new IOException("no output streams available ,request not finished");
- }
-
- if (mPrivateOutput == null) {
- mPrivateOutput = new PrivateOutputStream(this, getMaxPacketSize());
- }
- mPrivateOutputOpen = true;
- return mPrivateOutput;
- }
-
- /**
- * Open and return a data output stream for a connection.
- * @return an output stream
- * @throws IOException if an I/O error occurs
- */
- public DataOutputStream openDataOutputStream() throws IOException {
- return new DataOutputStream(openOutputStream());
- }
-
- /**
- * Closes the connection and ends the transaction
- * @throws IOException if the operation has already ended or is closed
- */
- public void close() throws IOException {
- ensureOpen();
- mClosed = true;
- }
-
- /**
- * Verifies that the connection is open and no exceptions should be thrown.
- * @throws IOException if an exception needs to be thrown
- */
- public void ensureOpen() throws IOException {
- if (mExceptionString != null) {
- throw new IOException(mExceptionString);
- }
- if (mClosed) {
- throw new IOException("Operation has already ended");
- }
- }
-
- /**
- * Verifies that additional information may be sent. In other words, the
- * operation is not done.
- * <P>
- * Included to implement the BaseStream interface only. It does not do
- * anything on the server side since the operation of the Operation object
- * is not done until after the handler returns from its method.
- * @throws IOException if the operation is completed
- */
- public void ensureNotDone() throws IOException {
- }
-
- /**
- * Called when the output or input stream is closed. It does not do anything
- * on the server side since the operation of the Operation object is not
- * done until after the handler returns from its method.
- * @param inStream <code>true</code> if the input stream is closed;
- * <code>false</code> if the output stream is closed
- * @throws IOException if an IO error occurs
- */
- public void streamClosed(boolean inStream) throws IOException {
-
- }
-
- public void noBodyHeader(){
- mSendBodyHeader = false;
- }
-}
diff --git a/obex/javax/obex/ServerRequestHandler.java b/obex/javax/obex/ServerRequestHandler.java
deleted file mode 100644
index 09cbc2c..0000000
--- a/obex/javax/obex/ServerRequestHandler.java
+++ /dev/null
@@ -1,287 +0,0 @@
-/*
- * Copyright (c) 2008-2009, Motorola, Inc.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * - Neither the name of the Motorola, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package javax.obex;
-
-/**
- * The <code>ServerRequestHandler</code> class defines an event listener that
- * will respond to OBEX requests made to the server.
- * <P>
- * The <code>onConnect()</code>, <code>onSetPath()</code>,
- * <code>onDelete()</code>, <code>onGet()</code>, and <code>onPut()</code>
- * methods may return any response code defined in the
- * <code>ResponseCodes</code> class except for <code>OBEX_HTTP_CONTINUE</code>.
- * If <code>OBEX_HTTP_CONTINUE</code> or a value not defined in the
- * <code>ResponseCodes</code> class is returned, the server implementation will
- * send an <code>OBEX_HTTP_INTERNAL_ERROR</code> response to the client.
- * <P>
- * <STRONG>Connection ID and Target Headers</STRONG>
- * <P>
- * According to the IrOBEX specification, a packet may not contain a Connection
- * ID and Target header. Since the Connection ID header is managed by the
- * implementation, it will not send a Connection ID header, if a Connection ID
- * was specified, in a packet that has a Target header. In other words, if an
- * application adds a Target header to a <code>HeaderSet</code> object used in
- * an OBEX operation and a Connection ID was specified, no Connection ID will be
- * sent in the packet containing the Target header.
- * <P>
- * <STRONG>CREATE-EMPTY Requests</STRONG>
- * <P>
- * A CREATE-EMPTY request allows clients to create empty objects on the server.
- * When a CREATE-EMPTY request is received, the <code>onPut()</code> method will
- * be called by the implementation. To differentiate between a normal PUT
- * request and a CREATE-EMPTY request, an application must open the
- * <code>InputStream</code> from the <code>Operation</code> object passed to the
- * <code>onPut()</code> method. For a PUT request, the application will be able
- * to read Body data from this <code>InputStream</code>. For a CREATE-EMPTY
- * request, there will be no Body data to read. Therefore, a call to
- * <code>InputStream.read()</code> will return -1.
- * @hide
- */
-public class ServerRequestHandler {
-
- private long mConnectionId;
-
- /**
- * Creates a <code>ServerRequestHandler</code>.
- */
- protected ServerRequestHandler() {
- /*
- * A connection ID of -1 implies there is no conenction ID
- */
- mConnectionId = -1;
- }
-
- /**
- * Sets the connection ID header to include in the reply packets.
- * @param connectionId the connection ID to use; -1 if no connection ID
- * should be sent
- * @throws IllegalArgumentException if <code>id</code> is not in the range
- * -1 to 2<sup>32</sup>-1
- */
- public void setConnectionId(final long connectionId) {
- if ((connectionId < -1) || (connectionId > 0xFFFFFFFFL)) {
- throw new IllegalArgumentException("Illegal Connection ID");
- }
- mConnectionId = connectionId;
- }
-
- /**
- * Retrieves the connection ID that is being used in the present connection.
- * This method will return -1 if no connection ID is being used.
- * @return the connection id being used or -1 if no connection ID is being
- * used
- */
- public long getConnectionId() {
- return mConnectionId;
- }
-
- /**
- * Called when a CONNECT request is received.
- * <P>
- * If this method is not implemented by the class that extends this class,
- * <code>onConnect()</code> will always return an <code>OBEX_HTTP_OK</code>
- * response code.
- * <P>
- * The headers received in the request can be retrieved from the
- * <code>request</code> argument. The headers that should be sent in the
- * reply must be specified in the <code>reply</code> argument.
- * @param request contains the headers sent by the client;
- * <code>request</code> will never be <code>null</code>
- * @param reply the headers that should be sent in the reply;
- * <code>reply</code> will never be <code>null</code>
- * @return a response code defined in <code>ResponseCodes</code> that will
- * be returned to the client; if an invalid response code is
- * provided, the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code
- * will be used
- */
- public int onConnect(HeaderSet request, HeaderSet reply) {
- return ResponseCodes.OBEX_HTTP_OK;
- }
-
- /**
- * Called when a DISCONNECT request is received.
- * <P>
- * The headers received in the request can be retrieved from the
- * <code>request</code> argument. The headers that should be sent in the
- * reply must be specified in the <code>reply</code> argument.
- * @param request contains the headers sent by the client;
- * <code>request</code> will never be <code>null</code>
- * @param reply the headers that should be sent in the reply;
- * <code>reply</code> will never be <code>null</code>
- */
- public void onDisconnect(HeaderSet request, HeaderSet reply) {
- }
-
- /**
- * Called when a SETPATH request is received.
- * <P>
- * If this method is not implemented by the class that extends this class,
- * <code>onSetPath()</code> will always return an
- * <code>OBEX_HTTP_NOT_IMPLEMENTED</code> response code.
- * <P>
- * The headers received in the request can be retrieved from the
- * <code>request</code> argument. The headers that should be sent in the
- * reply must be specified in the <code>reply</code> argument.
- * @param request contains the headers sent by the client;
- * <code>request</code> will never be <code>null</code>
- * @param reply the headers that should be sent in the reply;
- * <code>reply</code> will never be <code>null</code>
- * @param backup <code>true</code> if the client requests that the server
- * back up one directory before changing to the path described by
- * <code>name</code>; <code>false</code> to apply the request to the
- * present path
- * @param create <code>true</code> if the path should be created if it does
- * not already exist; <code>false</code> if the path should not be
- * created if it does not exist and an error code should be returned
- * @return a response code defined in <code>ResponseCodes</code> that will
- * be returned to the client; if an invalid response code is
- * provided, the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code
- * will be used
- */
- public int onSetPath(HeaderSet request, HeaderSet reply, boolean backup, boolean create) {
-
- return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
- }
-
- /**
- * Called when a DELETE request is received.
- * <P>
- * If this method is not implemented by the class that extends this class,
- * <code>onDelete()</code> will always return an
- * <code>OBEX_HTTP_NOT_IMPLEMENTED</code> response code.
- * <P>
- * The headers received in the request can be retrieved from the
- * <code>request</code> argument. The headers that should be sent in the
- * reply must be specified in the <code>reply</code> argument.
- * @param request contains the headers sent by the client;
- * <code>request</code> will never be <code>null</code>
- * @param reply the headers that should be sent in the reply;
- * <code>reply</code> will never be <code>null</code>
- * @return a response code defined in <code>ResponseCodes</code> that will
- * be returned to the client; if an invalid response code is
- * provided, the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code
- * will be used
- */
- public int onDelete(HeaderSet request, HeaderSet reply) {
- return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
- }
-
- /**
- * Called when a ABORT request is received.
- */
- public int onAbort(HeaderSet request, HeaderSet reply) {
- return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
- }
-
- /**
- * Called when a PUT request is received.
- * <P>
- * If this method is not implemented by the class that extends this class,
- * <code>onPut()</code> will always return an
- * <code>OBEX_HTTP_NOT_IMPLEMENTED</code> response code.
- * <P>
- * If an ABORT request is received during the processing of a PUT request,
- * <code>op</code> will be closed by the implementation.
- * @param operation contains the headers sent by the client and allows new
- * headers to be sent in the reply; <code>op</code> will never be
- * <code>null</code>
- * @return a response code defined in <code>ResponseCodes</code> that will
- * be returned to the client; if an invalid response code is
- * provided, the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code
- * will be used
- */
- public int onPut(Operation operation) {
- return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
- }
-
- /**
- * Called when a GET request is received.
- * <P>
- * If this method is not implemented by the class that extends this class,
- * <code>onGet()</code> will always return an
- * <code>OBEX_HTTP_NOT_IMPLEMENTED</code> response code.
- * <P>
- * If an ABORT request is received during the processing of a GET request,
- * <code>op</code> will be closed by the implementation.
- * @param operation contains the headers sent by the client and allows new
- * headers to be sent in the reply; <code>op</code> will never be
- * <code>null</code>
- * @return a response code defined in <code>ResponseCodes</code> that will
- * be returned to the client; if an invalid response code is
- * provided, the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code
- * will be used
- */
- public int onGet(Operation operation) {
- return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
- }
-
- /**
- * Called when this object attempts to authenticate a client and the
- * authentication request fails because the response digest in the
- * authentication response header was wrong.
- * <P>
- * If this method is not implemented by the class that extends this class,
- * this method will do nothing.
- * @param userName the user name returned in the authentication response;
- * <code>null</code> if no user name was provided in the response
- */
- public void onAuthenticationFailure(byte[] userName) {
- }
-
- /**
- * Called by ServerSession to update the status of current transaction
- * <P>
- * If this method is not implemented by the class that extends this class,
- * this method will do nothing.
- */
- public void updateStatus(String message) {
- }
-
- /**
- * Called when session is closed.
- * <P>
- * If this method is not implemented by the class that extends this class,
- * this method will do nothing.
- */
- public void onClose() {
- }
-
- /**
- * Override to add Single Response Mode support - e.g. if the supplied
- * transport is l2cap.
- * @return True if SRM is supported, else False
- */
- public boolean isSrmSupported() {
- return false;
- }
-}
diff --git a/obex/javax/obex/ServerSession.java b/obex/javax/obex/ServerSession.java
deleted file mode 100644
index dbfeefd..0000000
--- a/obex/javax/obex/ServerSession.java
+++ /dev/null
@@ -1,742 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- * Copyright (c) 2015 Samsung LSI
- * Copyright (c) 2008-2009, Motorola, Inc.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * - Neither the name of the Motorola, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package javax.obex;
-
-import android.util.Log;
-
-import java.io.InputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * This class in an implementation of the OBEX ServerSession.
- * @hide
- */
-public final class ServerSession extends ObexSession implements Runnable {
-
- private static final String TAG = "Obex ServerSession";
- private static final boolean V = ObexHelper.VDBG;
-
- private ObexTransport mTransport;
-
- private InputStream mInput;
-
- private OutputStream mOutput;
-
- private ServerRequestHandler mListener;
-
- private Thread mProcessThread;
-
- private int mMaxPacketLength;
-
- private boolean mClosed;
-
- /**
- * Creates new ServerSession.
- * @param trans the connection to the client
- * @param handler the event listener that will process requests
- * @param auth the authenticator to use with this connection
- * @throws IOException if an error occurred while opening the input and
- * output streams
- */
- public ServerSession(ObexTransport trans, ServerRequestHandler handler, Authenticator auth)
- throws IOException {
- mAuthenticator = auth;
- mTransport = trans;
- mInput = mTransport.openInputStream();
- mOutput = mTransport.openOutputStream();
- mListener = handler;
- mMaxPacketLength = 256;
-
- mClosed = false;
- mProcessThread = new Thread(this);
- mProcessThread.start();
- }
-
- /**
- * Processes requests made to the server and forwards them to the
- * appropriate event listener.
- */
- public void run() {
- try {
-
- boolean done = false;
- while (!done && !mClosed) {
- if(V) Log.v(TAG, "Waiting for incoming request...");
- int requestType = mInput.read();
- if(V) Log.v(TAG, "Read request: " + requestType);
- switch (requestType) {
- case ObexHelper.OBEX_OPCODE_CONNECT:
- handleConnectRequest();
- break;
-
- case ObexHelper.OBEX_OPCODE_DISCONNECT:
- handleDisconnectRequest();
- break;
-
- case ObexHelper.OBEX_OPCODE_GET:
- case ObexHelper.OBEX_OPCODE_GET_FINAL:
- handleGetRequest(requestType);
- break;
-
- case ObexHelper.OBEX_OPCODE_PUT:
- case ObexHelper.OBEX_OPCODE_PUT_FINAL:
- handlePutRequest(requestType);
- break;
-
- case ObexHelper.OBEX_OPCODE_SETPATH:
- handleSetPathRequest();
- break;
- case ObexHelper.OBEX_OPCODE_ABORT:
- handleAbortRequest();
- break;
-
- case -1:
- done = true;
- break;
-
- default:
-
- /*
- * Received a request type that is not recognized so I am
- * just going to read the packet and send a not implemented
- * to the client
- */
- int length = mInput.read();
- length = (length << 8) + mInput.read();
- for (int i = 3; i < length; i++) {
- mInput.read();
- }
- sendResponse(ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED, null);
- }
- }
-
- } catch (NullPointerException e) {
- Log.d(TAG, "Exception occured - ignoring", e);
- } catch (Exception e) {
- Log.d(TAG, "Exception occured - ignoring", e);
- }
- close();
- }
-
- /**
- * Handles a ABORT request from a client. This method will read the rest of
- * the request from the client. Assuming the request is valid, it will
- * create a <code>HeaderSet</code> object to pass to the
- * <code>ServerRequestHandler</code> object. After the handler processes the
- * request, this method will create a reply message to send to the server.
- *
- * @throws IOException if an error occurred at the transport layer
- */
- private void handleAbortRequest() throws IOException {
- int code = ResponseCodes.OBEX_HTTP_OK;
- HeaderSet request = new HeaderSet();
- HeaderSet reply = new HeaderSet();
-
- int length = mInput.read();
- length = (length << 8) + mInput.read();
- if (length > ObexHelper.getMaxRxPacketSize(mTransport)) {
- code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
- } else {
- for (int i = 3; i < length; i++) {
- mInput.read();
- }
- code = mListener.onAbort(request, reply);
- Log.v(TAG, "onAbort request handler return value- " + code);
- code = validateResponseCode(code);
- }
- sendResponse(code, null);
- }
-
- /**
- * Handles a PUT request from a client. This method will provide a
- * <code>ServerOperation</code> object to the request handler. The
- * <code>ServerOperation</code> object will handle the rest of the request.
- * It will also send replies and receive requests until the final reply
- * should be sent. When the final reply should be sent, this method will get
- * the response code to use and send the reply. The
- * <code>ServerOperation</code> object will always reply with a
- * OBEX_HTTP_CONTINUE reply. It will only reply if further information is
- * needed.
- * @param type the type of request received; either 0x02 or 0x82
- * @throws IOException if an error occurred at the transport layer
- */
- private void handlePutRequest(int type) throws IOException {
- ServerOperation op = new ServerOperation(this, mInput, type, mMaxPacketLength, mListener);
- try {
- int response = -1;
-
- if ((op.finalBitSet) && !op.isValidBody()) {
- response = validateResponseCode(mListener
- .onDelete(op.requestHeader, op.replyHeader));
- } else {
- response = validateResponseCode(mListener.onPut(op));
- }
- if (response != ResponseCodes.OBEX_HTTP_OK && !op.isAborted) {
- op.sendReply(response);
- } else if (!op.isAborted) {
- // wait for the final bit
- while (!op.finalBitSet) {
- op.sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
- }
- op.sendReply(response);
- }
- } catch (Exception e) {
- /*To fix bugs in aborted cases,
- *(client abort file transfer prior to the last packet which has the end of body header,
- *internal error should not be sent because server has already replied with
- *OK response in "sendReply")
- */
- if(V) Log.d(TAG,"Exception occured - sending OBEX_HTTP_INTERNAL_ERROR reply",e);
- if (!op.isAborted) {
- sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
- }
- }
- }
-
- /**
- * Handles a GET request from a client. This method will provide a
- * <code>ServerOperation</code> object to the request handler. The
- * <code>ServerOperation</code> object will handle the rest of the request.
- * It will also send replies and receive requests until the final reply
- * should be sent. When the final reply should be sent, this method will get
- * the response code to use and send the reply. The
- * <code>ServerOperation</code> object will always reply with a
- * OBEX_HTTP_CONTINUE reply. It will only reply if further information is
- * needed.
- * @param type the type of request received; either 0x03 or 0x83
- * @throws IOException if an error occurred at the transport layer
- */
- private void handleGetRequest(int type) throws IOException {
- ServerOperation op = new ServerOperation(this, mInput, type, mMaxPacketLength, mListener);
- try {
- int response = validateResponseCode(mListener.onGet(op));
-
- if (!op.isAborted) {
- op.sendReply(response);
- }
- } catch (Exception e) {
- if(V) Log.d(TAG,"Exception occured - sending OBEX_HTTP_INTERNAL_ERROR reply",e);
- sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
- }
- }
-
- /**
- * Send standard response.
- * @param code the response code to send
- * @param header the headers to include in the response
- * @throws IOException if an IO error occurs
- */
- public void sendResponse(int code, byte[] header) throws IOException {
- int totalLength = 3;
- byte[] data = null;
- OutputStream op = mOutput;
- if (op == null) {
- return;
- }
-
- if (header != null) {
- totalLength += header.length;
- data = new byte[totalLength];
- data[0] = (byte)code;
- data[1] = (byte)(totalLength >> 8);
- data[2] = (byte)totalLength;
- System.arraycopy(header, 0, data, 3, header.length);
- } else {
- data = new byte[totalLength];
- data[0] = (byte)code;
- data[1] = (byte)0x00;
- data[2] = (byte)totalLength;
- }
- op.write(data);
- op.flush(); // TODO: Do we need to flush?
- }
-
- /**
- * Handles a SETPATH request from a client. This method will read the rest
- * of the request from the client. Assuming the request is valid, it will
- * create a <code>HeaderSet</code> object to pass to the
- * <code>ServerRequestHandler</code> object. After the handler processes the
- * request, this method will create a reply message to send to the server
- * with the response code provided.
- * @throws IOException if an error occurred at the transport layer
- */
- private void handleSetPathRequest() throws IOException {
- int length;
- int flags;
- @SuppressWarnings("unused")
- int constants;
- int totalLength = 3;
- byte[] head = null;
- int code = -1;
- int bytesReceived;
- HeaderSet request = new HeaderSet();
- HeaderSet reply = new HeaderSet();
-
- length = mInput.read();
- length = (length << 8) + mInput.read();
- flags = mInput.read();
- constants = mInput.read();
-
- if (length > ObexHelper.getMaxRxPacketSize(mTransport)) {
- code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
- totalLength = 3;
- } else {
- if (length > 5) {
- byte[] headers = new byte[length - 5];
- bytesReceived = mInput.read(headers);
-
- while (bytesReceived != headers.length) {
- bytesReceived += mInput.read(headers, bytesReceived, headers.length
- - bytesReceived);
- }
-
- ObexHelper.updateHeaderSet(request, headers);
-
- if (mListener.getConnectionId() != -1 && request.mConnectionID != null) {
- mListener.setConnectionId(ObexHelper.convertToLong(request.mConnectionID));
- } else {
- mListener.setConnectionId(1);
- }
- // the Auth chan is initiated by the server, client sent back the authResp .
- if (request.mAuthResp != null) {
- if (!handleAuthResp(request.mAuthResp)) {
- code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED;
- mListener.onAuthenticationFailure(ObexHelper.getTagValue((byte)0x01,
- request.mAuthResp));
- }
- request.mAuthResp = null;
- }
- }
-
- if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) {
- // the Auth challenge is initiated by the client
- // the server will send back the authResp to the client
- if (request.mAuthChall != null) {
- handleAuthChall(request);
- reply.mAuthResp = new byte[request.mAuthResp.length];
- System.arraycopy(request.mAuthResp, 0, reply.mAuthResp, 0,
- reply.mAuthResp.length);
- request.mAuthChall = null;
- request.mAuthResp = null;
- }
- boolean backup = false;
- boolean create = true;
- if (!((flags & 1) == 0)) {
- backup = true;
- }
- if (!((flags & 2) == 0)) {
- create = false;
- }
-
- try {
- code = mListener.onSetPath(request, reply, backup, create);
- } catch (Exception e) {
- if(V) Log.d(TAG,"Exception occured - sending OBEX_HTTP_INTERNAL_ERROR reply",e);
- sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
- return;
- }
-
- code = validateResponseCode(code);
-
- if (reply.nonce != null) {
- mChallengeDigest = new byte[16];
- System.arraycopy(reply.nonce, 0, mChallengeDigest, 0, 16);
- } else {
- mChallengeDigest = null;
- }
-
- long id = mListener.getConnectionId();
- if (id == -1) {
- reply.mConnectionID = null;
- } else {
- reply.mConnectionID = ObexHelper.convertToByteArray(id);
- }
-
- head = ObexHelper.createHeader(reply, false);
- totalLength += head.length;
-
- if (totalLength > mMaxPacketLength) {
- totalLength = 3;
- head = null;
- code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
- }
- }
- }
-
- // Compute Length of OBEX SETPATH packet
- byte[] replyData = new byte[totalLength];
- replyData[0] = (byte)code;
- replyData[1] = (byte)(totalLength >> 8);
- replyData[2] = (byte)totalLength;
- if (head != null) {
- System.arraycopy(head, 0, replyData, 3, head.length);
- }
- /*
- * Write the OBEX SETPATH packet to the server. Byte 0: response code
- * Byte 1&2: Connect Packet Length Byte 3 to n: headers
- */
- mOutput.write(replyData);
- mOutput.flush();
- }
-
- /**
- * Handles a disconnect request from a client. This method will read the
- * rest of the request from the client. Assuming the request is valid, it
- * will create a <code>HeaderSet</code> object to pass to the
- * <code>ServerRequestHandler</code> object. After the handler processes the
- * request, this method will create a reply message to send to the server.
- * @throws IOException if an error occurred at the transport layer
- */
- private void handleDisconnectRequest() throws IOException {
- int length;
- int code = ResponseCodes.OBEX_HTTP_OK;
- int totalLength = 3;
- byte[] head = null;
- int bytesReceived;
- HeaderSet request = new HeaderSet();
- HeaderSet reply = new HeaderSet();
-
- length = mInput.read();
- length = (length << 8) + mInput.read();
-
- if (length > ObexHelper.getMaxRxPacketSize(mTransport)) {
- code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
- totalLength = 3;
- } else {
- if (length > 3) {
- byte[] headers = new byte[length - 3];
- bytesReceived = mInput.read(headers);
-
- while (bytesReceived != headers.length) {
- bytesReceived += mInput.read(headers, bytesReceived, headers.length
- - bytesReceived);
- }
-
- ObexHelper.updateHeaderSet(request, headers);
- }
-
- if (mListener.getConnectionId() != -1 && request.mConnectionID != null) {
- mListener.setConnectionId(ObexHelper.convertToLong(request.mConnectionID));
- } else {
- mListener.setConnectionId(1);
- }
-
- if (request.mAuthResp != null) {
- if (!handleAuthResp(request.mAuthResp)) {
- code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED;
- mListener.onAuthenticationFailure(ObexHelper.getTagValue((byte)0x01,
- request.mAuthResp));
- }
- request.mAuthResp = null;
- }
-
- if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) {
-
- if (request.mAuthChall != null) {
- handleAuthChall(request);
- request.mAuthChall = null;
- }
-
- try {
- mListener.onDisconnect(request, reply);
- } catch (Exception e) {
- if(V) Log.d(TAG,"Exception occured - sending OBEX_HTTP_INTERNAL_ERROR reply",e);
- sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
- return;
- }
-
- long id = mListener.getConnectionId();
- if (id == -1) {
- reply.mConnectionID = null;
- } else {
- reply.mConnectionID = ObexHelper.convertToByteArray(id);
- }
-
- head = ObexHelper.createHeader(reply, false);
- totalLength += head.length;
-
- if (totalLength > mMaxPacketLength) {
- totalLength = 3;
- head = null;
- code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
- }
- }
- }
-
- // Compute Length of OBEX CONNECT packet
- byte[] replyData;
- if (head != null) {
- replyData = new byte[3 + head.length];
- } else {
- replyData = new byte[3];
- }
- replyData[0] = (byte)code;
- replyData[1] = (byte)(totalLength >> 8);
- replyData[2] = (byte)totalLength;
- if (head != null) {
- System.arraycopy(head, 0, replyData, 3, head.length);
- }
- /*
- * Write the OBEX DISCONNECT packet to the server. Byte 0: response code
- * Byte 1&2: Connect Packet Length Byte 3 to n: headers
- */
- mOutput.write(replyData);
- mOutput.flush();
- }
-
- /**
- * Handles a connect request from a client. This method will read the rest
- * of the request from the client. Assuming the request is valid, it will
- * create a <code>HeaderSet</code> object to pass to the
- * <code>ServerRequestHandler</code> object. After the handler processes the
- * request, this method will create a reply message to send to the server
- * with the response code provided.
- * @throws IOException if an error occurred at the transport layer
- */
- private void handleConnectRequest() throws IOException {
- int packetLength;
- @SuppressWarnings("unused")
- int version;
- @SuppressWarnings("unused")
- int flags;
- int totalLength = 7;
- byte[] head = null;
- int code = -1;
- HeaderSet request = new HeaderSet();
- HeaderSet reply = new HeaderSet();
- int bytesReceived;
-
- if(V) Log.v(TAG,"handleConnectRequest()");
-
- /*
- * Read in the length of the OBEX packet, OBEX version, flags, and max
- * packet length
- */
- packetLength = mInput.read();
- packetLength = (packetLength << 8) + mInput.read();
- if(V) Log.v(TAG,"handleConnectRequest() - packetLength: " + packetLength);
-
- version = mInput.read();
- flags = mInput.read();
- mMaxPacketLength = mInput.read();
- mMaxPacketLength = (mMaxPacketLength << 8) + mInput.read();
-
- if(V) Log.v(TAG,"handleConnectRequest() - version: " + version
- + " MaxLength: " + mMaxPacketLength + " flags: " + flags);
-
- // should we check it?
- if (mMaxPacketLength > ObexHelper.MAX_PACKET_SIZE_INT) {
- mMaxPacketLength = ObexHelper.MAX_PACKET_SIZE_INT;
- }
-
- if(mMaxPacketLength > ObexHelper.getMaxTxPacketSize(mTransport)) {
- Log.w(TAG, "Requested MaxObexPacketSize " + mMaxPacketLength
- + " is larger than the max size supported by the transport: "
- + ObexHelper.getMaxTxPacketSize(mTransport)
- + " Reducing to this size.");
- mMaxPacketLength = ObexHelper.getMaxTxPacketSize(mTransport);
- }
-
- if (packetLength > ObexHelper.getMaxRxPacketSize(mTransport)) {
- code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
- totalLength = 7;
- } else {
- if (packetLength > 7) {
- byte[] headers = new byte[packetLength - 7];
- bytesReceived = mInput.read(headers);
-
- while (bytesReceived != headers.length) {
- bytesReceived += mInput.read(headers, bytesReceived, headers.length
- - bytesReceived);
- }
-
- ObexHelper.updateHeaderSet(request, headers);
- }
-
- if (mListener.getConnectionId() != -1 && request.mConnectionID != null) {
- mListener.setConnectionId(ObexHelper.convertToLong(request.mConnectionID));
- } else {
- mListener.setConnectionId(1);
- }
-
- if (request.mAuthResp != null) {
- if (!handleAuthResp(request.mAuthResp)) {
- code = ResponseCodes.OBEX_HTTP_UNAUTHORIZED;
- mListener.onAuthenticationFailure(ObexHelper.getTagValue((byte)0x01,
- request.mAuthResp));
- }
- request.mAuthResp = null;
- }
-
- if (code != ResponseCodes.OBEX_HTTP_UNAUTHORIZED) {
- if (request.mAuthChall != null) {
- handleAuthChall(request);
- reply.mAuthResp = new byte[request.mAuthResp.length];
- System.arraycopy(request.mAuthResp, 0, reply.mAuthResp, 0,
- reply.mAuthResp.length);
- request.mAuthChall = null;
- request.mAuthResp = null;
- }
-
- try {
- code = mListener.onConnect(request, reply);
- code = validateResponseCode(code);
-
- if (reply.nonce != null) {
- mChallengeDigest = new byte[16];
- System.arraycopy(reply.nonce, 0, mChallengeDigest, 0, 16);
- } else {
- mChallengeDigest = null;
- }
- long id = mListener.getConnectionId();
- if (id == -1) {
- reply.mConnectionID = null;
- } else {
- reply.mConnectionID = ObexHelper.convertToByteArray(id);
- }
-
- head = ObexHelper.createHeader(reply, false);
- totalLength += head.length;
-
- if (totalLength > mMaxPacketLength) {
- totalLength = 7;
- head = null;
- code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
- }
- } catch (Exception e) {
- if(V) Log.d(TAG,"Exception occured - sending OBEX_HTTP_INTERNAL_ERROR reply",e);
- totalLength = 7;
- head = null;
- code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
- }
-
- }
- }
-
- // Compute Length of OBEX CONNECT packet
- byte[] length = ObexHelper.convertToByteArray(totalLength);
-
- /*
- * Write the OBEX CONNECT packet to the server. Byte 0: response code
- * Byte 1&2: Connect Packet Length Byte 3: OBEX Version Number
- * (Presently, 0x10) Byte 4: Flags (For TCP 0x00) Byte 5&6: Max OBEX
- * Packet Length (Defined in MAX_PACKET_SIZE) Byte 7 to n: headers
- */
- byte[] sendData = new byte[totalLength];
- int maxRxLength = ObexHelper.getMaxRxPacketSize(mTransport);
- if (maxRxLength > mMaxPacketLength) {
- if(V) Log.v(TAG,"Set maxRxLength to min of maxRxServrLen:" + maxRxLength +
- " and MaxNegotiated from Client: " + mMaxPacketLength);
- maxRxLength = mMaxPacketLength;
- }
- sendData[0] = (byte)code;
- sendData[1] = length[2];
- sendData[2] = length[3];
- sendData[3] = (byte)0x10;
- sendData[4] = (byte)0x00;
- sendData[5] = (byte)(maxRxLength >> 8);
- sendData[6] = (byte)(maxRxLength & 0xFF);
-
- if (head != null) {
- System.arraycopy(head, 0, sendData, 7, head.length);
- }
-
- mOutput.write(sendData);
- mOutput.flush();
- }
-
- /**
- * Closes the server session - in detail close I/O streams and the
- * underlying transport layer. Internal flag is also set so that later
- * attempt to read/write will throw an exception.
- */
- public synchronized void close() {
- if (mListener != null) {
- mListener.onClose();
- }
- try {
- /* Set state to closed before interrupting the thread by closing the streams */
- mClosed = true;
- if(mInput != null)
- mInput.close();
- if(mOutput != null)
- mOutput.close();
- if(mTransport != null)
- mTransport.close();
- } catch (Exception e) {
- if(V) Log.d(TAG,"Exception occured during close() - ignore",e);
- }
- mTransport = null;
- mInput = null;
- mOutput = null;
- mListener = null;
- }
-
- /**
- * Verifies that the response code is valid. If it is not valid, it will
- * return the <code>OBEX_HTTP_INTERNAL_ERROR</code> response code.
- * @param code the response code to check
- * @return the valid response code or <code>OBEX_HTTP_INTERNAL_ERROR</code>
- * if <code>code</code> is not valid
- */
- private int validateResponseCode(int code) {
-
- if ((code >= ResponseCodes.OBEX_HTTP_OK) && (code <= ResponseCodes.OBEX_HTTP_PARTIAL)) {
- return code;
- }
- if ((code >= ResponseCodes.OBEX_HTTP_MULT_CHOICE)
- && (code <= ResponseCodes.OBEX_HTTP_USE_PROXY)) {
- return code;
- }
- if ((code >= ResponseCodes.OBEX_HTTP_BAD_REQUEST)
- && (code <= ResponseCodes.OBEX_HTTP_UNSUPPORTED_TYPE)) {
- return code;
- }
- if ((code >= ResponseCodes.OBEX_HTTP_INTERNAL_ERROR)
- && (code <= ResponseCodes.OBEX_HTTP_VERSION)) {
- return code;
- }
- if ((code >= ResponseCodes.OBEX_DATABASE_FULL)
- && (code <= ResponseCodes.OBEX_DATABASE_LOCKED)) {
- return code;
- }
- return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
- }
-
- public ObexTransport getTransport() {
- return mTransport;
- }
-}
diff --git a/obex/javax/obex/SessionNotifier.java b/obex/javax/obex/SessionNotifier.java
deleted file mode 100644
index 9836dd6..0000000
--- a/obex/javax/obex/SessionNotifier.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (c) 2008-2009, Motorola, Inc.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * - Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * - Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * - Neither the name of the Motorola, Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-package javax.obex;
-
-import java.io.IOException;
-
-/**
- * The <code>SessionNotifier</code> interface defines a connection notifier for
- * server-side OBEX connections. When a <code>SessionNotifier</code> is created
- * and calls <code>acceptAndOpen()</code>, it will begin listening for clients
- * to create a connection at the transport layer. When the transport layer
- * connection is received, the <code>acceptAndOpen()</code> method will return a
- * <code>javax.microedition.io.Connection</code> that is the connection to the
- * client. The <code>acceptAndOpen()</code> method also takes a
- * <code>ServerRequestHandler</code> argument that will process the requests
- * from the client that connects to the server.
- * @hide
- */
-public interface SessionNotifier {
-
- /**
- * Waits for a transport layer connection to be established and specifies
- * the handler to handle the requests from the client. No authenticator is
- * associated with this connection, therefore, it is implementation
- * dependent as to how an authentication challenge and authentication
- * response header will be received and processed.
- * <P>
- * <H4>Additional Note for OBEX over Bluetooth</H4> If this method is called
- * on a <code>SessionNotifier</code> object that does not have a
- * <code>ServiceRecord</code> in the SDDB, the <code>ServiceRecord</code>
- * for this object will be added to the SDDB. This method requests the BCC
- * to put the local device in connectable mode so that it will respond to
- * connection attempts by clients.
- * <P>
- * The following checks are done to verify that the service record provided
- * is valid. If any of these checks fail, then a
- * <code>ServiceRegistrationException</code> is thrown.
- * <UL>
- * <LI>ServiceClassIDList and ProtocolDescriptorList, the mandatory service
- * attributes for a <code>btgoep</code> service record, must be present in
- * the <code>ServiceRecord</code> associated with this notifier.
- * <LI>L2CAP, RFCOMM and OBEX must all be in the ProtocolDescriptorList
- * <LI>The <code>ServiceRecord</code> associated with this notifier must not
- * have changed the RFCOMM server channel number
- * </UL>
- * <P>
- * This method will not ensure that <code>ServiceRecord</code> associated
- * with this notifier is a completely valid service record. It is the
- * responsibility of the application to ensure that the service record
- * follows all of the applicable syntactic and semantic rules for service
- * record correctness.
- * @param handler the request handler that will respond to OBEX requests
- * @return the connection to the client
- * @throws IOException if an error occurs in the transport layer
- * @throws NullPointerException if <code>handler</code> is <code>null</code>
- */
- ObexSession acceptAndOpen(ServerRequestHandler handler) throws IOException;
-
- /**
- * Waits for a transport layer connection to be established and specifies
- * the handler to handle the requests from the client and the
- * <code>Authenticator</code> to use to respond to authentication challenge
- * and authentication response headers.
- * <P>
- * <H4>Additional Note for OBEX over Bluetooth</H4> If this method is called
- * on a <code>SessionNotifier</code> object that does not have a
- * <code>ServiceRecord</code> in the SDDB, the <code>ServiceRecord</code>
- * for this object will be added to the SDDB. This method requests the BCC
- * to put the local device in connectable mode so that it will respond to
- * connection attempts by clients.
- * <P>
- * The following checks are done to verify that the service record provided
- * is valid. If any of these checks fail, then a
- * <code>ServiceRegistrationException</code> is thrown.
- * <UL>
- * <LI>ServiceClassIDList and ProtocolDescriptorList, the mandatory service
- * attributes for a <code>btgoep</code> service record, must be present in
- * the <code>ServiceRecord</code> associated with this notifier.
- * <LI>L2CAP, RFCOMM and OBEX must all be in the ProtocolDescriptorList
- * <LI>The <code>ServiceRecord</code> associated with this notifier must not
- * have changed the RFCOMM server channel number
- * </UL>
- * <P>
- * This method will not ensure that <code>ServiceRecord</code> associated
- * with this notifier is a completely valid service record. It is the
- * responsibility of the application to ensure that the service record
- * follows all of the applicable syntactic and semantic rules for service
- * record correctness.
- * @param handler the request handler that will respond to OBEX requests
- * @param auth the <code>Authenticator</code> to use with this connection;
- * if <code>null</code> then no <code>Authenticator</code> will be
- * used
- * @return the connection to the client
- * @throws IOException if an error occurs in the transport layer
- * @throws NullPointerException if <code>handler</code> is <code>null</code>
- */
- ObexSession acceptAndOpen(ServerRequestHandler handler, Authenticator auth) throws IOException;
-}
diff --git a/packages/CompanionDeviceManager/res/color/selector.xml b/packages/CompanionDeviceManager/res/color/selector.xml
index fda827d..56e5dca 100644
--- a/packages/CompanionDeviceManager/res/color/selector.xml
+++ b/packages/CompanionDeviceManager/res/color/selector.xml
@@ -16,5 +16,5 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:color="@android:color/darker_gray"/> <!-- pressed -->
- <item android:color="@android:color/white"/>
+ <item android:color="?android:attr/colorBackground"/>
</selector>
\ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/drawable/dialog_background.xml b/packages/CompanionDeviceManager/res/drawable/dialog_background.xml
index ef7052d..a017f41 100644
--- a/packages/CompanionDeviceManager/res/drawable/dialog_background.xml
+++ b/packages/CompanionDeviceManager/res/drawable/dialog_background.xml
@@ -16,7 +16,7 @@
<inset xmlns:android="http://schemas.android.com/apk/res/android">
<shape android:shape="rectangle">
- <corners android:radius="@*android:dimen/config_dialogCornerRadius" />
+ <corners android:radius="?android:attr/dialogCornerRadius" />
<solid android:color="?android:attr/colorBackground" />
</shape>
</inset>
diff --git a/packages/CompanionDeviceManager/res/values/themes.xml b/packages/CompanionDeviceManager/res/values/themes.xml
index 8559ef6..7240432 100644
--- a/packages/CompanionDeviceManager/res/values/themes.xml
+++ b/packages/CompanionDeviceManager/res/values/themes.xml
@@ -17,9 +17,7 @@
<resources>
<style name="ChooserActivity"
- parent="@style/Theme.AppCompat.Light.Dialog">
- <item name="windowActionBar">false</item>
- <item name="windowNoTitle">true</item>
+ parent="@android:style/Theme.DeviceDefault.Light.Dialog">
<item name="*android:windowFixedHeightMajor">100%</item>
<item name="*android:windowFixedHeightMinor">100%</item>
<item name="android:windowBackground">@android:color/transparent</item>
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
index a6a8fcf..ae0c8cc 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
@@ -49,7 +49,7 @@
import android.widget.Button;
import android.widget.TextView;
-import androidx.appcompat.app.AppCompatActivity;
+import androidx.fragment.app.FragmentActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -59,7 +59,7 @@
* A CompanionDevice activity response for showing the available
* nearby devices to be associated with.
*/
-public class CompanionDeviceActivity extends AppCompatActivity {
+public class CompanionDeviceActivity extends FragmentActivity {
private static final boolean DEBUG = false;
private static final String TAG = CompanionDeviceActivity.class.getSimpleName();
diff --git a/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.java b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.java
index e879e40..a626971 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.java
@@ -17,6 +17,7 @@
package android.net;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -27,13 +28,13 @@
@SystemApi
public final class EthernetNetworkUpdateRequest implements Parcelable {
@NonNull
- private final StaticIpConfiguration mIpConfig;
+ private final IpConfiguration mIpConfig;
@NonNull
private final NetworkCapabilities mNetworkCapabilities;
@NonNull
- public StaticIpConfiguration getIpConfig() {
- return new StaticIpConfiguration(mIpConfig);
+ public IpConfiguration getIpConfiguration() {
+ return new IpConfiguration(mIpConfig);
}
@NonNull
@@ -41,20 +42,75 @@
return new NetworkCapabilities(mNetworkCapabilities);
}
- public EthernetNetworkUpdateRequest(@NonNull final StaticIpConfiguration ipConfig,
+ private EthernetNetworkUpdateRequest(@NonNull final IpConfiguration ipConfig,
@NonNull final NetworkCapabilities networkCapabilities) {
Objects.requireNonNull(ipConfig);
Objects.requireNonNull(networkCapabilities);
- mIpConfig = new StaticIpConfiguration(ipConfig);
+ mIpConfig = new IpConfiguration(ipConfig);
mNetworkCapabilities = new NetworkCapabilities(networkCapabilities);
}
private EthernetNetworkUpdateRequest(@NonNull final Parcel source) {
Objects.requireNonNull(source);
- mIpConfig = StaticIpConfiguration.CREATOR.createFromParcel(source);
+ mIpConfig = IpConfiguration.CREATOR.createFromParcel(source);
mNetworkCapabilities = NetworkCapabilities.CREATOR.createFromParcel(source);
}
+ /**
+ * Builder used to create {@link EthernetNetworkUpdateRequest} objects.
+ */
+ public static final class Builder {
+ @Nullable
+ private IpConfiguration mBuilderIpConfig;
+ @Nullable
+ private NetworkCapabilities mBuilderNetworkCapabilities;
+
+ public Builder(){}
+
+ /**
+ * Constructor to populate the builder's values with an already built
+ * {@link EthernetNetworkUpdateRequest}.
+ * @param request the {@link EthernetNetworkUpdateRequest} to populate with.
+ */
+ public Builder(@NonNull final EthernetNetworkUpdateRequest request) {
+ Objects.requireNonNull(request);
+ mBuilderIpConfig = new IpConfiguration(request.mIpConfig);
+ mBuilderNetworkCapabilities = new NetworkCapabilities(request.mNetworkCapabilities);
+ }
+
+ /**
+ * Set the {@link IpConfiguration} to be used with the {@code Builder}.
+ * @param ipConfig the {@link IpConfiguration} to set.
+ * @return The builder to facilitate chaining.
+ */
+ @NonNull
+ public Builder setIpConfiguration(@NonNull final IpConfiguration ipConfig) {
+ Objects.requireNonNull(ipConfig);
+ mBuilderIpConfig = new IpConfiguration(ipConfig);
+ return this;
+ }
+
+ /**
+ * Set the {@link NetworkCapabilities} to be used with the {@code Builder}.
+ * @param nc the {@link NetworkCapabilities} to set.
+ * @return The builder to facilitate chaining.
+ */
+ @NonNull
+ public Builder setNetworkCapabilities(@NonNull final NetworkCapabilities nc) {
+ Objects.requireNonNull(nc);
+ mBuilderNetworkCapabilities = new NetworkCapabilities(nc);
+ return this;
+ }
+
+ /**
+ * Build {@link EthernetNetworkUpdateRequest} return the current update request.
+ */
+ @NonNull
+ public EthernetNetworkUpdateRequest build() {
+ return new EthernetNetworkUpdateRequest(mBuilderIpConfig, mBuilderNetworkCapabilities);
+ }
+ }
+
@Override
public String toString() {
return "EthernetNetworkUpdateRequest{"
@@ -68,7 +124,7 @@
if (o == null || getClass() != o.getClass()) return false;
EthernetNetworkUpdateRequest that = (EthernetNetworkUpdateRequest) o;
- return Objects.equals(that.getIpConfig(), mIpConfig)
+ return Objects.equals(that.getIpConfiguration(), mIpConfig)
&& Objects.equals(that.getNetworkCapabilities(), mNetworkCapabilities);
}
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 014a033..da0381b 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1311,8 +1311,8 @@
<!-- Do not disturb: Label for button in enable zen dialog that will turn on zen mode. [CHAR LIMIT=30] -->
<string name="zen_mode_enable_dialog_turn_on">Turn on</string>
- <!-- Priority mode: Title for the Priority mode dialog to turn on Priority mode. [CHAR LIMIT=50]-->
- <string name="zen_mode_settings_turn_on_dialog_title" translatable="false">Turn on Priority mode</string>
+ <!-- Do not disturb: Title for the Do not Disturb dialog to turn on Do not disturb. [CHAR LIMIT=50]-->
+ <string name="zen_mode_settings_turn_on_dialog_title">Turn on Do Not Disturb</string>
<!-- Sound: Summary for the Do not Disturb option when there is no automatic rules turned on. [CHAR LIMIT=NONE]-->
<string name="zen_mode_settings_summary_off">Never</string>
<!--[CHAR LIMIT=40] Zen Interruption level: Priority. -->
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 8e35ee96..c7673aa 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -193,8 +193,10 @@
Settings.Secure.NOTIFICATION_BUBBLES,
Settings.Secure.LOCATION_TIME_ZONE_DETECTION_ENABLED,
Settings.Secure.LOCKSCREEN_SHOW_CONTROLS,
+ Settings.Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS,
Settings.Secure.LOCKSCREEN_SHOW_WALLET,
Settings.Secure.LOCK_SCREEN_SHOW_QR_CODE_SCANNER,
Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK,
+ Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON
};
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 5f549fd..42aa205 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -152,9 +152,11 @@
VALIDATORS.put(Secure.CONTROLS_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.POWER_MENU_LOCKED_SHOW_CONTENT, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.LOCKSCREEN_SHOW_CONTROLS, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.LOCKSCREEN_SHOW_WALLET, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.LOCK_SCREEN_SHOW_QR_CODE_SCANNER, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.STATUS_BAR_SHOW_VIBRATE_ICON, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.DOZE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.DOZE_ALWAYS_ON, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.DOZE_PICK_UP_GESTURE, BOOLEAN_VALIDATOR);
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 4b1d00b..7f8b2f5 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -154,7 +154,7 @@
<!-- Needed for WallpaperManager.clear in ImageWallpaper.updateWallpaperLocked -->
<uses-permission android:name="android.permission.SET_WALLPAPER"/>
- <!-- Needed for WallpaperManager.getWallpaperDimAmount in StatusBar.updateTheme -->
+ <!-- Needed for WallpaperManager.getWallpaperDimAmount in CentralSurfaces.updateTheme -->
<uses-permission android:name="android.permission.SET_WALLPAPER_DIM_AMOUNT"/>
<!-- Wifi Display -->
diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags
index 6352f81..c97ebe8 100644
--- a/packages/SystemUI/proguard.flags
+++ b/packages/SystemUI/proguard.flags
@@ -3,7 +3,7 @@
-keep class com.android.systemui.recents.OverviewProxyRecentsImpl
-keep class com.android.systemui.statusbar.car.CarStatusBar
--keep class com.android.systemui.statusbar.phone.StatusBar
+-keep class com.android.systemui.statusbar.phone.CentralSurfaces
-keep class com.android.systemui.statusbar.tv.TvStatusBar
-keep class com.android.systemui.car.CarSystemUIFactory
-keep class com.android.systemui.SystemUIFactory
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
index 31d848d..16a1d94 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_status_view.xml
@@ -26,6 +26,7 @@
systemui:layout_constraintStart_toStartOf="parent"
systemui:layout_constraintEnd_toEndOf="parent"
systemui:layout_constraintTop_toTopOf="parent"
+ android:layout_marginHorizontal="@dimen/status_view_margin_horizontal"
android:layout_width="0dp"
android:layout_height="wrap_content">
<LinearLayout
diff --git a/packages/SystemUI/res/drawable-mdpi/dream_preview_back_arrow.png b/packages/SystemUI/res/drawable-mdpi/dream_preview_back_arrow.png
new file mode 100644
index 0000000..2c2f94e
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/dream_preview_back_arrow.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/dream_preview_back_arrow.png b/packages/SystemUI/res/drawable-xhdpi/dream_preview_back_arrow.png
new file mode 100644
index 0000000..881b9af
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/dream_preview_back_arrow.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/dream_preview_back_arrow.png b/packages/SystemUI/res/drawable-xxhdpi/dream_preview_back_arrow.png
new file mode 100644
index 0000000..6063b42
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/dream_preview_back_arrow.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/notif_dungeon_bg_gradient.xml b/packages/SystemUI/res/drawable/notif_dungeon_bg_gradient.xml
deleted file mode 100644
index e456e29..0000000
--- a/packages/SystemUI/res/drawable/notif_dungeon_bg_gradient.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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.
- -->
-<shape
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
- <gradient
- android:angle="90"
- android:startColor="#ff000000"
- android:endColor="#00000000"
- android:type="linear" />
-</shape>
diff --git a/packages/SystemUI/res/layout/dream_overlay_complication_preview.xml b/packages/SystemUI/res/layout/dream_overlay_complication_preview.xml
index 37b8365..ca5c499 100644
--- a/packages/SystemUI/res/layout/dream_overlay_complication_preview.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_complication_preview.xml
@@ -24,6 +24,5 @@
android:shadowColor="@color/keyguard_shadow_color"
android:shadowRadius="?attr/shadowRadius"
android:gravity="center_vertical"
- android:drawableStart="@drawable/ic_arrow_back"
- android:drawablePadding="@dimen/dream_overlay_complication_preview_icon_padding"
- android:drawableTint="@android:color/white"/>
+ android:drawableStart="@drawable/dream_preview_back_arrow"
+ android:drawablePadding="@dimen/dream_overlay_complication_preview_icon_padding"/>
diff --git a/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml b/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
index 813787e..1cbc3c2 100644
--- a/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
@@ -29,7 +29,6 @@
android:layout_width="@dimen/dream_overlay_notification_indicator_size"
android:layout_height="@dimen/dream_overlay_notification_indicator_size"
android:visibility="gone"
- android:contentDescription="@string/dream_overlay_status_bar_notification_indicator"
app:dotColor="@android:color/white"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
@@ -68,7 +67,7 @@
android:layout_width="@dimen/dream_overlay_status_bar_icon_size"
android:layout_height="match_parent"
android:layout_marginEnd="@dimen/dream_overlay_status_icon_margin"
- android:src="@drawable/ic_remove_circle"
+ android:src="@drawable/ic_qs_dnd_on"
android:tint="@android:color/white"
android:visibility="gone"
android:contentDescription="@string/dream_overlay_status_bar_priority_mode" />
diff --git a/packages/SystemUI/res/layout/foreground_service_dungeon.xml b/packages/SystemUI/res/layout/foreground_service_dungeon.xml
deleted file mode 100644
index d4e98e2..0000000
--- a/packages/SystemUI/res/layout/foreground_service_dungeon.xml
+++ /dev/null
@@ -1,61 +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.
- -->
-
-<com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/foreground_service_dungeon"
- android:layout_width="@dimen/qs_panel_width"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal|bottom"
- android:visibility="visible"
->
- <LinearLayout
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- android:orientation="vertical"
- android:gravity="bottom"
- android:visibility="visible"
- android:background="@drawable/notif_dungeon_bg_gradient"
- >
-
- <!-- divider view -->
- <View
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:background="@color/GM2_grey_200"
- android:visibility="visible"
- />
-
- <TextView
- android:id="@+id/dungeon_title"
- android:layout_height="48dp"
- android:layout_width="match_parent"
- android:padding="8dp"
- android:text="Apps active in background"
- android:textColor="@color/GM2_grey_200"
- />
-
- <!-- List containing the actual foreground service notifications -->
- <LinearLayout
- android:id="@+id/entry_list"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="bottom"
- android:orientation="vertical" >
- </LinearLayout>
-
- </LinearLayout>
-</com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonView>
diff --git a/packages/SystemUI/res/layout/foreground_service_dungeon_row.xml b/packages/SystemUI/res/layout/foreground_service_dungeon_row.xml
deleted file mode 100644
index a6f1638..0000000
--- a/packages/SystemUI/res/layout/foreground_service_dungeon_row.xml
+++ /dev/null
@@ -1,43 +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.
- -->
-
-<com.android.systemui.statusbar.notification.row.DungeonRow
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/foreground_service_dungeon_row"
- android:layout_width="match_parent"
- android:layout_height="48dp"
- android:padding="8dp"
- android:clickable="true"
- android:orientation="horizontal" >
-
- <com.android.systemui.statusbar.StatusBarIconView
- android:id="@+id/icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:padding="4dp" />
-
- <TextView
- android:id="@+id/app_name"
- android:layout_width="0dp"
- android:layout_weight="1"
- android:layout_height="wrap_content"
- android:paddingStart="4dp"
- android:gravity="center_vertical"
- android:layout_gravity="center_vertical"
- android:textColor="@color/GM2_grey_200"
- />
-
-</com.android.systemui.statusbar.notification.row.DungeonRow>
diff --git a/packages/SystemUI/res/layout/media_projection_dialog_title.xml b/packages/SystemUI/res/layout/media_projection_dialog_title.xml
deleted file mode 100644
index b9e39da..0000000
--- a/packages/SystemUI/res/layout/media_projection_dialog_title.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright 2019, 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.
--->
-
-<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:theme="@style/Theme.SystemUI.MediaProjectionAlertDialog"
- android:paddingStart="?android:attr/dialogPreferredPadding"
- android:paddingEnd="?android:attr/dialogPreferredPadding"
- android:orientation="vertical">
- <ImageView
- android:id="@+id/dialog_icon"
- android:src="@drawable/ic_media_projection_permission"
- android:layout_height="24dp"
- android:layout_width="24dp"
- android:layout_marginTop="18dp"
- android:layout_marginBottom="12dp"
- android:layout_gravity="center_horizontal" />
- <TextView
- android:id="@+id/dialog_title"
- android:gravity="center"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="20sp"
- android:textColor="?android:attr/textColorPrimary"
- android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Title" />
-</LinearLayout>
diff --git a/packages/SystemUI/res/layout/notif_half_shelf.xml b/packages/SystemUI/res/layout/notif_half_shelf.xml
index b2ff46e..8ba1ff3 100644
--- a/packages/SystemUI/res/layout/notif_half_shelf.xml
+++ b/packages/SystemUI/res/layout/notif_half_shelf.xml
@@ -27,7 +27,7 @@
<LinearLayout
android:id="@+id/half_shelf"
- android:layout_width="@dimen/qs_panel_width"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="bottom"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index c7910afc..e4706e2 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -94,10 +94,11 @@
<FrameLayout
android:id="@+id/qs_frame"
android:layout="@layout/qs_panel"
- android:layout_width="@dimen/qs_panel_width"
+ android:layout_width="0dp"
android:layout_height="0dp"
android:clipToPadding="false"
android:clipChildren="false"
+ android:layout_marginHorizontal="@dimen/notification_panel_margin_horizontal"
systemui:viewType="com.android.systemui.plugins.qs.QS"
systemui:layout_constraintStart_toStartOf="parent"
systemui:layout_constraintEnd_toEndOf="parent"
@@ -115,8 +116,9 @@
<com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
android:id="@+id/notification_stack_scroller"
android:layout_marginTop="@dimen/notification_panel_margin_top"
- android:layout_width="@dimen/notification_panel_width"
+ android:layout_width="0dp"
android:layout_height="match_parent"
+ android:layout_marginHorizontal="@dimen/notification_panel_margin_horizontal"
android:layout_marginBottom="@dimen/notification_panel_margin_bottom"
android:importantForAccessibility="no"
systemui:layout_constraintStart_toStartOf="parent"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded_plugin_frame.xml b/packages/SystemUI/res/layout/status_bar_expanded_plugin_frame.xml
index 65f55d0..8a2310b 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded_plugin_frame.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded_plugin_frame.xml
@@ -19,7 +19,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/plugin_frame"
android:theme="@style/Theme.SystemUI.QuickSettings"
- android:layout_width="@dimen/qs_panel_width"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="@dimen/notification_side_paddings"
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
index c37c804..4f95811b 100644
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -42,4 +42,9 @@
the shade (in alpha) -->
<dimen name="lockscreen_shade_scrim_transition_distance">200dp</dimen>
+ <!-- Distance that the full shade transition takes in order for media to fully transition to
+ the shade -->
+ <dimen name="lockscreen_shade_media_transition_distance">200dp</dimen>
+
+ <dimen name="notification_panel_margin_horizontal">12dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp-port/dimens.xml b/packages/SystemUI/res/values-sw600dp-port/dimens.xml
index da2403a..56dc4a1 100644
--- a/packages/SystemUI/res/values-sw600dp-port/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-port/dimens.xml
@@ -15,7 +15,8 @@
~ limitations under the License.
-->
<resources>
- <!-- Size of the panel of large phones on portrait. This shouldn't fill, but have some padding on the side -->
- <dimen name="notification_panel_width">504dp</dimen>
-
+ <dimen name="notification_panel_margin_horizontal">60dp</dimen>
+ <dimen name="status_view_margin_horizontal">62dp</dimen>
+ <dimen name="keyguard_clock_top_margin">40dp</dimen>
+ <dimen name="keyguard_status_view_bottom_margin">40dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-sw720dp-land/dimens.xml b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
index e897f75..71c1958 100644
--- a/packages/SystemUI/res/values-sw720dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
@@ -22,4 +22,6 @@
<dimen name="notification_panel_margin_bottom">56dp</dimen>
<dimen name="keyguard_split_shade_top_margin">72dp</dimen>
+
+ <dimen name="notification_panel_margin_horizontal">24dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-sw720dp-port/dimens.xml b/packages/SystemUI/res/values-sw720dp-port/dimens.xml
new file mode 100644
index 0000000..594df34
--- /dev/null
+++ b/packages/SystemUI/res/values-sw720dp-port/dimens.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2022, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. -->
+<resources>
+ <dimen name="status_view_margin_horizontal">124dp</dimen>
+ <dimen name="notification_panel_margin_horizontal">120dp</dimen>
+ <dimen name="keyguard_clock_top_margin">80dp</dimen>
+ <dimen name="keyguard_status_view_bottom_margin">80dp</dimen>
+</resources>
diff --git a/packages/SystemUI/res/values-w650dp-land/dimens.xml b/packages/SystemUI/res/values-w650dp-land/dimens.xml
deleted file mode 100644
index 97b6da1..0000000
--- a/packages/SystemUI/res/values-w650dp-land/dimens.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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.
- -->
-<resources>
- <!-- Standard notification width + gravity -->
- <dimen name="notification_panel_width">-1px</dimen> <!-- match_parent -->
-</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 2de8324..83e6a54 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -74,12 +74,12 @@
<!-- The default tiles to display in QuickSettings -->
<string name="quick_settings_tiles_default" translatable="false">
- internet,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,custom(com.android.permissioncontroller/.permission.service.SafetyHubQsTileService)
+ internet,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,custom(com.android.permissioncontroller/.permission.service.SafetyCenterQsTileService)
</string>
<!-- The component name of the Safety Quick Settings Tile -->
<string name="safety_quick_settings_tile" translatable="false">
- custom(com.android.permissioncontroller/.permission.service.SafetyHubQsTileService)
+ custom(com.android.permissioncontroller/.permission.service.SafetyCenterQsTileService)
</string>
<!-- The minimum number of tiles to display in QuickSettings -->
@@ -487,6 +487,9 @@
space -->
<bool name="config_showBatteryEstimateQSBH">false</bool>
+ <!-- Whether to show a severe low battery dialog. -->
+ <bool name="config_severe_battery_dialog">false</bool>
+
<!-- A path similar to frameworks/base/core/res/res/values/config.xml
config_mainBuiltInDisplayCutout that describes a path larger than the exact path of a display
cutout. If present as well as config_enableDisplayCutoutProtection is set to true, then
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 92bc864..65c17b9 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -388,13 +388,10 @@
<dimen name="split_shade_notifications_scrim_margin_bottom">0dp</dimen>
- <dimen name="notification_panel_width">@dimen/match_parent</dimen>
+ <dimen name="notification_panel_margin_horizontal">0dp</dimen>
<dimen name="brightness_mirror_height">48dp</dimen>
- <!-- The width of the panel that holds the quick settings. -->
- <dimen name="qs_panel_width">@dimen/notification_panel_width</dimen>
-
<dimen name="volume_dialog_panel_transparent_padding_right">8dp</dimen>
<dimen name="volume_dialog_panel_transparent_padding">20dp</dimen>
@@ -1391,4 +1388,6 @@
<!-- The margin applied between complications -->
<dimen name="dream_overlay_complication_margin">0dp</dimen>
+
+ <dimen name="status_view_margin_horizontal">0dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index df16b0d..6d336e6 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -21,7 +21,13 @@
<string name="app_label">System UI</string>
<!-- When the battery is low, this is displayed to the user in a dialog. The title of the low battery alert. [CHAR LIMIT=NONE]-->
- <string name="battery_low_title">Battery may run out soon</string>
+ <string name="battery_low_title">Turn on Battery Saver?</string>
+
+ <!-- When the battery is low, this is displayed to the user in a dialog. The description of the low battery alert. [CHAR LIMIT=NONE]-->
+ <string name="battery_low_description">You have <xliff:g id="percentage" example="20%">%s</xliff:g> battery left. Battery Saver turns on Dark theme, restricts background activity, and delays notifications.</string>
+
+ <!-- When the battery is low at first time, this is displayed to the user in a dialog. The description of the low battery alert. [CHAR LIMIT=NONE]-->
+ <string name="battery_low_intro">Battery Saver turns on Dark theme, restricts background activity, and delays notifications.</string>
<!-- A message that appears when the battery level is getting low in a dialog. This is
appended to the subtitle of the low battery alert. "percentage" is the percentage of battery
@@ -441,8 +447,8 @@
<string name="accessibility_quick_settings_dnd_none_on">total silence</string>
<!-- Content description of the do not disturb tile in quick settings when on in alarms only (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_dnd_alarms_on">alarms only</string>
- <!-- Content description of the priority mode tile in quick settings (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_quick_settings_dnd" translatable="false">Priority mode.</string>
+ <!-- Content description of the do not disturb tile in quick settings (not shown on the screen). [CHAR LIMIT=NONE] -->
+ <string name="accessibility_quick_settings_dnd">Do Not Disturb.</string>
<!-- Content description of the bluetooth tile in quick settings (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_quick_settings_bluetooth">Bluetooth.</string>
<!-- Content description of the bluetooth tile in quick settings when on (not shown on the screen). [CHAR LIMIT=NONE] -->
@@ -520,8 +526,8 @@
<string name="ethernet_label">Ethernet</string>
<!-- QuickSettings: Onboarding text that introduces users to long press on an option in order to view the option's menu in Settings [CHAR LIMIT=NONE] -->
- <!-- QuickSettings: Priority mode [CHAR LIMIT=NONE] -->
- <string name="quick_settings_dnd_label" translatable="false">Priority mode</string>
+ <!-- QuickSettings: Do not disturb [CHAR LIMIT=NONE] -->
+ <string name="quick_settings_dnd_label">Do Not Disturb</string>
<!-- QuickSettings: Do not disturb - Priority only [CHAR LIMIT=NONE] -->
<!-- QuickSettings: Do not disturb - Alarms only [CHAR LIMIT=NONE] -->
<!-- QuickSettings: Do not disturb - Total silence [CHAR LIMIT=NONE] -->
@@ -909,8 +915,8 @@
<!-- Content description for accessibility: Tapping this button will dismiss all gentle notifications [CHAR LIMIT=NONE] -->
<string name="accessibility_notification_section_header_gentle_clear_all">Clear all silent notifications</string>
- <!-- The text to show in the notifications shade when Priority mode is suppressing notifications. [CHAR LIMIT=100] -->
- <string name="dnd_suppressing_shade_text" translatable="false">Notifications paused by Priority mode</string>
+ <!-- The text to show in the notifications shade when dnd is suppressing notifications. [CHAR LIMIT=100] -->
+ <string name="dnd_suppressing_shade_text">Notifications paused by Do Not Disturb</string>
<!-- Media projection permission dialog action text. [CHAR LIMIT=60] -->
<string name="media_projection_action_text">Start now</string>
@@ -1329,8 +1335,8 @@
<!-- [CHAR LIMIT=150] Notification Importance title: important conversation level summary -->
<string name="notification_channel_summary_priority_baseline">Shows at the top of conversation notifications and as a profile picture on lock screen</string>
<string name="notification_channel_summary_priority_bubble">Shows at the top of conversation notifications and as a profile picture on lock screen, appears as a bubble</string>
- <string name="notification_channel_summary_priority_dnd" translatable="false">Shows at the top of conversation notifications and as a profile picture on lock screen, interrupts Priority mode</string>
- <string name="notification_channel_summary_priority_all" translatable="false">Shows at the top of conversation notifications and as a profile picture on lock screen, appears as a bubble, interrupts Priority mode</string>
+ <string name="notification_channel_summary_priority_dnd">Shows at the top of conversation notifications and as a profile picture on lock screen, interrupts Do Not Disturb</string>
+ <string name="notification_channel_summary_priority_all">Shows at the top of conversation notifications and as a profile picture on lock screen, appears as a bubble, interrupts Do Not Disturb</string>
<!-- [CHAR LIMIT=150] Notification Importance title: important conversation level -->
<string name="notification_priority_title">Priority</string>
@@ -1521,8 +1527,8 @@
<!-- User visible title for the keyboard shortcut that takes the user to the calendar app. -->
<string name="keyboard_shortcut_group_applications_calendar">Calendar</string>
- <!-- SysUI Tuner: Label for screen about priority mode settings [CHAR LIMIT=60] -->
- <string name="volume_and_do_not_disturb" translatable="false">Priority mode</string>
+ <!-- SysUI Tuner: Label for screen about do not disturb settings [CHAR LIMIT=60] -->
+ <string name="volume_and_do_not_disturb">Do Not Disturb</string>
<!-- SysUI Tuner: Switch to control whether volume buttons enter/exit do
not disturb [CHAR LIMIT=60] -->
@@ -1878,17 +1884,17 @@
<!-- Label for when bluetooth is off in QS detail panel [CHAR LIMIT=NONE] -->
<string name="bt_is_off">Bluetooth is off</string>
- <!-- Label for when Priority mode is off in QS detail panel [CHAR LIMIT=NONE] -->
- <string name="dnd_is_off" translatable="false">Priority mode is off</string>
+ <!-- Label for when Do not disturb is off in QS detail panel [CHAR LIMIT=NONE] -->
+ <string name="dnd_is_off">Do Not Disturb is off</string>
- <!-- Prompt for when Priority mode is on from automatic rule in QS [CHAR LIMIT=NONE] -->
- <string name="qs_dnd_prompt_auto_rule" translatable="false">Priority mode was turned on by an automatic rule (<xliff:g name="rule">%s</xliff:g>).</string>
+ <!-- Prompt for when Do not disturb is on from automatic rule in QS [CHAR LIMIT=NONE] -->
+ <string name="qs_dnd_prompt_auto_rule">Do Not Disturb was turned on by an automatic rule (<xliff:g name="rule">%s</xliff:g>).</string>
- <!-- Prompt for when Priority mode is on from app in QS [CHAR LIMIT=NONE] -->
- <string name="qs_dnd_prompt_app" translatable="false">Priority mode was turned on by an app (<xliff:g name="app">%s</xliff:g>).</string>
+ <!-- Prompt for when Do not disturb is on from app in QS [CHAR LIMIT=NONE] -->
+ <string name="qs_dnd_prompt_app">Do Not Disturb was turned on by an app (<xliff:g name="app">%s</xliff:g>).</string>
- <!-- Prompt for when Priority mode is on from automatic rule or app in QS [CHAR LIMIT=NONE] -->
- <string name="qs_dnd_prompt_auto_rule_app" translatable="false">Priority mode was turned on by an automatic rule or app.</string>
+ <!-- Prompt for when Do not disturb is on from automatic rule or app in QS [CHAR LIMIT=NONE] -->
+ <string name="qs_dnd_prompt_auto_rule_app">Do Not Disturb was turned on by an automatic rule or app.</string>
<!-- Title of the "running foreground services" dialog. [CHAR LIMIT=NONE] -->
<string name="running_foreground_services_title">Apps running in background</string>
@@ -2284,8 +2290,8 @@
<string name="people_tile_description">See recent messages, missed calls, and status updates</string>
<!-- Title text displayed for the Conversation widget [CHAR LIMIT=50] -->
<string name="people_tile_title">Conversation</string>
- <!-- Text when the Conversation widget when Priority mode is suppressing the notification. [CHAR LIMIT=50] -->
- <string name="paused_by_dnd" translatable="false">Paused by Priority mode</string>
+ <!-- Text when the Conversation widget when Do Not Disturb is suppressing the notification. [CHAR LIMIT=50] -->
+ <string name="paused_by_dnd">Paused by Do Not Disturb</string>
<!-- Content description text on the Conversation widget when a person has sent a new text message [CHAR LIMIT=150] -->
<string name="new_notification_text_content_description"><xliff:g id="name" example="Anna">%1$s</xliff:g> sent a message: <xliff:g id="notification" example="Hey! How is your day going">%2$s</xliff:g></string>
<!-- Content description text on the Conversation widget when a person has sent a new image message [CHAR LIMIT=150] -->
@@ -2421,6 +2427,9 @@
<string name="dream_overlay_status_bar_assistant_guest_mode_enabled">Assistant guest mode enabled</string>
<!-- Content description for the camera and mic off icon in the dream overlay status bar [CHAR LIMIT=NONE] -->
<string name="dream_overlay_status_bar_camera_mic_off">Camera and mic are off</string>
- <!-- Content description for the camera and mic off icon in the dream overlay status bar [CHAR LIMIT=NONE] -->
- <string name="dream_overlay_status_bar_notification_indicator">There are notifications</string>
+ <!-- Content description for the notifications indicator icon in the dream overlay status bar [CHAR LIMIT=NONE] -->
+ <string name="dream_overlay_status_bar_notification_indicator">{count, plural,
+ =1 {# notification}
+ other {# notifications}
+ }</string>
</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java
index bf23a49..ba0a6d1 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/InputConsumerController.java
@@ -17,7 +17,6 @@
package com.android.systemui.shared.system;
import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.INPUT_CONSUMER_PIP;
import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
import android.os.Binder;
@@ -137,14 +136,6 @@
* Registers the input consumer.
*/
public void registerInputConsumer() {
- registerInputConsumer(false);
- }
-
- /**
- * Registers the input consumer.
- * @param withSfVsync the flag set using sf vsync signal or no
- */
- public void registerInputConsumer(boolean withSfVsync) {
if (mInputEventReceiver == null) {
final InputChannel inputChannel = new InputChannel();
try {
@@ -154,7 +145,7 @@
Log.e(TAG, "Failed to create input consumer", e);
}
mInputEventReceiver = new InputEventReceiver(inputChannel, Looper.myLooper(),
- withSfVsync ? Choreographer.getSfInstance() : Choreographer.getInstance());
+ Choreographer.getInstance());
if (mRegistrationListener != null) {
mRegistrationListener.onRegistrationChanged(true /* isRegistered */);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index a72a050e..31f466f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -16,8 +16,6 @@
package com.android.keyguard;
-import android.app.ActivityManager;
-import android.app.IActivityManager;
import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
@@ -27,7 +25,6 @@
import androidx.core.graphics.ColorUtils;
-import com.android.internal.widget.LockPatternUtils;
import com.android.systemui.R;
import com.android.systemui.statusbar.CrossFadeHelper;
@@ -44,9 +41,6 @@
private static final boolean DEBUG = KeyguardConstants.DEBUG;
private static final String TAG = "KeyguardStatusView";
- private final LockPatternUtils mLockPatternUtils;
- private final IActivityManager mIActivityManager;
-
private ViewGroup mStatusViewContainer;
private KeyguardClockSwitch mClockView;
private KeyguardSliceView mKeyguardSlice;
@@ -56,14 +50,6 @@
private int mTextColor;
private float mChildrenAlphaExcludingSmartSpace = 1f;
- /**
- * Bottom margin that defines the margin between bottom of smart space and top of notification
- * icons on AOD.
- */
- private int mIconTopMargin;
- private int mIconTopMarginWithHeader;
- private boolean mShowingHeader;
-
public KeyguardStatusView(Context context) {
this(context, null, 0);
}
@@ -74,8 +60,6 @@
public KeyguardStatusView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
- mIActivityManager = ActivityManager.getService();
- mLockPatternUtils = new LockPatternUtils(getContext());
}
@Override
@@ -91,25 +75,11 @@
mKeyguardSlice = findViewById(R.id.keyguard_slice_view);
mTextColor = mClockView.getCurrentTextColor();
- mKeyguardSlice.setContentChangeListener(this::onSliceContentChanged);
- onSliceContentChanged();
-
mMediaHostContainer = findViewById(R.id.status_view_media_container);
updateDark();
}
- /**
- * Moves clock, adjusting margins when slice content changes.
- */
- private void onSliceContentChanged() {
- final boolean hasHeader = mKeyguardSlice.hasHeader();
- if (mShowingHeader == hasHeader) {
- return;
- }
- mShowingHeader = hasHeader;
- }
-
void setDarkAmount(float darkAmount) {
if (mDarkAmount == darkAmount) {
return;
@@ -158,10 +128,4 @@
mKeyguardSlice.dump(fd, pw, args);
}
}
-
- private void loadBottomMargin() {
- mIconTopMargin = getResources().getDimensionPixelSize(R.dimen.widget_vertical_padding);
- mIconTopMarginWithHeader = getResources().getDimensionPixelSize(
- R.dimen.widget_vertical_padding_with_header);
- }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
index 122f3d7..295d77d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
@@ -24,9 +24,9 @@
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
/**
@@ -176,9 +176,9 @@
// achieving complete abstraction away from where the Keyguard View is mounted.
/**
- * Registers the StatusBar to which this Keyguard View is mounted.
+ * Registers the CentralSurfaces to which this Keyguard View is mounted.
*/
- void registerStatusBar(StatusBar statusBar,
+ void registerCentralSurfaces(CentralSurfaces centralSurfaces,
NotificationPanelViewController notificationPanelViewController,
@Nullable PanelExpansionStateManager panelExpansionStateManager,
BiometricUnlockController biometricUnlockController,
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 4ad5183..370686a 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -60,7 +60,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.ViewController;
@@ -78,7 +78,7 @@
* For devices with UDFPS, the lock icon will show at the sensor location. Else, the lock
* icon will show a set distance from the bottom of the device.
*/
-@StatusBarComponent.StatusBarScope
+@CentralSurfacesComponent.CentralSurfacesScope
public class LockIconViewController extends ViewController<LockIconView> implements Dumpable {
private static final String TAG = "LockIconViewController";
private static final float sDefaultDensity =
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerScope.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerScope.java
index 207ac28..8dbe5e0 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerScope.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerScope.java
@@ -24,7 +24,7 @@
import javax.inject.Scope;
/**
- * Scope annotation for singleton items within the StatusBarComponent.
+ * Scope annotation for singleton items within the CentralSurfacesComponent.
*/
@Documented
@Retention(RUNTIME)
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewScope.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewScope.java
index ba0642f..f498ef3 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewScope.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewScope.java
@@ -24,7 +24,7 @@
import javax.inject.Scope;
/**
- * Scope annotation for singleton items within the StatusBarComponent.
+ * Scope annotation for singleton items within the CentralSurfacesComponent.
*/
@Documented
@Retention(RUNTIME)
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewScope.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewScope.java
index 880822a..aeae8e3 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewScope.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewScope.java
@@ -24,7 +24,7 @@
import javax.inject.Scope;
/**
- * Scope annotation for singleton items within the StatusBarComponent.
+ * Scope annotation for singleton items within the CentralSurfacesComponent.
*/
@Documented
@Retention(RUNTIME)
diff --git a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
index cc166c2..5bd620e 100644
--- a/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/ActivityStarterDelegate.java
@@ -23,7 +23,7 @@
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.ActivityStarter;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import java.util.Optional;
@@ -33,17 +33,17 @@
/**
* Single common instance of ActivityStarter that can be gotten and referenced from anywhere, but
- * delegates to an actual implementation (StatusBar).
+ * delegates to an actual implementation (CentralSurfaces).
*/
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
@SysUISingleton
public class ActivityStarterDelegate implements ActivityStarter {
- private Lazy<Optional<StatusBar>> mActualStarterOptionalLazy;
+ private Lazy<Optional<CentralSurfaces>> mActualStarterOptionalLazy;
@Inject
- public ActivityStarterDelegate(Lazy<Optional<StatusBar>> statusBarOptionalLazy) {
- mActualStarterOptionalLazy = statusBarOptionalLazy;
+ public ActivityStarterDelegate(Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy) {
+ mActualStarterOptionalLazy = centralSurfacesOptionalLazy;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
index 881e6a9..bd8e44c 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
@@ -54,7 +54,7 @@
import com.android.systemui.recents.Recents;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
import com.android.systemui.util.Assert;
@@ -180,7 +180,7 @@
private final Optional<Recents> mRecentsOptional;
private Locale mLocale;
private final AccessibilityManager mA11yManager;
- private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
+ private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
private final NotificationShadeWindowController mNotificationShadeController;
private final StatusBarWindowCallback mNotificationShadeCallback;
private boolean mDismissNotificationShadeActionRegistered;
@@ -188,7 +188,7 @@
@Inject
public SystemActions(Context context,
NotificationShadeWindowController notificationShadeController,
- Lazy<Optional<StatusBar>> statusBarOptionalLazy,
+ Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
Optional<Recents> recentsOptional) {
super(context);
mRecentsOptional = recentsOptional;
@@ -201,7 +201,7 @@
// NotificationShadeWindowController.registerCallback() only keeps weak references.
mNotificationShadeCallback = (keyguardShowing, keyguardOccluded, bouncerShowing, mDozing) ->
registerOrUnregisterDismissNotificationShadeAction();
- mStatusBarOptionalLazy = statusBarOptionalLazy;
+ mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
}
@Override
@@ -311,9 +311,10 @@
// Saving state in instance variable since this callback is called quite often to avoid
// binder calls
- final Optional<StatusBar> statusBarOptional = mStatusBarOptionalLazy.get();
- if (statusBarOptional.map(StatusBar::isPanelExpanded).orElse(false)
- && !statusBarOptional.get().isKeyguardShowing()) {
+ final Optional<CentralSurfaces> centralSurfacesOptional =
+ mCentralSurfacesOptionalLazy.get();
+ if (centralSurfacesOptional.map(CentralSurfaces::isPanelExpanded).orElse(false)
+ && !centralSurfacesOptional.get().isKeyguardShowing()) {
if (!mDismissNotificationShadeActionRegistered) {
mA11yManager.registerSystemAction(
createRemoteAction(
@@ -466,12 +467,12 @@
}
private void handleNotifications() {
- mStatusBarOptionalLazy.get().ifPresent(StatusBar::animateExpandNotificationsPanel);
+ mCentralSurfacesOptionalLazy.get().ifPresent(CentralSurfaces::animateExpandNotificationsPanel);
}
private void handleQuickSettings() {
- mStatusBarOptionalLazy.get().ifPresent(
- statusBar -> statusBar.animateExpandSettingsPanel(null));
+ mCentralSurfacesOptionalLazy.get().ifPresent(
+ centralSurfaces -> centralSurfaces.animateExpandSettingsPanel(null));
}
private void handlePowerDialog() {
@@ -524,8 +525,8 @@
}
private void handleAccessibilityDismissNotificationShade() {
- mStatusBarOptionalLazy.get().ifPresent(
- statusBar -> statusBar.animateCollapsePanels(
+ mCentralSurfacesOptionalLazy.get().ifPresent(
+ centralSurfaces -> centralSurfaces.animateCollapsePanels(
CommandQueue.FLAG_EXCLUDE_NONE, false /* force */));
}
diff --git a/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java b/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
index aedaf96..dfff00b 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
@@ -36,7 +36,7 @@
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import java.util.ArrayList;
import java.util.List;
@@ -69,7 +69,7 @@
};
private final Context mContext;
- private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
+ private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
private final StatusBarStateController mStatusBarStateController;
private boolean mLauncherShowing;
@@ -77,10 +77,11 @@
@Inject
PhoneStateMonitor(Context context, BroadcastDispatcher broadcastDispatcher,
- Lazy<Optional<StatusBar>> statusBarOptionalLazy, BootCompleteCache bootCompleteCache,
+ Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
+ BootCompleteCache bootCompleteCache,
StatusBarStateController statusBarStateController) {
mContext = context;
- mStatusBarOptionalLazy = statusBarOptionalLazy;
+ mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
mStatusBarStateController = statusBarStateController;
mDefaultHome = getCurrentDefaultHome();
@@ -180,7 +181,8 @@
}
private boolean isBouncerShowing() {
- return mStatusBarOptionalLazy.get().map(StatusBar::isBouncerShowing).orElse(false);
+ return mCentralSurfacesOptionalLazy.get()
+ .map(CentralSurfaces::isBouncerShowing).orElse(false);
}
private boolean isKeyguardLocked() {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index f82ea79..99f27d7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -40,8 +40,8 @@
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.KeyguardBypassController
-import com.android.systemui.statusbar.phone.StatusBar
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent.StatusBarScope
+import com.android.systemui.statusbar.phone.CentralSurfaces
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.ViewController
@@ -54,9 +54,9 @@
* Controls the ripple effect that shows when authentication is successful.
* The ripple uses the accent color of the current theme.
*/
-@StatusBarScope
+@CentralSurfacesScope
class AuthRippleController @Inject constructor(
- private val statusBar: StatusBar,
+ private val centralSurfaces: CentralSurfaces,
private val sysuiContext: Context,
private val authController: AuthController,
private val configurationController: ConfigurationController,
@@ -137,7 +137,7 @@
private fun showUnlockedRipple() {
notificationShadeWindowController.setForcePluginOpen(true, this)
- val lightRevealScrim = statusBar.lightRevealScrim
+ val lightRevealScrim = centralSurfaces.lightRevealScrim
if (statusBarStateController.isDozing || biometricUnlockController.isWakeAndUnlock) {
circleReveal?.let {
lightRevealScrim?.revealEffect = it
@@ -155,7 +155,7 @@
override fun onKeyguardFadingAwayChanged() {
if (keyguardStateController.isKeyguardFadingAway) {
- val lightRevealScrim = statusBar.lightRevealScrim
+ val lightRevealScrim = centralSurfaces.lightRevealScrim
if (startLightRevealScrimOnKeyguardFadingAway && lightRevealScrim != null) {
ValueAnimator.ofFloat(.1f, 1f).apply {
interpolator = Interpolators.LINEAR_OUT_SLOW_IN
@@ -170,7 +170,7 @@
}
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) {
- // Reset light reveal scrim to the default, so the StatusBar
+ // Reset light reveal scrim to the default, so the CentralSurfaces
// can handle any subsequent light reveal changes
// (ie: from dozing changes)
if (lightRevealScrim.revealEffect == circleReveal) {
@@ -199,8 +199,8 @@
it.y,
0f,
Math.max(
- Math.max(it.x, statusBar.displayWidth - it.x),
- Math.max(it.y, statusBar.displayHeight - it.y)
+ Math.max(it.x, centralSurfaces.displayWidth - it.x),
+ Math.max(it.y, centralSurfaces.displayHeight - it.y)
)
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
index 7204a15..7efdd1a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
@@ -38,7 +38,6 @@
import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
-import com.android.systemui.statusbar.StatusBarState;
import com.airbnb.lottie.LottieAnimationView;
import com.airbnb.lottie.LottieProperty;
@@ -68,6 +67,7 @@
private float mBurnInOffsetY;
private float mBurnInProgress;
private float mInterpolatedDarkAmount;
+ private boolean mAnimatingBetweenAodAndLockscreen; // As opposed to Unlocked => AOD
private boolean mFullyInflated;
public UdfpsKeyguardView(Context context, @Nullable AttributeSet attrs) {
@@ -114,23 +114,32 @@
return;
}
+ final float darkAmountForAnimation = mAnimatingBetweenAodAndLockscreen
+ ? mInterpolatedDarkAmount : 1f /* animating from unlocked to AOD */;
mBurnInOffsetX = MathUtils.lerp(0f,
getBurnInOffset(mMaxBurnInOffsetX * 2, true /* xAxis */)
- - mMaxBurnInOffsetX, mInterpolatedDarkAmount);
+ - mMaxBurnInOffsetX, darkAmountForAnimation);
mBurnInOffsetY = MathUtils.lerp(0f,
getBurnInOffset(mMaxBurnInOffsetY * 2, false /* xAxis */)
- - mMaxBurnInOffsetY, mInterpolatedDarkAmount);
- mBurnInProgress = MathUtils.lerp(0f, getBurnInProgressOffset(), mInterpolatedDarkAmount);
+ - mMaxBurnInOffsetY, darkAmountForAnimation);
+ mBurnInProgress = MathUtils.lerp(0f, getBurnInProgressOffset(), darkAmountForAnimation);
+
+ if (mAnimatingBetweenAodAndLockscreen) {
+ mBgProtection.setAlpha(1f - mInterpolatedDarkAmount);
+
+ mLockScreenFp.setTranslationX(mBurnInOffsetX);
+ mLockScreenFp.setTranslationY(mBurnInOffsetY);
+ mLockScreenFp.setProgress(1f - mInterpolatedDarkAmount);
+ mLockScreenFp.setAlpha(1f - mInterpolatedDarkAmount);
+ } else {
+ mBgProtection.setAlpha(0f);
+ mLockScreenFp.setAlpha(0f);
+ }
mAodFp.setTranslationX(mBurnInOffsetX);
mAodFp.setTranslationY(mBurnInOffsetY);
mAodFp.setProgress(mBurnInProgress);
- mAodFp.setAlpha(255 * mInterpolatedDarkAmount);
-
- mLockScreenFp.setTranslationX(mBurnInOffsetX);
- mLockScreenFp.setTranslationY(mBurnInOffsetY);
- mLockScreenFp.setProgress(1f - mInterpolatedDarkAmount);
- mLockScreenFp.setAlpha((1f - mInterpolatedDarkAmount) * 255);
+ mAodFp.setAlpha(mInterpolatedDarkAmount);
}
void requestUdfps(boolean request, int color) {
@@ -171,15 +180,14 @@
protected int updateAlpha() {
int alpha = super.updateAlpha();
if (mFullyInflated) {
- mLockScreenFp.setAlpha(alpha / 255f);
- if (mInterpolatedDarkAmount != 0f) {
- mBgProtection.setAlpha(1f - mInterpolatedDarkAmount);
- } else {
+ if (mInterpolatedDarkAmount == 0f) {
+ mLockScreenFp.setAlpha(alpha / 255f);
mBgProtection.setAlpha(alpha / 255f);
+ } else {
+ updateBurnInOffsets();
}
}
-
return alpha;
}
@@ -191,8 +199,10 @@
return mAlpha;
}
- void onDozeAmountChanged(float linear, float eased) {
+ void onDozeAmountChanged(float linear, float eased, boolean animatingBetweenAodAndLockscreen) {
+ mAnimatingBetweenAodAndLockscreen = animatingBetweenAodAndLockscreen;
mInterpolatedDarkAmount = eased;
+
updateAlpha();
updateBurnInOffsets();
}
@@ -225,10 +235,6 @@
mBackgroundInAnimator.start();
}
- private boolean isShadeLocked() {
- return mStatusBarState == StatusBarState.SHADE_LOCKED;
- }
-
private final AsyncLayoutInflater.OnInflateFinishedListener mLayoutInflaterFinishListener =
new AsyncLayoutInflater.OnInflateFinishedListener() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index 24a655c..5ac21ff 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -18,6 +18,7 @@
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
+import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.content.res.Configuration;
import android.util.Log;
@@ -31,6 +32,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.SystemUIDialogManager;
@@ -59,6 +61,7 @@
@NonNull private final UnlockedScreenOffAnimationController
mUnlockedScreenOffAnimationController;
@NonNull private final ActivityLaunchAnimator mActivityLaunchAnimator;
+ private final ValueAnimator mUnlockedScreenOffDozeAnimator = ValueAnimator.ofFloat(0f, 1f);
private boolean mShowingUdfpsBouncer;
private boolean mUdfpsRequested;
@@ -107,6 +110,18 @@
mUdfpsController = udfpsController;
mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
mActivityLaunchAnimator = activityLaunchAnimator;
+
+ mUnlockedScreenOffDozeAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ mUnlockedScreenOffDozeAnimator.addUpdateListener(
+ new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mView.onDozeAmountChanged(
+ animation.getAnimatedFraction(),
+ (float) animation.getAnimatedValue(),
+ /* animatingBetweenAodAndLockScreen */ false);
+ }
+ });
}
@Override
@@ -143,7 +158,6 @@
mKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
mLockScreenShadeTransitionController.setUdfpsKeyguardViewController(this);
- mUnlockedScreenOffAnimationController.addCallback(mUnlockedScreenOffCallback);
mActivityLaunchAnimator.addListener(mActivityLaunchAnimatorListener);
}
@@ -161,7 +175,6 @@
if (mLockScreenShadeTransitionController.getUdfpsKeyguardViewController() == this) {
mLockScreenShadeTransitionController.setUdfpsKeyguardViewController(null);
}
- mUnlockedScreenOffAnimationController.removeCallback(mUnlockedScreenOffCallback);
mActivityLaunchAnimator.removeListener(mActivityLaunchAnimatorListener);
}
@@ -179,6 +192,7 @@
pw.println("mUdfpsRequested=" + mUdfpsRequested);
pw.println("mView.mUdfpsRequested=" + mView.mUdfpsRequested);
pw.println("mLaunchTransitionFadingAway=" + mLaunchTransitionFadingAway);
+ pw.println("mLastDozeAmount=" + mLastDozeAmount);
}
/**
@@ -239,7 +253,11 @@
return true;
}
- if (mStatusBarState != KEYGUARD) {
+ // Only pause auth if we're not on the keyguard AND we're not transitioning to doze
+ // (ie: dozeAmount = 0f). For the UnlockedScreenOffAnimation, the statusBarState is
+ // delayed. However, we still animate in the UDFPS affordance with the
+ // mUnlockedScreenOffDozeAnimator.
+ if (mStatusBarState != KEYGUARD && mLastDozeAmount == 0f) {
return true;
}
@@ -299,6 +317,10 @@
updateAlpha();
}
+ /**
+ * Update alpha for the UDFPS lock screen affordance. The AoD UDFPS visual affordance's
+ * alpha is based on the doze amount.
+ */
private void updateAlpha() {
// fade icon on transitions to showing the status bar, but if mUdfpsRequested, then
// the keyguard is occluded by some application - so instead use the input bouncer
@@ -327,7 +349,18 @@
if (mLastDozeAmount < linear) {
showUdfpsBouncer(false);
}
- mView.onDozeAmountChanged(linear, eased);
+ mUnlockedScreenOffDozeAnimator.cancel();
+ final boolean animatingFromUnlockedScreenOff =
+ mUnlockedScreenOffAnimationController.isAnimationPlaying();
+ if (animatingFromUnlockedScreenOff && linear != 0f) {
+ // we manually animate the fade in of the UDFPS icon since the unlocked
+ // screen off animation prevents the doze amounts to be incrementally eased in
+ mUnlockedScreenOffDozeAnimator.start();
+ } else {
+ mView.onDozeAmountChanged(linear, eased,
+ /* animatingBetweenAodAndLockScreen */ true);
+ }
+
mLastDozeAmount = linear;
updatePauseAuth();
}
@@ -446,9 +479,6 @@
}
};
- private final UnlockedScreenOffAnimationController.Callback mUnlockedScreenOffCallback =
- (linear, eased) -> mStateListener.onDozeAmountChanged(linear, eased);
-
private final ActivityLaunchAnimator.Listener mActivityLaunchAnimatorListener =
new ActivityLaunchAnimator.Listener() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
index f87fa96..5c1d8c3 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
@@ -23,7 +23,11 @@
import android.content.Intent
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
+import android.database.ContentObserver
+import android.net.Uri
+import android.os.Handler
import android.os.VibrationEffect
+import android.provider.Settings
import android.service.controls.Control
import android.service.controls.actions.BooleanAction
import android.service.controls.actions.CommandAction
@@ -38,6 +42,7 @@
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.settings.SecureSettings
import com.android.wm.shell.TaskViewFactory
import java.util.Optional
import javax.inject.Inject
@@ -51,19 +56,41 @@
private val keyguardStateController: KeyguardStateController,
private val taskViewFactory: Optional<TaskViewFactory>,
private val controlsMetricsLogger: ControlsMetricsLogger,
- private val vibrator: VibratorHelper
+ private val vibrator: VibratorHelper,
+ private val secureSettings: SecureSettings,
+ @Main mainHandler: Handler
) : ControlActionCoordinator {
private var dialog: Dialog? = null
private var pendingAction: Action? = null
private var actionsInProgress = mutableSetOf<String>()
private val isLocked: Boolean
get() = !keyguardStateController.isUnlocked()
+ private var mAllowTrivialControls: Boolean = secureSettings.getInt(
+ Settings.Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS, 0) != 0
override lateinit var activityContext: Context
companion object {
private const val RESPONSE_TIMEOUT_IN_MILLIS = 3000L
}
+ init {
+ val lockScreenShowControlsUri =
+ secureSettings.getUriFor(Settings.Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS)
+ val controlsContentObserver = object : ContentObserver(mainHandler) {
+ override fun onChange(selfChange: Boolean, uri: Uri?) {
+ super.onChange(selfChange, uri)
+ if (uri == lockScreenShowControlsUri) {
+ mAllowTrivialControls = secureSettings.getInt(
+ Settings.Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS, 0) != 0
+ }
+ }
+ }
+ secureSettings.registerContentObserver(
+ lockScreenShowControlsUri,
+ false /* notifyForDescendants */, controlsContentObserver
+ )
+ }
+
override fun closeDialogs() {
dialog?.dismiss()
dialog = null
@@ -80,7 +107,7 @@
},
true /* blockable */
),
- isAuthRequired(cvh)
+ isAuthRequired(cvh, mAllowTrivialControls)
)
}
@@ -100,7 +127,7 @@
},
blockable
),
- isAuthRequired(cvh)
+ isAuthRequired(cvh, mAllowTrivialControls)
)
}
@@ -120,7 +147,7 @@
{ cvh.action(FloatAction(templateId, newValue)) },
false /* blockable */
),
- isAuthRequired(cvh)
+ isAuthRequired(cvh, mAllowTrivialControls)
)
}
@@ -139,7 +166,7 @@
},
false /* blockable */
),
- isAuthRequired(cvh)
+ isAuthRequired(cvh, mAllowTrivialControls)
)
}
@@ -156,7 +183,11 @@
actionsInProgress.remove(controlId)
}
- private fun isAuthRequired(cvh: ControlViewHolder) = cvh.cws.control?.isAuthRequired() ?: true
+ @VisibleForTesting()
+ fun isAuthRequired(cvh: ControlViewHolder, allowTrivialControls: Boolean): Boolean {
+ val isAuthRequired = cvh.cws.control?.isAuthRequired ?: true
+ return isAuthRequired || !allowTrivialControls
+ }
private fun shouldRunAction(controlId: String) =
if (actionsInProgress.add(controlId)) {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
index b32f878..2f041ac 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -18,7 +18,7 @@
import com.android.systemui.keyguard.dagger.KeyguardModule;
import com.android.systemui.recents.RecentsModule;
-import com.android.systemui.statusbar.dagger.StatusBarModule;
+import com.android.systemui.statusbar.dagger.CentralSurfacesModule;
import dagger.Module;
@@ -27,7 +27,7 @@
*/
@Module(includes = {
RecentsModule.class,
- StatusBarModule.class,
+ CentralSurfacesModule.class,
KeyguardModule.class,
})
public abstract class SystemUIBinder {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
index a4da6b4..5d154c3 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java
@@ -48,7 +48,7 @@
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl;
import com.android.systemui.statusbar.NotificationShadeWindowController;
-import com.android.systemui.statusbar.dagger.StartStatusBarModule;
+import com.android.systemui.statusbar.dagger.StartCentralSurfacesModule;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
@@ -87,7 +87,7 @@
MediaModule.class,
PowerModule.class,
QSModule.class,
- StartStatusBarModule.class,
+ StartCentralSurfacesModule.class,
VolumeModule.class
})
public abstract class SystemUIDefaultModule {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 13067bf..2799301 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -67,8 +67,8 @@
import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent;
import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -131,7 +131,7 @@
WalletModule.class
},
subcomponents = {
- StatusBarComponent.class,
+ CentralSurfacesComponent.class,
NotificationRowComponent.class,
DozeComponent.class,
ExpandableNotificationRowComponent.class,
@@ -175,7 +175,7 @@
abstract Recents optionalRecents();
@BindsOptionalOf
- abstract StatusBar optionalStatusBar();
+ abstract CentralSurfaces optionalCentralSurfaces();
@BindsOptionalOf
abstract UdfpsHbmProvider optionalUdfpsHbmProvider();
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index 2beed4c..d89c0be 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -31,7 +31,6 @@
boolean isPowerSaveActive();
boolean isPulsingBlocked();
boolean isProvisioned();
- boolean isBlockingDoze();
/**
* Makes a current pulse last for twice as long.
@@ -80,8 +79,9 @@
*/
void stopPulsing();
- /** Returns whether doze is suppressed. */
- boolean isDozeSuppressed();
+ /** Returns whether always-on-display is suppressed. This does not include suppressing
+ * wake-up gestures. */
+ boolean isAlwaysOnSuppressed();
interface Callback {
/**
@@ -97,8 +97,10 @@
*/
default void onPowerSaveChanged(boolean active) {}
- /** Called when the doze suppression state changes. */
- default void onDozeSuppressedChanged(boolean suppressed) {}
+ /**
+ * Called when the always on suppression state changes. See {@link #isAlwaysOnSuppressed()}.
+ */
+ default void onAlwaysOnSuppressedChanged(boolean suppressed) {}
}
interface PulseCallback {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 2511520..0a2e69f 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -132,14 +132,6 @@
}
/**
- * Appends dozing event to the logs
- * @param suppressed true if dozing is suppressed
- */
- public void traceDozingSuppressed(boolean suppressed) {
- mLogger.logDozingSuppressed(suppressed);
- }
-
- /**
* Appends fling event to the logs
*/
public void traceFling(boolean expand, boolean aboveThreshold, boolean thresholdNeeded,
@@ -325,15 +317,40 @@
}
/**
- * Appends doze suppressed event to the logs
+ * Appends the doze state that was suppressed to the doze event log
* @param suppressedState The {@link DozeMachine.State} that was suppressed
*/
- public void traceDozeSuppressed(DozeMachine.State suppressedState) {
- mLogger.logDozeSuppressed(suppressedState);
+ public void traceAlwaysOnSuppressed(DozeMachine.State suppressedState) {
+ mLogger.logAlwaysOnSuppressed(suppressedState);
}
/**
- * Appends new AOD sreen brightness to logs
+ * Appends reason why doze immediately ended.
+ */
+ public void traceImmediatelyEndDoze(String reason) {
+ mLogger.logImmediatelyEndDoze(reason);
+ }
+
+ /**
+ * Appends power save changes that may cause a new doze state
+ * @param powerSaveActive true if power saving is active
+ * @param nextState the state that we'll transition to
+ */
+ public void tracePowerSaveChanged(boolean powerSaveActive, DozeMachine.State nextState) {
+ mLogger.logPowerSaveChanged(powerSaveActive, nextState);
+ }
+
+ /**
+ * Appends an event on AOD suppression change
+ * @param suppressed true if AOD is being suppressed
+ * @param nextState the state that we'll transition to
+ */
+ public void traceAlwaysOnSuppressedChange(boolean suppressed, DozeMachine.State nextState) {
+ mLogger.logAlwaysOnSuppressedChange(suppressed, nextState);
+ }
+
+ /**
+ * Appends new AOD screen brightness to logs
* @param brightness display brightness setting
*/
public void traceDozeScreenBrightness(int brightness) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
index 4ba6b51..f3f6be2 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
@@ -74,11 +74,21 @@
})
}
- fun logDozingSuppressed(isDozingSuppressed: Boolean) {
+ fun logPowerSaveChanged(powerSaveActive: Boolean, nextState: DozeMachine.State) {
buffer.log(TAG, INFO, {
- bool1 = isDozingSuppressed
+ bool1 = powerSaveActive
+ str1 = nextState.name
}, {
- "DozingSuppressed=$bool1"
+ "Power save active=$bool1 nextState=$str1"
+ })
+ }
+
+ fun logAlwaysOnSuppressedChange(isAodSuppressed: Boolean, nextState: DozeMachine.State) {
+ buffer.log(TAG, INFO, {
+ bool1 = isAodSuppressed
+ str1 = nextState.name
+ }, {
+ "Always on (AOD) suppressed changed, suppressed=$bool1 nextState=$str1"
})
}
@@ -257,11 +267,19 @@
})
}
- fun logDozeSuppressed(state: DozeMachine.State) {
+ fun logAlwaysOnSuppressed(state: DozeMachine.State) {
buffer.log(TAG, INFO, {
str1 = state.name
}, {
- "Doze state suppressed, state=$str1"
+ "Always-on state suppressed, suppressed state=$str1"
+ })
+ }
+
+ fun logImmediatelyEndDoze(reason: String) {
+ buffer.log(TAG, INFO, {
+ str1 = reason
+ }, {
+ "Doze immediately ended due to $str1"
})
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index 789ad62..ae01f0a 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -357,9 +357,9 @@
if (mState == State.FINISH) {
return State.FINISH;
}
- if (mDozeHost.isDozeSuppressed() && requestedState.isAlwaysOn()) {
+ if (mDozeHost.isAlwaysOnSuppressed() && requestedState.isAlwaysOn()) {
Log.i(TAG, "Doze is suppressed. Suppressing state: " + requestedState);
- mDozeLog.traceDozeSuppressed(requestedState);
+ mDozeLog.traceAlwaysOnSuppressed(requestedState);
return State.DOZE;
}
if ((mState == State.DOZE_AOD_PAUSED || mState == State.DOZE_AOD_PAUSING
@@ -415,7 +415,6 @@
pw.print(" state="); pw.println(mState);
pw.print(" wakeLockHeldForCurrentState="); pw.println(mWakeLockHeldForCurrentState);
pw.print(" wakeLock="); pw.println(mWakeLock);
- pw.print(" isDozeSuppressed="); pw.println(mDozeHost.isDozeSuppressed());
pw.println("Parts:");
for (Part p : mParts) {
p.dump(pw);
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSuppressor.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSuppressor.java
new file mode 100644
index 0000000..31d43b5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSuppressor.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.doze;
+
+import static android.app.UiModeManager.ACTION_ENTER_CAR_MODE;
+
+import android.app.UiModeManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Configuration;
+import android.hardware.display.AmbientDisplayConfiguration;
+import android.os.PowerManager;
+import android.os.UserHandle;
+import android.text.TextUtils;
+
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.doze.dagger.DozeScope;
+import com.android.systemui.statusbar.phone.BiometricUnlockController;
+
+import java.io.PrintWriter;
+
+import javax.inject.Inject;
+
+import dagger.Lazy;
+
+/**
+ * Handles suppressing doze on:
+ * 1. INITIALIZED, don't allow dozing at all when:
+ * - in CAR_MODE
+ * - device is NOT provisioned
+ * - there's a pending authentication
+ * 2. PowerSaveMode active
+ * - no always-on-display (DOZE_AOD)
+ * - continues to allow doze triggers (DOZE, DOZE_REQUEST_PULSE)
+ * 3. Suppression changes from the PowerManager API. See {@link PowerManager#suppressAmbientDisplay}
+ * and {@link DozeHost#isAlwaysOnSuppressed()}.
+ * - no always-on-display (DOZE_AOD)
+ * - allow doze triggers (DOZE), but disallow notifications (handled by {@link DozeTriggers})
+ * - See extra check in {@link DozeMachine} to guarantee device never enters always-on states
+ */
+@DozeScope
+public class DozeSuppressor implements DozeMachine.Part {
+ private static final String TAG = "DozeSuppressor";
+
+ private DozeMachine mMachine;
+ private final DozeHost mDozeHost;
+ private final AmbientDisplayConfiguration mConfig;
+ private final DozeLog mDozeLog;
+ private final BroadcastDispatcher mBroadcastDispatcher;
+ private final UiModeManager mUiModeManager;
+ private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
+
+ private boolean mBroadcastReceiverRegistered;
+
+ @Inject
+ public DozeSuppressor(
+ DozeHost dozeHost,
+ AmbientDisplayConfiguration config,
+ DozeLog dozeLog,
+ BroadcastDispatcher broadcastDispatcher,
+ UiModeManager uiModeManager,
+ Lazy<BiometricUnlockController> biometricUnlockControllerLazy) {
+ mDozeHost = dozeHost;
+ mConfig = config;
+ mDozeLog = dozeLog;
+ mBroadcastDispatcher = broadcastDispatcher;
+ mUiModeManager = uiModeManager;
+ mBiometricUnlockControllerLazy = biometricUnlockControllerLazy;
+ }
+
+ @Override
+ public void setDozeMachine(DozeMachine dozeMachine) {
+ mMachine = dozeMachine;
+ }
+
+ @Override
+ public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
+ switch (newState) {
+ case INITIALIZED:
+ registerBroadcastReceiver();
+ mDozeHost.addCallback(mHostCallback);
+ checkShouldImmediatelyEndDoze();
+ break;
+ case FINISH:
+ destroy();
+ break;
+ default:
+ }
+ }
+
+ @Override
+ public void destroy() {
+ unregisterBroadcastReceiver();
+ mDozeHost.removeCallback(mHostCallback);
+ }
+
+ private void checkShouldImmediatelyEndDoze() {
+ String reason = null;
+ if (mUiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR) {
+ reason = "car_mode";
+ } else if (!mDozeHost.isProvisioned()) {
+ reason = "device_unprovisioned";
+ } else if (mBiometricUnlockControllerLazy.get().hasPendingAuthentication()) {
+ reason = "has_pending_auth";
+ }
+
+ if (!TextUtils.isEmpty(reason)) {
+ mDozeLog.traceImmediatelyEndDoze(reason);
+ mMachine.requestState(DozeMachine.State.FINISH);
+ }
+ }
+
+ @Override
+ public void dump(PrintWriter pw) {
+ pw.println(" uiMode=" + mUiModeManager.getCurrentModeType());
+ pw.println(" hasPendingAuth="
+ + mBiometricUnlockControllerLazy.get().hasPendingAuthentication());
+ pw.println(" isProvisioned=" + mDozeHost.isProvisioned());
+ pw.println(" isAlwaysOnSuppressed=" + mDozeHost.isAlwaysOnSuppressed());
+ pw.println(" aodPowerSaveActive=" + mDozeHost.isPowerSaveActive());
+ }
+
+ private void registerBroadcastReceiver() {
+ if (mBroadcastReceiverRegistered) {
+ return;
+ }
+ IntentFilter filter = new IntentFilter(ACTION_ENTER_CAR_MODE);
+ mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter);
+ mBroadcastReceiverRegistered = true;
+ }
+
+ private void unregisterBroadcastReceiver() {
+ if (!mBroadcastReceiverRegistered) {
+ return;
+ }
+ mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver);
+ mBroadcastReceiverRegistered = false;
+ }
+
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (ACTION_ENTER_CAR_MODE.equals(intent.getAction())) {
+ mDozeLog.traceImmediatelyEndDoze("car_mode");
+ mMachine.requestState(DozeMachine.State.FINISH);
+ }
+ }
+ };
+
+ private DozeHost.Callback mHostCallback = new DozeHost.Callback() {
+ @Override
+ public void onPowerSaveChanged(boolean active) {
+ DozeMachine.State nextState = null;
+ if (mDozeHost.isPowerSaveActive()) {
+ nextState = DozeMachine.State.DOZE;
+ } else if (mMachine.getState() == DozeMachine.State.DOZE
+ && mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT)) {
+ nextState = DozeMachine.State.DOZE_AOD;
+ }
+
+ if (nextState != null) {
+ mDozeLog.tracePowerSaveChanged(mDozeHost.isPowerSaveActive(), nextState);
+ mMachine.requestState(nextState);
+ }
+ }
+
+ @Override
+ public void onAlwaysOnSuppressedChanged(boolean suppressed) {
+ final DozeMachine.State nextState;
+ if (mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT) && !suppressed) {
+ nextState = DozeMachine.State.DOZE_AOD;
+ } else {
+ nextState = DozeMachine.State.DOZE;
+ }
+ mDozeLog.traceAlwaysOnSuppressedChange(suppressed, nextState);
+ mMachine.requestState(nextState);
+ }
+ };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 8bff3ba..74044e2 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -17,12 +17,10 @@
package com.android.systemui.doze;
import android.annotation.Nullable;
-import android.app.UiModeManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.res.Configuration;
import android.hardware.display.AmbientDisplayConfiguration;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -88,7 +86,6 @@
private final AsyncSensorManager mSensorManager;
private final WakeLock mWakeLock;
private final boolean mAllowPulseTriggers;
- private final UiModeManager mUiModeManager;
private final TriggerReceiver mBroadcastReceiver = new TriggerReceiver();
private final DockEventListener mDockEventListener = new DockEventListener();
private final DockManager mDockManager;
@@ -203,8 +200,6 @@
mDozeSensors = new DozeSensors(context, mSensorManager, dozeParameters,
config, wakeLock, this::onSensor, this::onProximityFar, dozeLog, proximitySensor,
secureSettings, authController, devicePostureController);
-
- mUiModeManager = mContext.getSystemService(UiModeManager.class);
mDockManager = dockManager;
mProxCheck = proxCheck;
mDozeLog = dozeLog;
@@ -247,7 +242,7 @@
mDozeLog.tracePulseDropped("pulseOnNotificationsDisabled");
return;
}
- if (mDozeHost.isDozeSuppressed()) {
+ if (mDozeHost.isAlwaysOnSuppressed()) {
runIfNotNull(onPulseSuppressedListener);
mDozeLog.tracePulseDropped("dozeSuppressed");
return;
@@ -456,10 +451,9 @@
mAodInterruptRunnable = null;
sWakeDisplaySensorState = true;
mBroadcastReceiver.register(mBroadcastDispatcher);
- mDozeHost.addCallback(mHostCallback);
mDockManager.addListener(mDockEventListener);
mDozeSensors.requestTemporaryDisable();
- checkTriggersAtInit();
+ mDozeHost.addCallback(mHostCallback);
break;
case DOZE:
case DOZE_AOD:
@@ -516,15 +510,6 @@
}
}
-
- private void checkTriggersAtInit() {
- if (mUiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR
- || mDozeHost.isBlockingDoze()
- || !mDozeHost.isProvisioned()) {
- mMachine.requestState(DozeMachine.State.FINISH);
- }
- }
-
private void requestPulse(final int reason, boolean performedProxCheck,
Runnable onPulseSuppressedListener) {
Assert.isMainThread();
@@ -608,9 +593,6 @@
requestPulse(DozeLog.PULSE_REASON_INTENT, false, /* performedProxCheck */
null /* onPulseSuppressedListener */);
}
- if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(intent.getAction())) {
- mMachine.requestState(DozeMachine.State.FINISH);
- }
if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
mDozeSensors.onUserSwitched();
}
@@ -621,7 +603,6 @@
return;
}
IntentFilter filter = new IntentFilter(PULSE_ACTION);
- filter.addAction(UiModeManager.ACTION_ENTER_CAR_MODE);
filter.addAction(Intent.ACTION_USER_SWITCHED);
broadcastDispatcher.registerReceiver(this, filter);
mRegistered = true;
@@ -659,26 +640,5 @@
public void onNotificationAlerted(Runnable onPulseSuppressedListener) {
onNotification(onPulseSuppressedListener);
}
-
- @Override
- public void onPowerSaveChanged(boolean active) {
- if (mDozeHost.isPowerSaveActive()) {
- mMachine.requestState(DozeMachine.State.DOZE);
- } else if (mMachine.getState() == DozeMachine.State.DOZE
- && mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT)) {
- mMachine.requestState(DozeMachine.State.DOZE_AOD);
- }
- }
-
- @Override
- public void onDozeSuppressedChanged(boolean suppressed) {
- final DozeMachine.State nextState;
- if (mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT) && !suppressed) {
- nextState = DozeMachine.State.DOZE_AOD;
- } else {
- nextState = DozeMachine.State.DOZE;
- }
- mMachine.requestState(nextState);
- }
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
index 2d96920..d2ab611 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
@@ -16,23 +16,44 @@
package com.android.systemui.dreams;
+import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
-import android.widget.ImageView;
import androidx.constraintlayout.widget.ConstraintLayout;
-import com.android.internal.util.Preconditions;
import com.android.systemui.R;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
/**
* {@link DreamOverlayStatusBarView} is the view responsible for displaying the status bar in a
* dream. The status bar displays conditional status icons such as "priority mode" and "no wifi".
*/
public class DreamOverlayStatusBarView extends ConstraintLayout {
- private ImageView mWifiStatusView;
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "STATUS_ICON_" }, value = {
+ STATUS_ICON_NOTIFICATIONS,
+ STATUS_ICON_WIFI_UNAVAILABLE,
+ STATUS_ICON_ALARM_SET,
+ STATUS_ICON_MIC_CAMERA_DISABLED,
+ STATUS_ICON_PRIORITY_MODE_ON
+ })
+ public @interface StatusIconType {}
+ public static final int STATUS_ICON_NOTIFICATIONS = 0;
+ public static final int STATUS_ICON_WIFI_UNAVAILABLE = 1;
+ public static final int STATUS_ICON_ALARM_SET = 2;
+ public static final int STATUS_ICON_MIC_CAMERA_DISABLED = 3;
+ public static final int STATUS_ICON_PRIORITY_MODE_ON = 4;
+
+ private final Map<Integer, View> mStatusIcons = new HashMap<>();
public DreamOverlayStatusBarView(Context context) {
this(context, null);
@@ -55,16 +76,35 @@
protected void onFinishInflate() {
super.onFinishInflate();
- mWifiStatusView = Preconditions.checkNotNull(findViewById(R.id.dream_overlay_wifi_status),
- "R.id.dream_overlay_wifi_status must not be null");
+ mStatusIcons.put(STATUS_ICON_WIFI_UNAVAILABLE,
+ fetchStatusIconForResId(R.id.dream_overlay_wifi_status));
+ mStatusIcons.put(STATUS_ICON_ALARM_SET,
+ fetchStatusIconForResId(R.id.dream_overlay_alarm_set));
+ mStatusIcons.put(STATUS_ICON_MIC_CAMERA_DISABLED,
+ fetchStatusIconForResId(R.id.dream_overlay_camera_mic_off));
+ mStatusIcons.put(STATUS_ICON_NOTIFICATIONS,
+ fetchStatusIconForResId(R.id.dream_overlay_notification_indicator));
+ mStatusIcons.put(STATUS_ICON_PRIORITY_MODE_ON,
+ fetchStatusIconForResId(R.id.dream_overlay_priority_mode));
}
- /**
- * Whether to show the wifi status icon.
- * @param show True if the wifi status icon should be shown.
- */
- void showWifiStatus(boolean show) {
- // Only show the wifi status icon when wifi isn't available.
- mWifiStatusView.setVisibility(show ? View.VISIBLE : View.GONE);
+ void showIcon(@StatusIconType int iconType, boolean show) {
+ showIcon(iconType, show, null);
+ }
+
+ void showIcon(@StatusIconType int iconType, boolean show, @Nullable String contentDescription) {
+ View icon = mStatusIcons.get(iconType);
+ if (icon == null) {
+ return;
+ }
+ if (show && contentDescription != null) {
+ icon.setContentDescription(contentDescription);
+ }
+ icon.setVisibility(show ? View.VISIBLE : View.GONE);
+ }
+
+ private View fetchStatusIconForResId(int resId) {
+ final View statusIcon = findViewById(resId);
+ return Objects.requireNonNull(statusIcon);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
index 32b2309..a25a742 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
@@ -16,19 +16,35 @@
package com.android.systemui.dreams;
-import android.annotation.IntDef;
+import android.app.AlarmManager;
+import android.content.res.Resources;
+import android.hardware.SensorPrivacyManager;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.service.notification.NotificationListenerService.RankingMap;
+import android.service.notification.StatusBarNotification;
+import android.text.format.DateFormat;
+import android.util.PluralsMessageFormatter;
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
+import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
+import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
+import com.android.systemui.statusbar.policy.NextAlarmController;
+import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.touch.TouchInsetManager;
import com.android.systemui.util.ViewController;
+import com.android.systemui.util.time.DateFormatUtil;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
+import java.util.Locale;
+import java.util.Map;
import javax.inject.Inject;
@@ -37,19 +53,15 @@
*/
@DreamOverlayComponent.DreamOverlayScope
public class DreamOverlayStatusBarViewController extends ViewController<DreamOverlayStatusBarView> {
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = { "WIFI_STATUS_" }, value = {
- WIFI_STATUS_UNKNOWN,
- WIFI_STATUS_UNAVAILABLE,
- WIFI_STATUS_AVAILABLE
- })
- private @interface WifiStatus {}
- private static final int WIFI_STATUS_UNKNOWN = 0;
- private static final int WIFI_STATUS_UNAVAILABLE = 1;
- private static final int WIFI_STATUS_AVAILABLE = 2;
-
private final ConnectivityManager mConnectivityManager;
private final TouchInsetManager.TouchInsetSession mTouchInsetSession;
+ private final NextAlarmController mNextAlarmController;
+ private final AlarmManager mAlarmManager;
+ private final Resources mResources;
+ private final DateFormatUtil mDateFormatUtil;
+ private final IndividualSensorPrivacyController mSensorPrivacyController;
+ private final NotificationListener mNotificationListener;
+ private final ZenModeController mZenModeController;
private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder()
.clearCapabilities()
@@ -59,61 +71,183 @@
@Override
public void onCapabilitiesChanged(
Network network, NetworkCapabilities networkCapabilities) {
- onWifiAvailabilityChanged(
- networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI));
+ updateWifiUnavailableStatusIcon();
}
@Override
public void onAvailable(Network network) {
- onWifiAvailabilityChanged(true);
+ updateWifiUnavailableStatusIcon();
}
@Override
public void onLost(Network network) {
- onWifiAvailabilityChanged(false);
+ updateWifiUnavailableStatusIcon();
}
};
- private @WifiStatus int mWifiStatus = WIFI_STATUS_UNKNOWN;
+ private final IndividualSensorPrivacyController.Callback mSensorCallback =
+ (sensor, blocked) -> updateMicCameraBlockedStatusIcon();
+
+ private final NextAlarmController.NextAlarmChangeCallback mNextAlarmCallback =
+ nextAlarm -> updateAlarmStatusIcon();
+
+ private final NotificationHandler mNotificationHandler = new NotificationHandler() {
+ @Override
+ public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
+ updateNotificationsStatusIcon();
+ }
+
+ @Override
+ public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap) {
+ updateNotificationsStatusIcon();
+ }
+
+ @Override
+ public void onNotificationRemoved(
+ StatusBarNotification sbn,
+ RankingMap rankingMap,
+ int reason) {
+ updateNotificationsStatusIcon();
+ }
+
+ @Override
+ public void onNotificationRankingUpdate(RankingMap rankingMap) {
+ }
+
+ @Override
+ public void onNotificationsInitialized() {
+ updateNotificationsStatusIcon();
+ }
+ };
+
+ private final ZenModeController.Callback mZenModeCallback = new ZenModeController.Callback() {
+ @Override
+ public void onZenChanged(int zen) {
+ updatePriorityModeStatusIcon();
+ }
+ };
@Inject
public DreamOverlayStatusBarViewController(
DreamOverlayStatusBarView view,
+ @Main Resources resources,
ConnectivityManager connectivityManager,
- TouchInsetManager.TouchInsetSession touchInsetSession) {
+ TouchInsetManager.TouchInsetSession touchInsetSession,
+ AlarmManager alarmManager,
+ NextAlarmController nextAlarmController,
+ DateFormatUtil dateFormatUtil,
+ IndividualSensorPrivacyController sensorPrivacyController,
+ NotificationListener notificationListener,
+ ZenModeController zenModeController) {
super(view);
+ mResources = resources;
mConnectivityManager = connectivityManager;
mTouchInsetSession = touchInsetSession;
+ mAlarmManager = alarmManager;
+ mNextAlarmController = nextAlarmController;
+ mDateFormatUtil = dateFormatUtil;
+ mSensorPrivacyController = sensorPrivacyController;
+ mNotificationListener = notificationListener;
+ mZenModeController = zenModeController;
+
+ // Handlers can be added to NotificationListener, but apparently they can't be removed. So
+ // add the handler here in the constructor rather than in onViewAttached to avoid confusion.
+ mNotificationListener.addNotificationHandler(mNotificationHandler);
}
@Override
protected void onViewAttached() {
- mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback);
+ updateNotificationsStatusIcon();
- NetworkCapabilities capabilities =
- mConnectivityManager.getNetworkCapabilities(
- mConnectivityManager.getActiveNetwork());
- onWifiAvailabilityChanged(
- capabilities != null
- && capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI));
+ mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback);
+ updateWifiUnavailableStatusIcon();
+
+ mNextAlarmController.addCallback(mNextAlarmCallback);
+ updateAlarmStatusIcon();
+
+ mSensorPrivacyController.addCallback(mSensorCallback);
+ updateMicCameraBlockedStatusIcon();
+
+ mZenModeController.addCallback(mZenModeCallback);
+ updatePriorityModeStatusIcon();
+
mTouchInsetSession.addViewToTracking(mView);
}
@Override
protected void onViewDetached() {
+ mZenModeController.removeCallback(mZenModeCallback);
+ mSensorPrivacyController.removeCallback(mSensorCallback);
+ mNextAlarmController.removeCallback(mNextAlarmCallback);
mConnectivityManager.unregisterNetworkCallback(mNetworkCallback);
mTouchInsetSession.clear();
}
- /**
- * Wifi availability has changed. Update the wifi status icon as appropriate.
- * @param available Whether wifi is available.
- */
- private void onWifiAvailabilityChanged(boolean available) {
- final int newWifiStatus = available ? WIFI_STATUS_AVAILABLE : WIFI_STATUS_UNAVAILABLE;
- if (mWifiStatus != newWifiStatus) {
- mWifiStatus = newWifiStatus;
- mView.showWifiStatus(mWifiStatus == WIFI_STATUS_UNAVAILABLE);
+ private void updateWifiUnavailableStatusIcon() {
+ final NetworkCapabilities capabilities =
+ mConnectivityManager.getNetworkCapabilities(
+ mConnectivityManager.getActiveNetwork());
+ final boolean available = capabilities != null
+ && capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI);
+ mView.showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, !available);
+ }
+
+ private void updateAlarmStatusIcon() {
+ final AlarmManager.AlarmClockInfo alarm =
+ mAlarmManager.getNextAlarmClock(UserHandle.USER_CURRENT);
+ final boolean hasAlarm = alarm != null && alarm.getTriggerTime() > 0;
+ mView.showIcon(
+ DreamOverlayStatusBarView.STATUS_ICON_ALARM_SET,
+ hasAlarm,
+ hasAlarm ? buildAlarmContentDescription(alarm) : null);
+ }
+
+ private String buildAlarmContentDescription(AlarmManager.AlarmClockInfo alarm) {
+ final String skeleton = mDateFormatUtil.is24HourFormat() ? "EHm" : "Ehma";
+ final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
+ final String dateString = DateFormat.format(pattern, alarm.getTriggerTime()).toString();
+
+ return mResources.getString(R.string.accessibility_quick_settings_alarm, dateString);
+ }
+
+ private void updateMicCameraBlockedStatusIcon() {
+ final boolean micBlocked = mSensorPrivacyController
+ .isSensorBlocked(SensorPrivacyManager.Sensors.MICROPHONE);
+ final boolean cameraBlocked = mSensorPrivacyController
+ .isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA);
+ mView.showIcon(
+ DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED,
+ micBlocked && cameraBlocked);
+ }
+
+ private void updateNotificationsStatusIcon() {
+ if (mView == null) {
+ // It is possible for this method to be called before the view is attached, which makes
+ // null-checking necessary.
+ return;
}
+
+ final StatusBarNotification[] notifications =
+ mNotificationListener.getActiveNotifications();
+ final int notificationCount = notifications != null ? notifications.length : 0;
+ mView.showIcon(
+ DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS,
+ notificationCount > 0,
+ notificationCount > 0
+ ? buildNotificationsContentDescription(notificationCount)
+ : null);
+ }
+
+ private String buildNotificationsContentDescription(int notificationCount) {
+ return PluralsMessageFormatter.format(
+ mResources,
+ Map.of("count", notificationCount),
+ R.string.dream_overlay_status_bar_notification_indicator);
+ }
+
+ private void updatePriorityModeStatusIcon() {
+ mView.showIcon(
+ DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON,
+ mZenModeController.getZen() != Settings.Global.ZEN_MODE_OFF);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamPreviewComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamPreviewComplication.java
index 23343b1..cc2e571 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamPreviewComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamPreviewComplication.java
@@ -20,6 +20,8 @@
import static com.android.systemui.dreams.complication.dagger.DreamPreviewComplicationComponent.DreamPreviewComplicationModule.DREAM_PREVIEW_COMPLICATION_LAYOUT_PARAMS;
import static com.android.systemui.dreams.complication.dagger.DreamPreviewComplicationComponent.DreamPreviewComplicationModule.DREAM_PREVIEW_COMPLICATION_VIEW;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.view.View;
import android.widget.TextView;
@@ -86,6 +88,11 @@
if (!TextUtils.isEmpty(dreamLabel)) {
mView.setText(dreamLabel);
}
+ for (Drawable drawable : mView.getCompoundDrawablesRelative()) {
+ if (drawable instanceof BitmapDrawable) {
+ drawable.setAutoMirrored(true);
+ }
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
index d16c8c8..e140f6b 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java
@@ -21,6 +21,9 @@
import static com.android.systemui.dreams.touch.dagger.BouncerSwipeModule.SWIPE_TO_BOUNCER_START_REGION;
import android.animation.ValueAnimator;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.util.DisplayMetrics;
import android.util.Log;
import android.view.GestureDetector;
import android.view.InputEvent;
@@ -29,7 +32,7 @@
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.wm.shell.animation.FlingAnimationUtils;
@@ -68,13 +71,15 @@
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private float mCurrentExpansion;
- private final StatusBar mStatusBar;
+ private final CentralSurfaces mCentralSurfaces;
private VelocityTracker mVelocityTracker;
private final FlingAnimationUtils mFlingAnimationUtils;
private final FlingAnimationUtils mFlingAnimationUtilsClosing;
+ private final DisplayMetrics mDisplayMetrics;
+
private Boolean mCapture;
private TouchSession mTouchSession;
@@ -85,40 +90,9 @@
private final GestureDetector.OnGestureListener mOnGestureListener =
new GestureDetector.SimpleOnGestureListener() {
- boolean mTrack;
- boolean mBouncerPresent;
-
- @Override
- public boolean onDown(MotionEvent e) {
- // We only consider gestures that originate from the lower portion of the
- // screen.
- final float displayHeight = mStatusBar.getDisplayHeight();
-
- mBouncerPresent = mStatusBar.isBouncerShowing();
-
- // The target zone is either at the top or bottom of the screen, dependent on
- // whether the bouncer is present.
- final float zonePercentage =
- Math.abs(e.getY() - (mBouncerPresent ? 0 : displayHeight))
- / displayHeight;
-
- mTrack = zonePercentage < mBouncerZoneScreenPercentage;
-
- // Never capture onDown. While this might lead to some false positive touches
- // being sent to other windows/layers, this is necessary to make sure the
- // proper touch event sequence is received by others in the event we do not
- // consume the sequence here.
- return false;
- }
-
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
- // Do not handle scroll gestures if not tracking touch events.
- if (!mTrack) {
- return false;
- }
-
if (mCapture == null) {
// If the user scrolling favors a vertical direction, begin capturing
// scrolls.
@@ -140,10 +114,9 @@
// is fully hidden at full expansion (1) and fully visible when fully collapsed
// (0).
final float screenTravelPercentage =
- Math.abs((e1.getY() - e2.getY()) / mStatusBar.getDisplayHeight());
- setPanelExpansion(
- mBouncerPresent ? screenTravelPercentage : 1 - screenTravelPercentage);
-
+ Math.abs((e1.getY() - e2.getY()) / mCentralSurfaces.getDisplayHeight());
+ setPanelExpansion(mCentralSurfaces.isBouncerShowing()
+ ? screenTravelPercentage : 1 - screenTravelPercentage);
return true;
}
};
@@ -155,8 +128,9 @@
@Inject
public BouncerSwipeTouchHandler(
+ DisplayMetrics displayMetrics,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
- StatusBar statusBar,
+ CentralSurfaces centralSurfaces,
NotificationShadeWindowController notificationShadeWindowController,
ValueAnimatorCreator valueAnimatorCreator,
VelocityTrackerFactory velocityTrackerFactory,
@@ -165,7 +139,8 @@
@Named(SWIPE_TO_BOUNCER_FLING_ANIMATION_UTILS_OPENING)
FlingAnimationUtils flingAnimationUtilsClosing,
@Named(SWIPE_TO_BOUNCER_START_REGION) float swipeRegionPercentage) {
- mStatusBar = statusBar;
+ mDisplayMetrics = displayMetrics;
+ mCentralSurfaces = centralSurfaces;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mNotificationShadeWindowController = notificationShadeWindowController;
mBouncerZoneScreenPercentage = swipeRegionPercentage;
@@ -176,6 +151,21 @@
}
@Override
+ public void getTouchInitiationRegion(Region region) {
+ if (mCentralSurfaces.isBouncerShowing()) {
+ region.op(new Rect(0, 0, mDisplayMetrics.widthPixels,
+ Math.round(mDisplayMetrics.heightPixels * mBouncerZoneScreenPercentage)),
+ Region.Op.UNION);
+ } else {
+ region.op(new Rect(0,
+ Math.round(mDisplayMetrics.heightPixels * (1 - mBouncerZoneScreenPercentage)),
+ mDisplayMetrics.widthPixels,
+ mDisplayMetrics.heightPixels),
+ Region.Op.UNION);
+ }
+ }
+
+ @Override
public void onSessionStart(TouchSession session) {
mVelocityTracker = mVelocityTrackerFactory.obtain();
mTouchSession = session;
@@ -202,7 +192,9 @@
final MotionEvent motionEvent = (MotionEvent) event;
switch(motionEvent.getAction()) {
+ case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
+ mTouchSession.pop();
// If we are not capturing any input, there is no need to consider animating to
// finish transition.
if (mCapture == null || !mCapture) {
@@ -226,7 +218,6 @@
if (expansion == KeyguardBouncer.EXPANSION_HIDDEN) {
mStatusBarKeyguardViewManager.reset(false);
}
- mTouchSession.pop();
break;
default:
mVelocityTracker.addMovement(motionEvent);
@@ -255,7 +246,7 @@
}
protected void flingToExpansion(float velocity, float expansion) {
- final float viewHeight = mStatusBar.getDisplayHeight();
+ final float viewHeight = mCentralSurfaces.getDisplayHeight();
final float currentHeight = viewHeight * mCurrentExpansion;
final float targetHeight = viewHeight * expansion;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java
index 3e5efb2..695b59a 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java
@@ -16,6 +16,7 @@
package com.android.systemui.dreams.touch;
+import android.graphics.Region;
import android.view.GestureDetector;
import android.view.InputEvent;
import android.view.MotionEvent;
@@ -34,6 +35,7 @@
import com.google.common.util.concurrent.ListenableFuture;
import java.util.Collection;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.Executor;
@@ -100,6 +102,10 @@
});
}
+ private int getSessionCount() {
+ return mActiveTouchSessions.size();
+ }
+
/**
* {@link TouchSessionImpl} implements {@link DreamTouchHandler.TouchSession} for
* {@link DreamOverlayTouchMonitor}. It enables the monitor to access the associated listeners
@@ -146,6 +152,11 @@
return mTouchMonitor.pop(this);
}
+ @Override
+ public int getActiveSessionCount() {
+ return mTouchMonitor.getSessionCount();
+ }
+
/**
* Returns the active listeners to receive touch events.
*/
@@ -229,12 +240,39 @@
public void onInputEvent(InputEvent ev) {
// No Active sessions are receiving touches. Create sessions for each listener
if (mActiveTouchSessions.isEmpty()) {
+ final HashMap<DreamTouchHandler, DreamTouchHandler.TouchSession> sessionMap =
+ new HashMap<>();
+
for (DreamTouchHandler handler : mHandlers) {
+ final Region initiationRegion = Region.obtain();
+ handler.getTouchInitiationRegion(initiationRegion);
+
+ if (!initiationRegion.isEmpty()) {
+ // Initiation regions require a motion event to determine pointer location
+ // within the region.
+ if (!(ev instanceof MotionEvent)) {
+ continue;
+ }
+
+ final MotionEvent motionEvent = (MotionEvent) ev;
+
+ // If the touch event is outside the region, then ignore.
+ if (!initiationRegion.contains(Math.round(motionEvent.getX()),
+ Math.round(motionEvent.getY()))) {
+ continue;
+ }
+ }
+
final TouchSessionImpl sessionStack =
new TouchSessionImpl(DreamOverlayTouchMonitor.this, null);
mActiveTouchSessions.add(sessionStack);
- handler.onSessionStart(sessionStack);
+ sessionMap.put(handler, sessionStack);
}
+
+ // Informing handlers of new sessions is delayed until we have all created so the
+ // final session is correct.
+ sessionMap.forEach((dreamTouchHandler, touchSession)
+ -> dreamTouchHandler.onSessionStart(touchSession));
}
// Find active sessions and invoke on InputEvent.
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamTouchHandler.java
index c73ff73..20008d5 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamTouchHandler.java
@@ -16,6 +16,7 @@
package com.android.systemui.dreams.touch;
+import android.graphics.Region;
import android.view.GestureDetector;
import com.android.systemui.shared.system.InputChannelCompat;
@@ -71,6 +72,19 @@
* if the popped {@link TouchSession} was the initial session or has already been popped.
*/
ListenableFuture<TouchSession> pop();
+
+ /**
+ * Returns the number of currently active sessions.
+ */
+ int getActiveSessionCount();
+ }
+
+ /**
+ * Returns the region the touch handler is interested in. By default, no region is specified,
+ * indicating the entire screen should be considered.
+ * @param region A {@link Region} that is passed in to the target entry touch region.
+ */
+ default void getTouchInitiationRegion(Region region) {
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
index e746caf..74d5bd5 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsComponent.java
@@ -33,7 +33,7 @@
import javax.inject.Provider;
/**
- * Manages power menu plugins and communicates power menu actions to the StatusBar.
+ * Manages power menu plugins and communicates power menu actions to the CentralSurfaces.
*/
@SysUISingleton
public class GlobalActionsComponent extends CoreStartable
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index e3886cd..7a278f7 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -121,7 +121,7 @@
import com.android.systemui.scrim.ScrimDrawable;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -236,7 +236,7 @@
private int mDialogPressDelay = DIALOG_PRESS_DELAY; // ms
protected Handler mMainHandler;
private int mSmallestScreenWidthDp;
- private final Optional<StatusBar> mStatusBarOptional;
+ private final Optional<CentralSurfaces> mCentralSurfacesOptional;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final DialogLaunchAnimator mDialogLaunchAnimator;
@@ -344,7 +344,7 @@
RingerModeTracker ringerModeTracker,
@Main Handler handler,
PackageManager packageManager,
- Optional<StatusBar> statusBarOptional,
+ Optional<CentralSurfaces> centralSurfacesOptional,
KeyguardUpdateMonitor keyguardUpdateMonitor,
DialogLaunchAnimator dialogLaunchAnimator) {
mContext = context;
@@ -374,7 +374,7 @@
mRingerModeTracker = ringerModeTracker;
mMainHandler = handler;
mSmallestScreenWidthDp = resources.getConfiguration().smallestScreenWidthDp;
- mStatusBarOptional = statusBarOptional;
+ mCentralSurfacesOptional = centralSurfacesOptional;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mDialogLaunchAnimator = dialogLaunchAnimator;
@@ -426,8 +426,8 @@
return mUiEventLogger;
}
- protected Optional<StatusBar> getStatusBar() {
- return mStatusBarOptional;
+ protected Optional<CentralSurfaces> getCentralSurfaces() {
+ return mCentralSurfacesOptional;
}
protected KeyguardUpdateMonitor getKeyguardUpdateMonitor() {
@@ -675,7 +675,7 @@
com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActionsLite,
mAdapter, mOverflowAdapter, mSysuiColorExtractor, mStatusBarService,
mNotificationShadeWindowController, this::onRefresh, mKeyguardShowing,
- mPowerAdapter, mUiEventLogger, mStatusBarOptional, mKeyguardUpdateMonitor,
+ mPowerAdapter, mUiEventLogger, mCentralSurfacesOptional, mKeyguardUpdateMonitor,
mLockPatternUtils);
dialog.setOnDismissListener(this);
@@ -866,7 +866,7 @@
mUiEventLogger.log(GlobalActionsEvent.GA_EMERGENCY_DIALER_PRESS);
if (mTelecomManager != null) {
// Close shade so user sees the activity
- mStatusBarOptional.ifPresent(StatusBar::collapseShade);
+ mCentralSurfacesOptional.ifPresent(CentralSurfaces::collapseShade);
Intent intent = mTelecomManager.createLaunchEmergencyDialerIntent(
null /* number */);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
@@ -998,7 +998,7 @@
mIActivityManager.requestInteractiveBugReport();
}
// Close shade so user sees the activity
- mStatusBarOptional.ifPresent(StatusBar::collapseShade);
+ mCentralSurfacesOptional.ifPresent(CentralSurfaces::collapseShade);
} catch (RemoteException e) {
}
}
@@ -1018,7 +1018,7 @@
mUiEventLogger.log(GlobalActionsEvent.GA_BUGREPORT_LONG_PRESS);
mIActivityManager.requestFullBugReport();
// Close shade so user sees the activity
- mStatusBarOptional.ifPresent(StatusBar::collapseShade);
+ mCentralSurfacesOptional.ifPresent(CentralSurfaces::collapseShade);
} catch (RemoteException e) {
}
return false;
@@ -2160,7 +2160,7 @@
protected final Runnable mOnRefreshCallback;
private UiEventLogger mUiEventLogger;
private GestureDetector mGestureDetector;
- private Optional<StatusBar> mStatusBarOptional;
+ private Optional<CentralSurfaces> mCentralSurfacesOptional;
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private LockPatternUtils mLockPatternUtils;
private float mWindowDimAmount;
@@ -2188,8 +2188,8 @@
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
if (distanceY < 0 && distanceY > distanceX
- && e1.getY() <= mStatusBarOptional.map(
- StatusBar::getStatusBarHeight).orElse(0)) {
+ && e1.getY() <= mCentralSurfacesOptional.map(
+ CentralSurfaces::getStatusBarHeight).orElse(0)) {
// Downwards scroll from top
openShadeAndDismiss();
return true;
@@ -2201,8 +2201,8 @@
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
if (velocityY > 0 && Math.abs(velocityY) > Math.abs(velocityX)
- && e1.getY() <= mStatusBarOptional.map(
- StatusBar::getStatusBarHeight).orElse(0)) {
+ && e1.getY() <= mCentralSurfacesOptional.map(
+ CentralSurfaces::getStatusBarHeight).orElse(0)) {
// Downwards fling from top
openShadeAndDismiss();
return true;
@@ -2217,7 +2217,8 @@
NotificationShadeWindowController notificationShadeWindowController,
Runnable onRefreshCallback, boolean keyguardShowing,
MyPowerOptionsAdapter powerAdapter, UiEventLogger uiEventLogger,
- Optional<StatusBar> statusBarOptional, KeyguardUpdateMonitor keyguardUpdateMonitor,
+ Optional<CentralSurfaces> centralSurfacesOptional,
+ KeyguardUpdateMonitor keyguardUpdateMonitor,
LockPatternUtils lockPatternUtils) {
// We set dismissOnDeviceLock to false because we have a custom broadcast receiver to
// dismiss this dialog when the device is locked.
@@ -2232,7 +2233,7 @@
mOnRefreshCallback = onRefreshCallback;
mKeyguardShowing = keyguardShowing;
mUiEventLogger = uiEventLogger;
- mStatusBarOptional = statusBarOptional;
+ mCentralSurfacesOptional = centralSurfacesOptional;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLockPatternUtils = lockPatternUtils;
mGestureDetector = new GestureDetector(mContext, mGestureListener);
@@ -2262,14 +2263,14 @@
private void openShadeAndDismiss() {
mUiEventLogger.log(GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
- if (mStatusBarOptional.map(StatusBar::isKeyguardShowing).orElse(false)) {
+ if (mCentralSurfacesOptional.map(CentralSurfaces::isKeyguardShowing).orElse(false)) {
// match existing lockscreen behavior to open QS when swiping from status bar
- mStatusBarOptional.ifPresent(
- statusBar -> statusBar.animateExpandSettingsPanel(null));
+ mCentralSurfacesOptional.ifPresent(
+ centralSurfaces -> centralSurfaces.animateExpandSettingsPanel(null));
} else {
// otherwise, swiping down should expand notification shade
- mStatusBarOptional.ifPresent(
- statusBar -> statusBar.animateExpandNotificationsPanel());
+ mCentralSurfacesOptional.ifPresent(
+ centralSurfaces -> centralSurfaces.animateExpandNotificationsPanel());
}
dismiss();
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index ae7147e..c01d2c3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -127,7 +127,7 @@
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
@@ -1667,11 +1667,16 @@
return;
}
- // if the keyguard is already showing, don't bother. check flags in both files
- // to account for the hiding animation which results in a delay and discrepancy
- // between flags
- if (mShowing && mKeyguardViewControllerLazy.get().isShowing()) {
- if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
+ // If the keyguard is already showing, don't bother unless it was in the process of going
+ // away. If it was going away, keyguard state may be out of sync and we should make sure to
+ // re-show it explicitly. Check flags in both files to account for the hiding animation
+ // which results in a delay and discrepancy between flags.
+ if ((mShowing && mKeyguardViewControllerLazy.get().isShowing())
+ && !mKeyguardStateController.isKeyguardGoingAway()) {
+ if (DEBUG) {
+ Log.d(TAG, "doKeyguard: not showing "
+ + "because it is already showing and not going away");
+ }
resetStateLocked();
return;
}
@@ -2186,7 +2191,14 @@
mKeyguardExitAnimationRunner = null;
mScreenOnCoordinator.setWakeAndUnlocking(false);
mPendingLock = false;
- setShowingLocked(true);
+
+ // If we're asked to re-show while the keyguard is going away, force callbacks to ensure
+ // that state is re-set correctly. Otherwise, we might short circuit since mShowing is
+ // true during the keyguard going away process, despite having partially set some state
+ // to unlocked.
+ setShowingLocked(
+ true, mKeyguardStateController.isKeyguardGoingAway() /* forceCallbacks */);
+
mKeyguardViewControllerLazy.get().show(options);
resetKeyguardDonePendingLocked();
mHideAnimationRun = false;
@@ -2356,14 +2368,28 @@
@Override
public void onAnimationFinished() throws RemoteException {
try {
+ // WindowManager always needs to know that this animation
+ // finished so it does not wait the 10s until timeout.
finishedCallback.onAnimationFinished();
} catch (RemoteException e) {
Slog.w(TAG, "Failed to call onAnimationFinished", e);
}
- onKeyguardExitFinished();
- mKeyguardViewControllerLazy.get().hide(0 /* startTime */,
- 0 /* fadeoutDuration */);
- mInteractionJankMonitor.end(CUJ_LOCKSCREEN_UNLOCK_ANIMATION);
+
+ // If we're not interactive, it means the device is going back to
+ // sleep. This happens if the power button is pressed during the
+ // activity launch. If we're going back to sleep, we should *not*
+ // run keyguard exit finished callbacks and hide the keyguard, since
+ // we are in the process of locking again and this might result in
+ // the device staying unlocked when it shouldn't.
+ // We need to directly query isInteractive rather than mGoingToSleep
+ // because mGoingToSleep is set in onStartedGoingToSleep, which is
+ // dispatched asynchronously.
+ if (mPM.isInteractive()) {
+ onKeyguardExitFinished();
+ mKeyguardViewControllerLazy.get().hide(0 /* startTime */,
+ 0 /* fadeoutDuration */);
+ mInteractionJankMonitor.end(CUJ_LOCKSCREEN_UNLOCK_ANIMATION);
+ }
}
@Override
@@ -2717,22 +2743,22 @@
}
/**
- * Registers the StatusBar to which the Keyguard View is mounted.
+ * Registers the CentralSurfaces to which the Keyguard View is mounted.
*
- * @param statusBar
+ * @param centralSurfaces
* @param panelView
* @param biometricUnlockController
* @param notificationContainer
* @param bypassController
* @return the View Controller for the Keyguard View this class is mediating.
*/
- public KeyguardViewController registerStatusBar(StatusBar statusBar,
+ public KeyguardViewController registerCentralSurfaces(CentralSurfaces centralSurfaces,
NotificationPanelViewController panelView,
@Nullable PanelExpansionStateManager panelExpansionStateManager,
BiometricUnlockController biometricUnlockController,
View notificationContainer, KeyguardBypassController bypassController) {
- mKeyguardViewControllerLazy.get().registerStatusBar(
- statusBar,
+ mKeyguardViewControllerLazy.get().registerCentralSurfaces(
+ centralSurfaces,
panelView,
panelExpansionStateManager,
biometricUnlockController,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 195ef1a..c69f947 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -48,10 +48,10 @@
import com.android.systemui.statusbar.NotificationShadeDepthController;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.KeyguardLiftController;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.util.DeviceConfigProxy;
@@ -64,7 +64,7 @@
import dagger.Provides;
/**
- * Dagger Module providing {@link StatusBar}.
+ * Dagger Module providing keyguard.
*/
@Module(subcomponents = {
KeyguardQsUserSwitchComponent.class,
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index 7b85050..fab06c2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -550,13 +550,13 @@
// Album art
val notif: Notification = sbn.notification
- var artworkBitmap = metadata?.getBitmap(MediaMetadata.METADATA_KEY_ART)
+ var artworkBitmap = metadata?.let { loadBitmapFromUri(it) }
+ if (artworkBitmap == null) {
+ artworkBitmap = metadata?.getBitmap(MediaMetadata.METADATA_KEY_ART)
+ }
if (artworkBitmap == null) {
artworkBitmap = metadata?.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART)
}
- if (artworkBitmap == null && metadata != null) {
- artworkBitmap = loadBitmapFromUri(metadata)
- }
val artWorkIcon = if (artworkBitmap == null) {
notif.getLargeIcon()
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index eee3955..c6f716c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -785,7 +785,11 @@
* @return true if this transformation is guided by an external progress like a finger
*/
private fun isCurrentlyInGuidedTransformation(): Boolean {
- return getTransformationProgress() >= 0
+ return hasValidStartAndEndLocations() && getTransformationProgress() >= 0
+ }
+
+ private fun hasValidStartAndEndLocations(): Boolean {
+ return previousLocation != -1 && desiredLocation != -1
}
/**
@@ -795,6 +799,9 @@
@TransformationType
fun calculateTransformationType(): Int {
if (isTransitioningToFullShade) {
+ if (inSplitShade) {
+ return TRANSFORMATION_TYPE_TRANSITION
+ }
return TRANSFORMATION_TYPE_FADE
}
if (previousLocation == LOCATION_LOCKSCREEN && desiredLocation == LOCATION_QS ||
@@ -961,6 +968,7 @@
(qsExpansion > 0.0f || inSplitShade) && !onLockscreen -> LOCATION_QS
qsExpansion > 0.4f && onLockscreen -> LOCATION_QS
!hasActiveMedia -> LOCATION_QS
+ onLockscreen && isSplitShadeExpanding() -> LOCATION_QS
onLockscreen && isTransformingToFullShadeAndInQQS() -> LOCATION_QQS
onLockscreen && allowedOnLockscreen -> LOCATION_LOCKSCREEN
else -> LOCATION_QQS
@@ -986,6 +994,10 @@
return location
}
+ private fun isSplitShadeExpanding(): Boolean {
+ return inSplitShade && isTransitioningToFullShade
+ }
+
/**
* Are we currently transforming to the full shade and already in QQS
*/
@@ -993,6 +1005,10 @@
if (!isTransitioningToFullShade) {
return false
}
+ if (inSplitShade) {
+ // Split shade doesn't use QQS.
+ return false
+ }
return fullShadeTransitionProgress > 0.5f
}
@@ -1000,6 +1016,10 @@
* Is the current transformationType fading
*/
private fun isCurrentlyFading(): Boolean {
+ if (isSplitShadeExpanding()) {
+ // Split shade always uses transition instead of fade.
+ return false
+ }
if (isTransitioningToFullShade) {
return true
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
index e2716e9..77873e8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
@@ -38,12 +38,10 @@
import android.text.TextUtils;
import android.text.style.StyleSpan;
import android.util.Log;
-import android.view.View;
import android.view.Window;
-import android.view.WindowManager;
-import android.widget.TextView;
import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.util.Utils;
public class MediaProjectionPermissionActivity extends Activity
@@ -56,7 +54,7 @@
private int mUid;
private IMediaProjectionManager mService;
- private AlertDialog mDialog;
+ private SystemUIDialog mDialog;
@Override
public void onCreate(Bundle icicle) {
@@ -143,25 +141,18 @@
dialogTitle = getString(R.string.media_projection_dialog_title, appName);
}
- View dialogTitleView = View.inflate(this, R.layout.media_projection_dialog_title, null);
- TextView titleText = (TextView) dialogTitleView.findViewById(R.id.dialog_title);
- titleText.setText(dialogTitle);
-
- mDialog = new AlertDialog.Builder(this)
- .setCustomTitle(dialogTitleView)
- .setMessage(dialogText)
- .setPositiveButton(R.string.media_projection_action_text, this)
- .setNegativeButton(android.R.string.cancel, this)
- .setOnCancelListener(this)
- .create();
+ mDialog = new SystemUIDialog(this);
+ mDialog.setTitle(dialogTitle);
+ mDialog.setIcon(R.drawable.ic_media_projection_permission);
+ mDialog.setMessage(dialogText);
+ mDialog.setPositiveButton(R.string.media_projection_action_text, this);
+ mDialog.setNeutralButton(android.R.string.cancel, this);
+ mDialog.setOnCancelListener(this);
mDialog.create();
mDialog.getButton(DialogInterface.BUTTON_POSITIVE).setFilterTouchesWhenObscured(true);
final Window w = mDialog.getWindow();
- // QS is not closed when pressing CastTile. Match the type of the dialog shown from the
- // tile.
- w.setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
w.addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
mDialog.show();
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
index 6ec2b6e..15b8f13 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt
@@ -22,6 +22,7 @@
import android.graphics.PixelFormat
import android.view.Gravity
import android.view.LayoutInflater
+import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
@@ -31,6 +32,7 @@
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.statusbar.gesture.TapGestureDetector
import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.view.ViewUtil
/**
* A superclass controller that provides common functionality for showing chips on the sender device
@@ -42,6 +44,7 @@
abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
internal val context: Context,
private val windowManager: WindowManager,
+ private val viewUtil: ViewUtil,
@Main private val mainExecutor: DelayableExecutor,
private val tapGestureDetector: TapGestureDetector,
@LayoutRes private val chipLayoutRes: Int
@@ -84,7 +87,7 @@
// Add view if necessary
if (oldChipView == null) {
- tapGestureDetector.addOnGestureDetectedCallback(TAG, this::removeChip)
+ tapGestureDetector.addOnGestureDetectedCallback(TAG, this::onScreenTapped)
windowManager.addView(chipView, windowLayoutParams)
}
@@ -127,6 +130,15 @@
appIconView.setImageDrawable(appIcon)
appIconView.visibility = visibility
}
+
+ private fun onScreenTapped(e: MotionEvent) {
+ val view = chipView ?: return
+ // If the tap is within the chip bounds, we shouldn't hide the chip (in case users think the
+ // chip is tappable).
+ if (!viewUtil.touchIsWithinView(view, e.x, e.y)) {
+ removeChip()
+ }
+ }
}
// Used in CTS tests UpdateMediaTapToTransferSenderDisplayTest and
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index b6f1aea..3d43ebe 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -31,6 +31,7 @@
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.gesture.TapGestureDetector
import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.view.ViewUtil
import javax.inject.Inject
/**
@@ -43,11 +44,17 @@
commandQueue: CommandQueue,
context: Context,
windowManager: WindowManager,
+ viewUtil: ViewUtil,
mainExecutor: DelayableExecutor,
tapGestureDetector: TapGestureDetector,
@Main private val mainHandler: Handler,
) : MediaTttChipControllerCommon<ChipStateReceiver>(
- context, windowManager, mainExecutor, tapGestureDetector, R.layout.media_ttt_chip_receiver
+ context,
+ windowManager,
+ viewUtil,
+ mainExecutor,
+ tapGestureDetector,
+ R.layout.media_ttt_chip_receiver
) {
private val commandQueueCallbacks = object : CommandQueue.Callbacks {
override fun updateMediaTapToTransferReceiverDisplay(
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
index fef17fdc..180e4ee 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
@@ -32,6 +32,7 @@
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.gesture.TapGestureDetector
import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.view.ViewUtil
import javax.inject.Inject
/**
@@ -43,10 +44,11 @@
commandQueue: CommandQueue,
context: Context,
windowManager: WindowManager,
+ viewUtil: ViewUtil,
@Main mainExecutor: DelayableExecutor,
tapGestureDetector: TapGestureDetector,
) : MediaTttChipControllerCommon<ChipStateSender>(
- context, windowManager, mainExecutor, tapGestureDetector, R.layout.media_ttt_chip
+ context, windowManager, viewUtil, mainExecutor, tapGestureDetector, R.layout.media_ttt_chip
) {
private val commandQueueCallbacks = object : CommandQueue.Callbacks {
override fun updateMediaTapToTransferSenderDisplay(
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
index 5e9edb7..a1a3198 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -49,8 +49,7 @@
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.system.QuickStepContract;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -79,7 +78,7 @@
Dumpable {
private final AccessibilityManager mAccessibilityManager;
private final Lazy<AssistManager> mAssistManagerLazy;
- private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
+ private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
private final UserTracker mUserTracker;
private final SystemActions mSystemActions;
private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
@@ -113,7 +112,7 @@
SystemActions systemActions,
OverviewProxyService overviewProxyService,
Lazy<AssistManager> assistManagerLazy,
- Lazy<Optional<StatusBar>> statusBarOptionalLazy,
+ Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
NavigationModeController navigationModeController,
UserTracker userTracker,
DumpManager dumpManager) {
@@ -121,7 +120,7 @@
mContentResolver = mContext.getContentResolver();
mAccessibilityManager = accessibilityManager;
mAssistManagerLazy = assistManagerLazy;
- mStatusBarOptionalLazy = statusBarOptionalLazy;
+ mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
mUserTracker = userTracker;
mSystemActions = systemActions;
accessibilityManager.addAccessibilityServicesStateChangeListener(
@@ -295,8 +294,8 @@
* {@link InputMethodService} and the keyguard states.
*/
public boolean isImeShown(int vis) {
- View shadeWindowView = mStatusBarOptionalLazy.get().get().getNotificationShadeWindowView();
- boolean isKeyguardShowing = mStatusBarOptionalLazy.get().get().isKeyguardShowing();
+ View shadeWindowView = mCentralSurfacesOptionalLazy.get().get().getNotificationShadeWindowView();
+ boolean isKeyguardShowing = mCentralSurfacesOptionalLazy.get().get().isKeyguardShowing();
boolean imeVisibleOnShade = shadeWindowView != null && shadeWindowView.isAttachedToWindow()
&& shadeWindowView.getRootWindowInsets().isVisible(WindowInsets.Type.ime());
return imeVisibleOnShade
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 76914f3..3ab1216 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -53,8 +53,8 @@
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
import static com.android.systemui.statusbar.phone.BarTransitions.TransitionMode;
-import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_WINDOW_STATE;
-import static com.android.systemui.statusbar.phone.StatusBar.dumpBarTransitions;
+import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG_WINDOW_STATE;
+import static com.android.systemui.statusbar.phone.CentralSurfaces.dumpBarTransitions;
import android.annotation.IdRes;
import android.app.ActivityTaskManager;
@@ -134,9 +134,9 @@
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.BarTransitions;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.pip.Pip;
@@ -176,7 +176,7 @@
private final MetricsLogger mMetricsLogger;
private final Lazy<AssistManager> mAssistManagerLazy;
private final SysUiState mSysUiFlagsContainer;
- private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
+ private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
private final ShadeController mShadeController;
private final NotificationRemoteInputManager mNotificationRemoteInputManager;
private final OverviewProxyService mOverviewProxyService;
@@ -486,7 +486,7 @@
CommandQueue commandQueue,
Optional<Pip> pipOptional,
Optional<Recents> recentsOptional,
- Lazy<Optional<StatusBar>> statusBarOptionalLazy,
+ Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
ShadeController shadeController,
NotificationRemoteInputManager notificationRemoteInputManager,
NotificationShadeDepthController notificationShadeDepthController,
@@ -509,7 +509,7 @@
mMetricsLogger = metricsLogger;
mAssistManagerLazy = assistManagerLazy;
mSysUiFlagsContainer = sysUiFlagsContainer;
- mStatusBarOptionalLazy = statusBarOptionalLazy;
+ mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
mShadeController = shadeController;
mNotificationRemoteInputManager = notificationRemoteInputManager;
mOverviewProxyService = overviewProxyService;
@@ -611,7 +611,7 @@
public void onViewAttachedToWindow(View v) {
final Display display = v.getDisplay();
mNavigationBarView.setComponents(mRecentsOptional);
- mNavigationBarView.setComponents(mStatusBarOptionalLazy.get().get().getPanelController());
+ mNavigationBarView.setComponents(mCentralSurfacesOptionalLazy.get().get().getPanelController());
mNavigationBarView.setDisabledFlags(mDisabledFlags1);
mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged);
mNavigationBarView.setOnTouchListener(this::onNavigationTouch);
@@ -1165,13 +1165,14 @@
// If an incoming call is ringing, HOME is totally disabled.
// (The user is already on the InCallUI at this point,
// and their ONLY options are to answer or reject the call.)
- final Optional<StatusBar> statusBarOptional = mStatusBarOptionalLazy.get();
+ final Optional<CentralSurfaces> centralSurfacesOptional = mCentralSurfacesOptionalLazy.get();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mHomeBlockedThisTouch = false;
if (mTelecomManagerOptional.isPresent()
&& mTelecomManagerOptional.get().isRinging()) {
- if (statusBarOptional.map(StatusBar::isKeyguardShowing).orElse(false)) {
+ if (centralSurfacesOptional.map(CentralSurfaces::isKeyguardShowing)
+ .orElse(false)) {
Log.i(TAG, "Ignoring HOME; there's a ringing incoming call. " +
"No heads up");
mHomeBlockedThisTouch = true;
@@ -1187,14 +1188,14 @@
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mHandler.removeCallbacks(mOnVariableDurationHomeLongClick);
- statusBarOptional.ifPresent(StatusBar::awakenDreams);
+ centralSurfacesOptional.ifPresent(CentralSurfaces::awakenDreams);
break;
}
return false;
}
private void onVerticalChanged(boolean isVertical) {
- mStatusBarOptionalLazy.get().ifPresent(
+ mCentralSurfacesOptionalLazy.get().ifPresent(
statusBar -> statusBar.setQsScrimEnabled(!isVertical));
}
@@ -1221,7 +1222,7 @@
AssistManager.INVOCATION_TYPE_KEY,
AssistManager.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS);
mAssistManagerLazy.get().startAssist(args);
- mStatusBarOptionalLazy.get().ifPresent(StatusBar::awakenDreams);
+ mCentralSurfacesOptionalLazy.get().ifPresent(CentralSurfaces::awakenDreams);
mNavigationBarView.abortCurrentGesture();
return true;
}
@@ -1247,7 +1248,7 @@
LatencyTracker.getInstance(mContext).onActionStart(
LatencyTracker.ACTION_TOGGLE_RECENTS);
}
- mStatusBarOptionalLazy.get().ifPresent(StatusBar::awakenDreams);
+ mCentralSurfacesOptionalLazy.get().ifPresent(CentralSurfaces::awakenDreams);
mCommandQueue.toggleRecentApps();
}
@@ -1431,7 +1432,7 @@
private void checkBarModes() {
// We only have status bar on default display now.
if (mIsOnDefaultDisplay) {
- mStatusBarOptionalLazy.get().ifPresent(StatusBar::checkBarModes);
+ mCentralSurfacesOptionalLazy.get().ifPresent(CentralSurfaces::checkBarModes);
} else {
checkNavBarModes();
}
@@ -1450,7 +1451,8 @@
*/
public void checkNavBarModes() {
final boolean anim =
- mStatusBarOptionalLazy.get().map(StatusBar::isDeviceInteractive).orElse(false)
+ mCentralSurfacesOptionalLazy.get().map(CentralSurfaces::isDeviceInteractive)
+ .orElse(false)
&& mNavigationBarWindowState != WINDOW_STATE_HIDDEN;
mNavigationBarView.getBarTransitions().transitionTo(mNavigationBarMode, anim);
}
@@ -1626,7 +1628,7 @@
private final CommandQueue mCommandQueue;
private final Optional<Pip> mPipOptional;
private final Optional<Recents> mRecentsOptional;
- private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
+ private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
private final ShadeController mShadeController;
private final NotificationRemoteInputManager mNotificationRemoteInputManager;
private final NotificationShadeDepthController mNotificationShadeDepthController;
@@ -1657,7 +1659,7 @@
CommandQueue commandQueue,
Optional<Pip> pipOptional,
Optional<Recents> recentsOptional,
- Lazy<Optional<StatusBar>> statusBarOptionalLazy,
+ Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
ShadeController shadeController,
NotificationRemoteInputManager notificationRemoteInputManager,
NotificationShadeDepthController notificationShadeDepthController,
@@ -1685,7 +1687,7 @@
mCommandQueue = commandQueue;
mPipOptional = pipOptional;
mRecentsOptional = recentsOptional;
- mStatusBarOptionalLazy = statusBarOptionalLazy;
+ mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
mShadeController = shadeController;
mNotificationRemoteInputManager = notificationRemoteInputManager;
mNotificationShadeDepthController = notificationShadeDepthController;
@@ -1710,7 +1712,7 @@
mOverviewProxyService, mNavigationModeController,
mAccessibilityButtonModeObserver, mStatusBarStateController,
mSysUiFlagsContainer, mBroadcastDispatcher, mCommandQueue, mPipOptional,
- mRecentsOptional, mStatusBarOptionalLazy,
+ mRecentsOptional, mCentralSurfacesOptionalLazy,
mShadeController, mNotificationRemoteInputManager,
mNotificationShadeDepthController, mMainHandler,
mNavbarOverlayController, mUiEventLogger, mNavBarHelper,
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index ede10a5..017bbdf 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -90,7 +90,7 @@
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.LightBarTransitionsController;
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.pip.Pip;
@@ -1362,7 +1362,7 @@
getContextDisplay().getRealSize(size);
pw.println("NavigationBarView:");
- pw.println(String.format(" this: " + StatusBar.viewInfo(this)
+ pw.println(String.format(" this: " + CentralSurfaces.viewInfo(this)
+ " " + visibilityToString(getVisibility())));
getWindowVisibleDisplayFrame(r);
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index dbd641b..039c333 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -118,6 +118,8 @@
private static final String ACTION_AUTO_SAVER_NO_THANKS =
"PNW.autoSaverNoThanks";
+ private static final String ACTION_ENABLE_SEVERE_BATTERY_DIALOG = "PNW.enableSevereDialog";
+
private static final String SETTINGS_ACTION_OPEN_BATTERY_SAVER_SETTING =
"android.settings.BATTERY_SAVER_SETTINGS";
public static final String BATTERY_SAVER_SCHEDULE_SCREEN_INTENT_ACTION =
@@ -253,20 +255,25 @@
}
protected void showWarningNotification() {
- final String percentage = NumberFormat.getPercentInstance()
- .format((double) mCurrentBatterySnapshot.getBatteryLevel() / 100.0);
-
- // get shared standard notification copy
- String title = mContext.getString(R.string.battery_low_title);
- String contentText;
-
- // get correct content text if notification is hybrid or not
- if (mCurrentBatterySnapshot.isHybrid()) {
- contentText = getHybridContentString(percentage);
- } else {
- contentText = mContext.getString(R.string.battery_low_percent_format, percentage);
+ if (showSevereLowBatteryDialog()) {
+ mContext.sendBroadcast(new Intent(ACTION_ENABLE_SEVERE_BATTERY_DIALOG)
+ .setPackage(mContext.getPackageName())
+ .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
+ | Intent.FLAG_RECEIVER_FOREGROUND));
+ // Reset the state once dialog been enabled
+ dismissLowBatteryNotification();
+ mPlaySound = false;
+ return;
}
+ final int warningLevel = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_lowBatteryWarningLevel);
+ final String percentage = NumberFormat.getPercentInstance()
+ .format((double) warningLevel / 100.0);
+ final String title = mContext.getString(R.string.battery_low_title);
+ final String contentText = mContext.getString(
+ R.string.battery_low_description, percentage);
+
final Notification.Builder nb =
new Notification.Builder(mContext, NotificationChannels.BATTERY)
.setSmallIcon(R.drawable.ic_power_low)
@@ -284,7 +291,7 @@
}
// Make the notification red if the percentage goes below a certain amount or the time
// remaining estimate is disabled
- if (!mCurrentBatterySnapshot.isHybrid() || mBucket < 0
+ if (!mCurrentBatterySnapshot.isHybrid() || mBucket < -1
|| mCurrentBatterySnapshot.getTimeRemainingMillis()
< mCurrentBatterySnapshot.getSevereThresholdMillis()) {
nb.setColor(Utils.getColorAttrDefaultColor(mContext, android.R.attr.colorError));
@@ -303,6 +310,13 @@
mNoMan.notifyAsUser(TAG_BATTERY, SystemMessage.NOTE_POWER_LOW, n, UserHandle.ALL);
}
+ private boolean showSevereLowBatteryDialog() {
+ final boolean isSevereState = !mCurrentBatterySnapshot.isHybrid() || mBucket < -1;
+ final boolean useSevereDialog = mContext.getResources().getBoolean(
+ R.bool.config_severe_battery_dialog);
+ return isSevereState && useSevereDialog;
+ }
+
private void showAutoSaverSuggestionNotification() {
final CharSequence message = mContext.getString(R.string.auto_saver_text);
final Notification.Builder nb =
@@ -662,8 +676,7 @@
// If there's no link, use the string with no "learn more".
if (TextUtils.isEmpty(learnMoreUrl)) {
- return mContext.getText(
- com.android.internal.R.string.battery_saver_description);
+ return mContext.getText(R.string.battery_low_intro);
}
// If we have a link, use the string with the "learn more" link.
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 642af59..56528c9 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -47,7 +47,7 @@
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -69,7 +69,7 @@
private static final long TEMPERATURE_LOGGING_INTERVAL = DateUtils.HOUR_IN_MILLIS;
private static final int MAX_RECENT_TEMPS = 125; // TEMPERATURE_LOGGING_INTERVAL plus a buffer
static final long THREE_HOURS_IN_MILLIS = DateUtils.HOUR_IN_MILLIS * 3;
- private static final int CHARGE_CYCLE_PERCENT_RESET = 45;
+ private static final int CHARGE_CYCLE_PERCENT_RESET = 30;
private static final long SIX_HOURS_MILLIS = Duration.ofHours(6).toMillis();
public static final int NO_ESTIMATE_AVAILABLE = -1;
private static final String BOOT_COUNT_KEY = "boot_count";
@@ -108,17 +108,17 @@
private IThermalEventListener mUsbThermalEventListener;
private final BroadcastDispatcher mBroadcastDispatcher;
private final CommandQueue mCommandQueue;
- private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
+ private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
@Inject
public PowerUI(Context context, BroadcastDispatcher broadcastDispatcher,
- CommandQueue commandQueue, Lazy<Optional<StatusBar>> statusBarOptionalLazy,
+ CommandQueue commandQueue, Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
WarningsUI warningsUI, EnhancedEstimates enhancedEstimates,
PowerManager powerManager) {
super(context);
mBroadcastDispatcher = broadcastDispatcher;
mCommandQueue = commandQueue;
- mStatusBarOptionalLazy = statusBarOptionalLazy;
+ mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
mWarnings = warningsUI;
mEnhancedEstimates = enhancedEstimates;
mPowerManager = powerManager;
@@ -206,7 +206,8 @@
*
* 1 means that the battery is "ok"
* 0 means that the battery is between "ok" and what we should warn about.
- * less than 0 means that the battery is low
+ * less than 0 means that the battery is low, -1 means the battery is reaching warning level,
+ * -2 means the battery is reaching severe level.
*/
private int findBatteryLevelBucket(int level) {
if (level >= mLowBatteryAlertCloseLevel) {
@@ -388,12 +389,8 @@
@VisibleForTesting
void maybeShowHybridWarning(BatteryStateSnapshot currentSnapshot,
BatteryStateSnapshot lastSnapshot) {
- // if we are now over 45% battery & 6 hours remaining so we can trigger hybrid
- // notification again
- final long timeRemainingMillis = currentSnapshot.getTimeRemainingMillis();
- if (currentSnapshot.getBatteryLevel() >= CHARGE_CYCLE_PERCENT_RESET
- && (timeRemainingMillis > SIX_HOURS_MILLIS
- || timeRemainingMillis == NO_ESTIMATE_AVAILABLE)) {
+ // if we are now over 30% battery, we can trigger hybrid notification again
+ if (currentSnapshot.getBatteryLevel() >= CHARGE_CYCLE_PERCENT_RESET) {
mLowWarningShownThisChargeCycle = false;
mSevereWarningShownThisChargeCycle = false;
if (DEBUG) {
@@ -403,6 +400,7 @@
final boolean playSound = currentSnapshot.getBucket() != lastSnapshot.getBucket()
|| lastSnapshot.getPlugged();
+ final long timeRemainingMillis = currentSnapshot.getTimeRemainingMillis();
if (shouldShowHybridWarning(currentSnapshot)) {
mWarnings.showLowBatteryWarning(playSound);
@@ -444,19 +442,13 @@
return false;
}
- final long timeRemainingMillis = snapshot.getTimeRemainingMillis();
// Only show the low warning if enabled once per charge cycle & no battery saver
- final boolean canShowWarning = snapshot.isLowWarningEnabled()
- && !mLowWarningShownThisChargeCycle && !snapshot.isPowerSaver()
- && ((timeRemainingMillis != NO_ESTIMATE_AVAILABLE
- && timeRemainingMillis < snapshot.getLowThresholdMillis())
- || snapshot.getBatteryLevel() <= snapshot.getLowLevelThreshold());
+ final boolean canShowWarning = !mLowWarningShownThisChargeCycle && !snapshot.isPowerSaver()
+ && snapshot.getBatteryLevel() <= snapshot.getLowLevelThreshold();
// Only show the severe warning once per charge cycle
final boolean canShowSevereWarning = !mSevereWarningShownThisChargeCycle
- && ((timeRemainingMillis != NO_ESTIMATE_AVAILABLE
- && timeRemainingMillis < snapshot.getSevereThresholdMillis())
- || snapshot.getBatteryLevel() <= snapshot.getSevereLevelThreshold());
+ && snapshot.getBatteryLevel() <= snapshot.getSevereLevelThreshold();
final boolean canShow = canShowWarning || canShowSevereWarning;
@@ -712,8 +704,10 @@
int status = temp.getStatus();
if (status >= Temperature.THROTTLING_EMERGENCY) {
- final Optional<StatusBar> statusBarOptional = mStatusBarOptionalLazy.get();
- if (!statusBarOptional.map(StatusBar::isDeviceInVrMode).orElse(false)) {
+ final Optional<CentralSurfaces> centralSurfacesOptional =
+ mCentralSurfacesOptionalLazy.get();
+ if (!centralSurfacesOptional.map(CentralSurfaces::isDeviceInVrMode)
+ .orElse(false)) {
mWarnings.showHighTemperatureWarning();
Slog.d(TAG, "SkinThermalEventListener: notifyThrottling was called "
+ ", current skin status = " + status
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
index 89735c3..5d9361d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
@@ -370,6 +370,7 @@
PowerExemptionManager.REASON_SYSTEM_UID,
PowerExemptionManager.REASON_DEVICE_DEMO_MODE -> UIControl.HIDE_ENTRY
+ PowerExemptionManager.REASON_ALLOWLISTED_PACKAGE,
PowerExemptionManager.REASON_DEVICE_OWNER,
PowerExemptionManager.REASON_PROFILE_OWNER,
PowerExemptionManager.REASON_PROC_STATE_PERSISTENT,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt b/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
index 8afb793..95b4b72 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
@@ -118,7 +118,7 @@
// If the privacy chip is visible, it means there were some indicators
uiEventLogger.log(PrivacyChipEvent.ONGOING_INDICATORS_CHIP_CLICK)
if (safetyCenterEnabled) {
- showSafetyHub()
+ showSafetyCenter()
} else {
privacyDialogController.showDialog(privacyChip.context)
}
@@ -131,16 +131,16 @@
updatePrivacyIconSlots()
}
- private fun showSafetyHub() {
+ private fun showSafetyCenter() {
backgroundExecutor.execute {
val usage = ArrayList(permGroupUsage())
privacyLogger.logUnfilteredPermGroupUsage(usage)
- val startSafetyHub = Intent(Intent.ACTION_VIEW_SAFETY_HUB)
- startSafetyHub.putParcelableArrayListExtra(PermissionManager.EXTRA_PERMISSION_USAGES,
+ val startSafetyCenter = Intent(Intent.ACTION_VIEW_SAFETY_CENTER_QS)
+ startSafetyCenter.putParcelableArrayListExtra(PermissionManager.EXTRA_PERMISSION_USAGES,
usage)
- startSafetyHub.flags = Intent.FLAG_ACTIVITY_NEW_TASK
+ startSafetyCenter.flags = Intent.FLAG_ACTIVITY_NEW_TASK
uiExecutor.execute {
- activityStarter.startActivity(startSafetyHub, true,
+ activityStarter.startActivity(startSafetyCenter, true,
ActivityLaunchAnimator.Controller.fromView(privacyChip))
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 3c7933f..3ef7220 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -513,7 +513,8 @@
mContainer.setExpansion(expansion);
final float translationScaleY = (mInSplitShade
? 1 : QSAnimator.SHORT_PARALLAX_AMOUNT) * (expansion - 1);
- boolean onKeyguardAndExpanded = isKeyguardState() && !mShowCollapsedOnKeyguard;
+ boolean onKeyguard = isKeyguardState();
+ boolean onKeyguardAndExpanded = onKeyguard && !mShowCollapsedOnKeyguard;
if (!mHeaderAnimating && !headerWillBeAnimating()) {
getView().setTranslationY(
onKeyguardAndExpanded
@@ -547,6 +548,7 @@
mHeader.updateResources();
}
}
+ mQSPanelController.setIsOnKeyguard(onKeyguard);
mFooter.setExpansion(onKeyguardAndExpanded ? 1 : expansion);
mQSFooterActionController.setExpansion(onKeyguardAndExpanded ? 1 : expansion);
mQSPanelController.setRevealExpansion(expansion);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 5126fcb..b04d752 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -110,6 +110,8 @@
private final ArrayMap<View, Integer> mChildrenLayoutTop = new ArrayMap<>();
private final Rect mClippingRect = new Rect();
private boolean mUseNewFooter = false;
+ private ViewGroup mMediaHostView;
+ private boolean mShouldMoveMediaOnExpansion = true;
public QSPanel(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -289,9 +291,15 @@
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
if (move) {
+ int topOffset;
+ if (child == mMediaHostView && !mShouldMoveMediaOnExpansion) {
+ topOffset = 0;
+ } else {
+ topOffset = tileHeightOffset;
+ }
int top = Objects.requireNonNull(mChildrenLayoutTop.get(child));
- child.setLeftTopRightBottom(child.getLeft(), top + tileHeightOffset,
- child.getRight(), top + tileHeightOffset + child.getHeight());
+ child.setLeftTopRightBottom(child.getLeft(), top + topOffset,
+ child.getRight(), top + topOffset + child.getHeight());
}
if (child == mTileLayout) {
move = true;
@@ -463,6 +471,7 @@
if (!mUsingMediaPlayer) {
return;
}
+ mMediaHostView = hostView;
ViewGroup newParent = horizontal ? mHorizontalLinearLayout : this;
ViewGroup currentParent = (ViewGroup) hostView.getParent();
if (currentParent != newParent) {
@@ -656,6 +665,19 @@
updatePadding();
}
+ /**
+ * Sets whether the media container should move during the expansion of the QS Panel.
+ *
+ * As the QS Panel expands and the QS unsquish, the views below the QS tiles move to adapt to
+ * the new height of the QS tiles.
+ *
+ * In some cases this might not be wanted for media. One example is when there is a transition
+ * animation of the media container happening on split shade lock screen.
+ */
+ public void setShouldMoveMediaOnExpansion(boolean shouldMoveMediaOnExpansion) {
+ mShouldMoveMediaOnExpansion = shouldMoveMediaOnExpansion;
+ }
+
private class H extends Handler {
private static final int ANNOUNCE_FOR_ACCESSIBILITY = 1;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index 9834129..0014279 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -33,8 +33,10 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.media.MediaFlags;
import com.android.systemui.media.MediaHierarchyManager;
import com.android.systemui.media.MediaHost;
+import com.android.systemui.media.MediaHostState;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.qs.customize.QSCustomizerController;
import com.android.systemui.qs.dagger.QSScope;
@@ -63,6 +65,7 @@
private final FalsingManager mFalsingManager;
private final BrightnessController mBrightnessController;
private final BrightnessSliderController mBrightnessSliderController;
+ private final MediaFlags mMediaFlags;
private final BrightnessMirrorHandler mBrightnessMirrorHandler;
private final FeatureFlags mFeatureFlags;
@@ -102,7 +105,8 @@
DumpManager dumpManager, MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
QSLogger qsLogger, BrightnessController.Factory brightnessControllerFactory,
BrightnessSliderController.Factory brightnessSliderFactory,
- FalsingManager falsingManager, FeatureFlags featureFlags) {
+ FalsingManager falsingManager, FeatureFlags featureFlags,
+ MediaFlags mediaFlags) {
super(view, qstileHost, qsCustomizerController, usingMediaPlayer, mediaHost,
metricsLogger, uiEventLogger, qsLogger, dumpManager);
mQSFgsManagerFooter = qsFgsManagerFooter;
@@ -113,6 +117,7 @@
mFalsingManager = falsingManager;
mBrightnessSliderController = brightnessSliderFactory.create(getContext(), mView);
+ mMediaFlags = mediaFlags;
mView.setBrightnessView(mBrightnessSliderController.getRootView());
mBrightnessController = brightnessControllerFactory.create(mBrightnessSliderController);
@@ -133,7 +138,14 @@
}
private void updateMediaExpansion() {
- mMediaHost.setExpansion(Utils.shouldUseSplitNotificationShade(getResources()) ? 0 : 1);
+ boolean inSplitShade = Utils.shouldUseSplitNotificationShade(getResources());
+ float expansion;
+ if (inSplitShade && !mMediaFlags.useMediaSessionLayout()) {
+ expansion = MediaHostState.COLLAPSED;
+ } else {
+ expansion = MediaHostState.EXPANDED;
+ }
+ mMediaHost.setExpansion(expansion);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 3172aa9..6572daa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -419,6 +419,16 @@
return mView.getBrightnessView();
}
+ /** Sets whether we are currently on lock screen. */
+ public void setIsOnKeyguard(boolean isOnKeyguard) {
+ boolean isOnSplitShadeLockscreen = mShouldUseSplitNotificationShade && isOnKeyguard;
+ // When the split shade is expanding on lockscreen, the media container transitions from the
+ // lockscreen to QS.
+ // We have to prevent the media container position from moving during the transition to have
+ // a smooth translation animation without stuttering.
+ mView.setShouldMoveMediaOnExpansion(!isOnSplitShadeLockscreen);
+ }
+
/** */
public static final class TileRecord {
public TileRecord(QSTile tile, com.android.systemui.plugins.qs.QSTileView tileView) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index c693075..47af7de 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -53,7 +53,7 @@
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.phone.AutoTileManager;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
@@ -102,7 +102,7 @@
private final StatusBarIconController mIconController;
private final ArrayList<QSFactory> mQsFactories = new ArrayList<>();
private int mCurrentUser;
- private final Optional<StatusBar> mStatusBarOptional;
+ private final Optional<CentralSurfaces> mCentralSurfacesOptional;
private Context mUserContext;
private UserTracker mUserTracker;
private SecureSettings mSecureSettings;
@@ -121,7 +121,7 @@
Provider<AutoTileManager> autoTiles,
DumpManager dumpManager,
BroadcastDispatcher broadcastDispatcher,
- Optional<StatusBar> statusBarOptional,
+ Optional<CentralSurfaces> centralSurfacesOptional,
QSLogger qsLogger,
UiEventLogger uiEventLogger,
UserTracker userTracker,
@@ -143,7 +143,7 @@
mTileLifeCycleManagerFactory = tileLifecycleManagerFactory;
mInstanceIdSequence = new InstanceIdSequence(MAX_QS_INSTANCE_ID);
- mStatusBarOptional = statusBarOptional;
+ mCentralSurfacesOptional = centralSurfacesOptional;
mQsFactories.add(defaultFactory);
pluginManager.addPluginListener(this, QSFactory.class, true);
@@ -227,17 +227,17 @@
@Override
public void collapsePanels() {
- mStatusBarOptional.ifPresent(StatusBar::postAnimateCollapsePanels);
+ mCentralSurfacesOptional.ifPresent(CentralSurfaces::postAnimateCollapsePanels);
}
@Override
public void forceCollapsePanels() {
- mStatusBarOptional.ifPresent(StatusBar::postAnimateForceCollapsePanels);
+ mCentralSurfacesOptional.ifPresent(CentralSurfaces::postAnimateForceCollapsePanels);
}
@Override
public void openPanels() {
- mStatusBarOptional.ifPresent(StatusBar::postAnimateOpenPanels);
+ mCentralSurfacesOptional.ifPresent(CentralSurfaces::postAnimateOpenPanels);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
index 597f7b7..f389df0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
@@ -26,7 +26,7 @@
import com.android.systemui.Dependency;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.shared.recents.IOverviewProxy;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import java.util.Optional;
@@ -42,7 +42,7 @@
private final static String TAG = "OverviewProxyRecentsImpl";
@Nullable
- private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
+ private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
private Context mContext;
private Handler mHandler;
@@ -51,8 +51,8 @@
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
@Inject
- public OverviewProxyRecentsImpl(Lazy<Optional<StatusBar>> statusBarOptionalLazy) {
- mStatusBarOptionalLazy = statusBarOptionalLazy;
+ public OverviewProxyRecentsImpl(Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy) {
+ mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
}
@Override
@@ -109,9 +109,10 @@
}
};
// Preload only if device for current user is unlocked
- final Optional<StatusBar> statusBarOptional = mStatusBarOptionalLazy.get();
- if (statusBarOptional.map(StatusBar::isKeyguardShowing).orElse(false)) {
- statusBarOptional.get().executeRunnableDismissingKeyguard(() -> {
+ final Optional<CentralSurfaces> centralSurfacesOptional =
+ mCentralSurfacesOptionalLazy.get();
+ if (centralSurfacesOptional.map(CentralSurfaces::isKeyguardShowing).orElse(false)) {
+ centralSurfacesOptional.get().executeRunnableDismissingKeyguard(() -> {
mHandler.post(toggleRecents);
}, null, true /* dismissShade */, false /* afterKeyguardGone */,
true /* deferred */);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 7ceaee5..a3dea1c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -102,7 +102,7 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
import com.android.systemui.statusbar.policy.CallbackController;
import com.android.wm.shell.back.BackAnimation;
@@ -146,7 +146,7 @@
private final Context mContext;
private final Optional<Pip> mPipOptional;
- private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
+ private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
private final Optional<SplitScreen> mSplitScreenOptional;
private SysUiState mSysUiState;
private final Handler mHandler;
@@ -186,7 +186,7 @@
@Override
public void startScreenPinning(int taskId) {
verifyCallerAndClearCallingIdentityPostMain("startScreenPinning", () ->
- mStatusBarOptionalLazy.get().ifPresent(
+ mCentralSurfacesOptionalLazy.get().ifPresent(
statusBar -> statusBar.showScreenPinningRequest(taskId,
false /* allowCancel */)));
}
@@ -207,9 +207,9 @@
public void onStatusBarMotionEvent(MotionEvent event) {
verifyCallerAndClearCallingIdentity("onStatusBarMotionEvent", () -> {
// TODO move this logic to message queue
- mStatusBarOptionalLazy.get().ifPresent(statusBar -> {
+ mCentralSurfacesOptionalLazy.get().ifPresent(centralSurfaces -> {
if (event.getActionMasked() == ACTION_DOWN) {
- statusBar.getPanelController().startExpandLatencyTracking();
+ centralSurfaces.getPanelController().startExpandLatencyTracking();
}
mHandler.post(() -> {
int action = event.getActionMasked();
@@ -217,7 +217,7 @@
mInputFocusTransferStarted = true;
mInputFocusTransferStartY = event.getY();
mInputFocusTransferStartMillis = event.getEventTime();
- statusBar.onInputFocusTransfer(
+ centralSurfaces.onInputFocusTransfer(
mInputFocusTransferStarted, false /* cancel */,
0 /* velocity */);
}
@@ -225,7 +225,7 @@
mInputFocusTransferStarted = false;
float velocity = (event.getY() - mInputFocusTransferStartY)
/ (event.getEventTime() - mInputFocusTransferStartMillis);
- statusBar.onInputFocusTransfer(mInputFocusTransferStarted,
+ centralSurfaces.onInputFocusTransfer(mInputFocusTransferStarted,
action == ACTION_CANCEL,
velocity);
}
@@ -401,7 +401,7 @@
@Override
public void toggleNotificationPanel() {
verifyCallerAndClearCallingIdentityPostMain("toggleNotificationPanel", () ->
- mStatusBarOptionalLazy.get().ifPresent(StatusBar::togglePanel));
+ mCentralSurfacesOptionalLazy.get().ifPresent(CentralSurfaces::togglePanel));
}
@@ -555,7 +555,7 @@
@Inject
public OverviewProxyService(Context context, CommandQueue commandQueue,
Lazy<NavigationBarController> navBarControllerLazy,
- Lazy<Optional<StatusBar>> statusBarOptionalLazy,
+ Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
NavigationModeController navModeController,
NotificationShadeWindowController statusBarWinController, SysUiState sysUiState,
Optional<Pip> pipOptional,
@@ -573,7 +573,7 @@
super(broadcastDispatcher);
mContext = context;
mPipOptional = pipOptional;
- mStatusBarOptionalLazy = statusBarOptionalLazy;
+ mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
mHandler = new Handler();
mNavBarControllerLazy = navBarControllerLazy;
mStatusBarWinController = statusBarWinController;
@@ -665,7 +665,7 @@
final NavigationBarView navBarView =
mNavBarControllerLazy.get().getNavigationBarView(mContext.getDisplayId());
final NotificationPanelViewController panelController =
- mStatusBarOptionalLazy.get().get().getPanelController();
+ mCentralSurfacesOptionalLazy.get().get().getPanelController();
if (SysUiState.DEBUG) {
Log.d(TAG_OPS, "Updating sysui state flags: navBarFragment=" + navBarFragment
+ " navBarView=" + navBarView + " panelController=" + panelController);
@@ -731,9 +731,9 @@
public void cleanupAfterDeath() {
if (mInputFocusTransferStarted) {
mHandler.post(() -> {
- mStatusBarOptionalLazy.get().ifPresent(statusBar -> {
+ mCentralSurfacesOptionalLazy.get().ifPresent(centralSurfaces -> {
mInputFocusTransferStarted = false;
- statusBar.onInputFocusTransfer(false, true /* cancel */, 0 /* velocity */);
+ centralSurfaces.onInputFocusTransfer(false, true /* cancel */, 0 /* velocity */);
});
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 7f130cb..15ad779 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -56,7 +56,7 @@
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.WindowManagerWrapper;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.util.leak.RotationUtils;
import java.util.ArrayList;
@@ -70,7 +70,7 @@
NavigationModeController.ModeChangedListener {
private final Context mContext;
- private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
+ private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
private final AccessibilityManager mAccessibilityService;
private final WindowManager mWindowManager;
@@ -83,9 +83,11 @@
private int taskId;
@Inject
- public ScreenPinningRequest(Context context, Lazy<Optional<StatusBar>> statusBarOptionalLazy) {
+ public ScreenPinningRequest(
+ Context context,
+ Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy) {
mContext = context;
- mStatusBarOptionalLazy = statusBarOptionalLazy;
+ mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
mAccessibilityService = (AccessibilityManager)
mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
mWindowManager = (WindowManager)
@@ -267,9 +269,10 @@
.setVisibility(View.INVISIBLE);
}
- final Optional<StatusBar> statusBarOptional = mStatusBarOptionalLazy.get();
+ final Optional<CentralSurfaces> centralSurfacesOptional =
+ mCentralSurfacesOptionalLazy.get();
NavigationBarView navigationBarView =
- statusBarOptional.map(StatusBar::getNavigationBarView).orElse(null);
+ centralSurfacesOptional.map(CentralSurfaces::getNavigationBarView).orElse(null);
final boolean recentsVisible = navigationBarView != null
&& navigationBarView.isRecentsButtonVisible();
boolean touchExplorationEnabled = mAccessibilityService.isTouchExplorationEnabled();
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
index f140446..daaa897 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java
@@ -24,7 +24,7 @@
import static com.android.systemui.screenshot.ScreenshotController.EXTRA_DISALLOW_ENTER_PIP;
import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ID;
import static com.android.systemui.screenshot.ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED;
-import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_SCREENSHOT;
+import static com.android.systemui.statusbar.phone.CentralSurfaces.SYSTEM_DIALOG_REASON_SCREENSHOT;
import android.app.ActivityOptions;
import android.app.PendingIntent;
@@ -36,7 +36,7 @@
import android.view.WindowManagerGlobal;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import java.util.Optional;
@@ -49,15 +49,15 @@
public class ActionProxyReceiver extends BroadcastReceiver {
private static final String TAG = "ActionProxyReceiver";
- private final StatusBar mStatusBar;
+ private final CentralSurfaces mCentralSurfaces;
private final ActivityManagerWrapper mActivityManagerWrapper;
private final ScreenshotSmartActions mScreenshotSmartActions;
@Inject
- public ActionProxyReceiver(Optional<StatusBar> statusBar,
+ public ActionProxyReceiver(Optional<CentralSurfaces> centralSurfacesOptional,
ActivityManagerWrapper activityManagerWrapper,
ScreenshotSmartActions screenshotSmartActions) {
- mStatusBar = statusBar.orElse(null);
+ mCentralSurfaces = centralSurfacesOptional.orElse(null);
mActivityManagerWrapper = activityManagerWrapper;
mScreenshotSmartActions = screenshotSmartActions;
}
@@ -89,8 +89,8 @@
};
- if (mStatusBar != null) {
- mStatusBar.executeRunnableDismissingKeyguard(startActivityRunnable, null,
+ if (mCentralSurfaces != null) {
+ mCentralSurfaces.executeRunnableDismissingKeyguard(startActivityRunnable, null,
true /* dismissShade */, true /* afterKeyguardGone */,
true /* deferred */);
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
index 991a68f..7801c68 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
@@ -54,7 +54,7 @@
import javax.inject.Inject;
public class BrightnessController implements ToggleSlider.Listener, MirroredBrightnessController {
- private static final String TAG = "StatusBar.BrightnessController";
+ private static final String TAG = "CentralSurfaces.BrightnessController";
private static final int SLIDER_ANIMATION_DURATION = 3000;
private static final int MSG_UPDATE_SLIDER = 1;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 645c5ac..8a02e59 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -23,7 +23,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
-import static com.android.systemui.statusbar.phone.StatusBar.ONLY_CORE_APPS;
+import static com.android.systemui.statusbar.phone.CentralSurfaces.ONLY_CORE_APPS;
import android.annotation.Nullable;
import android.app.ITransientNotificationCallback;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 8366bdd..17f42b1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -38,7 +38,7 @@
import com.android.systemui.statusbar.phone.LSShadeTransitionLogger
import com.android.systemui.statusbar.phone.NotificationPanelViewController
import com.android.systemui.statusbar.phone.ScrimController
-import com.android.systemui.statusbar.phone.StatusBar
+import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.Utils
import java.io.FileDescriptor
@@ -73,7 +73,7 @@
private var useSplitShade: Boolean = false
private lateinit var nsslController: NotificationStackScrollLayoutController
lateinit var notificationPanelController: NotificationPanelViewController
- lateinit var statusbar: StatusBar
+ lateinit var centralSurfaces: CentralSurfaces
lateinit var qS: QS
/**
@@ -197,7 +197,7 @@
// Bind the click listener of the shelf to go to the full shade
notificationShelfController.setOnClickListener {
if (statusBarStateController.state == StatusBarState.KEYGUARD) {
- statusbar.wakeUpIfDozing(SystemClock.uptimeMillis(), it, "SHADE_CLICK")
+ centralSurfaces.wakeUpIfDozing(SystemClock.uptimeMillis(), it, "SHADE_CLICK")
goToLockedShade(it)
}
}
@@ -224,7 +224,7 @@
if (nsslController.isInLockedDownShade()) {
logger.logDraggedDownLockDownShade(startingChild)
statusBarStateController.setLeaveOpenOnKeyguardHide(true)
- statusbar.dismissKeyguardThenExecute(OnDismissAction {
+ centralSurfaces.dismissKeyguardThenExecute(OnDismissAction {
nextHideKeyguardNeedsNoAnimation = true
false
}, cancelRunnable, false /* afterKeyguardGone */)
@@ -342,9 +342,7 @@
qS.setTransitionToFullShadeAmount(field, qSDragProgress)
notificationPanelController.setTransitionToFullShadeAmount(field,
false /* animate */, 0 /* delay */)
- // TODO: appear media also in split shade
- val mediaAmount = if (useSplitShade) 0f else field
- mediaHierarchyManager.setTransitionToFullShadeAmount(mediaAmount)
+ mediaHierarchyManager.setTransitionToFullShadeAmount(field)
transitionToShadeAmountCommon(field)
}
}
@@ -363,7 +361,7 @@
notificationPanelController.setKeyguardOnlyContentAlpha(1.0f - scrimProgress)
depthController.transitionToFullShadeProgress = scrimProgress
udfpsKeyguardViewController?.setTransitionToFullShadeProgress(scrimProgress)
- statusbar.setTransitionToFullShadeProgress(scrimProgress)
+ centralSurfaces.setTransitionToFullShadeProgress(scrimProgress)
}
private fun setDragDownAmountAnimated(
@@ -463,7 +461,7 @@
animationHandler: ((Long) -> Unit)? = null,
cancelAction: Runnable? = null
) {
- if (statusbar.isShadeDisabled) {
+ if (centralSurfaces.isShadeDisabled) {
cancelAction?.run()
logger.logShadeDisabledOnGoToLockedShade()
return
@@ -505,7 +503,7 @@
cancelAction?.run()
}
logger.logShowBouncerOnGoToLockedShade()
- statusbar.showBouncerWithDimissAndCancelIfKeyguard(onDismissAction, cancelHandler)
+ centralSurfaces.showBouncerWithDimissAndCancelIfKeyguard(onDismissAction, cancelHandler)
draggedDownEntry = entry
} else {
logger.logGoingToLockedShade(animationHandler != null)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index 01bdb40..68d35f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -32,10 +32,10 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.statusbar.dagger.StatusBarModule;
+import com.android.systemui.statusbar.dagger.CentralSurfacesModule;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.NotificationListenerWithPlugins;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.util.time.SystemClock;
import java.util.ArrayList;
@@ -54,7 +54,7 @@
@SuppressLint("OverrideAbstract")
public class NotificationListener extends NotificationListenerWithPlugins {
private static final String TAG = "NotificationListener";
- private static final boolean DEBUG = StatusBar.DEBUG;
+ private static final boolean DEBUG = CentralSurfaces.DEBUG;
private static final long MAX_RANKING_DELAY_MILLIS = 500L;
private final Context mContext;
@@ -69,7 +69,7 @@
private long mSkippingRankingUpdatesSince = -1;
/**
- * Injected constructor. See {@link StatusBarModule}.
+ * Injected constructor. See {@link CentralSurfacesModule}.
*/
@Inject
public NotificationListener(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 3730d12..052c57e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -16,9 +16,9 @@
package com.android.systemui.statusbar;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
-import static com.android.systemui.statusbar.phone.StatusBar.DEBUG_MEDIA_FAKE_ARTWORK;
-import static com.android.systemui.statusbar.phone.StatusBar.ENABLE_LOCKSCREEN_WALLPAPER;
-import static com.android.systemui.statusbar.phone.StatusBar.SHOW_LOCKSCREEN_MEDIA_ARTWORK;
+import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG_MEDIA_FAKE_ARTWORK;
+import static com.android.systemui.statusbar.phone.CentralSurfaces.ENABLE_LOCKSCREEN_WALLPAPER;
+import static com.android.systemui.statusbar.phone.CentralSurfaces.SHOW_LOCKSCREEN_MEDIA_ARTWORK;
import android.annotation.MainThread;
import android.annotation.NonNull;
@@ -55,7 +55,7 @@
import com.android.systemui.media.MediaDataManager;
import com.android.systemui.media.SmartspaceMediaData;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.dagger.StatusBarModule;
+import com.android.systemui.statusbar.dagger.CentralSurfacesModule;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -66,11 +66,11 @@
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.LockscreenWallpaper;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ScrimState;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.Utils;
import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -131,7 +131,7 @@
private final Context mContext;
private final ArrayList<MediaListener> mMediaListeners;
- private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
+ private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
private final MediaArtworkProcessor mMediaArtworkProcessor;
private final Set<AsyncTask<?, ?, ?>> mProcessArtworkTasks = new ArraySet<>();
@@ -172,11 +172,11 @@
};
/**
- * Injected constructor. See {@link StatusBarModule}.
+ * Injected constructor. See {@link CentralSurfacesModule}.
*/
public NotificationMediaManager(
Context context,
- Lazy<Optional<StatusBar>> statusBarOptionalLazy,
+ Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
Lazy<NotificationShadeWindowController> notificationShadeWindowController,
NotificationVisibilityProvider visibilityProvider,
NotificationEntryManager notificationEntryManager,
@@ -193,7 +193,7 @@
mKeyguardBypassController = keyguardBypassController;
mMediaListeners = new ArrayList<>();
// TODO: use KeyguardStateController#isOccluded to remove this dependency
- mStatusBarOptionalLazy = statusBarOptionalLazy;
+ mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
mNotificationShadeWindowController = notificationShadeWindowController;
mVisibilityProvider = visibilityProvider;
mEntryManager = notificationEntryManager;
@@ -575,7 +575,7 @@
* Refresh or remove lockscreen artwork from media metadata or the lockscreen wallpaper.
*/
public void updateMediaMetaData(boolean metaDataChanged, boolean allowEnterAnimation) {
- Trace.beginSection("StatusBar#updateMediaMetaData");
+ Trace.beginSection("CentralSurfaces#updateMediaMetaData");
if (!SHOW_LOCKSCREEN_MEDIA_ARTWORK) {
Trace.endSection();
return;
@@ -653,7 +653,8 @@
NotificationShadeWindowController windowController =
mNotificationShadeWindowController.get();
boolean hideBecauseOccluded =
- mStatusBarOptionalLazy.get().map(StatusBar::isOccluded).orElse(false);
+ mCentralSurfacesOptionalLazy.get()
+ .map(CentralSurfaces::isOccluded).orElse(false);
final boolean hasArtwork = artworkDrawable != null;
mColorExtractor.setHasMediaArtwork(hasMediaArtwork);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
index 17bf346..3b3b5a2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
@@ -19,7 +19,7 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
/**
- * An abstraction of something that presents notifications, e.g. StatusBar. Contains methods
+ * An abstraction of something that presents notifications, e.g. CentralSurfaces. Contains methods
* for both querying the state of the system (some modularised piece of functionality may
* want to act differently based on e.g. whether the presenter is visible to the user or not) and
* for affecting the state of the system (e.g. starting an intent, given that the presenter may
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 2b5453a..94a6d3e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -55,7 +55,7 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.dagger.StatusBarDependenciesModule;
+import com.android.systemui.statusbar.dagger.CentralSurfacesDependenciesModule;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -64,7 +64,7 @@
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
import com.android.systemui.statusbar.policy.RemoteInputView;
import com.android.systemui.util.DumpUtilsKt;
@@ -103,7 +103,7 @@
private final Handler mMainHandler;
private final ActionClickLogger mLogger;
- private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
+ private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
protected final Context mContext;
protected final NotifPipelineFlags mNotifPipelineFlags;
@@ -125,8 +125,8 @@
@Override
public boolean onInteraction(
View view, PendingIntent pendingIntent, RemoteViews.RemoteResponse response) {
- mStatusBarOptionalLazy.get().ifPresent(
- statusBar -> statusBar.wakeUpIfDozing(
+ mCentralSurfacesOptionalLazy.get().ifPresent(
+ centralSurfaces -> centralSurfaces.wakeUpIfDozing(
SystemClock.uptimeMillis(), view, "NOTIFICATION_CLICK"));
final NotificationEntry entry = getNotificationForParent(view.getParent());
@@ -253,7 +253,7 @@
};
/**
- * Injected constructor. See {@link StatusBarDependenciesModule}.
+ * Injected constructor. See {@link CentralSurfacesDependenciesModule}.
*/
public NotificationRemoteInputManager(
Context context,
@@ -263,7 +263,7 @@
NotificationVisibilityProvider visibilityProvider,
NotificationEntryManager notificationEntryManager,
RemoteInputNotificationRebuilder rebuilder,
- Lazy<Optional<StatusBar>> statusBarOptionalLazy,
+ Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
StatusBarStateController statusBarStateController,
@Main Handler mainHandler,
RemoteInputUriController remoteInputUriController,
@@ -276,7 +276,7 @@
mSmartReplyController = smartReplyController;
mVisibilityProvider = visibilityProvider;
mEntryManager = notificationEntryManager;
- mStatusBarOptionalLazy = statusBarOptionalLazy;
+ mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
mMainHandler = mainHandler;
mLogger = logger;
mBarService = IStatusBarService.Stub.asInterface(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java
index 4f70fdb..3b1fa17 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelfController.java
@@ -18,6 +18,7 @@
import android.view.View;
+import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationViewController;
import com.android.systemui.statusbar.notification.row.dagger.NotificationRowScope;
import com.android.systemui.statusbar.notification.stack.AmbientState;
@@ -25,7 +26,6 @@
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationIconContainer;
-import com.android.systemui.statusbar.phone.StatusBarNotificationPresenter;
import javax.inject.Inject;
@@ -114,8 +114,8 @@
return mView.getIntrinsicHeight();
}
- public void setOnActivatedListener(StatusBarNotificationPresenter presenter) {
- mView.setOnActivatedListener(presenter);
+ public void setOnActivatedListener(ActivatableNotificationView.OnActivatedListener listener) {
+ mView.setOnActivatedListener(listener);
}
public void setOnClickListener(View.OnClickListener onClickListener) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 092e86d..054543c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -32,7 +32,7 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.dagger.StatusBarModule;
+import com.android.systemui.statusbar.dagger.CentralSurfacesModule;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
import com.android.systemui.statusbar.notification.DynamicChildBindController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
@@ -45,7 +45,6 @@
import com.android.systemui.statusbar.notification.collection.render.NotifStackController;
import com.android.systemui.statusbar.notification.collection.render.NotifStats;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -97,7 +96,6 @@
private final Optional<Bubbles> mBubblesOptional;
private final DynamicPrivacyController mDynamicPrivacyController;
private final KeyguardBypassController mBypassController;
- private final ForegroundServiceSectionController mFgsSectionController;
private final NotifPipelineFlags mNotifPipelineFlags;
private AssistantFeedbackController mAssistantFeedbackController;
private final KeyguardStateController mKeyguardStateController;
@@ -115,7 +113,7 @@
private boolean mIsHandleDynamicPrivacyChangeScheduled;
/**
- * Injected constructor. See {@link StatusBarModule}.
+ * Injected constructor. See {@link CentralSurfacesModule}.
*/
public NotificationViewHierarchyManager(
Context context,
@@ -129,7 +127,6 @@
KeyguardBypassController bypassController,
Optional<Bubbles> bubblesOptional,
DynamicPrivacyController privacyController,
- ForegroundServiceSectionController fgsSectionController,
DynamicChildBindController dynamicChildBindController,
LowPriorityInflationHelper lowPriorityInflationHelper,
AssistantFeedbackController assistantFeedbackController,
@@ -145,7 +142,6 @@
mVisualStabilityManager = visualStabilityManager;
mStatusBarStateController = (SysuiStatusBarStateController) statusBarStateController;
mEntryManager = notificationEntryManager;
- mFgsSectionController = fgsSectionController;
mNotifPipelineFlags = notifPipelineFlags;
Resources res = context.getResources();
mAlwaysExpandNonGroupedNotification =
@@ -417,8 +413,7 @@
&& mBubblesOptional.get().isBubbleNotificationSuppressedFromShade(
ent.getKey(), ent.getSbn().getGroupKey());
if (ent.isRowDismissed() || ent.isRowRemoved()
- || isBubbleNotificationSuppressedFromShade
- || mFgsSectionController.hasEntry(ent)) {
+ || isBubbleNotificationSuppressedFromShade) {
// we want to suppress removed notifications because they could
// temporarily become children if they were isolated before.
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/QsFrameTranslateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/QsFrameTranslateController.java
index 2e1762a..7807738 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/QsFrameTranslateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/QsFrameTranslateController.java
@@ -20,17 +20,17 @@
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
/**
* Calculates and moves the QS frame vertically.
*/
public abstract class QsFrameTranslateController {
- protected StatusBar mStatusBar;
+ protected CentralSurfaces mCentralSurfaces;
- public QsFrameTranslateController(StatusBar statusBar) {
- mStatusBar = statusBar;
+ public QsFrameTranslateController(CentralSurfaces centralSurfaces) {
+ mCentralSurfaces = centralSurfaces;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/QsFrameTranslateImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/QsFrameTranslateImpl.java
index c156797..33e2245 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/QsFrameTranslateImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/QsFrameTranslateImpl.java
@@ -21,7 +21,7 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import javax.inject.Inject;
@@ -32,8 +32,8 @@
public class QsFrameTranslateImpl extends QsFrameTranslateController {
@Inject
- public QsFrameTranslateImpl(StatusBar statusBar) {
- super(statusBar);
+ public QsFrameTranslateImpl(CentralSurfaces centralSurfaces) {
+ super(centralSurfaces);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
index 4ad01aa..058edda 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
@@ -25,7 +25,7 @@
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dumpable;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.statusbar.dagger.StatusBarModule;
+import com.android.systemui.statusbar.dagger.CentralSurfacesModule;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
@@ -45,7 +45,7 @@
private Callback mCallback;
/**
- * Injected constructor. See {@link StatusBarModule}.
+ * Injected constructor. See {@link CentralSurfacesModule}.
*/
public SmartReplyController(
DumpManager dumpManager,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index 02870a3..2763bd7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -420,8 +420,9 @@
* notified before unranked, and we will sort ranked listeners from low to high
*
* @deprecated This method exists only to solve latent inter-dependencies from refactoring
- * StatusBarState out of StatusBar.java. Any new listeners should be built not to need ranking
- * (i.e., they are non-dependent on the order of operations of StatusBarState listeners).
+ * StatusBarState out of CentralSurfaces.java. Any new listeners should be built not to need
+ * ranking (i.e., they are non-dependent on the order of operations of StatusBarState
+ * listeners).
*/
@Deprecated
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
index f0b2c2d..2b31901 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SysuiStatusBarStateController.java
@@ -25,7 +25,7 @@
import android.view.WindowInsetsController.Behavior;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import java.lang.annotation.Retention;
@@ -51,8 +51,9 @@
* notified before unranked, and we will sort ranked listeners from low to high
*
* @deprecated This method exists only to solve latent inter-dependencies from refactoring
- * StatusBarState out of StatusBar.java. Any new listeners should be built not to need ranking
- * (i.e., they are non-dependent on the order of operations of StatusBarState listeners).
+ * StatusBarState out of CentralSurfaces.java. Any new listeners should be built not to need
+ * ranking (i.e., they are non-dependent on the order of operations of StatusBarState
+ * listeners).
*/
@Deprecated
void addCallback(StateListener listener, int rank);
@@ -91,7 +92,7 @@
int getCurrentOrUpcomingState();
/**
- * Update the dozing state from {@link StatusBar}'s perspective
+ * Update the dozing state from {@link CentralSurfaces}'s perspective
* @param isDozing well, are we dozing?
* @return {@code true} if the state changed, else {@code false}
*/
@@ -116,7 +117,7 @@
void setAndInstrumentDozeAmount(View view, float dozeAmount, boolean animated);
/**
- * Update the expanded state from {@link StatusBar}'s perspective
+ * Update the expanded state from {@link CentralSurfaces}'s perspective
* @param expanded are we expanded?
* @return {@code true} if the state changed, else {@code false}
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
index fe5a699..41d2b65 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
@@ -843,7 +843,7 @@
}
}
- /** Box for StatusBar icon info */
+ /** Box for status bar icon info */
private static final class SbInfo {
final boolean showTriangle;
final int ratTypeIcon;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
index d65fa3a..a62a152 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
@@ -21,18 +21,18 @@
import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions
import com.android.systemui.statusbar.phone.PhoneStatusBarView
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent.StatusBarScope
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment
import com.android.systemui.statusbar.window.StatusBarWindowController
import java.lang.IllegalStateException
import javax.inject.Inject
/**
- * Responsible for creating the StatusBar window and initializing the root components of that window
- * (see [CollapsedStatusBarFragment])
+ * Responsible for creating the status bar window and initializing the root components of that
+ * window (see [CollapsedStatusBarFragment])
*/
-@StatusBarScope
+@CentralSurfacesScope
class StatusBarInitializer @Inject constructor(
private val windowController: StatusBarWindowController
) {
@@ -43,7 +43,7 @@
* Creates the status bar window and root views, and initializes the component
*/
fun initializeStatusBar(
- sbComponent: StatusBarComponent
+ centralSurfacesComponent: CentralSurfacesComponent
) {
windowController.fragmentHostManager.addTagListener(
CollapsedStatusBarFragment.TAG,
@@ -64,7 +64,7 @@
}).fragmentManager
.beginTransaction()
.replace(R.id.status_bar_container,
- sbComponent.createCollapsedStatusBarFragment(),
+ centralSurfacesComponent.createCollapsedStatusBarFragment(),
CollapsedStatusBarFragment.TAG)
.commit()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
similarity index 94%
rename from packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index c687e82..83290af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -60,11 +60,10 @@
import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
-import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ManagedProfileController;
import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl;
import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
@@ -88,12 +87,13 @@
import dagger.Provides;
/**
- * This module provides instances needed to construct {@link StatusBar}. These are moved to this
- * separate from {@link StatusBarModule} module so that components that wish to build their own
- * version of StatusBar can include just dependencies, without injecting StatusBar itself.
+ * This module provides instances needed to construct {@link CentralSurfaces}. These are moved to
+ * this separate from {@link CentralSurfacesModule} module so that components that wish to build
+ * their own version of CentralSurfaces can include just dependencies, without injecting
+ * CentralSurfaces itself.
*/
@Module
-public interface StatusBarDependenciesModule {
+public interface CentralSurfacesDependenciesModule {
/** */
@SysUISingleton
@Provides
@@ -105,7 +105,7 @@
NotificationVisibilityProvider visibilityProvider,
NotificationEntryManager notificationEntryManager,
RemoteInputNotificationRebuilder rebuilder,
- Lazy<Optional<StatusBar>> statusBarOptionalLazy,
+ Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
StatusBarStateController statusBarStateController,
Handler mainHandler,
RemoteInputUriController remoteInputUriController,
@@ -120,7 +120,7 @@
visibilityProvider,
notificationEntryManager,
rebuilder,
- statusBarOptionalLazy,
+ centralSurfacesOptionalLazy,
statusBarStateController,
mainHandler,
remoteInputUriController,
@@ -134,7 +134,7 @@
@Provides
static NotificationMediaManager provideNotificationMediaManager(
Context context,
- Lazy<Optional<StatusBar>> statusBarOptionalLazy,
+ Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
Lazy<NotificationShadeWindowController> notificationShadeWindowController,
NotificationVisibilityProvider visibilityProvider,
NotificationEntryManager notificationEntryManager,
@@ -148,7 +148,7 @@
DumpManager dumpManager) {
return new NotificationMediaManager(
context,
- statusBarOptionalLazy,
+ centralSurfacesOptionalLazy,
notificationShadeWindowController,
visibilityProvider,
notificationEntryManager,
@@ -198,7 +198,6 @@
KeyguardBypassController bypassController,
Optional<Bubbles> bubblesOptional,
DynamicPrivacyController privacyController,
- ForegroundServiceSectionController fgsSectionController,
DynamicChildBindController dynamicChildBindController,
LowPriorityInflationHelper lowPriorityInflationHelper,
AssistantFeedbackController assistantFeedbackController,
@@ -217,7 +216,6 @@
bypassController,
bubblesOptional,
privacyController,
- fgsSectionController,
dynamicChildBindController,
lowPriorityInflationHelper,
assistantFeedbackController,
@@ -261,6 +259,7 @@
@Provides
@SysUISingleton
static OngoingCallController provideOngoingCallController(
+ Context context,
CommonNotifCollection notifCollection,
SystemClock systemClock,
ActivityStarter activityStarter,
@@ -284,6 +283,7 @@
: Optional.empty();
OngoingCallController ongoingCallController =
new OngoingCallController(
+ context,
notifCollection,
ongoingCallFlags,
systemClock,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesModule.java
similarity index 88%
rename from packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesModule.java
index ad5ef20..99d4b2e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesModule.java
@@ -23,7 +23,7 @@
import dagger.Module;
/** */
-@Module(includes = {StatusBarPhoneModule.class, StatusBarDependenciesModule.class,
+@Module(includes = {StatusBarPhoneModule.class, CentralSurfacesDependenciesModule.class,
NotificationsModule.class, NotificationRowModule.class})
-public interface StatusBarModule {
+public interface CentralSurfacesModule {
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StartStatusBarModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StartCentralSurfacesModule.kt
similarity index 76%
rename from packages/SystemUI/src/com/android/systemui/statusbar/dagger/StartStatusBarModule.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/dagger/StartCentralSurfacesModule.kt
index 46c1abb..fe55dea7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StartStatusBarModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StartCentralSurfacesModule.kt
@@ -17,17 +17,17 @@
package com.android.systemui.statusbar.dagger
import com.android.systemui.CoreStartable
-import com.android.systemui.statusbar.phone.StatusBar
+import com.android.systemui.statusbar.phone.CentralSurfaces
import dagger.Binds
import dagger.Module
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
@Module
-interface StartStatusBarModule {
- /** Start the StatusBar */
+interface StartCentralSurfacesModule {
+ /** Start the CentralSurfaces */
@Binds
@IntoMap
- @ClassKey(StatusBar::class)
- abstract fun bindsStatusBar(statusBar: StatusBar): CoreStartable
-}
\ No newline at end of file
+ @ClassKey(CentralSurfaces::class)
+ abstract fun bindsCentralSurfaces(centralSurfaces: CentralSurfaces): CoreStartable
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GenericGestureDetector.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GenericGestureDetector.kt
index 76766b0..3a4731a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GenericGestureDetector.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/GenericGestureDetector.kt
@@ -22,6 +22,7 @@
import android.view.Choreographer
import android.view.Display
import android.view.InputEvent
+import android.view.MotionEvent
import com.android.systemui.shared.system.InputChannelCompat
import com.android.systemui.shared.system.InputMonitorCompat
@@ -43,13 +44,17 @@
* Active callbacks, each associated with a tag. Gestures will only be monitored if
* [callbacks.size] > 0.
*/
- private val callbacks: MutableMap<String, () -> Unit> = mutableMapOf()
+ private val callbacks: MutableMap<String, (MotionEvent) -> Unit> = mutableMapOf()
private var inputMonitor: InputMonitorCompat? = null
private var inputReceiver: InputChannelCompat.InputEventReceiver? = null
- /** Adds a callback that will be triggered when the tap gesture is detected. */
- fun addOnGestureDetectedCallback(tag: String, callback: () -> Unit) {
+ /**
+ * Adds a callback that will be triggered when the tap gesture is detected.
+ *
+ * The callback receive the last motion event in the gesture.
+ */
+ fun addOnGestureDetectedCallback(tag: String, callback: (MotionEvent) -> Unit) {
val callbacksWasEmpty = callbacks.isEmpty()
callbacks[tag] = callback
if (callbacksWasEmpty) {
@@ -68,9 +73,12 @@
/** Triggered each time a touch event occurs (and at least one callback is registered). */
abstract fun onInputEvent(ev: InputEvent)
- /** Should be called by subclasses when their specific gesture is detected. */
- internal fun onGestureDetected() {
- callbacks.values.forEach { it.invoke() }
+ /**
+ * Should be called by subclasses when their specific gesture is detected with the last motion
+ * event in the gesture.
+ */
+ internal fun onGestureDetected(e: MotionEvent) {
+ callbacks.values.forEach { it.invoke(e) }
}
/** Start listening to touch events. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt
index fcb285a..6115819 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeStatusBarAwayGestureHandler.kt
@@ -80,7 +80,7 @@
) {
monitoringCurrentTouch = false
logger.logGestureDetected(ev.y.toInt())
- onGestureDetected()
+ onGestureDetected(ev)
}
}
ACTION_CANCEL, ACTION_UP -> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/TapGestureDetector.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/TapGestureDetector.kt
index 4107ce2..7ffb07a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/TapGestureDetector.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/TapGestureDetector.kt
@@ -33,8 +33,8 @@
) : GenericGestureDetector(TapGestureDetector::class.simpleName!!) {
private val gestureListener = object : GestureDetector.SimpleOnGestureListener() {
- override fun onSingleTapUp(e: MotionEvent?): Boolean {
- onGestureDetected()
+ override fun onSingleTapUp(e: MotionEvent): Boolean {
+ onGestureDetected(e)
return true
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ForegroundServiceDismissalFeatureController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ForegroundServiceDismissalFeatureController.kt
deleted file mode 100644
index 314051c..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ForegroundServiceDismissalFeatureController.kt
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification
-
-import android.content.Context
-import android.provider.DeviceConfig
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NOTIFICATIONS_ALLOW_FGS_DISMISSAL
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.util.DeviceConfigProxy
-import javax.inject.Inject
-
-private var sIsEnabled: Boolean? = null
-
-/**
- * Feature controller for NOTIFICATIONS_ALLOW_FGS_DISMISSAL config.
- */
-// TODO: this is really boilerplatey, make a base class that just wraps the device config
-@SysUISingleton
-class ForegroundServiceDismissalFeatureController @Inject constructor(
- val proxy: DeviceConfigProxy,
- val context: Context
-) {
- fun isForegroundServiceDismissalEnabled(): Boolean {
- return isEnabled(proxy)
- }
-}
-
-private fun isEnabled(proxy: DeviceConfigProxy): Boolean {
- if (sIsEnabled == null) {
- sIsEnabled = proxy.getBoolean(
- DeviceConfig.NAMESPACE_SYSTEMUI, NOTIFICATIONS_ALLOW_FGS_DISMISSAL, false)
- }
-
- return sIsEnabled!!
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
index da70621..392145a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
@@ -24,7 +24,7 @@
import com.android.systemui.DejankUtils;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.wm.shell.bubbles.Bubbles;
import java.util.Optional;
@@ -39,7 +39,7 @@
private static final String TAG = "NotificationClicker";
private final NotificationClickerLogger mLogger;
- private final Optional<StatusBar> mStatusBarOptional;
+ private final Optional<CentralSurfaces> mCentralSurfacesOptional;
private final Optional<Bubbles> mBubblesOptional;
private final NotificationActivityStarter mNotificationActivityStarter;
@@ -53,11 +53,11 @@
private NotificationClicker(
NotificationClickerLogger logger,
- Optional<StatusBar> statusBarOptional,
+ Optional<CentralSurfaces> centralSurfacesOptional,
Optional<Bubbles> bubblesOptional,
NotificationActivityStarter notificationActivityStarter) {
mLogger = logger;
- mStatusBarOptional = statusBarOptional;
+ mCentralSurfacesOptional = centralSurfacesOptional;
mBubblesOptional = bubblesOptional;
mNotificationActivityStarter = notificationActivityStarter;
}
@@ -69,7 +69,7 @@
return;
}
- mStatusBarOptional.ifPresent(statusBar -> statusBar.wakeUpIfDozing(
+ mCentralSurfacesOptional.ifPresent(centralSurfaces -> centralSurfaces.wakeUpIfDozing(
SystemClock.uptimeMillis(), v, "NOTIFICATION_CLICK"));
final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
@@ -137,13 +137,13 @@
/** Builds an instance. */
public NotificationClicker build(
- Optional<StatusBar> statusBarOptional,
+ Optional<CentralSurfaces> centralSurfacesOptional,
Optional<Bubbles> bubblesOptional,
NotificationActivityStarter notificationActivityStarter
) {
return new NotificationClicker(
mLogger,
- statusBarOptional,
+ centralSurfacesOptional,
bubblesOptional,
notificationActivityStarter);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index f97b936..ac5beec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -111,7 +111,6 @@
private final Lazy<NotificationRowBinder> mNotificationRowBinderLazy;
private final Lazy<NotificationRemoteInputManager> mRemoteInputManagerLazy;
private final LeakDetector mLeakDetector;
- private final ForegroundServiceDismissalFeatureController mFgsFeatureController;
private final IStatusBarService mStatusBarService;
private final NotifLiveDataStoreImpl mNotifLiveDataStore;
private final DumpManager mDumpManager;
@@ -159,7 +158,6 @@
Lazy<NotificationRowBinder> notificationRowBinderLazy,
Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
LeakDetector leakDetector,
- ForegroundServiceDismissalFeatureController fgsFeatureController,
IStatusBarService statusBarService,
NotifLiveDataStoreImpl notifLiveDataStore,
DumpManager dumpManager
@@ -170,7 +168,6 @@
mNotificationRowBinderLazy = notificationRowBinderLazy;
mRemoteInputManagerLazy = notificationRemoteInputManagerLazy;
mLeakDetector = leakDetector;
- mFgsFeatureController = fgsFeatureController;
mStatusBarService = statusBarService;
mNotifLiveDataStore = notifLiveDataStore;
mDumpManager = dumpManager;
@@ -958,7 +955,7 @@
Trace.endSection();
}
- /** dump the current active notification list. Called from StatusBar */
+ /** dump the current active notification list. Called from CentralSurfaces */
public void dump(PrintWriter pw, String indent) {
pw.println("NotificationEntryManager (Legacy)");
int filteredLen = mSortedAndFiltered.size();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
index 9da7d21..2c1296f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
@@ -8,12 +8,15 @@
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
import com.android.systemui.statusbar.phone.NotificationShadeWindowViewController
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent
import com.android.systemui.statusbar.policy.HeadsUpUtil
+import javax.inject.Inject
import kotlin.math.ceil
import kotlin.math.max
/** A provider of [NotificationLaunchAnimatorController]. */
-class NotificationLaunchAnimatorControllerProvider(
+@CentralSurfacesComponent.CentralSurfacesScope
+class NotificationLaunchAnimatorControllerProvider @Inject constructor(
private val notificationShadeWindowViewController: NotificationShadeWindowViewController,
private val notificationListContainer: NotificationListContainer,
private val headsUpManager: HeadsUpManagerPhone,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolver.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolver.kt
index 5dc0dcc..c71eade 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolver.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolver.kt
@@ -23,7 +23,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
-import com.android.systemui.statusbar.phone.StatusBar
+import com.android.systemui.statusbar.phone.CentralSurfaces
import javax.inject.Inject
@SysUISingleton
@@ -39,7 +39,7 @@
}
private fun resolveNotificationSdk(sbn: StatusBarNotification): Int {
- val pmUser = StatusBar.getPackageManagerForUser(context, sbn.user.identifier)
+ val pmUser = CentralSurfaces.getPackageManagerForUser(context, sbn.user.identifier)
var targetSdk = 0
// Extract target SDK version.
try {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationPresenterExtensions.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationPresenterExtensions.java
index 4ee08ed..bdbb0eb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationPresenterExtensions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationPresenterExtensions.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.notification.collection.legacy;
-import static com.android.systemui.statusbar.phone.StatusBar.SPEW;
+import static com.android.systemui.statusbar.phone.CentralSurfaces.SPEW;
import android.service.notification.StatusBarNotification;
import android.util.Log;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifPanelEventSource.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifPanelEventSource.kt
index 920d3c4..470737e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifPanelEventSource.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifPanelEventSource.kt
@@ -17,8 +17,8 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.phone.NotificationPanelViewController
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent.StatusBarScope
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope
import com.android.systemui.util.ListenerSet
import dagger.Binds
import dagger.Module
@@ -67,18 +67,18 @@
@JvmStatic
@Provides
@IntoSet
- @StatusBarScope
+ @CentralSurfacesScope
fun bindStartable(
manager: NotifPanelEventSourceManager,
notifPanelController: NotificationPanelViewController
- ): StatusBarComponent.Startable =
+ ): CentralSurfacesComponent.Startable =
EventSourceStatusBarStartableImpl(manager, notifPanelController)
}
/**
- * Management layer that bridges [SysUiSingleton] and [StatusBarScope]. Necessary because code that
- * wants to listen to [NotifPanelEventSource] lives in [SysUiSingleton], but the events themselves
- * come from [NotificationPanelViewController] in [StatusBarScope].
+ * Management layer that bridges [SysUiSingleton] and [CentralSurfacesScope]. Necessary because code
+ * that wants to listen to [NotifPanelEventSource] lives in [SysUiSingleton], but the events
+ * themselves come from [NotificationPanelViewController] in [CentralSurfacesScope].
*/
interface NotifPanelEventSourceManager : NotifPanelEventSource {
var eventSource: NotifPanelEventSource?
@@ -116,7 +116,7 @@
private class EventSourceStatusBarStartableImpl(
private val manager: NotifPanelEventSourceManager,
private val notifPanelController: NotificationPanelViewController
-) : StatusBarComponent.Startable {
+) : CentralSurfacesComponent.Startable {
override fun start() {
manager.eventSource = notifPanelController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index e3ebef9..51bbf1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -38,7 +38,6 @@
import com.android.systemui.statusbar.NotificationListener;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
-import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger;
@@ -87,7 +86,7 @@
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.leak.LeakDetector;
import com.android.systemui.wmshell.BubblesManager;
@@ -95,6 +94,8 @@
import java.util.Optional;
import java.util.concurrent.Executor;
+import javax.inject.Provider;
+
import dagger.Binds;
import dagger.Lazy;
import dagger.Module;
@@ -128,7 +129,6 @@
Lazy<NotificationRowBinder> notificationRowBinderLazy,
Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
LeakDetector leakDetector,
- ForegroundServiceDismissalFeatureController fgsFeatureController,
IStatusBarService statusBarService,
NotifLiveDataStoreImpl notifLiveDataStore,
DumpManager dumpManager) {
@@ -139,7 +139,6 @@
notificationRowBinderLazy,
notificationRemoteInputManagerLazy,
leakDetector,
- fgsFeatureController,
statusBarService,
notifLiveDataStore,
dumpManager);
@@ -150,7 +149,7 @@
@Provides
static NotificationGutsManager provideNotificationGutsManager(
Context context,
- Lazy<Optional<StatusBar>> statusBarOptionalLazy,
+ Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
@Main Handler mainHandler,
@Background Handler bgHandler,
AccessibilityManager accessibilityManager,
@@ -170,7 +169,7 @@
DumpManager dumpManager) {
return new NotificationGutsManager(
context,
- statusBarOptionalLazy,
+ centralSurfacesOptionalLazy,
mainHandler,
bgHandler,
accessibilityManager,
@@ -279,8 +278,8 @@
@Provides
static NotificationsController provideNotificationsController(
Context context,
- Lazy<NotificationsControllerImpl> realController,
- Lazy<NotificationsControllerStub> stubController) {
+ Provider<NotificationsControllerImpl> realController,
+ Provider<NotificationsControllerStub> stubController) {
if (context.getResources().getBoolean(R.bool.config_renderNotifications)) {
return realController.get();
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt
index a59d421..18abfca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt
@@ -23,11 +23,8 @@
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl
import com.android.systemui.statusbar.notification.collection.render.NotifStackController
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
-import com.android.systemui.statusbar.phone.StatusBar
-import com.android.wm.shell.bubbles.Bubbles
import java.io.FileDescriptor
import java.io.PrintWriter
-import java.util.Optional
/**
* The master controller for all notifications-related work
@@ -37,8 +34,6 @@
*/
interface NotificationsController {
fun initialize(
- statusBar: StatusBar,
- bubblesOptional: Optional<Bubbles>,
presenter: NotificationPresenter,
listContainer: NotificationListContainer,
stackController: NotifStackController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index 48f2daf..98f45fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -44,7 +44,7 @@
import com.android.systemui.statusbar.notification.row.NotifBindPipelineInitializer
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper
-import com.android.systemui.statusbar.phone.StatusBar
+import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.statusbar.policy.RemoteInputUriController
@@ -64,6 +64,7 @@
*/
@SysUISingleton
class NotificationsControllerImpl @Inject constructor(
+ private val centralSurfaces: Lazy<CentralSurfaces>,
private val notifPipelineFlags: NotifPipelineFlags,
private val notificationListener: NotificationListener,
private val entryManager: NotificationEntryManager,
@@ -86,12 +87,11 @@
private val headsUpViewBinder: HeadsUpViewBinder,
private val clickerBuilder: NotificationClicker.Builder,
private val animatedImageNotificationManager: AnimatedImageNotificationManager,
- private val peopleSpaceWidgetManager: PeopleSpaceWidgetManager
+ private val peopleSpaceWidgetManager: PeopleSpaceWidgetManager,
+ private val bubblesOptional: Optional<Bubbles>,
) : NotificationsController {
override fun initialize(
- statusBar: StatusBar,
- bubblesOptional: Optional<Bubbles>,
presenter: NotificationPresenter,
listContainer: NotificationListContainer,
stackController: NotifStackController,
@@ -109,7 +109,7 @@
notificationRowBinder.setNotificationClicker(
clickerBuilder.build(
- Optional.of(statusBar), bubblesOptional, notificationActivityStarter))
+ Optional.of(centralSurfaces.get()), bubblesOptional, notificationActivityStarter))
notificationRowBinder.setUpWithPresenter(
presenter,
listContainer,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt
index 1c9af11..66701d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt
@@ -24,11 +24,8 @@
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl
import com.android.systemui.statusbar.notification.collection.render.NotifStackController
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
-import com.android.systemui.statusbar.phone.StatusBar
-import com.android.wm.shell.bubbles.Bubbles
import java.io.FileDescriptor
import java.io.PrintWriter
-import java.util.Optional
import javax.inject.Inject
/**
@@ -39,8 +36,6 @@
) : NotificationsController {
override fun initialize(
- statusBar: StatusBar,
- bubblesOptional: Optional<Bubbles>,
presenter: NotificationPresenter,
listContainer: NotificationListContainer,
stackController: NotifStackController,
@@ -75,4 +70,4 @@
pw.println("Notification handling disabled")
pw.println()
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index dc39413..9fbd5c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -468,7 +468,7 @@
}
/**
- * Called by StatusBar to notify the logger that the panel expansion has changed.
+ * Called by CentralSurfaces to notify the logger that the panel expansion has changed.
* The panel may be showing any of the normal notification panel, the AOD, or the bouncer.
* @param isExpanded True if the panel is expanded.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/DungeonRow.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/DungeonRow.kt
deleted file mode 100644
index dbfa27f..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/DungeonRow.kt
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
-* Copyright (C) 2020 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
-
-package com.android.systemui.statusbar.notification.row
-
-import android.content.Context
-import android.util.AttributeSet
-import android.widget.LinearLayout
-import android.widget.TextView
-import com.android.systemui.R
-import com.android.systemui.statusbar.StatusBarIconView
-import com.android.systemui.statusbar.notification.collection.NotificationEntry
-
-class DungeonRow(context: Context, attrs: AttributeSet) : LinearLayout(context, attrs) {
- var entry: NotificationEntry? = null
- set(value) {
- field = value
- update()
- }
-
- private fun update() {
- (findViewById(R.id.app_name) as TextView).apply {
- text = entry?.row?.appName
- }
-
- (findViewById(R.id.icon) as StatusBarIconView).apply {
- set(entry?.icons?.statusBarIcon?.statusBarIcon)
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 1f7d930..c237e1d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -112,7 +112,7 @@
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.SwipeableView;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent;
@@ -388,7 +388,7 @@
}
return false;
} else {
- PackageManager packageManager = StatusBar.getPackageManagerForUser(
+ PackageManager packageManager = CentralSurfaces.getPackageManagerForUser(
context, sbn.getUser().getIdentifier());
Boolean isSystemNotification = null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ForegroundServiceDungeonView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ForegroundServiceDungeonView.kt
deleted file mode 100644
index 17396ad..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ForegroundServiceDungeonView.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.row
-
-import android.content.Context
-import android.util.AttributeSet
-import android.view.View
-
-import com.android.systemui.R
-
-class ForegroundServiceDungeonView(context: Context, attrs: AttributeSet)
- : StackScrollerDecorView(context, attrs) {
- override fun findContentView(): View? {
- return findViewById(R.id.foreground_service_dungeon)
- }
-
- override fun findSecondaryView(): View? {
- return null
- }
-
- override fun setVisible(visible: Boolean, animate: Boolean) {
- // Visibility is controlled by the ForegroundServiceSectionController
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 1530e523..4c69304 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -47,7 +47,7 @@
import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewWrapper;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.policy.InflatedSmartReplyState;
import com.android.systemui.statusbar.policy.InflatedSmartReplyViewHolder;
import com.android.systemui.statusbar.policy.SmartReplyStateInflater;
@@ -841,7 +841,7 @@
StatusBarNotification sbn = mEntry.getSbn();
final String ident = sbn.getPackageName() + "/0x"
+ Integer.toHexString(sbn.getId());
- Log.e(StatusBar.TAG, "couldn't inflate view for notification " + ident, e);
+ Log.e(CentralSurfaces.TAG, "couldn't inflate view for notification " + ident, e);
if (mCallback != null) {
mCallback.handleInflationException(mRow.getEntry(),
new InflationException("Couldn't inflate contentViews" + e));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 6d13024..ebe6f03 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -70,8 +70,8 @@
import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.wmshell.BubblesManager;
@@ -120,7 +120,7 @@
@VisibleForTesting
protected String mKeyToRemoveOnGutsClosed;
- private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
+ private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
private final Handler mMainHandler;
private final Handler mBgHandler;
private final Optional<BubblesManager> mBubblesManagerOptional;
@@ -139,7 +139,7 @@
* Injected constructor. See {@link NotificationsModule}.
*/
public NotificationGutsManager(Context context,
- Lazy<Optional<StatusBar>> statusBarOptionalLazy,
+ Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
@Main Handler mainHandler,
@Background Handler bgHandler,
AccessibilityManager accessibilityManager,
@@ -158,7 +158,7 @@
ShadeController shadeController,
DumpManager dumpManager) {
mContext = context;
- mStatusBarOptionalLazy = statusBarOptionalLazy;
+ mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
mMainHandler = mainHandler;
mBgHandler = bgHandler;
mAccessibilityManager = accessibilityManager;
@@ -342,7 +342,7 @@
}
StatusBarNotification sbn = row.getEntry().getSbn();
UserHandle userHandle = sbn.getUser();
- PackageManager pmUser = StatusBar.getPackageManagerForUser(mContext,
+ PackageManager pmUser = CentralSurfaces.getPackageManagerForUser(mContext,
userHandle.getIdentifier());
feedbackInfo.bindGuts(pmUser, sbn, row.getEntry(), row, mAssistantFeedbackController);
@@ -363,7 +363,7 @@
// Settings link is only valid for notifications that specify a non-system user
NotificationInfo.OnSettingsClickListener onSettingsClick = null;
UserHandle userHandle = sbn.getUser();
- PackageManager pmUser = StatusBar.getPackageManagerForUser(
+ PackageManager pmUser = CentralSurfaces.getPackageManagerForUser(
mContext, userHandle.getIdentifier());
final NotificationInfo.OnAppSettingsClickListener onAppSettingsClick =
(View v, Intent intent) -> {
@@ -416,7 +416,7 @@
// Settings link is only valid for notifications that specify a non-system user
NotificationInfo.OnSettingsClickListener onSettingsClick = null;
UserHandle userHandle = sbn.getUser();
- PackageManager pmUser = StatusBar.getPackageManagerForUser(
+ PackageManager pmUser = CentralSurfaces.getPackageManagerForUser(
mContext, userHandle.getIdentifier());
if (!userHandle.equals(UserHandle.ALL)
@@ -458,7 +458,7 @@
// Settings link is only valid for notifications that specify a non-system user
NotificationConversationInfo.OnSettingsClickListener onSettingsClick = null;
UserHandle userHandle = sbn.getUser();
- PackageManager pmUser = StatusBar.getPackageManagerForUser(
+ PackageManager pmUser = CentralSurfaces.getPackageManagerForUser(
mContext, userHandle.getIdentifier());
final NotificationConversationInfo.OnAppSettingsClickListener onAppSettingsClick =
(View v, Intent intent) -> {
@@ -571,11 +571,12 @@
.setLeaveOpenOnKeyguardHide(true);
}
- Optional<StatusBar> statusBarOptional = mStatusBarOptionalLazy.get();
- if (statusBarOptional.isPresent()) {
+ Optional<CentralSurfaces> centralSurfacesOptional =
+ mCentralSurfacesOptionalLazy.get();
+ if (centralSurfacesOptional.isPresent()) {
Runnable r = () -> mMainHandler.post(
() -> openGutsInternal(view, x, y, menuItem));
- statusBarOptional.get().executeRunnableDismissingKeyguard(
+ centralSurfacesOptional.get().executeRunnableDismissingKeyguard(
r,
null /* cancelAction */,
false /* dismissShade */,
@@ -584,7 +585,7 @@
return true;
}
/**
- * When {@link StatusBar} doesn't exist, falling through to call
+ * When {@link CentralSurfaces} doesn't exist, falling through to call
* {@link #openGutsInternal(View,int,int,NotificationMenuRowPlugin.MenuItem)}.
*/
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java
index a12d0073..1a7417a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java
@@ -26,7 +26,7 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import dagger.Binds;
import dagger.BindsInstance;
@@ -100,7 +100,7 @@
// but since this field is used in the guts, it must be accurate.
// Therefore we will only show the application label, or, failing that, the
// package name. No substitutions.
- PackageManager pmUser = StatusBar.getPackageManagerForUser(
+ PackageManager pmUser = CentralSurfaces.getPackageManagerForUser(
context, statusBarNotification.getUser().getIdentifier());
final String pkg = statusBarNotification.getPackageName();
try {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/NotificationRowScope.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/NotificationRowScope.java
index 4555b83..fa14123 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/NotificationRowScope.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/NotificationRowScope.java
@@ -24,7 +24,7 @@
import javax.inject.Scope;
/**
- * Scope annotation for singleton items within the StatusBarComponent.
+ * Scope annotation for singleton items within the CentralSurfacesComponent.
*/
@Documented
@Retention(RUNTIME)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ForegroundServiceSectionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ForegroundServiceSectionController.kt
deleted file mode 100644
index 75ca337..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ForegroundServiceSectionController.kt
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.stack
-
-import android.content.Context
-import android.service.notification.NotificationListenerService.REASON_CANCEL
-import android.service.notification.NotificationListenerService.REASON_CANCEL_ALL
-import android.service.notification.NotificationListenerService.REASON_CLICK
-import android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED
-import android.view.LayoutInflater
-import android.view.View
-import android.widget.LinearLayout
-import com.android.systemui.R
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController
-import com.android.systemui.statusbar.notification.NotificationEntryListener
-import com.android.systemui.statusbar.notification.NotificationEntryManager
-import com.android.systemui.statusbar.notification.collection.NotificationEntry
-import com.android.systemui.statusbar.notification.row.DungeonRow
-import com.android.systemui.util.Assert
-import javax.inject.Inject
-
-/**
- * Controller for the bottom area of NotificationStackScrollLayout. It owns swiped-away foreground
- * service notifications and can reinstantiate them when requested.
- */
-@SysUISingleton
-class ForegroundServiceSectionController @Inject constructor(
- val entryManager: NotificationEntryManager,
- val featureController: ForegroundServiceDismissalFeatureController
-) {
- private val TAG = "FgsSectionController"
- private var context: Context? = null
-
- private val entries = mutableSetOf<NotificationEntry>()
-
- private var entriesView: View? = null
-
- init {
- if (featureController.isForegroundServiceDismissalEnabled()) {
- entryManager.addNotificationRemoveInterceptor(this::shouldInterceptRemoval)
-
- entryManager.addNotificationEntryListener(object : NotificationEntryListener {
- override fun onPostEntryUpdated(entry: NotificationEntry) {
- if (entries.contains(entry)) {
- removeEntry(entry)
- addEntry(entry)
- update()
- }
- }
- })
- }
- }
-
- private fun shouldInterceptRemoval(
- key: String,
- entry: NotificationEntry?,
- reason: Int
- ): Boolean {
- Assert.isMainThread()
- val isClearAll = reason == REASON_CANCEL_ALL
- val isUserDismiss = reason == REASON_CANCEL || reason == REASON_CLICK
- // REASON_APP_CANCEL and REASON_APP_CANCEL_ALL are ignored, because the
- // foreground service associated with it is gone.
- val isSummaryCancel = reason == REASON_GROUP_SUMMARY_CANCELED
-
- if (entry == null) return false
-
- // We only want to retain notifications that the user dismissed
- // TODO: centralize the entry.isClearable logic and this so that it's clear when a notif is
- // clearable
- if (isUserDismiss && !entry.sbn.isClearable) {
- if (!hasEntry(entry)) {
- addEntry(entry)
- update()
- }
- // TODO: This isn't ideal. Slightly better would at least be to have NEM update the
- // notif list when an entry gets intercepted
- entryManager.updateNotifications(
- "FgsSectionController.onNotificationRemoveRequested")
- return true
- } else if ((isClearAll || isSummaryCancel) && !entry.sbn.isClearable) {
- // In the case where a FGS notification is part of a group that is cleared or a clear
- // all, we actually want to stop its removal but also not put it into the dungeon
- return true
- } else if (hasEntry(entry)) {
- removeEntry(entry)
- update()
- return false
- }
-
- return false
- }
-
- private fun removeEntry(entry: NotificationEntry) {
- Assert.isMainThread()
- entries.remove(entry)
- }
-
- private fun addEntry(entry: NotificationEntry) {
- Assert.isMainThread()
- entries.add(entry)
- }
-
- fun hasEntry(entry: NotificationEntry): Boolean {
- Assert.isMainThread()
- return entries.contains(entry)
- }
-
- fun initialize(context: Context) {
- this.context = context
- }
-
- fun createView(li: LayoutInflater): View {
- entriesView = li.inflate(R.layout.foreground_service_dungeon, null)
- // Start out gone
- entriesView!!.visibility = View.GONE
- return entriesView!!
- }
-
- private fun update() {
- Assert.isMainThread()
- if (entriesView == null) {
- throw IllegalStateException("ForegroundServiceSectionController is trying to show " +
- "dismissed fgs notifications without having been initialized!")
- }
-
- // TODO: these views should be recycled and not inflating on the main thread
- (entriesView!!.findViewById(R.id.entry_list) as LinearLayout).apply {
- removeAllViews()
- entries.sortedBy { it.ranking.rank }.forEach { entry ->
- val child = LayoutInflater.from(context)
- .inflate(R.layout.foreground_service_dungeon_row, null) as DungeonRow
-
- child.entry = entry
- child.setOnClickListener {
- removeEntry(child.entry!!)
- update()
- entry.row.unDismiss()
- entry.row.resetTranslation()
- entryManager.updateNotifications("ForegroundServiceSectionController.onClick")
- }
-
- addView(child)
- }
- }
-
- if (entries.isEmpty()) {
- entriesView?.visibility = View.GONE
- } else {
- entriesView?.visibility = View.VISIBLE
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 2c4db77..efe559a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -100,13 +100,12 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.FooterView;
-import com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonView;
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.HeadsUpTouchHelper;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.HeadsUpUtil;
import com.android.systemui.statusbar.policy.ScrollAdapter;
import com.android.systemui.util.Assert;
@@ -309,7 +308,7 @@
}
};
private NotificationStackScrollLogger mLogger;
- private StatusBar mStatusBar;
+ private CentralSurfaces mCentralSurfaces;
private int[] mTempInt2 = new int[2];
private boolean mGenerateChildOrderChangedEvent;
private HashSet<Runnable> mAnimationFinishedRunnables = new HashSet<>();
@@ -453,7 +452,6 @@
private Interpolator mHideXInterpolator = Interpolators.FAST_OUT_SLOW_IN;
private final NotificationSectionsManager mSectionsManager;
- private ForegroundServiceDungeonView mFgsSectionView;
private boolean mAnimateBottomOnLayout;
private float mLastSentAppear;
private float mLastSentExpandedHeight;
@@ -614,14 +612,6 @@
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
}
- void initializeForegroundServiceSection(ForegroundServiceDungeonView fgsSectionView) {
- if (mFgsSectionView != null) {
- return;
- }
- mFgsSectionView = fgsSectionView;
- addView(mFgsSectionView, -1);
- }
-
/**
* Set the overexpansion of the panel to be applied to the view.
*/
@@ -3969,7 +3959,7 @@
mAmbientState.setExpansionChanging(false);
if (!mIsExpanded) {
resetScrollPosition();
- mStatusBar.resetUserExpandedStates();
+ mCentralSurfaces.resetUserExpandedStates();
clearTemporaryViews();
clearUserLockedViews();
if (mSwipeHelper.isSwiping()) {
@@ -4601,8 +4591,8 @@
}
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
- public void setStatusBar(StatusBar statusBar) {
- this.mStatusBar = statusBar;
+ public void setCentralSurfaces(CentralSurfaces centralSurfaces) {
+ this.mCentralSurfaces = centralSurfaces;
}
@ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -5270,7 +5260,7 @@
Intent intent = showHistory
? new Intent(Settings.ACTION_NOTIFICATION_HISTORY)
: new Intent(Settings.ACTION_NOTIFICATION_SETTINGS);
- mStatusBar.startActivity(intent, true, true, Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ mCentralSurfaces.startActivity(intent, true, true, Intent.FLAG_ACTIVITY_SINGLE_TOP);
});
setEmptyShadeView(view);
}
@@ -5286,9 +5276,6 @@
// incremented in the following "changeViewPosition" calls so that its value is correct for
// subsequent calls.
int offsetFromEnd = 1;
- if (mFgsSectionView != null) {
- changeViewPosition(mFgsSectionView, getChildCount() - offsetFromEnd++);
- }
changeViewPosition(mFooterView, getChildCount() - offsetFromEnd++);
changeViewPosition(mEmptyShadeView, getChildCount() - offsetFromEnd++);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index d1c63e3..df6b8f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -86,7 +86,6 @@
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.ExpandAnimationParameters;
-import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
@@ -109,7 +108,6 @@
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
-import com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonView;
import com.android.systemui.statusbar.notification.row.NotificationGuts;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.NotificationSnooze;
@@ -119,8 +117,8 @@
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -143,7 +141,7 @@
/**
* Controller for {@link NotificationStackScrollLayout}.
*/
-@StatusBarComponent.StatusBarScope
+@CentralSurfacesComponent.CentralSurfacesScope
public class NotificationStackScrollLayoutController {
private static final String TAG = "StackScrollerController";
private static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG);
@@ -170,8 +168,6 @@
private final NotificationEntryManager mNotificationEntryManager;
private final IStatusBarService mIStatusBarService;
private final UiEventLogger mUiEventLogger;
- private final ForegroundServiceDismissalFeatureController mFgFeatureController;
- private final ForegroundServiceSectionController mFgServicesSectionController;
private final LayoutInflater mLayoutInflater;
private final NotificationRemoteInputManager mRemoteInputManager;
private final VisualStabilityManager mVisualStabilityManager;
@@ -180,8 +176,8 @@
private final SysuiStatusBarStateController mStatusBarStateController;
private final KeyguardBypassController mKeyguardBypassController;
private final NotificationLockscreenUserManager mLockscreenUserManager;
- // TODO: StatusBar should be encapsulated behind a Controller
- private final StatusBar mStatusBar;
+ // TODO: CentralSurfaces should be encapsulated behind a Controller
+ private final CentralSurfaces mCentralSurfaces;
private final SectionHeaderController mSilentHeaderController;
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
private final InteractionJankMonitor mJankMonitor;
@@ -334,7 +330,7 @@
mView.updateSensitiveness(mStatusBarStateController.goingToFullShade(),
mLockscreenUserManager.isAnyProfilePublicMode());
mView.onStatePostChange(mStatusBarStateController.fromShadeLocked());
- mNotificationEntryManager.updateNotifications("StatusBar state changed");
+ mNotificationEntryManager.updateNotifications("CentralSurfaces state changed");
}
};
@@ -435,7 +431,7 @@
@Override
public void onSnooze(StatusBarNotification sbn,
NotificationSwipeActionHelper.SnoozeOption snoozeOption) {
- mStatusBar.setNotificationSnoozed(sbn, snoozeOption);
+ mCentralSurfaces.setNotificationSnoozed(sbn, snoozeOption);
}
@Override
@@ -488,7 +484,7 @@
mView.addSwipedOutView(view);
mFalsingCollector.onNotificationDismissed();
if (mFalsingCollector.shouldEnforceBouncer()) {
- mStatusBar.executeRunnableDismissingKeyguard(
+ mCentralSurfaces.executeRunnableDismissingKeyguard(
null,
null /* cancelAction */,
false /* dismissShade */,
@@ -561,7 +557,7 @@
@Override
public float getFalsingThresholdFactor() {
- return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
+ return mCentralSurfaces.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
}
@Override
@@ -648,7 +644,7 @@
FalsingManager falsingManager,
@Main Resources resources,
NotificationSwipeHelper.Builder notificationSwipeHelperBuilder,
- StatusBar statusBar,
+ CentralSurfaces centralSurfaces,
ScrimController scrimController,
NotificationGroupManagerLegacy legacyGroupManager,
GroupExpansionManager groupManager,
@@ -660,8 +656,6 @@
LockscreenShadeTransitionController lockscreenShadeTransitionController,
IStatusBarService iStatusBarService,
UiEventLogger uiEventLogger,
- ForegroundServiceDismissalFeatureController fgFeatureController,
- ForegroundServiceSectionController fgServicesSectionController,
LayoutInflater layoutInflater,
NotificationRemoteInputManager remoteInputManager,
VisualStabilityManager visualStabilityManager,
@@ -691,7 +685,7 @@
mFalsingManager = falsingManager;
mResources = resources;
mNotificationSwipeHelperBuilder = notificationSwipeHelperBuilder;
- mStatusBar = statusBar;
+ mCentralSurfaces = centralSurfaces;
mScrimController = scrimController;
mJankMonitor = jankMonitor;
groupManager.registerGroupExpansionChangeListener(
@@ -699,7 +693,7 @@
legacyGroupManager.registerGroupChangeListener(new OnGroupChangeListener() {
@Override
public void onGroupsChanged() {
- mStatusBar.requestNotificationUpdate("onGroupsChanged");
+ mCentralSurfaces.requestNotificationUpdate("onGroupsChanged");
}
});
mNotifPipelineFlags = notifPipelineFlags;
@@ -709,8 +703,6 @@
mNotificationEntryManager = notificationEntryManager;
mIStatusBarService = iStatusBarService;
mUiEventLogger = uiEventLogger;
- mFgFeatureController = fgFeatureController;
- mFgServicesSectionController = fgServicesSectionController;
mLayoutInflater = layoutInflater;
mRemoteInputManager = remoteInputManager;
mVisualStabilityManager = visualStabilityManager;
@@ -724,7 +716,7 @@
mView.setController(this);
mView.setLogger(mLogger);
mView.setTouchHandler(new TouchHandler());
- mView.setStatusBar(mStatusBar);
+ mView.setCentralSurfaces(mCentralSurfaces);
mView.setClearAllAnimationListener(this::onAnimationEnd);
mView.setClearAllListener((selection) -> mUiEventLogger.log(
NotificationPanelEvent.fromSelection(selection)));
@@ -744,12 +736,6 @@
mNotificationRoundnessManager.setShouldRoundPulsingViews(
!mKeyguardBypassController.getBypassEnabled());
- if (mFgFeatureController.isForegroundServiceDismissalEnabled()) {
- mView.initializeForegroundServiceSection(
- (ForegroundServiceDungeonView) mFgServicesSectionController.createView(
- mLayoutInflater));
- }
-
mSwipeHelper = mNotificationSwipeHelperBuilder
.setSwipeDirection(SwipeHelper.X)
.setNotificationCallback(mNotificationCallback)
@@ -1420,7 +1406,7 @@
return mNotificationRoundnessManager;
}
- public NotificationListContainer getNotificationListContainer() {
+ NotificationListContainer getNotificationListContainer() {
return mNotificationListContainer;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutListContainerModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutListContainerModule.java
new file mode 100644
index 0000000..3dcaae2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutListContainerModule.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.stack;
+
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+public abstract class NotificationStackScrollLayoutListContainerModule {
+ @Provides
+ @CentralSurfacesComponent.CentralSurfacesScope
+ static NotificationListContainer provideListContainer(
+ NotificationStackScrollLayoutController nsslController) {
+ return nsslController.getNotificationListContainer();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index d5d1cea..fe637c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -717,7 +717,7 @@
public void run() {
mNotificationShadeWindowController.setForceDozeBrightness(false);
}
- }, StatusBar.FADE_KEYGUARD_DURATION_PULSING);
+ }, CentralSurfaces.FADE_KEYGUARD_DURATION_PULSING);
}
public void finishKeyguardFadingAway() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index 95e08ea..5642744 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -222,7 +222,7 @@
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
@@ -260,8 +260,20 @@
import dagger.Lazy;
-/** */
-public class StatusBar extends CoreStartable implements
+/**
+ * A class handling initialization and coordination between some of the key central surfaces in
+ * System UI: The notification shade, the keyguard (lockscreen), and the status bar.
+ *
+ * This class is not our ideal architecture because it doesn't enforce much isolation between these
+ * three mostly disparate surfaces. In an ideal world, this class would not exist. Instead, we would
+ * break it up into three modules -- one for each of those three surfaces -- and we would define any
+ * APIs that are needed for these surfaces to communicate with each other when necessary.
+ *
+ * <b>If at all possible, please avoid adding additional code to this monstrous class! Our goal is
+ * to break up this class into many small classes, and any code added here will slow down that goal.
+ * </b>
+ */
+public class CentralSurfaces extends CoreStartable implements
ActivityStarter,
LifecycleOwner {
public static final boolean MULTIUSER_DEBUG = false;
@@ -278,7 +290,7 @@
"com.android.systemui.statusbar.banner_action_cancel";
private static final String BANNER_ACTION_SETUP =
"com.android.systemui.statusbar.banner_action_setup";
- public static final String TAG = "StatusBar";
+ public static final String TAG = "CentralSurfaces";
public static final boolean DEBUG = false;
public static final boolean SPEW = false;
public static final boolean DUMPTRUCK = true; // extra dumpsys info
@@ -343,8 +355,9 @@
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
private final DreamOverlayStateController mDreamOverlayStateController;
- private StatusBarCommandQueueCallbacks mCommandQueueCallbacks;
+ private CentralSurfacesCommandQueueCallbacks mCommandQueueCallbacks;
private float mTransitionToFullShadeProgress = 0f;
+ private NotificationListContainer mNotifListContainer;
void onStatusBarWindowStateChanged(@WindowVisibleState int state) {
updateBubblesVisibility();
@@ -468,7 +481,7 @@
private PhoneStatusBarTransitions mStatusBarTransitions;
private AuthRippleController mAuthRippleController;
@WindowVisibleState private int mStatusBarWindowState = WINDOW_STATE_SHOWING;
- protected NotificationShadeWindowController mNotificationShadeWindowController;
+ protected final NotificationShadeWindowController mNotificationShadeWindowController;
private final StatusBarWindowController mStatusBarWindowController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@VisibleForTesting
@@ -494,10 +507,8 @@
protected NotificationShadeWindowViewController mNotificationShadeWindowViewController;
private final DozeParameters mDozeParameters;
private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
- private final StatusBarComponent.Factory mStatusBarComponentFactory;
+ private final CentralSurfacesComponent.Factory mCentralSurfacesComponentFactory;
private final PluginManager mPluginManager;
- private final StatusBarNotificationActivityStarter.Builder
- mStatusBarNotificationActivityStarterBuilder;
private final ShadeController mShadeController;
private final InitController mInitController;
@@ -543,7 +554,7 @@
private final MessageRouter mMessageRouter;
private final WallpaperManager mWallpaperManager;
- private StatusBarComponent mStatusBarComponent;
+ private CentralSurfacesComponent mCentralSurfacesComponent;
// Flags for disabling the status bar
// Two variables becaseu the first one evidently ran out of room for new flags.
@@ -665,7 +676,7 @@
private final ActivityLaunchAnimator mActivityLaunchAnimator;
private NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider;
- protected StatusBarNotificationPresenter mPresenter;
+ protected NotificationPresenter mPresenter;
private NotificationActivityStarter mNotificationActivityStarter;
private final Lazy<NotificationShadeDepthController> mNotificationShadeDepthControllerLazy;
private final Optional<BubblesManager> mBubblesManagerOptional;
@@ -684,13 +695,13 @@
/**
- * Public constructor for StatusBar.
+ * Public constructor for CentralSurfaces.
*
- * StatusBar is considered optional, and therefore can not be marked as @Inject directly.
+ * CentralSurfaces is considered optional, and therefore can not be marked as @Inject directly.
* Instead, an @Provide method is included. See {@link StatusBarPhoneModule}.
*/
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
- public StatusBar(
+ public CentralSurfaces(
Context context,
NotificationsController notificationsController,
FragmentService fragmentService,
@@ -750,10 +761,8 @@
DozeScrimController dozeScrimController,
VolumeComponent volumeComponent,
CommandQueue commandQueue,
- StatusBarComponent.Factory statusBarComponentFactory,
+ CentralSurfacesComponent.Factory centralSurfacesComponentFactory,
PluginManager pluginManager,
- StatusBarNotificationActivityStarter.Builder
- statusBarNotificationActivityStarterBuilder,
ShadeController shadeController,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
ViewMediatorCallback viewMediatorCallback,
@@ -848,9 +857,8 @@
mNotificationShadeDepthControllerLazy = notificationShadeDepthControllerLazy;
mVolumeComponent = volumeComponent;
mCommandQueue = commandQueue;
- mStatusBarComponentFactory = statusBarComponentFactory;
+ mCentralSurfacesComponentFactory = centralSurfacesComponentFactory;
mPluginManager = pluginManager;
- mStatusBarNotificationActivityStarterBuilder = statusBarNotificationActivityStarterBuilder;
mShadeController = shadeController;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mKeyguardViewMediatorCallback = viewMediatorCallback;
@@ -879,7 +887,7 @@
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
mStartingSurfaceOptional = startingSurfaceOptional;
mNotifPipelineFlags = notifPipelineFlags;
- lockscreenShadeTransitionController.setStatusbar(this);
+ lockscreenShadeTransitionController.setCentralSurfaces(this);
statusBarWindowStateController.addListener(this::onStatusBarWindowStateChanged);
mScreenOffAnimationController = screenOffAnimationController;
@@ -1111,7 +1119,7 @@
}
private void onFoldedStateChanged(boolean isFolded, boolean willGoToSleep) {
- Trace.beginSection("StatusBar#onFoldedStateChanged");
+ Trace.beginSection("CentralSurfaces#onFoldedStateChanged");
onFoldedStateChangedInternal(isFolded, willGoToSleep);
Trace.endSection();
}
@@ -1155,19 +1163,14 @@
updateTheme();
inflateStatusBarWindow();
- mNotificationShadeWindowViewController.setService(this, mNotificationShadeWindowController);
mNotificationShadeWindowView.setOnTouchListener(getStatusBarWindowTouchListener());
mWallpaperController.setRootView(mNotificationShadeWindowView);
- // TODO: Deal with the ugliness that comes from having some of the statusbar broken out
+ // TODO: Deal with the ugliness that comes from having some of the status bar broken out
// into fragments, but the rest here, it leaves some awkward lifecycle and whatnot.
- NotificationListContainer notifListContainer =
- mStackScrollerController.getNotificationListContainer();
- mNotificationLogger.setUpWithContainer(notifListContainer);
-
+ mNotificationLogger.setUpWithContainer(mNotifListContainer);
mNotificationIconAreaController.setupShelf(mNotificationShelfController);
mPanelExpansionStateManager.addExpansionListener(mWakeUpCoordinator);
-
mUserSwitcherController.init(mNotificationShadeWindowView);
// Allow plugins to reference DarkIconDispatcher and StatusBarStateController
@@ -1175,7 +1178,7 @@
mPluginDependencyProvider.allowPluginDependency(StatusBarStateController.class);
// Set up CollapsedStatusBarFragment and PhoneStatusBarView
- StatusBarInitializer initializer = mStatusBarComponent.getStatusBarInitializer();
+ StatusBarInitializer initializer = mCentralSurfacesComponent.getStatusBarInitializer();
initializer.setStatusBarViewUpdatedListener(
(statusBarView, statusBarViewController, statusBarTransitions) -> {
mStatusBarView = statusBarView;
@@ -1192,7 +1195,7 @@
setBouncerShowingForStatusBarComponents(mBouncerShowing);
checkBarModes();
});
- initializer.initializeStatusBar(mStatusBarComponent);
+ initializer.initializeStatusBar(mCentralSurfacesComponent);
mStatusBarTouchableRegionManager.setup(this, mNotificationShadeWindowView);
mHeadsUpManager.addListener(mNotificationPanelViewController.getOnHeadsUpChangedListener());
@@ -1442,65 +1445,19 @@
mActivityLaunchAnimator.addListener(mActivityLaunchAnimatorListener);
mNotificationAnimationProvider = new NotificationLaunchAnimatorControllerProvider(
mNotificationShadeWindowViewController,
- mStackScrollerController.getNotificationListContainer(),
+ mNotifListContainer,
mHeadsUpManager,
- mJankMonitor
- );
-
- // TODO: inject this.
- mPresenter = new StatusBarNotificationPresenter(
- mContext,
- mNotificationPanelViewController,
- mHeadsUpManager,
- mNotificationShadeWindowView,
- mStackScrollerController,
- mDozeScrimController,
- mScrimController,
- mNotificationShadeWindowController,
- mDynamicPrivacyController,
- mKeyguardStateController,
- mKeyguardIndicationController,
- this /* statusBar */,
- mShadeController,
- mLockscreenShadeTransitionController,
- mCommandQueue,
- mViewHierarchyManager,
- mLockscreenUserManager,
- mStatusBarStateController,
- mNotifShadeEventSource,
- mEntryManager,
- mMediaManager,
- mGutsManager,
- mKeyguardUpdateMonitor,
- mLockscreenGestureLogger,
- mInitController,
- mNotificationInterruptStateProvider,
- mRemoteInputManager,
- mConfigurationController,
- mNotifPipelineFlags);
-
+ mJankMonitor);
mNotificationShelfController.setOnActivatedListener(mPresenter);
mRemoteInputManager.addControllerCallback(mNotificationShadeWindowController);
-
- mNotificationActivityStarter =
- mStatusBarNotificationActivityStarterBuilder
- .setStatusBar(this)
- .setActivityLaunchAnimator(mActivityLaunchAnimator)
- .setNotificationAnimatorControllerProvider(mNotificationAnimationProvider)
- .setNotificationPresenter(mPresenter)
- .setNotificationPanelViewController(mNotificationPanelViewController)
- .build();
mStackScrollerController.setNotificationActivityStarter(mNotificationActivityStarter);
mGutsManager.setNotificationActivityStarter(mNotificationActivityStarter);
-
mNotificationsController.initialize(
- this,
- mBubblesOptional,
mPresenter,
- mStackScrollerController.getNotificationListContainer(),
+ mNotifListContainer,
mStackScrollerController.getNotifStackController(),
mNotificationActivityStarter,
- mPresenter);
+ mCentralSurfacesComponent.getBindRowCallback());
}
/**
@@ -1558,30 +1515,34 @@
}
private void inflateStatusBarWindow() {
- if (mStatusBarComponent != null) {
+ if (mCentralSurfacesComponent != null) {
// Tear down
- for (StatusBarComponent.Startable startable : mStatusBarComponent.getStartables()) {
- startable.stop();
+ for (CentralSurfacesComponent.Startable s : mCentralSurfacesComponent.getStartables()) {
+ s.stop();
}
}
- mStatusBarComponent = mStatusBarComponentFactory.create();
- mFragmentService.addFragmentInstantiationProvider(mStatusBarComponent);
+ mCentralSurfacesComponent = mCentralSurfacesComponentFactory.create();
+ mFragmentService.addFragmentInstantiationProvider(mCentralSurfacesComponent);
- mNotificationShadeWindowView = mStatusBarComponent.getNotificationShadeWindowView();
- mNotificationShadeWindowViewController = mStatusBarComponent
+ mNotificationShadeWindowView = mCentralSurfacesComponent.getNotificationShadeWindowView();
+ mNotificationShadeWindowViewController = mCentralSurfacesComponent
.getNotificationShadeWindowViewController();
mNotificationShadeWindowController.setNotificationShadeView(mNotificationShadeWindowView);
mNotificationShadeWindowViewController.setupExpandedStatusBar();
- mNotificationPanelViewController = mStatusBarComponent.getNotificationPanelViewController();
- mStatusBarComponent.getLockIconViewController().init();
- mStackScrollerController = mStatusBarComponent.getNotificationStackScrollLayoutController();
+ mNotificationPanelViewController =
+ mCentralSurfacesComponent.getNotificationPanelViewController();
+ mCentralSurfacesComponent.getLockIconViewController().init();
+ mStackScrollerController =
+ mCentralSurfacesComponent.getNotificationStackScrollLayoutController();
mStackScroller = mStackScrollerController.getView();
-
- mNotificationShelfController = mStatusBarComponent.getNotificationShelfController();
- mAuthRippleController = mStatusBarComponent.getAuthRippleController();
+ mNotifListContainer = mCentralSurfacesComponent.getNotificationListContainer();
+ mPresenter = mCentralSurfacesComponent.getNotificationPresenter();
+ mNotificationActivityStarter = mCentralSurfacesComponent.getNotificationActivityStarter();
+ mNotificationShelfController = mCentralSurfacesComponent.getNotificationShelfController();
+ mAuthRippleController = mCentralSurfacesComponent.getAuthRippleController();
mAuthRippleController.init();
- mHeadsUpManager.addListener(mStatusBarComponent.getStatusBarHeadsUpChangeListener());
+ mHeadsUpManager.addListener(mCentralSurfacesComponent.getStatusBarHeadsUpChangeListener());
// Listen for demo mode changes
mDemoModeController.addCallback(mDemoModeCallback);
@@ -1589,18 +1550,19 @@
if (mCommandQueueCallbacks != null) {
mCommandQueue.removeCallback(mCommandQueueCallbacks);
}
- mCommandQueueCallbacks = mStatusBarComponent.getStatusBarCommandQueueCallbacks();
+ mCommandQueueCallbacks =
+ mCentralSurfacesComponent.getCentralSurfacesCommandQueueCallbacks();
// Connect in to the status bar manager service
mCommandQueue.addCallback(mCommandQueueCallbacks);
- // Perform all other initialization for StatusBarScope
- for (StatusBarComponent.Startable startable : mStatusBarComponent.getStartables()) {
- startable.start();
+ // Perform all other initialization for CentralSurfacesScope
+ for (CentralSurfacesComponent.Startable s : mCentralSurfacesComponent.getStartables()) {
+ s.start();
}
}
protected void startKeyguard() {
- Trace.beginSection("StatusBar#startKeyguard");
+ Trace.beginSection("CentralSurfaces#startKeyguard");
mBiometricUnlockController = mBiometricUnlockControllerLazy.get();
mBiometricUnlockController.setBiometricModeListener(
new BiometricUnlockController.BiometricModeListener() {
@@ -1621,7 +1583,7 @@
@Override
public void notifyBiometricAuthModeChanged() {
- StatusBar.this.notifyBiometricAuthModeChanged();
+ CentralSurfaces.this.notifyBiometricAuthModeChanged();
}
private void setWakeAndUnlocking(boolean wakeAndUnlocking) {
@@ -1630,7 +1592,7 @@
}
}
});
- mStatusBarKeyguardViewManager.registerStatusBar(
+ mStatusBarKeyguardViewManager.registerCentralSurfaces(
/* statusBar= */ this,
mNotificationPanelViewController,
mPanelExpansionStateManager,
@@ -1759,7 +1721,7 @@
getDelegate().onIntentStarted(willAnimate);
if (willAnimate) {
- StatusBar.this.mIsLaunchingActivityOverLockscreen = true;
+ CentralSurfaces.this.mIsLaunchingActivityOverLockscreen = true;
}
}
@@ -1769,7 +1731,7 @@
// animation so that we can assume that mIsLaunchingActivityOverLockscreen
// being true means that we will collapse the shade (or at least run the
// post collapse runnables) later on.
- StatusBar.this.mIsLaunchingActivityOverLockscreen = false;
+ CentralSurfaces.this.mIsLaunchingActivityOverLockscreen = false;
getDelegate().onLaunchAnimationEnd(isExpandingFullyAbove);
}
@@ -1779,7 +1741,7 @@
// animation so that we can assume that mIsLaunchingActivityOverLockscreen
// being true means that we will collapse the shade (or at least run the
// post collapse runnables) later on.
- StatusBar.this.mIsLaunchingActivityOverLockscreen = false;
+ CentralSurfaces.this.mIsLaunchingActivityOverLockscreen = false;
getDelegate().onLaunchAnimationCancelled();
}
};
@@ -2603,7 +2565,7 @@
// ordering.
mMainExecutor.execute(mShadeController::runPostCollapseRunnables);
}
- } else if (StatusBar.this.isInLaunchTransition()
+ } else if (CentralSurfaces.this.isInLaunchTransition()
&& mNotificationPanelViewController.isLaunchTransitionFinished()) {
// We are not dismissing the shade, but the launch transition is already
@@ -2626,7 +2588,7 @@
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- Trace.beginSection("StatusBar#onReceive");
+ Trace.beginSection("CentralSurfaces#onReceive");
if (DEBUG) Log.v(TAG, "onReceive: " + intent);
String action = intent.getAction();
String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY);
@@ -2772,7 +2734,9 @@
void handleVisibleToUserChangedImpl(boolean visibleToUser) {
if (visibleToUser) {
/* The LEDs are turned off when the notification panel is shown, even just a little bit.
- * See also StatusBar.setPanelExpanded for another place where we attempt to do this. */
+ * See also CentralSurfaces.setPanelExpanded for another place where we attempt to do
+ * this.
+ */
boolean pinnedHeadsUp = mHeadsUpManager.hasPinnedHeadsUp();
boolean clearNotificationEffects =
!mPresenter.isPresenterFullyCollapsed() &&
@@ -2921,8 +2885,9 @@
// turned off fully.
boolean keyguardForDozing = mDozeServiceHost.getDozingRequested()
&& (!mDeviceInteractive || isGoingToSleep() && (isScreenFullyOff() || mIsKeyguard));
+ boolean isWakingAndOccluded = isOccluded() && isWaking();
boolean shouldBeKeyguard = (mStatusBarStateController.isKeyguardRequested()
- || keyguardForDozing) && !wakeAndUnlocking;
+ || keyguardForDozing) && !wakeAndUnlocking && !isWakingAndOccluded;
if (keyguardForDozing) {
updatePanelExpansionForKeyguard();
}
@@ -2951,7 +2916,7 @@
}
public void showKeyguardImpl() {
- Trace.beginSection("StatusBar#showKeyguard");
+ Trace.beginSection("CentralSurfaces#showKeyguard");
mIsKeyguard = true;
// In case we're locking while a smartspace transition is in progress, reset it.
mKeyguardUnlockAnimationController.resetSmartspaceTransition();
@@ -3074,7 +3039,7 @@
*/
public boolean hideKeyguardImpl(boolean forceStateChange) {
mIsKeyguard = false;
- Trace.beginSection("StatusBar#hideKeyguard");
+ Trace.beginSection("CentralSurfaces#hideKeyguard");
boolean staying = mStatusBarStateController.leaveOpenOnKeyguardHide();
int previousState = mStatusBarStateController.getState();
if (!(mStatusBarStateController.setState(StatusBarState.SHADE, forceStateChange))) {
@@ -3191,7 +3156,7 @@
private void updateDozingState() {
Trace.traceCounter(Trace.TRACE_TAG_APP, "dozing", mDozing ? 1 : 0);
- Trace.beginSection("StatusBar#updateDozingState");
+ Trace.beginSection("CentralSurfaces#updateDozingState");
boolean visibleNotOccluded = mStatusBarKeyguardViewManager.isShowing()
&& !mStatusBarKeyguardViewManager.isOccluded();
@@ -3546,7 +3511,7 @@
@Override
public void onStartedGoingToSleep() {
- String tag = "StatusBar#onStartedGoingToSleep";
+ String tag = "CentralSurfaces#onStartedGoingToSleep";
DejankUtils.startDetectingBlockingIpcs(tag);
updateRevealEffect(false /* wakingUp */);
updateNotificationPanelTouchState();
@@ -3566,7 +3531,7 @@
@Override
public void onStartedWakingUp() {
- String tag = "StatusBar#onStartedWakingUp";
+ String tag = "CentralSurfaces#onStartedWakingUp";
DejankUtils.startDetectingBlockingIpcs(tag);
mNotificationShadeWindowController.batchApplyWindowLayoutParams(()-> {
mDeviceInteractive = true;
@@ -3641,7 +3606,7 @@
@Override
public void onScreenTurnedOff() {
- Trace.beginSection("StatusBar#onScreenTurnedOff");
+ Trace.beginSection("CentralSurfaces#onScreenTurnedOff");
mFalsingCollector.onScreenOff();
mScrimController.onScreenTurnedOff();
if (mCloseQsBeforeScreenOff) {
@@ -3732,6 +3697,10 @@
== WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP;
}
+ boolean isWaking() {
+ return mWakefulnessLifecycle.getWakefulness() == WakefulnessLifecycle.WAKEFULNESS_WAKING;
+ }
+
public void notifyBiometricAuthModeChanged() {
mDozeServiceHost.updateDozing();
updateScrimController();
@@ -3748,7 +3717,7 @@
@VisibleForTesting
public void updateScrimController() {
- Trace.beginSection("StatusBar#updateScrimController");
+ Trace.beginSection("CentralSurfaces#updateScrimController");
boolean unlocking = mKeyguardStateController.isShowing() && (
mBiometricUnlockController.isWakeAndUnlock()
@@ -4314,7 +4283,7 @@
if (mBrightnessMirrorController != null) {
mBrightnessMirrorController.onDensityOrFontScaleChanged();
}
- // TODO: Bring these out of StatusBar.
+ // TODO: Bring these out of CentralSurfaces.
mUserInfoControllerImpl.onDensityOrFontScaleChanged();
mUserSwitcherController.onDensityOrFontScaleChanged();
mNotificationIconAreaController.onDensityOrFontScaleChanged(mContext);
@@ -4372,7 +4341,7 @@
mDozeServiceHost.updateDozing();
updateTheme();
mNavigationBarController.touchAutoDim(mDisplayId);
- Trace.beginSection("StatusBar#updateKeyguardState");
+ Trace.beginSection("CentralSurfaces#updateKeyguardState");
if (mState == StatusBarState.KEYGUARD) {
mNotificationPanelViewController.cancelPendingPanelCollapse();
}
@@ -4395,7 +4364,7 @@
@Override
public void onDozingChanged(boolean isDozing) {
- Trace.beginSection("StatusBar#updateDozing");
+ Trace.beginSection("CentralSurfaces#updateDozing");
mDozing = isDozing;
// Collapse the notification panel if open
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
similarity index 81%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index aeb7826..536be1c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -59,7 +59,7 @@
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -70,9 +70,9 @@
import javax.inject.Inject;
/** */
-@StatusBarComponent.StatusBarScope
-public class StatusBarCommandQueueCallbacks implements CommandQueue.Callbacks {
- private final StatusBar mStatusBar;
+@CentralSurfacesComponent.CentralSurfacesScope
+public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callbacks {
+ private final CentralSurfaces mCentralSurfaces;
private final Context mContext;
private final ShadeController mShadeController;
private final CommandQueue mCommandQueue;
@@ -105,8 +105,8 @@
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
@Inject
- StatusBarCommandQueueCallbacks(
- StatusBar statusBar,
+ CentralSurfacesCommandQueueCallbacks(
+ CentralSurfaces centralSurfaces,
Context context,
@Main Resources resources,
ShadeController shadeController,
@@ -133,7 +133,7 @@
DisableFlagsLogger disableFlagsLogger,
@DisplayId int displayId) {
- mStatusBar = statusBar;
+ mCentralSurfaces = centralSurfaces;
mContext = context;
mShadeController = shadeController;
mCommandQueue = commandQueue;
@@ -172,12 +172,12 @@
if (!containsType(types, ITYPE_STATUS_BAR)) {
return;
}
- mStatusBar.clearTransient();
+ mCentralSurfaces.clearTransient();
}
@Override
public void addQsTile(ComponentName tile) {
- QSPanelController qsPanelController = mStatusBar.getQSPanelController();
+ QSPanelController qsPanelController = mCentralSurfaces.getQSPanelController();
if (qsPanelController != null && qsPanelController.getHost() != null) {
qsPanelController.getHost().addTile(tile);
}
@@ -185,7 +185,7 @@
@Override
public void remQsTile(ComponentName tile) {
- QSPanelController qsPanelController = mStatusBar.getQSPanelController();
+ QSPanelController qsPanelController = mCentralSurfaces.getQSPanelController();
if (qsPanelController != null && qsPanelController.getHost() != null) {
qsPanelController.getHost().removeTile(tile);
}
@@ -193,7 +193,7 @@
@Override
public void clickTile(ComponentName tile) {
- QSPanelController qsPanelController = mStatusBar.getQSPanelController();
+ QSPanelController qsPanelController = mCentralSurfaces.getQSPanelController();
if (qsPanelController != null) {
qsPanelController.clickTile(tile);
}
@@ -207,9 +207,9 @@
@Override
public void animateExpandNotificationsPanel() {
- if (StatusBar.SPEW) {
- Log.d(StatusBar.TAG,
- "animateExpand: mExpandedVisible=" + mStatusBar.isExpandedVisible());
+ if (CentralSurfaces.SPEW) {
+ Log.d(CentralSurfaces.TAG,
+ "animateExpand: mExpandedVisible=" + mCentralSurfaces.isExpandedVisible());
}
if (!mCommandQueue.panelsEnabled()) {
return;
@@ -220,9 +220,9 @@
@Override
public void animateExpandSettingsPanel(@Nullable String subPanel) {
- if (StatusBar.SPEW) {
- Log.d(StatusBar.TAG,
- "animateExpand: mExpandedVisible=" + mStatusBar.isExpandedVisible());
+ if (CentralSurfaces.SPEW) {
+ Log.d(CentralSurfaces.TAG,
+ "animateExpand: mExpandedVisible=" + mCentralSurfaces.isExpandedVisible());
}
if (!mCommandQueue.panelsEnabled()) {
return;
@@ -244,7 +244,7 @@
@Override
public void dismissKeyboardShortcutsMenu() {
- mStatusBar.resendMessage(StatusBar.MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU);
+ mCentralSurfaces.resendMessage(CentralSurfaces.MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU);
}
/**
* State is one or more of the DISABLE constants from StatusBarManager.
@@ -257,22 +257,22 @@
int state2BeforeAdjustment = state2;
state2 = mRemoteInputQuickSettingsDisabler.adjustDisableFlags(state2);
- Log.d(StatusBar.TAG,
+ Log.d(CentralSurfaces.TAG,
mDisableFlagsLogger.getDisableFlagsString(
/* old= */ new DisableFlagsLogger.DisableState(
- mStatusBar.getDisabled1(), mStatusBar.getDisabled2()),
+ mCentralSurfaces.getDisabled1(), mCentralSurfaces.getDisabled2()),
/* new= */ new DisableFlagsLogger.DisableState(
state1, state2BeforeAdjustment),
/* newStateAfterLocalModification= */ new DisableFlagsLogger.DisableState(
state1, state2)));
- final int old1 = mStatusBar.getDisabled1();
+ final int old1 = mCentralSurfaces.getDisabled1();
final int diff1 = state1 ^ old1;
- mStatusBar.setDisabled1(state1);
+ mCentralSurfaces.setDisabled1(state1);
- final int old2 = mStatusBar.getDisabled2();
+ final int old2 = mCentralSurfaces.getDisabled2();
final int diff2 = state2 ^ old2;
- mStatusBar.setDisabled2(state2);
+ mCentralSurfaces.setDisabled2(state2);
if ((diff1 & StatusBarManager.DISABLE_EXPAND) != 0) {
if ((state1 & StatusBarManager.DISABLE_EXPAND) != 0) {
@@ -281,17 +281,17 @@
}
if ((diff1 & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
- if (mStatusBar.areNotificationAlertsDisabled()) {
+ if (mCentralSurfaces.areNotificationAlertsDisabled()) {
mHeadsUpManager.releaseAllImmediately();
}
}
if ((diff2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) != 0) {
- mStatusBar.updateQsExpansionEnabled();
+ mCentralSurfaces.updateQsExpansionEnabled();
}
if ((diff2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) {
- mStatusBar.updateQsExpansionEnabled();
+ mCentralSurfaces.updateQsExpansionEnabled();
if ((state2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0) {
mShadeController.animateCollapsePanels();
}
@@ -304,8 +304,8 @@
*/
@Override
public void handleSystemKey(int key) {
- if (StatusBar.SPEW) {
- Log.d(StatusBar.TAG, "handleNavigationKey: " + key);
+ if (CentralSurfaces.SPEW) {
+ Log.d(CentralSurfaces.TAG, "handleNavigationKey: " + key);
}
if (!mCommandQueue.panelsEnabled() || !mKeyguardUpdateMonitor.isDeviceInteractive()
|| mKeyguardStateController.isShowing() && !mKeyguardStateController.isOccluded()) {
@@ -341,81 +341,82 @@
@Override
public void onCameraLaunchGestureDetected(int source) {
- mStatusBar.setLastCameraLaunchSource(source);
- if (mStatusBar.isGoingToSleep()) {
- if (StatusBar.DEBUG_CAMERA_LIFT) {
- Slog.d(StatusBar.TAG, "Finish going to sleep before launching camera");
+ mCentralSurfaces.setLastCameraLaunchSource(source);
+ if (mCentralSurfaces.isGoingToSleep()) {
+ if (CentralSurfaces.DEBUG_CAMERA_LIFT) {
+ Slog.d(CentralSurfaces.TAG, "Finish going to sleep before launching camera");
}
- mStatusBar.setLaunchCameraOnFinishedGoingToSleep(true);
+ mCentralSurfaces.setLaunchCameraOnFinishedGoingToSleep(true);
return;
}
if (!mNotificationPanelViewController.canCameraGestureBeLaunched()) {
- if (StatusBar.DEBUG_CAMERA_LIFT) {
- Slog.d(StatusBar.TAG, "Can't launch camera right now");
+ if (CentralSurfaces.DEBUG_CAMERA_LIFT) {
+ Slog.d(CentralSurfaces.TAG, "Can't launch camera right now");
}
return;
}
- if (!mStatusBar.isDeviceInteractive()) {
+ if (!mCentralSurfaces.isDeviceInteractive()) {
mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_CAMERA_LAUNCH,
"com.android.systemui:CAMERA_GESTURE");
}
vibrateForCameraGesture();
if (source == StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP) {
- Log.v(StatusBar.TAG, "Camera launch");
+ Log.v(CentralSurfaces.TAG, "Camera launch");
mKeyguardUpdateMonitor.onCameraLaunched();
}
if (!mStatusBarKeyguardViewManager.isShowing()) {
final Intent cameraIntent = CameraIntents.getInsecureCameraIntent(mContext);
- mStatusBar.startActivityDismissingKeyguard(cameraIntent,
+ mCentralSurfaces.startActivityDismissingKeyguard(cameraIntent,
false /* onlyProvisioned */, true /* dismissShade */,
true /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */, 0,
null /* animationController */);
} else {
- if (!mStatusBar.isDeviceInteractive()) {
+ if (!mCentralSurfaces.isDeviceInteractive()) {
// Avoid flickering of the scrim when we instant launch the camera and the bouncer
// comes on.
- mStatusBar.acquireGestureWakeLock(StatusBar.LAUNCH_TRANSITION_TIMEOUT_MS + 1000L);
+ mCentralSurfaces.acquireGestureWakeLock(
+ CentralSurfaces.LAUNCH_TRANSITION_TIMEOUT_MS + 1000L);
}
if (isWakingUpOrAwake()) {
- if (StatusBar.DEBUG_CAMERA_LIFT) {
- Slog.d(StatusBar.TAG, "Launching camera");
+ if (CentralSurfaces.DEBUG_CAMERA_LIFT) {
+ Slog.d(CentralSurfaces.TAG, "Launching camera");
}
if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
mStatusBarKeyguardViewManager.reset(true /* hide */);
}
mNotificationPanelViewController.launchCamera(
- mStatusBar.isDeviceInteractive() /* animate */, source);
- mStatusBar.updateScrimController();
+ mCentralSurfaces.isDeviceInteractive() /* animate */, source);
+ mCentralSurfaces.updateScrimController();
} else {
// We need to defer the camera launch until the screen comes on, since otherwise
// we will dismiss us too early since we are waiting on an activity to be drawn and
// incorrectly get notified because of the screen on event (which resumes and pauses
// some activities)
- if (StatusBar.DEBUG_CAMERA_LIFT) {
- Slog.d(StatusBar.TAG, "Deferring until screen turns on");
+ if (CentralSurfaces.DEBUG_CAMERA_LIFT) {
+ Slog.d(CentralSurfaces.TAG, "Deferring until screen turns on");
}
- mStatusBar.setLaunchCameraOnFinishedWaking(true);
+ mCentralSurfaces.setLaunchCameraOnFinishedWaking(true);
}
}
}
@Override
public void onEmergencyActionLaunchGestureDetected() {
- Intent emergencyIntent = mStatusBar.getEmergencyActionIntent();
+ Intent emergencyIntent = mCentralSurfaces.getEmergencyActionIntent();
if (emergencyIntent == null) {
- Log.wtf(StatusBar.TAG, "Couldn't find an app to process the emergency intent.");
+ Log.wtf(CentralSurfaces.TAG, "Couldn't find an app to process the emergency intent.");
return;
}
if (isGoingToSleep()) {
- mStatusBar.setLaunchEmergencyActionOnFinishedGoingToSleep(true);
+ mCentralSurfaces.setLaunchEmergencyActionOnFinishedGoingToSleep(true);
return;
}
- if (!mStatusBar.isDeviceInteractive()) {
+ if (!mCentralSurfaces.isDeviceInteractive()) {
mPowerManager.wakeUp(SystemClock.uptimeMillis(),
PowerManager.WAKE_REASON_GESTURE,
"com.android.systemui:EMERGENCY_GESTURE");
@@ -424,17 +425,18 @@
// app-side haptic experimentation.
if (!mStatusBarKeyguardViewManager.isShowing()) {
- mStatusBar.startActivityDismissingKeyguard(emergencyIntent,
+ mCentralSurfaces.startActivityDismissingKeyguard(emergencyIntent,
false /* onlyProvisioned */, true /* dismissShade */,
true /* disallowEnterPictureInPictureWhileLaunching */, null /* callback */, 0,
null /* animationController */);
return;
}
- if (!mStatusBar.isDeviceInteractive()) {
+ if (!mCentralSurfaces.isDeviceInteractive()) {
// Avoid flickering of the scrim when we instant launch the camera and the bouncer
// comes on.
- mStatusBar.acquireGestureWakeLock(StatusBar.LAUNCH_TRANSITION_TIMEOUT_MS + 1000L);
+ mCentralSurfaces.acquireGestureWakeLock(
+ CentralSurfaces.LAUNCH_TRANSITION_TIMEOUT_MS + 1000L);
}
if (isWakingUpOrAwake()) {
@@ -448,12 +450,12 @@
// we will dismiss us too early since we are waiting on an activity to be drawn and
// incorrectly get notified because of the screen on event (which resumes and pauses
// some activities)
- mStatusBar.setLaunchEmergencyActionOnFinishedWaking(true);
+ mCentralSurfaces.setLaunchEmergencyActionOnFinishedWaking(true);
}
@Override
public void onRecentsAnimationStateChanged(boolean running) {
- mStatusBar.setInteracting(StatusBarManager.WINDOW_NAVIGATION_BAR, running);
+ mCentralSurfaces.setInteracting(StatusBarManager.WINDOW_NAVIGATION_BAR, running);
}
@@ -464,12 +466,12 @@
if (displayId != mDisplayId) {
return;
}
- boolean barModeChanged = mStatusBar.setAppearance(appearance);
+ boolean barModeChanged = mCentralSurfaces.setAppearance(appearance);
mLightBarController.onStatusBarAppearanceChanged(appearanceRegions, barModeChanged,
- mStatusBar.getBarMode(), navbarColorManagedByIme);
+ mCentralSurfaces.getBarMode(), navbarColorManagedByIme);
- mStatusBar.updateBubblesVisibility();
+ mCentralSurfaces.updateBubblesVisibility();
mStatusBarStateController.setSystemBarAttributes(
appearance, behavior, requestedVisibilities, packageName);
}
@@ -483,12 +485,12 @@
if (!containsType(types, ITYPE_STATUS_BAR)) {
return;
}
- mStatusBar.showTransientUnchecked();
+ mCentralSurfaces.showTransientUnchecked();
}
@Override
public void toggleKeyboardShortcutsMenu(int deviceId) {
- mStatusBar.resendMessage(new StatusBar.KeyboardShortcutsMessage(deviceId));
+ mCentralSurfaces.resendMessage(new CentralSurfaces.KeyboardShortcutsMessage(deviceId));
}
@Override
@@ -504,12 +506,12 @@
@Override
public void showPinningEnterExitToast(boolean entering) {
- mStatusBar.showPinningEnterExitToast(entering);
+ mCentralSurfaces.showPinningEnterExitToast(entering);
}
@Override
public void showPinningEscapeToast() {
- mStatusBar.showPinningEscapeToast();
+ mCentralSurfaces.showPinningEscapeToast();
}
@Override
@@ -519,12 +521,12 @@
return;
}
// Show screen pinning request, since this comes from an app, show 'no thanks', button.
- mStatusBar.showScreenPinningRequest(taskId, true);
+ mCentralSurfaces.showScreenPinningRequest(taskId, true);
}
@Override
public void showWirelessChargingAnimation(int batteryLevel) {
- mStatusBar.showWirelessChargingAnimation(batteryLevel);
+ mCentralSurfaces.showWirelessChargingAnimation(batteryLevel);
}
@Override
@@ -534,12 +536,12 @@
@Override
public void suppressAmbientDisplay(boolean suppressed) {
- mDozeServiceHost.setDozeSuppressed(suppressed);
+ mDozeServiceHost.setAlwaysOnSuppressed(suppressed);
}
@Override
public void togglePanel() {
- if (mStatusBar.isPanelExpanded()) {
+ if (mCentralSurfaces.isPanelExpanded()) {
mShadeController.animateCollapsePanels();
} else {
animateExpandNotificationsPanel();
@@ -576,8 +578,8 @@
// Make sure to pass -1 for repeat so VibratorManagerService doesn't stop us when going
// to sleep.
return VibrationEffect.createWaveform(
- StatusBar.CAMERA_LAUNCH_GESTURE_VIBRATION_TIMINGS,
- StatusBar.CAMERA_LAUNCH_GESTURE_VIBRATION_AMPLITUDES,
+ CentralSurfaces.CAMERA_LAUNCH_GESTURE_VIBRATION_TIMINGS,
+ CentralSurfaces.CAMERA_LAUNCH_GESTURE_VIBRATION_AMPLITUDES,
/* repeat= */ -1);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 8b25c2b..ebe91c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -292,11 +292,20 @@
}
public void updateControlScreenOff() {
- if (!getDisplayNeedsBlanking()) {
- final boolean controlScreenOff =
- getAlwaysOn() && (mKeyguardShowing || shouldControlUnlockedScreenOff());
- setControlScreenOffAnimation(controlScreenOff);
- }
+ final boolean controlScreenOff = shouldControlUnlockedScreenOff()
+ || (!getDisplayNeedsBlanking() && getAlwaysOn() && mKeyguardShowing);
+ setControlScreenOffAnimation(controlScreenOff);
+ }
+
+ /**
+ * Whether we're capable of controlling the screen off animation if we want to. This isn't
+ * possible if AOD isn't even enabled or if the flag is disabled, or if the display needs
+ * blanking.
+ */
+ public boolean canControlUnlockedScreenOff() {
+ return getAlwaysOn()
+ && mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ANIMATIONS)
+ && !getDisplayNeedsBlanking();
}
/**
@@ -309,8 +318,7 @@
* disabled for a11y.
*/
public boolean shouldControlUnlockedScreenOff() {
- return canControlUnlockedScreenOff()
- && mUnlockedScreenOffAnimationController.shouldPlayUnlockedScreenOffAnimation();
+ return mUnlockedScreenOffAnimationController.shouldPlayUnlockedScreenOffAnimation();
}
public boolean shouldDelayKeyguardShow() {
@@ -342,16 +350,6 @@
return getAlwaysOn() && mKeyguardShowing;
}
- /**
- * Whether we're capable of controlling the screen off animation if we want to. This isn't
- * possible if AOD isn't even enabled or if the flag is disabled.
- */
- public boolean canControlUnlockedScreenOff() {
- return getAlwaysOn()
- && mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ANIMATIONS)
- && !getDisplayNeedsBlanking();
- }
-
private boolean getBoolean(String propName, int resId) {
return SystemProperties.getBoolean(propName, mResources.getBoolean(resId));
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index 05fba54..55b310f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -97,8 +97,8 @@
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private NotificationPanelViewController mNotificationPanel;
private View mAmbientIndicationContainer;
- private StatusBar mStatusBar;
- private boolean mSuppressed;
+ private CentralSurfaces mCentralSurfaces;
+ private boolean mAlwaysOnSuppressed;
@Inject
public DozeServiceHost(DozeLog dozeLog, PowerManager powerManager,
@@ -146,12 +146,12 @@
* Initialize instance with objects only available later during execution.
*/
public void initialize(
- StatusBar statusBar,
+ CentralSurfaces centralSurfaces,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
NotificationShadeWindowViewController notificationShadeWindowViewController,
NotificationPanelViewController notificationPanel,
View ambientIndicationContainer) {
- mStatusBar = statusBar;
+ mCentralSurfaces = centralSurfaces;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mNotificationPanel = notificationPanel;
mNotificationShadeWindowViewController = notificationShadeWindowViewController;
@@ -205,7 +205,7 @@
mDozingRequested = true;
updateDozing();
mDozeLog.traceDozing(mStatusBarStateController.isDozing());
- mStatusBar.updateIsKeyguard();
+ mCentralSurfaces.updateIsKeyguard();
}
}
@@ -247,13 +247,13 @@
&& mWakeLockScreenPerformsAuth;
// Set the state to pulsing, so ScrimController will know what to do once we ask it to
// execute the transition. The pulse callback will then be invoked when the scrims
- // are black, indicating that StatusBar is ready to present the rest of the UI.
+ // are black, indicating that CentralSurfaces is ready to present the rest of the UI.
mPulsing = true;
mDozeScrimController.pulse(new PulseCallback() {
@Override
public void onPulseStarted() {
callback.onPulseStarted();
- mStatusBar.updateNotificationPanelTouchState();
+ mCentralSurfaces.updateNotificationPanelTouchState();
setPulsing(true);
}
@@ -261,7 +261,7 @@
public void onPulseFinished() {
mPulsing = false;
callback.onPulseFinished();
- mStatusBar.updateNotificationPanelTouchState();
+ mCentralSurfaces.updateNotificationPanelTouchState();
mScrimController.setWakeLockScreenSensorActive(false);
setPulsing(false);
}
@@ -274,14 +274,14 @@
if (mKeyguardUpdateMonitor != null && passiveAuthInterrupt) {
mKeyguardUpdateMonitor.onAuthInterruptDetected(pulsing /* active */);
}
- mStatusBar.updateScrimController();
+ mCentralSurfaces.updateScrimController();
mPulseExpansionHandler.setPulsing(pulsing);
mNotificationWakeUpCoordinator.setPulsing(pulsing);
}
}, reason);
// DozeScrimController is in pulse state, now let's ask ScrimController to start
// pulsing and draw the black frame, if necessary.
- mStatusBar.updateScrimController();
+ mCentralSurfaces.updateScrimController();
}
@Override
@@ -332,15 +332,6 @@
}
@Override
- public boolean isBlockingDoze() {
- if (mBiometricUnlockControllerLazy.get().hasPendingAuthentication()) {
- Log.i(StatusBar.TAG, "Blocking AOD because fingerprint has authenticated");
- return true;
- }
- return false;
- }
-
- @Override
public void extendPulse(int reason) {
if (reason == DozeLog.PULSE_REASON_SENSOR_WAKE_REACH) {
mScrimController.setWakeLockScreenSensorActive(true);
@@ -412,14 +403,14 @@
Log.w(TAG, "Overlapping onDisplayOffCallback. Ignoring previous one.");
}
mPendingScreenOffCallback = onDisplayOffCallback;
- mStatusBar.updateScrimController();
+ mCentralSurfaces.updateScrimController();
}
@Override
public void cancelGentleSleep() {
mPendingScreenOffCallback = null;
if (mScrimController.getState() == ScrimState.OFF) {
- mStatusBar.updateScrimController();
+ mCentralSurfaces.updateScrimController();
}
}
@@ -452,18 +443,25 @@
return mIgnoreTouchWhilePulsing;
}
- void setDozeSuppressed(boolean suppressed) {
- if (suppressed == mSuppressed) {
+ /**
+ * Suppresses always-on-display and waking up the display for notifications.
+ * Does not disable wakeup gestures like pickup and tap.
+ */
+ void setAlwaysOnSuppressed(boolean suppressed) {
+ if (suppressed == mAlwaysOnSuppressed) {
return;
}
- mSuppressed = suppressed;
- mDozeLog.traceDozingSuppressed(mSuppressed);
+ mAlwaysOnSuppressed = suppressed;
for (Callback callback : mCallbacks) {
- callback.onDozeSuppressedChanged(suppressed);
+ callback.onAlwaysOnSuppressedChanged(suppressed);
}
}
- public boolean isDozeSuppressed() {
- return mSuppressed;
+ /**
+ * Whether always-on-display is being suppressed. This does not affect wakeup gestures like
+ * pickup and tap.
+ */
+ public boolean isAlwaysOnSuppressed() {
+ return mAlwaysOnSuppressed;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 84103c0..541aeab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -118,7 +118,7 @@
KeyguardStateController.Callback,
AccessibilityController.AccessibilityStateChangedCallback {
- final static String TAG = "StatusBar/KeyguardBottomAreaView";
+ final static String TAG = "CentralSurfaces/KeyguardBottomAreaView";
public static final String CAMERA_LAUNCH_SOURCE_AFFORDANCE = "lockscreen_affordance";
public static final String CAMERA_LAUNCH_SOURCE_WIGGLE = "wiggle_gesture";
@@ -169,7 +169,7 @@
private FlashlightController mFlashlightController;
private PreviewInflater mPreviewInflater;
private AccessibilityController mAccessibilityController;
- private StatusBar mStatusBar;
+ private CentralSurfaces mCentralSurfaces;
private KeyguardAffordanceHelper mAffordanceHelper;
private FalsingManager mFalsingManager;
private boolean mUserSetupComplete;
@@ -274,7 +274,7 @@
};
public void initFrom(KeyguardBottomAreaView oldBottomArea) {
- setStatusBar(oldBottomArea.mStatusBar);
+ setCentralSurfaces(oldBottomArea.mCentralSurfaces);
// if it exists, continue to use the original ambient indication container
// instead of the newly inflated one
@@ -473,8 +473,8 @@
mRightAffordanceView.setContentDescription(state.contentDescription);
}
- public void setStatusBar(StatusBar statusBar) {
- mStatusBar = statusBar;
+ public void setCentralSurfaces(CentralSurfaces centralSurfaces) {
+ mCentralSurfaces = centralSurfaces;
updateCameraVisibility(); // in case onFinishInflate() was called too early
}
@@ -732,7 +732,7 @@
} else {
boolean dismissShade = !TextUtils.isEmpty(mRightButtonStr)
&& Dependency.get(TunerService.class).getValue(LOCKSCREEN_RIGHT_UNLOCK, 1) != 0;
- mStatusBar.executeRunnableDismissingKeyguard(runnable, null /* cancelAction */,
+ mCentralSurfaces.executeRunnableDismissingKeyguard(runnable, null /* cancelAction */,
dismissShade, false /* afterKeyguardGone */, true /* deferred */);
}
}
@@ -1019,7 +1019,8 @@
@Override
public IconState getIcon() {
- boolean isCameraDisabled = (mStatusBar != null) && !mStatusBar.isCameraAllowedByAdmin();
+ boolean isCameraDisabled = (mCentralSurfaces != null)
+ && !mCentralSurfaces.isCameraAllowedByAdmin();
mIconState.isVisible = !isCameraDisabled
&& mShowCameraAffordance
&& mUserSetupComplete
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java
index 9bdefcd..2c4fc6c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardEnvironmentImpl.java
@@ -14,8 +14,8 @@
package com.android.systemui.statusbar.phone;
-import static com.android.systemui.statusbar.phone.StatusBar.DEBUG;
-import static com.android.systemui.statusbar.phone.StatusBar.MULTIUSER_DEBUG;
+import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG;
+import static com.android.systemui.statusbar.phone.CentralSurfaces.MULTIUSER_DEBUG;
import android.service.notification.StatusBarNotification;
import android.util.Log;
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 a0f8d05..01860a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -184,11 +184,12 @@
import com.android.systemui.statusbar.notification.stack.AmbientState;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.MediaContainerView;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.phone.panelstate.PanelState;
@@ -221,7 +222,7 @@
import javax.inject.Inject;
import javax.inject.Provider;
-@StatusBarComponent.StatusBarScope
+@CentralSurfacesComponent.CentralSurfacesScope
public class NotificationPanelViewController extends PanelViewController
implements NotifPanelEventSource {
@@ -665,6 +666,8 @@
private final ListenerSet<Callbacks> mNotifEventSourceCallbacks = new ListenerSet<>();
+ private final NotificationListContainer mNotificationListContainer;
+
private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() {
@Override
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
@@ -792,7 +795,8 @@
InteractionJankMonitor interactionJankMonitor,
QsFrameTranslateController qsFrameTranslateController,
SysUiState sysUiState,
- KeyguardUnlockAnimationController keyguardUnlockAnimationController) {
+ KeyguardUnlockAnimationController keyguardUnlockAnimationController,
+ NotificationListContainer notificationListContainer) {
super(view,
falsingManager,
dozeLog,
@@ -823,6 +827,7 @@
mMediaHierarchyManager = mediaHierarchyManager;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mNotificationsQSContainerController = notificationsQSContainerController;
+ mNotificationListContainer = notificationListContainer;
mNotificationsQSContainerController.init();
mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
mGroupManager = groupManager;
@@ -1127,10 +1132,10 @@
return mKeyguardStatusViewController.hasCustomClock();
}
- private void setStatusBar(StatusBar bar) {
+ private void setCentralSurfaces(CentralSurfaces centralSurfaces) {
// TODO: this can be injected.
- mStatusBar = bar;
- mKeyguardBottomArea.setStatusBar(mStatusBar);
+ mCentralSurfaces = centralSurfaces;
+ mKeyguardBottomArea.setCentralSurfaces(mCentralSurfaces);
}
public void updateResources() {
@@ -1139,10 +1144,16 @@
mSplitShadeNotificationsScrimMarginBottom =
mResources.getDimensionPixelSize(
R.dimen.split_shade_notifications_scrim_margin_bottom);
- int qsWidth = mResources.getDimensionPixelSize(R.dimen.qs_panel_width);
- int panelWidth = mResources.getDimensionPixelSize(R.dimen.notification_panel_width);
- mShouldUseSplitNotificationShade =
+
+ int panelMarginHorizontal = mResources.getDimensionPixelSize(
+ R.dimen.notification_panel_margin_horizontal);
+
+ final boolean newShouldUseSplitNotificationShade =
Utils.shouldUseSplitNotificationShade(mResources);
+ final boolean splitNotificationShadeChanged =
+ mShouldUseSplitNotificationShade != newShouldUseSplitNotificationShade;
+
+ mShouldUseSplitNotificationShade = newShouldUseSplitNotificationShade;
if (mQs != null) {
mQs.setInSplitShade(mShouldUseSplitNotificationShade);
}
@@ -1157,11 +1168,12 @@
ensureAllViewsHaveIds(mNotificationContainerParent);
ConstraintSet constraintSet = new ConstraintSet();
constraintSet.clone(mNotificationContainerParent);
-
+ int statusViewMarginHorizontal = mResources.getDimensionPixelSize(
+ R.dimen.status_view_margin_horizontal);
+ constraintSet.setMargin(R.id.keyguard_status_view, START, statusViewMarginHorizontal);
+ constraintSet.setMargin(R.id.keyguard_status_view, END, statusViewMarginHorizontal);
if (mShouldUseSplitNotificationShade) {
// 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,
@@ -1174,11 +1186,15 @@
constraintSet.constrainHeight(R.id.split_shade_status_bar, WRAP_CONTENT);
}
}
- constraintSet.getConstraint(R.id.notification_stack_scroller).layout.mWidth = panelWidth;
- constraintSet.getConstraint(R.id.qs_frame).layout.mWidth = qsWidth;
+ constraintSet.setMargin(R.id.notification_stack_scroller, START,
+ mShouldUseSplitNotificationShade ? 0 : panelMarginHorizontal);
+ constraintSet.setMargin(R.id.notification_stack_scroller, END, panelMarginHorizontal);
constraintSet.setMargin(R.id.notification_stack_scroller, TOP, topMargin);
constraintSet.setMargin(R.id.notification_stack_scroller, BOTTOM,
notificationsBottomMargin);
+ constraintSet.setMargin(R.id.qs_frame, START, panelMarginHorizontal);
+ constraintSet.setMargin(R.id.qs_frame, END,
+ mShouldUseSplitNotificationShade ? 0 : panelMarginHorizontal);
constraintSet.setMargin(R.id.qs_frame, TOP, topMargin);
constraintSet.applyTo(mNotificationContainerParent);
mAmbientState.setStackTopMargin(topMargin);
@@ -1188,6 +1204,10 @@
updateKeyguardStatusViewAlignment(/* animate= */false);
mKeyguardMediaController.refreshMediaPosition();
+
+ if (splitNotificationShadeChanged) {
+ updateClockAppearance();
+ }
}
private static void ensureAllViewsHaveIds(ViewGroup parentView) {
@@ -1311,7 +1331,7 @@
mAffordanceHelper = new KeyguardAffordanceHelper(
mKeyguardAffordanceHelperCallback, mView.getContext(), mFalsingManager);
mKeyguardBottomArea.setAffordanceHelper(mAffordanceHelper);
- mKeyguardBottomArea.setStatusBar(mStatusBar);
+ mKeyguardBottomArea.setCentralSurfaces(mCentralSurfaces);
mKeyguardBottomArea.setUserSetupComplete(mUserSetupComplete);
mKeyguardBottomArea.setFalsingManager(mFalsingManager);
mKeyguardBottomArea.initWallet(mQuickAccessWalletController);
@@ -1574,7 +1594,7 @@
float lockIconPadding = 0;
if (mLockIconViewController.getTop() != 0) {
- lockIconPadding = mStatusBar.getDisplayHeight() - mLockIconViewController.getTop()
+ lockIconPadding = mCentralSurfaces.getDisplayHeight() - mLockIconViewController.getTop()
+ mResources.getDimensionPixelSize(R.dimen.min_lock_icon_padding);
}
@@ -1728,7 +1748,7 @@
mAffordanceHelper.reset(false);
mLastCameraLaunchSource = KeyguardBottomAreaView.CAMERA_LAUNCH_SOURCE_AFFORDANCE;
}
- mStatusBar.getGutsManager().closeAndSaveGuts(true /* leavebehind */, true /* force */,
+ mCentralSurfaces.getGutsManager().closeAndSaveGuts(true /* leavebehind */, true /* force */,
true /* controls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
if (animate && !isFullyCollapsed()) {
animateCloseQs(true /* animateAway */);
@@ -1827,7 +1847,7 @@
@Override
public void fling(float vel, boolean expand) {
- GestureRecorder gr = mStatusBar.getGestureRecorder();
+ GestureRecorder gr = mCentralSurfaces.getGestureRecorder();
if (gr != null) {
gr.tag("fling " + ((vel > 0) ? "open" : "closed"), "notifications,v=" + vel);
}
@@ -2016,8 +2036,8 @@
mBarState == KEYGUARD ? MetricsEvent.ACTION_LS_QS
: MetricsEvent.ACTION_SHADE_QS_PULL;
mLockscreenGestureLogger.write(gesture,
- (int) ((y - mInitialTouchY) / mStatusBar.getDisplayDensity()),
- (int) (vel / mStatusBar.getDisplayDensity()));
+ (int) ((y - mInitialTouchY) / mCentralSurfaces.getDisplayDensity()),
+ (int) (vel / mCentralSurfaces.getDisplayDensity()));
}
private boolean flingExpandsQs(float vel) {
@@ -2295,7 +2315,7 @@
}
private int getFalsingThreshold() {
- float factor = mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
+ float factor = mCentralSurfaces.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
return (int) (mQsFalsingThreshold * factor);
}
@@ -2322,7 +2342,7 @@
// When expanding QS, let's authenticate the user if possible,
// this will speed up notification actions.
if (height == 0) {
- mStatusBar.requestFaceAuth(false);
+ mCentralSurfaces.requestFaceAuth(false);
}
}
@@ -2333,7 +2353,7 @@
updateQsState();
requestPanelHeightUpdate();
mFalsingCollector.setQsExpanded(expanded);
- mStatusBar.setQsExpanded(expanded);
+ mCentralSurfaces.setQsExpanded(expanded);
mNotificationsQSContainerController.setQsExpanded(expanded);
mPulseExpansionHandler.setQsExpanded(expanded);
mKeyguardBypassController.setQSExpanded(expanded);
@@ -2417,7 +2437,7 @@
if (!mFalsingManager.isUnlockingDisabled() && mQsFullyExpanded
&& mFalsingCollector.shouldEnforceBouncer()) {
- mStatusBar.executeRunnableDismissingKeyguard(null, null /* cancelAction */,
+ mCentralSurfaces.executeRunnableDismissingKeyguard(null, null /* cancelAction */,
false /* dismissShade */, true /* afterKeyguardGone */, false /* deferred */);
}
if (DEBUG) {
@@ -3105,7 +3125,7 @@
if (mPanelExpanded != isExpanded) {
mHeadsUpManager.setIsPanelExpanded(isExpanded);
mStatusBarTouchableRegionManager.setPanelExpanded(isExpanded);
- mStatusBar.setPanelExpanded(isExpanded);
+ mCentralSurfaces.setPanelExpanded(isExpanded);
mPanelExpanded = isExpanded;
if (!isExpanded && mQs != null && mQs.isCustomizing()) {
@@ -3230,7 +3250,7 @@
mKeyguardBottomArea.setImportantForAccessibility(
alpha == 0f ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
: View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
- View ambientIndicationContainer = mStatusBar.getAmbientIndicationContainer();
+ View ambientIndicationContainer = mCentralSurfaces.getAmbientIndicationContainer();
if (ambientIndicationContainer != null) {
ambientIndicationContainer.setAlpha(alpha);
}
@@ -3569,7 +3589,7 @@
@Override
protected void onClosingFinished() {
- mStatusBar.onClosingFinished();
+ mCentralSurfaces.onClosingFinished();
setClosingWithAlphaFadeout(false);
mMediaHierarchyManager.closeGuts();
}
@@ -3631,7 +3651,7 @@
}
public void clearNotificationEffects() {
- mStatusBar.clearNotificationEffects();
+ mCentralSurfaces.clearNotificationEffects();
}
@Override
@@ -3693,7 +3713,7 @@
* Whether the camera application can be launched for the camera launch gesture.
*/
public boolean canCameraGestureBeLaunched() {
- if (!mStatusBar.isCameraAllowedByAdmin()) {
+ if (!mCentralSurfaces.isCameraAllowedByAdmin()) {
return false;
}
@@ -4037,8 +4057,7 @@
}
public boolean hasPulsingNotifications() {
- return mNotificationStackScrollLayoutController
- .getNotificationListContainer().hasPulsingNotifications();
+ return mNotificationListContainer.hasPulsingNotifications();
}
public ActivatableNotificationView getActivatedChild() {
@@ -4073,10 +4092,10 @@
* @param hideExpandedRunnable a runnable to run when we need to hide the expanded panel.
*/
public void initDependencies(
- StatusBar statusBar,
+ CentralSurfaces centralSurfaces,
Runnable hideExpandedRunnable,
NotificationShelfController notificationShelfController) {
- setStatusBar(statusBar);
+ setCentralSurfaces(centralSurfaces);
mHideExpandedRunnable = hideExpandedRunnable;
mNotificationStackScrollLayoutController.setShelfController(notificationShelfController);
mNotificationShelfController = notificationShelfController;
@@ -4149,7 +4168,7 @@
initDownStates(event);
// Do not let touches go to shade or QS if the bouncer is visible,
// but still let user swipe down to expand the panel, dismissing the bouncer.
- if (mStatusBar.isBouncerShowing()) {
+ if (mCentralSurfaces.isBouncerShowing()) {
return true;
}
if (mCommandQueue.panelsEnabled()
@@ -4192,8 +4211,8 @@
// Do not allow panel expansion if bouncer is scrimmed or showing over a dream,
// otherwise user would be able to pull down QS or expand the shade.
- if (mStatusBar.isBouncerShowingScrimmed()
- || mStatusBar.isBouncerShowingOverDream()) {
+ if (mCentralSurfaces.isBouncerShowingScrimmed()
+ || mCentralSurfaces.isBouncerShowingOverDream()) {
return false;
}
@@ -4257,15 +4276,15 @@
new PhoneStatusBarView.TouchEventHandler() {
@Override
public void onInterceptTouchEvent(MotionEvent event) {
- mStatusBar.onTouchEvent(event);
+ mCentralSurfaces.onTouchEvent(event);
}
@Override
public boolean handleTouchEvent(MotionEvent event) {
- mStatusBar.onTouchEvent(event);
+ mCentralSurfaces.onTouchEvent(event);
// TODO(b/202981994): Move the touch debugging in this method to a central
- // location. (Right now, it's split between StatusBar and here.)
+ // location. (Right now, it's split between CentralSurfaces and here.)
// If panels aren't enabled, ignore the gesture and don't pass it down to the
// panel view.
@@ -4482,7 +4501,7 @@
: !rightPage;
mIsLaunchTransitionRunning = true;
mLaunchAnimationEndRunnable = null;
- float displayDensity = mStatusBar.getDisplayDensity();
+ float displayDensity = mCentralSurfaces.getDisplayDensity();
int lengthDp = Math.abs((int) (translation / displayDensity));
int velocityDp = Math.abs((int) (vel / displayDensity));
if (start) {
@@ -4490,7 +4509,7 @@
mLockscreenGestureLogger.log(LockscreenUiEvent.LOCKSCREEN_DIALER);
mFalsingCollector.onLeftAffordanceOn();
if (mFalsingCollector.shouldEnforceBouncer()) {
- mStatusBar.executeRunnableDismissingKeyguard(
+ mCentralSurfaces.executeRunnableDismissingKeyguard(
() -> mKeyguardBottomArea.launchLeftAffordance(), null,
true /* dismissShade */, false /* afterKeyguardGone */,
true /* deferred */);
@@ -4506,7 +4525,7 @@
}
mFalsingCollector.onCameraOn();
if (mFalsingCollector.shouldEnforceBouncer()) {
- mStatusBar.executeRunnableDismissingKeyguard(
+ mCentralSurfaces.executeRunnableDismissingKeyguard(
() -> mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource), null,
true /* dismissShade */, false /* afterKeyguardGone */,
true /* deferred */);
@@ -4514,7 +4533,7 @@
mKeyguardBottomArea.launchCamera(mLastCameraLaunchSource);
}
}
- mStatusBar.startLaunchTransitionTimeout();
+ mCentralSurfaces.startLaunchTransitionTimeout();
mBlockTouches = true;
}
@@ -4526,7 +4545,7 @@
mLaunchAnimationEndRunnable.run();
mLaunchAnimationEndRunnable = null;
}
- mStatusBar.readyForKeyguardDone();
+ mCentralSurfaces.readyForKeyguardDone();
}
@Override
@@ -4563,18 +4582,18 @@
mHintAnimationRunning = true;
mAffordanceHelper.startHintAnimation(rightIcon, () -> {
mHintAnimationRunning = false;
- mStatusBar.onHintFinished();
+ mCentralSurfaces.onHintFinished();
});
rightIcon =
mView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL ? !rightIcon
: rightIcon;
if (rightIcon) {
- mStatusBar.onCameraHintStarted();
+ mCentralSurfaces.onCameraHintStarted();
} else {
if (mKeyguardBottomArea.isLeftVoiceAssist()) {
- mStatusBar.onVoiceAssistHintStarted();
+ mCentralSurfaces.onVoiceAssistHintStarted();
} else {
- mStatusBar.onPhoneHintStarted();
+ mCentralSurfaces.onPhoneHintStarted();
}
}
}
@@ -4605,7 +4624,7 @@
@Override
public float getAffordanceFalsingFactor() {
- return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
+ return mCentralSurfaces.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
}
@Override
@@ -5121,7 +5140,7 @@
mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
}
if (state == STATE_OPENING) {
- mStatusBar.makeExpandedVisible(false);
+ mCentralSurfaces.makeExpandedVisible(false);
}
if (state == STATE_CLOSED) {
// Close the status bar in the next frame so we can show the end of the
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
index 5caf4f6..0ff010a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
@@ -840,7 +840,7 @@
Set<String> mComponentsForcingTopUi = new HashSet<>();
/**
- * The {@link StatusBar} state from the status bar.
+ * The status bar state from {@link CentralSurfaces}.
*/
int mStatusBarState;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
index fb0e306..1e3a02b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
@@ -58,7 +58,7 @@
*/
public class NotificationShadeWindowView extends FrameLayout {
public static final String TAG = "NotificationShadeWindowView";
- public static final boolean DEBUG = StatusBar.DEBUG;
+ public static final boolean DEBUG = CentralSurfaces.DEBUG;
private int mRightInset = 0;
private int mLeftInset = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
index 396703b..101c86f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
@@ -44,6 +44,7 @@
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.tuner.TunerService;
@@ -57,6 +58,7 @@
/**
* Controller for {@link NotificationShadeWindowView}.
*/
+@CentralSurfacesComponent.CentralSurfacesScope
public class NotificationShadeWindowViewController {
private static final String TAG = "NotifShadeWindowVC";
private final FalsingCollector mFalsingCollector;
@@ -77,8 +79,8 @@
private boolean mExpandAnimationRunning;
private NotificationStackScrollLayout mStackScrollLayout;
private PhoneStatusBarViewController mStatusBarViewController;
- private StatusBar mService;
- private NotificationShadeWindowController mNotificationShadeWindowController;
+ private final CentralSurfaces mService;
+ private final NotificationShadeWindowController mNotificationShadeWindowController;
private DragDownHelper mDragDownHelper;
private boolean mDoubleTapEnabled;
private boolean mSingleTapEnabled;
@@ -105,7 +107,9 @@
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
StatusBarWindowStateController statusBarWindowStateController,
LockIconViewController lockIconViewController,
- Optional<LowLightClockController> lowLightClockController) {
+ Optional<LowLightClockController> lowLightClockController,
+ CentralSurfaces centralSurfaces,
+ NotificationShadeWindowController controller) {
mLockscreenShadeTransitionController = transitionController;
mFalsingCollector = falsingCollector;
mTunerService = tunerService;
@@ -120,6 +124,8 @@
mStatusBarWindowStateController = statusBarWindowStateController;
mLockIconViewController = lockIconViewController;
mLowLightClockController = lowLightClockController;
+ mService = centralSurfaces;
+ mNotificationShadeWindowController = controller;
// This view is not part of the newly inflated expanded status bar.
mBrightnessMirror = mView.findViewById(R.id.brightness_mirror_container);
@@ -452,11 +458,6 @@
mStatusBarViewController = statusBarViewController;
}
- public void setService(StatusBar statusBar, NotificationShadeWindowController controller) {
- mService = statusBar;
- mNotificationShadeWindowController = controller;
- }
-
/**
* Tell the controller that dozing has begun or ended.
* @param dozing True if dozing has begun.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 249f988..45dc943 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -27,7 +27,7 @@
public static final String TAG = PanelView.class.getSimpleName();
private PanelViewController.TouchHandler mTouchHandler;
- protected StatusBar mStatusBar;
+ protected CentralSurfaces mCentralSurfaces;
protected HeadsUpManagerPhone mHeadsUpManager;
protected KeyguardBottomAreaView mKeyguardBottomArea;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index 85e8042..7c1775e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -113,7 +113,7 @@
Log.v(TAG, (mViewName != null ? (mViewName + ": ") : "") + String.format(fmt, args));
}
- protected StatusBar mStatusBar;
+ protected CentralSurfaces mCentralSurfaces;
protected HeadsUpManagerPhone mHeadsUpManager;
protected final StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
@@ -349,9 +349,9 @@
//TODO: keyguard opens QS a different way; log that too?
// Log the position of the swipe that opened the panel
- float width = mStatusBar.getDisplayWidth();
- float height = mStatusBar.getDisplayHeight();
- int rot = mStatusBar.getRotation();
+ float width = mCentralSurfaces.getDisplayWidth();
+ float height = mCentralSurfaces.getDisplayHeight();
+ int rot = mCentralSurfaces.getRotation();
mLockscreenGestureLogger.writeAtFractionalPosition(MetricsEvent.ACTION_PANEL_VIEW_EXPAND,
(int) (event.getX() / width * 100), (int) (event.getY() / height * 100), rot);
@@ -433,10 +433,11 @@
}
mDozeLog.traceFling(expand, mTouchAboveFalsingThreshold,
- mStatusBar.isFalsingThresholdNeeded(), mStatusBar.isWakeUpComingFromTouch());
+ mCentralSurfaces.isFalsingThresholdNeeded(),
+ mCentralSurfaces.isWakeUpComingFromTouch());
// Log collapse gesture if on lock screen.
if (!expand && onKeyguard) {
- float displayDensity = mStatusBar.getDisplayDensity();
+ float displayDensity = mCentralSurfaces.getDisplayDensity();
int heightDp = (int) Math.abs((y - mInitialTouchY) / displayDensity);
int velocityDp = (int) Math.abs(vel / displayDensity);
mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_UNLOCK, heightDp, velocityDp);
@@ -453,7 +454,7 @@
if (mUpdateFlingOnLayout) {
mUpdateFlingVelocity = vel;
}
- } else if (!mStatusBar.isBouncerShowing()
+ } else if (!mCentralSurfaces.isBouncerShowing()
&& !mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()
&& !mKeyguardStateController.isKeyguardGoingAway()) {
boolean expands = onEmptySpaceClick(mInitialTouchX);
@@ -469,7 +470,7 @@
}
private int getFalsingThreshold() {
- float factor = mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
+ float factor = mCentralSurfaces.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
return (int) (mUnlockFalsingThreshold * factor);
}
@@ -479,14 +480,14 @@
protected void onTrackingStopped(boolean expand) {
mTracking = false;
- mStatusBar.onTrackingStopped(expand);
+ mCentralSurfaces.onTrackingStopped(expand);
updatePanelExpansionAndVisibility();
}
protected void onTrackingStarted() {
endClosing();
mTracking = true;
- mStatusBar.onTrackingStarted();
+ mCentralSurfaces.onTrackingStarted();
notifyExpandingStarted();
updatePanelExpansionAndVisibility();
}
@@ -556,7 +557,7 @@
*/
private boolean isFalseTouch(float x, float y,
@Classifier.InteractionType int interactionType) {
- if (!mStatusBar.isFalsingThresholdNeeded()) {
+ if (!mCentralSurfaces.isFalsingThresholdNeeded()) {
return false;
}
if (mFalsingManager.isClassifierEnabled()) {
@@ -921,7 +922,7 @@
mView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
return;
}
- if (mStatusBar.getNotificationShadeWindowView().isVisibleToUser()) {
+ if (mCentralSurfaces.getNotificationShadeWindowView().isVisibleToUser()) {
mView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
if (mAnimateAfterExpanding) {
notifyExpandingStarted();
@@ -976,11 +977,11 @@
}
protected void onUnlockHintFinished() {
- mStatusBar.onHintFinished();
+ mCentralSurfaces.onHintFinished();
}
protected void onUnlockHintStarted() {
- mStatusBar.onUnlockHintStarted();
+ mCentralSurfaces.onUnlockHintStarted();
}
public boolean isUnlockHintRunning() {
@@ -1018,7 +1019,7 @@
View[] viewsToAnimate = {
mKeyguardBottomArea.getIndicationArea(),
- mStatusBar.getAmbientIndicationContainer()};
+ mCentralSurfaces.getAmbientIndicationContainer()};
for (View v : viewsToAnimate) {
if (v == null) {
continue;
@@ -1210,7 +1211,7 @@
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
- mStatusBar.userActivity();
+ mCentralSurfaces.userActivity();
mAnimatingOnDown = mHeightAnimator != null && !mIsSpringBackAnimation;
mMinExpandHeight = 0.0f;
mDownTime = SystemClock.uptimeMillis();
@@ -1340,7 +1341,7 @@
onTrackingStarted();
}
if (isFullyCollapsed() && !mHeadsUpManager.hasPinnedHeadsUp()
- && !mStatusBar.isBouncerShowing()) {
+ && !mCentralSurfaces.isBouncerShowing()) {
startOpening(event);
}
break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index 224b2e4..9da2ef73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -31,6 +31,7 @@
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
import com.android.systemui.util.ViewController
import com.android.systemui.util.kotlin.getOrNull
+import com.android.systemui.util.view.ViewUtil
import java.util.Optional
@@ -43,6 +44,7 @@
@Named(UNFOLD_STATUS_BAR) private val progressProvider: ScopedUnfoldTransitionProgressProvider?,
private val moveFromCenterAnimationController: StatusBarMoveFromCenterAnimationController?,
private val userSwitcherController: StatusBarUserSwitcherController,
+ private val viewUtil: ViewUtil,
touchEventHandler: PhoneStatusBarView.TouchEventHandler,
private val configurationController: ConfigurationController
) : ViewController<PhoneStatusBarView>(view) {
@@ -118,12 +120,7 @@
* view's range and false otherwise.
*/
fun touchIsWithinView(x: Float, y: Float): Boolean {
- val left = mView.locationOnScreen[0]
- val top = mView.locationOnScreen[1]
- return left <= x &&
- x <= left + mView.width &&
- top <= y &&
- y <= top + mView.height
+ return viewUtil.touchIsWithinView(mView, x, y)
}
class StatusBarViewsCenterProvider : UnfoldMoveFromCenterAnimator.ViewCenterProvider {
@@ -163,6 +160,7 @@
@Named(UNFOLD_STATUS_BAR)
private val progressProvider: Optional<ScopedUnfoldTransitionProgressProvider>,
private val userSwitcherController: StatusBarUserSwitcherController,
+ private val viewUtil: ViewUtil,
private val configurationController: ConfigurationController
) {
fun create(
@@ -174,6 +172,7 @@
progressProvider.getOrNull(),
unfoldComponent.getOrNull()?.getStatusBarMoveFromCenterAnimationController(),
userSwitcherController,
+ viewUtil,
touchEventHandler,
configurationController
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
index ea61a8b..c817466 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
@@ -37,8 +37,8 @@
private val animations: List<ScreenOffAnimation> =
listOfNotNull(foldToAodAnimation, unlockedScreenOffAnimation)
- fun initialize(statusBar: StatusBar, lightRevealScrim: LightRevealScrim) {
- animations.forEach { it.initialize(statusBar, lightRevealScrim) }
+ fun initialize(centralSurfaces: CentralSurfaces, lightRevealScrim: LightRevealScrim) {
+ animations.forEach { it.initialize(centralSurfaces, lightRevealScrim) }
wakefulnessLifecycle.addObserver(this)
}
@@ -131,7 +131,7 @@
animations.any { it.isKeyguardHideDelayed() }
/**
- * Return true to make the StatusBar expanded so we can animate [LightRevealScrim]
+ * Return true to make the status bar expanded so we can animate [LightRevealScrim]
*/
fun shouldShowLightRevealScrim(): Boolean =
animations.any { it.shouldPlayAnimation() }
@@ -197,7 +197,7 @@
}
interface ScreenOffAnimation {
- fun initialize(statusBar: StatusBar, lightRevealScrim: LightRevealScrim) {}
+ fun initialize(centralSurfaces: CentralSurfaces, lightRevealScrim: LightRevealScrim) {}
/**
* Called when started going to sleep, should return true if the animation will be played
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 8d64041..a3c795f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -413,7 +413,7 @@
}
if (mKeyguardUpdateMonitor.needsSlowUnlockTransition() && mState == ScrimState.UNLOCKED) {
- mAnimationDelay = StatusBar.FADE_KEYGUARD_START_DELAY;
+ mAnimationDelay = CentralSurfaces.FADE_KEYGUARD_START_DELAY;
scheduleUpdate();
} else if (((oldState == ScrimState.AOD || oldState == ScrimState.PULSING) // leaving doze
&& (!mDozeParameters.getAlwaysOn() || mState == ScrimState.UNLOCKED))
@@ -1255,7 +1255,7 @@
public void setScrimBehindChangeRunnable(Runnable changeRunnable) {
// TODO: remove this. This is necessary because of an order-of-operations limitation.
- // The fix is to move more of these class into @StatusBarScope
+ // The fix is to move more of these class into @CentralSurfacesScope
if (mScrimBehind == null) {
mScrimBehindChangeRunnable = changeRunnable;
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index bfd625b..9028870 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -238,7 +238,7 @@
mAnimationDuration = mKeyguardFadingAway
? mKeyguardFadingAwayDuration
- : StatusBar.FADE_KEYGUARD_DURATION;
+ : CentralSurfaces.FADE_KEYGUARD_DURATION;
boolean fromAod = previousState == AOD || previousState == PULSING;
mAnimateChange = !mLaunchingAffordanceWithPreview && !fromAod;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java
index 24bb7f2..83ee125 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java
@@ -18,9 +18,9 @@
/**
* {@link ShadeController} is an abstraction of the work that used to be hard-coded in
- * {@link StatusBar}. The shade itself represents the concept of the status bar window state, and
- * can be in multiple states: dozing, locked, showing the bouncer, occluded, etc. All/some of these
- * are coordinated with {@link StatusBarKeyguardViewManager} via
+ * {@link CentralSurfaces}. The shade itself represents the concept of the status bar window state,
+ * and can be in multiple states: dozing, locked, showing the bouncer, occluded, etc. All/some of
+ * these are coordinated with {@link StatusBarKeyguardViewManager} via
* {@link com.android.systemui.keyguard.KeyguardViewMediator} and others.
*/
public interface ShadeController {
@@ -38,7 +38,7 @@
/**
* Collapse the shade animated, showing the bouncer when on {@link StatusBarState#KEYGUARD} or
- * dismissing {@link StatusBar} when on {@link StatusBarState#SHADE}.
+ * dismissing {@link CentralSurfaces} when on {@link StatusBarState#SHADE}.
*/
void animateCollapsePanels(int flags, boolean force);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
index 53ef97d..cee8b33 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
@@ -47,7 +47,7 @@
protected final NotificationShadeWindowController mNotificationShadeWindowController;
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private final int mDisplayId;
- protected final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
+ protected final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
private final Lazy<AssistManager> mAssistManagerLazy;
private final ArrayList<Runnable> mPostCollapseRunnables = new ArrayList<>();
@@ -59,7 +59,7 @@
NotificationShadeWindowController notificationShadeWindowController,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
WindowManager windowManager,
- Lazy<Optional<StatusBar>> statusBarOptionalLazy,
+ Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
Lazy<AssistManager> assistManagerLazy
) {
mCommandQueue = commandQueue;
@@ -67,15 +67,15 @@
mNotificationShadeWindowController = notificationShadeWindowController;
mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
mDisplayId = windowManager.getDefaultDisplay().getDisplayId();
- // TODO: Remove circular reference to StatusBar when possible.
- mStatusBarOptionalLazy = statusBarOptionalLazy;
+ // TODO: Remove circular reference to CentralSurfaces when possible.
+ mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy;
mAssistManagerLazy = assistManagerLazy;
}
@Override
public void instantExpandNotificationsPanel() {
// Make our window larger and the panel expanded.
- getStatusBar().makeExpandedVisible(true /* force */);
+ getCentralSurfaces().makeExpandedVisible(true /* force */);
getNotificationPanelViewController().expand(false /* animate */);
mCommandQueue.recomputeDisableFlags(mDisplayId, false /* animate */);
}
@@ -110,7 +110,7 @@
}
if (SPEW) {
Log.d(TAG, "animateCollapse():"
- + " mExpandedVisible=" + getStatusBar().isExpandedVisible()
+ + " mExpandedVisible=" + getCentralSurfaces().isExpandedVisible()
+ " flags=" + flags);
}
@@ -124,7 +124,7 @@
// release focus immediately to kick off focus change transition
mNotificationShadeWindowController.setNotificationShadeFocusable(false);
- getStatusBar().getNotificationShadeWindowViewController().cancelExpandHelper();
+ getCentralSurfaces().getNotificationShadeWindowViewController().cancelExpandHelper();
getNotificationPanelViewController()
.collapsePanel(true /* animate */, delayed, speedUpFactor);
}
@@ -136,7 +136,7 @@
if (!getNotificationPanelViewController().isFullyCollapsed()) {
mCommandQueue.animateCollapsePanels(
CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
- getStatusBar().visibilityChanged(false);
+ getCentralSurfaces().visibilityChanged(false);
mAssistManagerLazy.get().hideAssist();
}
return false;
@@ -155,7 +155,7 @@
new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
- if (getStatusBar().getNotificationShadeWindowView().isVisibleToUser()) {
+ if (getCentralSurfaces().getNotificationShadeWindowView().isVisibleToUser()) {
getNotificationPanelViewController().removeOnGlobalLayoutListener(this);
getNotificationPanelViewController().getView().post(executable);
}
@@ -185,7 +185,7 @@
// close the shade if it was open
animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
true /* force */, true /* delayed */);
- getStatusBar().visibilityChanged(false);
+ getCentralSurfaces().visibilityChanged(false);
return true;
} else {
@@ -201,26 +201,26 @@
runPostCollapseRunnables();
}
} else if (!getPresenter().isPresenterFullyCollapsed()) {
- getStatusBar().instantCollapseNotificationPanel();
- getStatusBar().visibilityChanged(false);
+ getCentralSurfaces().instantCollapseNotificationPanel();
+ getCentralSurfaces().visibilityChanged(false);
} else {
runPostCollapseRunnables();
}
}
- private StatusBar getStatusBar() {
- return mStatusBarOptionalLazy.get().get();
+ private CentralSurfaces getCentralSurfaces() {
+ return mCentralSurfacesOptionalLazy.get().get();
}
private NotificationPresenter getPresenter() {
- return getStatusBar().getPresenter();
+ return getCentralSurfaces().getPresenter();
}
protected NotificationShadeWindowView getNotificationShadeWindowView() {
- return getStatusBar().getNotificationShadeWindowView();
+ return getCentralSurfaces().getNotificationShadeWindowView();
}
private NotificationPanelViewController getNotificationPanelViewController() {
- return getStatusBar().getPanelController();
+ return getCentralSurfaces().getPanelController();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
index a1be5ac..7555356 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SplitShadeHeaderController.kt
@@ -30,7 +30,7 @@
import com.android.systemui.qs.ChipVisibilityListener
import com.android.systemui.qs.HeaderPrivacyIconsController
import com.android.systemui.qs.carrier.QSCarrierGroupController
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent.StatusBarScope
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope
import com.android.systemui.statusbar.phone.dagger.StatusBarViewModule.SPLIT_SHADE_BATTERY_CONTROLLER
import com.android.systemui.statusbar.phone.dagger.StatusBarViewModule.SPLIT_SHADE_HEADER
import java.io.FileDescriptor
@@ -38,7 +38,7 @@
import javax.inject.Inject
import javax.inject.Named
-@StatusBarScope
+@CentralSurfacesScope
class SplitShadeHeaderController @Inject constructor(
@Named(SPLIT_SHADE_HEADER) private val statusBar: View,
private val statusBarIconController: StatusBarIconController,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
index 6eeae7f..50f2169 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
@@ -22,14 +22,16 @@
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.init.NotificationsController;
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
import com.android.systemui.statusbar.window.StatusBarWindowController;
import javax.inject.Inject;
-/** Ties the {@link StatusBar} to {@link com.android.systemui.statusbar.policy.HeadsUpManager}. */
-@StatusBarComponent.StatusBarScope
+/**
+ * Ties the {@link CentralSurfaces} to {@link com.android.systemui.statusbar.policy.HeadsUpManager}.
+ */
+@CentralSurfacesComponent.CentralSurfacesScope
public class StatusBarHeadsUpChangeListener implements OnHeadsUpChangedListener {
private final NotificationShadeWindowController mNotificationShadeWindowController;
private final StatusBarWindowController mStatusBarWindowController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 81fb903..31d9266 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -48,6 +48,7 @@
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
+import com.android.systemui.util.Assert;
import java.util.ArrayList;
import java.util.List;
@@ -66,6 +67,8 @@
void addIconGroup(IconManager iconManager);
/** */
void removeIconGroup(IconManager iconManager);
+ /** Refresh the state of an IconManager by recreating the views */
+ void refreshIconGroup(IconManager iconManager);
/** */
void setExternalIcon(String slot);
/** */
@@ -243,6 +246,7 @@
protected final int mIconSize;
// Whether or not these icons show up in dumpsys
protected boolean mShouldLog = false;
+ private StatusBarIconController mController;
// Enables SystemUI demo mode to take effect in this group
protected boolean mDemoable = true;
@@ -267,13 +271,17 @@
mDemoable = demoable;
}
- public void setBlockList(@Nullable List<String> blockList) {
- mBlockList.clear();
- if (blockList == null || blockList.isEmpty()) {
- return;
- }
+ void setController(StatusBarIconController controller) {
+ mController = controller;
+ }
+ public void setBlockList(@Nullable List<String> blockList) {
+ Assert.isMainThread();
+ mBlockList.clear();
mBlockList.addAll(blockList);
+ if (mController != null) {
+ mController.refreshIconGroup(this);
+ }
}
public void setShouldLog(boolean should) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
index c5d3937..623ec5e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -100,6 +100,7 @@
}
}
+ group.setController(this);
mIconGroups.add(group);
List<Slot> allSlots = getSlots();
for (int i = 0; i < allSlots.size(); i++) {
@@ -115,6 +116,12 @@
}
}
+ @Override
+ public void refreshIconGroup(IconManager iconManager) {
+ removeIconGroup(iconManager);
+ addIconGroup(iconManager);
+ }
+
private void refreshIconGroups() {
for (int i = mIconGroups.size() - 1; i >= 0; --i) {
IconManager group = mIconGroups.get(i);
@@ -245,7 +252,7 @@
/**
* Accept a list of CallIndicatorIconStates, and show the call strength icons.
- * @param slot StatusBar slot for the call strength icons
+ * @param slot statusbar slot for the call strength icons
* @param states All of the no Calling & SMS icon states
*/
@Override
@@ -272,7 +279,7 @@
/**
* Accept a list of CallIndicatorIconStates, and show the no calling icons.
- * @param slot StatusBar slot for the no calling icons
+ * @param slot statusbar slot for the no calling icons
* @param states All of the no Calling & SMS icon states
*/
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index a96ba56..c160c22 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -124,8 +124,8 @@
@Override
public void onFullyShown() {
updateStates();
- mStatusBar.wakeUpIfDozing(SystemClock.uptimeMillis(),
- mStatusBar.getBouncerContainer(), "BOUNCER_VISIBLE");
+ mCentralSurfaces.wakeUpIfDozing(SystemClock.uptimeMillis(),
+ mCentralSurfaces.getBouncerContainer(), "BOUNCER_VISIBLE");
}
@Override
@@ -175,7 +175,7 @@
protected LockPatternUtils mLockPatternUtils;
protected ViewMediatorCallback mViewMediatorCallback;
- protected StatusBar mStatusBar;
+ protected CentralSurfaces mCentralSurfaces;
private NotificationPanelViewController mNotificationPanelViewController;
private BiometricUnlockController mBiometricUnlockController;
@@ -277,16 +277,16 @@
}
@Override
- public void registerStatusBar(StatusBar statusBar,
+ public void registerCentralSurfaces(CentralSurfaces centralSurfaces,
NotificationPanelViewController notificationPanelViewController,
PanelExpansionStateManager panelExpansionStateManager,
BiometricUnlockController biometricUnlockController,
View notificationContainer,
KeyguardBypassController bypassController) {
- mStatusBar = statusBar;
+ mCentralSurfaces = centralSurfaces;
mBiometricUnlockController = biometricUnlockController;
- ViewGroup container = mStatusBar.getBouncerContainer();
+ ViewGroup container = mCentralSurfaces.getBouncerContainer();
mBouncer = mKeyguardBouncerFactory.create(container, mExpansionCallback);
mNotificationPanelViewController = notificationPanelViewController;
if (panelExpansionStateManager != null) {
@@ -354,13 +354,13 @@
} else if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED
&& mKeyguardUpdateManager.isUdfpsEnrolled()) {
// Don't expand to the bouncer. Instead transition back to the lock screen (see
- // StatusBar#showBouncerOrLockScreenIfKeyguard) where the user can use the UDFPS
+ // CentralSurfaces#showBouncerOrLockScreenIfKeyguard) where the user can use the UDFPS
// affordance to enter the device (or swipe up to the input bouncer)
return;
} else if (bouncerNeedsScrimming()) {
mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
} else if (mShowing) {
- if (!isWakeAndUnlocking() && !mStatusBar.isInLaunchTransition()) {
+ if (!isWakeAndUnlocking() && !mCentralSurfaces.isInLaunchTransition()) {
mBouncer.setExpansion(fraction);
}
if (fraction != KeyguardBouncer.EXPANSION_HIDDEN && tracking
@@ -371,7 +371,9 @@
} else if (mPulsing && fraction == KeyguardBouncer.EXPANSION_VISIBLE) {
// Panel expanded while pulsing but didn't translate the bouncer (because we are
// unlocked.) Let's simply wake-up to dismiss the lock screen.
- mStatusBar.wakeUpIfDozing(SystemClock.uptimeMillis(), mStatusBar.getBouncerContainer(),
+ mCentralSurfaces.wakeUpIfDozing(
+ SystemClock.uptimeMillis(),
+ mCentralSurfaces.getBouncerContainer(),
"BOUNCER_VISIBLE");
}
}
@@ -408,10 +410,10 @@
protected void showBouncerOrKeyguard(boolean hideBouncerWhenShowing) {
if (mBouncer.needsFullscreenBouncer() && !mDozing) {
// The keyguard might be showing (already). So we need to hide it.
- mStatusBar.hideKeyguard();
+ mCentralSurfaces.hideKeyguard();
mBouncer.show(true /* resetSecuritySelection */);
} else {
- mStatusBar.showKeyguard();
+ mCentralSurfaces.showKeyguard();
if (hideBouncerWhenShowing) {
hideBouncer(false /* destroyView */);
mBouncer.prepare();
@@ -540,7 +542,7 @@
mNotificationPanelViewController.resetViews(/* animate= */ true);
// Hide bouncer and quick-quick settings.
if (mOccluded && !mDozing) {
- mStatusBar.hideKeyguard();
+ mCentralSurfaces.hideKeyguard();
if (hideBouncerWhenShowing || mBouncer.needsFullscreenBouncer()) {
hideBouncer(false /* destroyView */);
}
@@ -570,15 +572,15 @@
mBypassController.setAltBouncerShowing(isShowingAlternateAuth());
if (updateScrim) {
- mStatusBar.updateScrimController();
+ mCentralSurfaces.updateScrimController();
}
}
@Override
public void onStartedWakingUp() {
- mStatusBar.getNotificationShadeWindowView().getWindowInsetsController()
+ mCentralSurfaces.getNotificationShadeWindowView().getWindowInsetsController()
.setAnimationsDisabled(false);
- NavigationBarView navBarView = mStatusBar.getNavigationBarView();
+ NavigationBarView navBarView = mCentralSurfaces.getNavigationBarView();
if (navBarView != null) {
navBarView.forEachView(view ->
view.animate()
@@ -590,9 +592,9 @@
@Override
public void onStartedGoingToSleep() {
- mStatusBar.getNotificationShadeWindowView().getWindowInsetsController()
+ mCentralSurfaces.getNotificationShadeWindowView().getWindowInsetsController()
.setAnimationsDisabled(true);
- NavigationBarView navBarView = mStatusBar.getNavigationBarView();
+ NavigationBarView navBarView = mCentralSurfaces.getNavigationBarView();
if (navBarView != null) {
navBarView.forEachView(view ->
view.animate()
@@ -628,7 +630,7 @@
}
/**
- * If {@link StatusBar} is pulsing.
+ * If {@link CentralSurfaces} is pulsing.
*/
public void setPulsing(boolean pulsing) {
if (mPulsing != pulsing) {
@@ -649,13 +651,13 @@
@Override
public void setOccluded(boolean occluded, boolean animate) {
- mStatusBar.setOccluded(occluded);
+ mCentralSurfaces.setOccluded(occluded);
if (occluded && !mOccluded && mShowing) {
SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_STATE_CHANGED,
SysUiStatsLog.KEYGUARD_STATE_CHANGED__STATE__OCCLUDED);
- if (mStatusBar.isInLaunchTransition()) {
+ if (mCentralSurfaces.isInLaunchTransition()) {
setOccludedAndUpdateStates(true);
- mStatusBar.fadeKeyguardAfterLaunchTransition(null /* beforeFading */,
+ mCentralSurfaces.fadeKeyguardAfterLaunchTransition(null /* beforeFading */,
new Runnable() {
@Override
public void run() {
@@ -666,7 +668,7 @@
return;
}
- if (mStatusBar.isLaunchingActivityOverLockscreen()) {
+ if (mCentralSurfaces.isLaunchingActivityOverLockscreen()) {
setOccludedAndUpdateStates(true);
// When isLaunchingActivityOverLockscreen() is true, we know for sure that the post
@@ -695,7 +697,7 @@
reset(isOccluding /* hideBouncerWhenShowing*/);
}
if (animate && !occluded && mShowing && !mBouncer.isShowing()) {
- mStatusBar.animateKeyguardUnoccluding();
+ mCentralSurfaces.animateKeyguardUnoccluding();
}
}
@@ -712,7 +714,7 @@
public void startPreHideAnimation(Runnable finishRunnable) {
if (mBouncer.isShowing()) {
mBouncer.startPreHideAnimation(finishRunnable);
- mStatusBar.onBouncerPreHideAnimation();
+ mCentralSurfaces.onBouncerPreHideAnimation();
// We update the state (which will show the keyguard) only if an animation will run on
// the keyguard. If there is no animation, we wait before updating the state so that we
@@ -745,11 +747,11 @@
long uptimeMillis = SystemClock.uptimeMillis();
long delay = Math.max(0, startTime + HIDE_TIMING_CORRECTION_MS - uptimeMillis);
- if (mStatusBar.isInLaunchTransition()
+ if (mCentralSurfaces.isInLaunchTransition()
|| mKeyguardStateController.isFlingingToDismissKeyguard()) {
final boolean wasFlingingToDismissKeyguard =
mKeyguardStateController.isFlingingToDismissKeyguard();
- mStatusBar.fadeKeyguardAfterLaunchTransition(new Runnable() {
+ mCentralSurfaces.fadeKeyguardAfterLaunchTransition(new Runnable() {
@Override
public void run() {
mNotificationShadeWindowController.setKeyguardShowing(false);
@@ -760,11 +762,11 @@
}, new Runnable() {
@Override
public void run() {
- mStatusBar.hideKeyguard();
+ mCentralSurfaces.hideKeyguard();
mNotificationShadeWindowController.setKeyguardFadingAway(false);
if (wasFlingingToDismissKeyguard) {
- mStatusBar.finishKeyguardFadingAway();
+ mCentralSurfaces.finishKeyguardFadingAway();
}
mViewMediatorCallback.keyguardGone();
@@ -783,7 +785,7 @@
delay = 0;
fadeoutDuration = 240;
}
- mStatusBar.setKeyguardFadingAway(startTime, delay, fadeoutDuration, needsFading);
+ mCentralSurfaces.setKeyguardFadingAway(startTime, delay, fadeoutDuration, needsFading);
mBiometricUnlockController.startKeyguardFadingAway();
hideBouncer(true /* destroyView */);
if (wakeUnlockPulsing) {
@@ -793,11 +795,11 @@
mNotificationContainer,
fadeoutDuration,
() -> {
- mStatusBar.hideKeyguard();
+ mCentralSurfaces.hideKeyguard();
onKeyguardFadedAway();
});
} else {
- mStatusBar.fadeKeyguardWhilePulsing();
+ mCentralSurfaces.fadeKeyguardWhilePulsing();
}
wakeAndUnlockDejank();
} else {
@@ -810,20 +812,20 @@
mNotificationContainer,
fadeoutDuration,
() -> {
- mStatusBar.hideKeyguard();
+ mCentralSurfaces.hideKeyguard();
});
} else {
- mStatusBar.hideKeyguard();
+ mCentralSurfaces.hideKeyguard();
}
// hide() will happen asynchronously and might arrive after the scrims
// were already hidden, this means that the transition callback won't
// be triggered anymore and StatusBarWindowController will be forever in
// the fadingAway state.
- mStatusBar.updateScrimController();
+ mCentralSurfaces.updateScrimController();
wakeAndUnlockDejank();
} else {
- mStatusBar.hideKeyguard();
- mStatusBar.finishKeyguardFadingAway();
+ mCentralSurfaces.hideKeyguard();
+ mCentralSurfaces.finishKeyguardFadingAway();
mBiometricUnlockController.finishKeyguardFadingAway();
}
}
@@ -866,7 +868,7 @@
mNotificationContainer.postDelayed(() -> mNotificationShadeWindowController
.setKeyguardFadingAway(false), 100);
ViewGroupFadeHelper.reset(mNotificationPanelViewController.getView());
- mStatusBar.finishKeyguardFadingAway();
+ mCentralSurfaces.finishKeyguardFadingAway();
mBiometricUnlockController.finishKeyguardFadingAway();
WindowManagerGlobal.getInstance().trimMemory(
ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
@@ -897,7 +899,7 @@
@Override
public void dismissAndCollapse() {
- mStatusBar.executeRunnableDismissingKeyguard(null, null, true, false, true);
+ mCentralSurfaces.executeRunnableDismissingKeyguard(null, null, true, false, true);
}
/**
@@ -922,7 +924,7 @@
*/
public boolean onBackPressed(boolean hideImmediately) {
if (mBouncer.isShowing()) {
- mStatusBar.endAffordanceLaunch();
+ mCentralSurfaces.endAffordanceLaunch();
// The second condition is for SIM card locked bouncer
if (mBouncer.isScrimmed() && !mBouncer.needsFullscreenBouncer()) {
hideBouncer(false);
@@ -978,11 +980,11 @@
private Runnable mMakeNavigationBarVisibleRunnable = new Runnable() {
@Override
public void run() {
- NavigationBarView view = mStatusBar.getNavigationBarView();
+ NavigationBarView view = mCentralSurfaces.getNavigationBarView();
if (view != null) {
view.setVisibility(View.VISIBLE);
}
- mStatusBar.getNotificationShadeWindowView().getWindowInsetsController()
+ mCentralSurfaces.getNotificationShadeWindowView().getWindowInsetsController()
.show(navigationBars());
}
};
@@ -1013,7 +1015,7 @@
if (bouncerShowing != mLastBouncerShowing || mFirstUpdate) {
mNotificationShadeWindowController.setBouncerShowing(bouncerShowing);
- mStatusBar.setBouncerShowing(bouncerShowing);
+ mCentralSurfaces.setBouncerShowing(bouncerShowing);
}
if (occluded != mLastOccluded || mFirstUpdate) {
@@ -1040,11 +1042,11 @@
mLastBiometricMode = mBiometricUnlockController.getMode();
mLastGesturalNav = mGesturalNav;
mLastIsDocked = mIsDocked;
- mStatusBar.onKeyguardViewManagerStatesUpdated();
+ mCentralSurfaces.onKeyguardViewManagerStatesUpdated();
}
private View getCurrentNavBarView() {
- final NavigationBarView navBarView = mStatusBar.getNavigationBarView();
+ final NavigationBarView navBarView = mCentralSurfaces.getNavigationBarView();
return navBarView != null ? navBarView.getCurrentView() : null;
}
@@ -1052,7 +1054,7 @@
* Updates the visibility of the nav bar window (which will cause insets changes).
*/
protected void updateNavigationBarVisibility(boolean navBarVisible) {
- if (mStatusBar.getNavigationBarView() != null) {
+ if (mCentralSurfaces.getNavigationBarView() != null) {
if (navBarVisible) {
long delay = getNavBarShowDelay();
if (delay == 0) {
@@ -1063,7 +1065,7 @@
}
} else {
mNotificationContainer.removeCallbacks(mMakeNavigationBarVisibleRunnable);
- mStatusBar.getNotificationShadeWindowView().getWindowInsetsController()
+ mCentralSurfaces.getNotificationShadeWindowView().getWindowInsetsController()
.hide(navigationBars());
}
}
@@ -1120,7 +1122,7 @@
@Override
public boolean shouldDisableWindowAnimationsForUnlock() {
- return mStatusBar.isInLaunchTransition();
+ return mCentralSurfaces.isInLaunchTransition();
}
@Override
@@ -1139,7 +1141,7 @@
@Override
public void keyguardGoingAway() {
- mStatusBar.keyguardGoingAway();
+ mCentralSurfaces.keyguardGoingAway();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
index 09fca100..56b6dfc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
@@ -10,34 +10,34 @@
*/
class StatusBarLaunchAnimatorController(
private val delegate: ActivityLaunchAnimator.Controller,
- private val statusBar: StatusBar,
+ private val centralSurfaces: CentralSurfaces,
private val isLaunchForActivity: Boolean = true
) : ActivityLaunchAnimator.Controller by delegate {
// Always sync the opening window with the shade, given that we draw a hole punch in the shade
// of the same size and position as the opening app to make it visible.
override val openingWindowSyncView: View?
- get() = statusBar.notificationShadeWindowView
+ get() = centralSurfaces.notificationShadeWindowView
override fun onIntentStarted(willAnimate: Boolean) {
delegate.onIntentStarted(willAnimate)
if (!willAnimate) {
- statusBar.collapsePanelOnMainThread()
+ centralSurfaces.collapsePanelOnMainThread()
}
}
override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {
delegate.onLaunchAnimationStart(isExpandingFullyAbove)
- statusBar.notificationPanelViewController.setIsLaunchAnimationRunning(true)
+ centralSurfaces.notificationPanelViewController.setIsLaunchAnimationRunning(true)
if (!isExpandingFullyAbove) {
- statusBar.collapsePanelWithDuration(
+ centralSurfaces.collapsePanelWithDuration(
ActivityLaunchAnimator.TIMINGS.totalDuration.toInt())
}
}
override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {
delegate.onLaunchAnimationEnd(isExpandingFullyAbove)
- statusBar.notificationPanelViewController.setIsLaunchAnimationRunning(false)
- statusBar.onLaunchAnimationEnd(isExpandingFullyAbove)
+ centralSurfaces.notificationPanelViewController.setIsLaunchAnimationRunning(false)
+ centralSurfaces.onLaunchAnimationEnd(isExpandingFullyAbove)
}
override fun onLaunchAnimationProgress(
@@ -46,11 +46,11 @@
linearProgress: Float
) {
delegate.onLaunchAnimationProgress(state, progress, linearProgress)
- statusBar.notificationPanelViewController.applyLaunchAnimationProgress(linearProgress)
+ centralSurfaces.notificationPanelViewController.applyLaunchAnimationProgress(linearProgress)
}
override fun onLaunchAnimationCancelled() {
delegate.onLaunchAnimationCancelled()
- statusBar.onLaunchAnimationCancelled(isLaunchForActivity)
+ centralSurfaces.onLaunchAnimationCancelled(isLaunchForActivity)
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index ff86d74..edbddbb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -18,7 +18,7 @@
import static android.service.notification.NotificationListenerService.REASON_CLICK;
-import static com.android.systemui.statusbar.phone.StatusBar.getActivityOptions;
+import static com.android.systemui.statusbar.phone.CentralSurfaces.getActivityOptions;
import android.app.ActivityManager;
import android.app.KeyguardManager;
@@ -51,9 +51,6 @@
import com.android.systemui.EventLogTags;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.assist.AssistManager;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
@@ -75,6 +72,7 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowDragController;
import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.policy.HeadsUpUtil;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.wmshell.BubblesManager;
@@ -89,7 +87,8 @@
/**
* Status bar implementation of {@link NotificationActivityStarter}.
*/
-public class StatusBarNotificationActivityStarter implements NotificationActivityStarter {
+@CentralSurfacesComponent.CentralSurfacesScope
+class StatusBarNotificationActivityStarter implements NotificationActivityStarter {
private final Context mContext;
@@ -123,7 +122,7 @@
private final MetricsLogger mMetricsLogger;
private final StatusBarNotificationActivityStarterLogger mLogger;
- private final StatusBar mStatusBar;
+ private final CentralSurfaces mCentralSurfaces;
private final NotificationPresenter mPresenter;
private final NotificationPanelViewController mNotificationPanel;
private final ActivityLaunchAnimator mActivityLaunchAnimator;
@@ -132,7 +131,8 @@
private boolean mIsCollapsingToShowActivityOverLockscreen;
- private StatusBarNotificationActivityStarter(
+ @Inject
+ StatusBarNotificationActivityStarter(
Context context,
CommandQueue commandQueue,
Handler mainThreadHandler,
@@ -162,7 +162,7 @@
MetricsLogger metricsLogger,
StatusBarNotificationActivityStarterLogger logger,
OnUserInteractionCallback onUserInteractionCallback,
- StatusBar statusBar,
+ CentralSurfaces centralSurfaces,
NotificationPresenter presenter,
NotificationPanelViewController panel,
ActivityLaunchAnimator activityLaunchAnimator,
@@ -199,7 +199,7 @@
mOnUserInteractionCallback = onUserInteractionCallback;
// TODO: use KeyguardStateController#isOccluded to remove this dependency
- mStatusBar = statusBar;
+ mCentralSurfaces = centralSurfaces;
mPresenter = presenter;
mNotificationPanel = panel;
mActivityLaunchAnimator = activityLaunchAnimator;
@@ -259,7 +259,7 @@
&& mActivityIntentHelper.wouldLaunchResolverActivity(intent.getIntent(),
mLockscreenUserManager.getCurrentUserId());
final boolean animate = !willLaunchResolverActivity
- && mStatusBar.shouldAnimateLaunch(isActivityIntent);
+ && mCentralSurfaces.shouldAnimateLaunch(isActivityIntent);
boolean showOverLockscreen = mKeyguardStateController.isShowing() && intent != null
&& mActivityIntentHelper.wouldShowOverLockscreen(intent.getIntent(),
mLockscreenUserManager.getCurrentUserId());
@@ -300,7 +300,7 @@
mShadeController.addPostCollapseAction(runnable);
mShadeController.collapsePanel(true /* animate */);
} else if (mKeyguardStateController.isShowing()
- && mStatusBar.isOccluded()) {
+ && mCentralSurfaces.isOccluded()) {
mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable);
mShadeController.collapsePanel();
} else {
@@ -475,7 +475,8 @@
try {
ActivityLaunchAnimator.Controller animationController =
new StatusBarLaunchAnimatorController(
- mNotificationAnimationProvider.getAnimatorController(row), mStatusBar,
+ mNotificationAnimationProvider.getAnimatorController(row),
+ mCentralSurfaces,
isActivityIntent);
mActivityLaunchAnimator.startPendingIntentWithAnimation(animationController,
@@ -483,11 +484,11 @@
long eventTime = row.getAndResetLastActionUpTime();
Bundle options = eventTime > 0
? getActivityOptions(
- mStatusBar.getDisplayId(),
+ mCentralSurfaces.getDisplayId(),
adapter,
mKeyguardStateController.isShowing(),
eventTime)
- : getActivityOptions(mStatusBar.getDisplayId(), adapter);
+ : getActivityOptions(mCentralSurfaces.getDisplayId(), adapter);
return intent.sendAndReturnResult(mContext, 0, fillInIntent, null,
null, null, options);
});
@@ -502,7 +503,7 @@
@Override
public void startNotificationGutsIntent(final Intent intent, final int appUid,
ExpandableNotificationRow row) {
- boolean animate = mStatusBar.shouldAnimateLaunch(true /* isActivityIntent */);
+ boolean animate = mCentralSurfaces.shouldAnimateLaunch(true /* isActivityIntent */);
ActivityStarter.OnDismissAction onDismissAction = new ActivityStarter.OnDismissAction() {
@Override
public boolean onDismiss() {
@@ -510,14 +511,14 @@
ActivityLaunchAnimator.Controller animationController =
new StatusBarLaunchAnimatorController(
mNotificationAnimationProvider.getAnimatorController(row),
- mStatusBar, true /* isActivityIntent */);
+ mCentralSurfaces, true /* isActivityIntent */);
mActivityLaunchAnimator.startIntentWithAnimation(
animationController, animate, intent.getPackage(),
(adapter) -> TaskStackBuilder.create(mContext)
.addNextIntentWithParentStack(intent)
.startActivities(getActivityOptions(
- mStatusBar.getDisplayId(),
+ mCentralSurfaces.getDisplayId(),
adapter),
new UserHandle(UserHandle.getUserId(appUid))));
});
@@ -535,7 +536,7 @@
@Override
public void startHistoryIntent(View view, boolean showHistory) {
- boolean animate = mStatusBar.shouldAnimateLaunch(true /* isActivityIntent */);
+ boolean animate = mCentralSurfaces.shouldAnimateLaunch(true /* isActivityIntent */);
ActivityStarter.OnDismissAction onDismissAction = new ActivityStarter.OnDismissAction() {
@Override
public boolean onDismiss() {
@@ -555,13 +556,14 @@
);
ActivityLaunchAnimator.Controller animationController =
viewController == null ? null
- : new StatusBarLaunchAnimatorController(viewController, mStatusBar,
+ : new StatusBarLaunchAnimatorController(viewController,
+ mCentralSurfaces,
true /* isActivityIntent */);
mActivityLaunchAnimator.startIntentWithAnimation(animationController, animate,
intent.getPackage(),
(adapter) -> tsb.startActivities(
- getActivityOptions(mStatusBar.getDisplayId(), adapter),
+ getActivityOptions(mCentralSurfaces.getDisplayId(), adapter),
UserHandle.CURRENT));
});
return true;
@@ -615,7 +617,7 @@
try {
EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,
entry.getKey());
- mStatusBar.wakeUpForFullScreenIntent();
+ mCentralSurfaces.wakeUpForFullScreenIntent();
fullscreenIntent.send();
entry.notifyFullScreenIntentLaunched();
mMetricsLogger.count("note_fullscreen", 1);
@@ -657,180 +659,4 @@
return entry.shouldSuppressFullScreenIntent();
}
-
- // --------------------- NotificationEntryManager/NotifPipeline methods ------------------------
-
- /**
- * Public builder for {@link StatusBarNotificationActivityStarter}.
- */
- @SysUISingleton
- public static class Builder {
- private final Context mContext;
- private final CommandQueue mCommandQueue;
- private final Handler mMainThreadHandler;
-
- private final Executor mUiBgExecutor;
- private final NotificationEntryManager mEntryManager;
- private final NotifPipeline mNotifPipeline;
- private final NotificationVisibilityProvider mVisibilityProvider;
- private final HeadsUpManagerPhone mHeadsUpManager;
- private final ActivityStarter mActivityStarter;
- private final NotificationClickNotifier mClickNotifier;
- private final StatusBarStateController mStatusBarStateController;
- private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
- private final KeyguardManager mKeyguardManager;
- private final IDreamManager mDreamManager;
- private final Optional<BubblesManager> mBubblesManagerOptional;
- private final Lazy<AssistManager> mAssistManagerLazy;
- private final NotificationRemoteInputManager mRemoteInputManager;
- private final GroupMembershipManager mGroupMembershipManager;
- private final NotificationLockscreenUserManager mLockscreenUserManager;
- private final ShadeController mShadeController;
- private final KeyguardStateController mKeyguardStateController;
- private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
- private final LockPatternUtils mLockPatternUtils;
- private final StatusBarRemoteInputCallback mRemoteInputCallback;
- private final ActivityIntentHelper mActivityIntentHelper;;
- private final MetricsLogger mMetricsLogger;
- private final StatusBarNotificationActivityStarterLogger mLogger;
- private final OnUserInteractionCallback mOnUserInteractionCallback;
- private final NotifPipelineFlags mNotifPipelineFlags;
- private StatusBar mStatusBar;
- private NotificationPresenter mNotificationPresenter;
- private NotificationPanelViewController mNotificationPanelViewController;
- private ActivityLaunchAnimator mActivityLaunchAnimator;
- private NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider;
-
- @Inject
- public Builder(
- Context context,
- CommandQueue commandQueue,
- @Main Handler mainThreadHandler,
- @UiBackground Executor uiBgExecutor,
- NotificationEntryManager entryManager,
- NotifPipeline notifPipeline,
- NotificationVisibilityProvider visibilityProvider,
- HeadsUpManagerPhone headsUpManager,
- ActivityStarter activityStarter,
- NotificationClickNotifier clickNotifier,
- StatusBarStateController statusBarStateController,
- StatusBarKeyguardViewManager statusBarKeyguardViewManager,
- KeyguardManager keyguardManager,
- IDreamManager dreamManager,
- Optional<BubblesManager> bubblesManager,
- Lazy<AssistManager> assistManagerLazy,
- NotificationRemoteInputManager remoteInputManager,
- GroupMembershipManager groupMembershipManager,
- NotificationLockscreenUserManager lockscreenUserManager,
- ShadeController shadeController,
- KeyguardStateController keyguardStateController,
- NotificationInterruptStateProvider notificationInterruptStateProvider,
- LockPatternUtils lockPatternUtils,
- StatusBarRemoteInputCallback remoteInputCallback,
- ActivityIntentHelper activityIntentHelper,
- NotifPipelineFlags notifPipelineFlags,
- MetricsLogger metricsLogger,
- StatusBarNotificationActivityStarterLogger logger,
- OnUserInteractionCallback onUserInteractionCallback) {
-
- mContext = context;
- mCommandQueue = commandQueue;
- mMainThreadHandler = mainThreadHandler;
- mUiBgExecutor = uiBgExecutor;
- mEntryManager = entryManager;
- mNotifPipeline = notifPipeline;
- mVisibilityProvider = visibilityProvider;
- mHeadsUpManager = headsUpManager;
- mActivityStarter = activityStarter;
- mClickNotifier = clickNotifier;
- mStatusBarStateController = statusBarStateController;
- mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
- mKeyguardManager = keyguardManager;
- mDreamManager = dreamManager;
- mBubblesManagerOptional = bubblesManager;
- mAssistManagerLazy = assistManagerLazy;
- mRemoteInputManager = remoteInputManager;
- mGroupMembershipManager = groupMembershipManager;
- mLockscreenUserManager = lockscreenUserManager;
- mShadeController = shadeController;
- mKeyguardStateController = keyguardStateController;
- mNotificationInterruptStateProvider = notificationInterruptStateProvider;
- mLockPatternUtils = lockPatternUtils;
- mRemoteInputCallback = remoteInputCallback;
- mActivityIntentHelper = activityIntentHelper;
- mNotifPipelineFlags = notifPipelineFlags;
- mMetricsLogger = metricsLogger;
- mLogger = logger;
- mOnUserInteractionCallback = onUserInteractionCallback;
- }
-
- /** Sets the status bar to use as {@link StatusBar}. */
- public Builder setStatusBar(StatusBar statusBar) {
- mStatusBar = statusBar;
- return this;
- }
-
- public Builder setNotificationPresenter(NotificationPresenter notificationPresenter) {
- mNotificationPresenter = notificationPresenter;
- return this;
- }
-
- /** Set the ActivityLaunchAnimator. */
- public Builder setActivityLaunchAnimator(ActivityLaunchAnimator activityLaunchAnimator) {
- mActivityLaunchAnimator = activityLaunchAnimator;
- return this;
- }
-
- /** Set the NotificationLaunchAnimatorControllerProvider. */
- public Builder setNotificationAnimatorControllerProvider(
- NotificationLaunchAnimatorControllerProvider notificationAnimationProvider) {
- mNotificationAnimationProvider = notificationAnimationProvider;
- return this;
- }
-
- /** Set the NotificationPanelViewController. */
- public Builder setNotificationPanelViewController(
- NotificationPanelViewController notificationPanelViewController) {
- mNotificationPanelViewController = notificationPanelViewController;
- return this;
- }
-
- public StatusBarNotificationActivityStarter build() {
- return new StatusBarNotificationActivityStarter(
- mContext,
- mCommandQueue,
- mMainThreadHandler,
- mUiBgExecutor,
- mEntryManager,
- mNotifPipeline,
- mVisibilityProvider,
- mHeadsUpManager,
- mActivityStarter,
- mClickNotifier,
- mStatusBarStateController,
- mStatusBarKeyguardViewManager,
- mKeyguardManager,
- mDreamManager,
- mBubblesManagerOptional,
- mAssistManagerLazy,
- mRemoteInputManager,
- mGroupMembershipManager,
- mLockscreenUserManager,
- mShadeController,
- mKeyguardStateController,
- mNotificationInterruptStateProvider,
- mLockPatternUtils,
- mRemoteInputCallback,
- mActivityIntentHelper,
- mNotifPipelineFlags,
- mMetricsLogger,
- mLogger,
- mOnUserInteractionCallback,
- mStatusBar,
- mNotificationPresenter,
- mNotificationPanelViewController,
- mActivityLaunchAnimator,
- mNotificationAnimationProvider);
- }
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterModule.java
new file mode 100644
index 0000000..caa149e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterModule.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import com.android.systemui.statusbar.notification.NotificationActivityStarter;
+
+import dagger.Binds;
+import dagger.Module;
+
+@Module
+public abstract class StatusBarNotificationActivityStarterModule {
+ @Binds
+ abstract NotificationActivityStarter bindActivityStarter(
+ StatusBarNotificationActivityStarter impl);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index c8e1cdc..aa061d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -14,9 +14,9 @@
package com.android.systemui.statusbar.phone;
-import static com.android.systemui.statusbar.phone.StatusBar.CLOSE_PANEL_WHEN_EMPTIED;
-import static com.android.systemui.statusbar.phone.StatusBar.DEBUG;
-import static com.android.systemui.statusbar.phone.StatusBar.MULTIUSER_DEBUG;
+import static com.android.systemui.statusbar.phone.CentralSurfaces.CLOSE_PANEL_WHEN_EMPTIED;
+import static com.android.systemui.statusbar.phone.CentralSurfaces.DEBUG;
+import static com.android.systemui.statusbar.phone.CentralSurfaces.MULTIUSER_DEBUG;
import android.app.KeyguardManager;
import android.content.Context;
@@ -68,20 +68,25 @@
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent;
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import java.util.List;
-public class StatusBarNotificationPresenter implements NotificationPresenter,
+import javax.inject.Inject;
+
+@CentralSurfacesComponent.CentralSurfacesScope
+class StatusBarNotificationPresenter implements NotificationPresenter,
ConfigurationController.ConfigurationListener,
NotificationRowBinderImpl.BindRowCallback,
CommandQueue.Callbacks {
private static final String TAG = "StatusBarNotificationPresenter";
- private final ActivityStarter mActivityStarter = Dependency.get(ActivityStarter.class);
+ private final ActivityStarter mActivityStarter;
private final KeyguardStateController mKeyguardStateController;
private final NotificationViewHierarchyManager mViewHierarchyManager;
private final NotificationLockscreenUserManager mLockscreenUserManager;
@@ -99,7 +104,7 @@
private final DozeScrimController mDozeScrimController;
private final ScrimController mScrimController;
private final KeyguardIndicationController mKeyguardIndicationController;
- private final StatusBar mStatusBar;
+ private final CentralSurfaces mCentralSurfaces;
private final ShadeController mShadeController;
private final LockscreenShadeTransitionController mShadeTransitionController;
private final CommandQueue mCommandQueue;
@@ -110,16 +115,19 @@
private final NotifPipelineFlags mNotifPipelineFlags;
private final IStatusBarService mBarService;
private final DynamicPrivacyController mDynamicPrivacyController;
+ private final NotificationListContainer mNotifListContainer;
private boolean mReinflateNotificationsOnUserSwitched;
private boolean mDispatchUiModeChangeOnUserSwitched;
private TextView mNotificationPanelDebugText;
protected boolean mVrMode;
- public StatusBarNotificationPresenter(Context context,
+ @Inject
+ StatusBarNotificationPresenter(Context context,
NotificationPanelViewController panel,
HeadsUpManagerPhone headsUp,
NotificationShadeWindowView statusBarWindow,
+ ActivityStarter activityStarter,
NotificationStackScrollLayoutController stackScrollerController,
DozeScrimController dozeScrimController,
ScrimController scrimController,
@@ -127,7 +135,7 @@
DynamicPrivacyController dynamicPrivacyController,
KeyguardStateController keyguardStateController,
KeyguardIndicationController keyguardIndicationController,
- StatusBar statusBar,
+ CentralSurfaces centralSurfaces,
ShadeController shadeController,
LockscreenShadeTransitionController shadeTransitionController,
CommandQueue commandQueue,
@@ -144,14 +152,17 @@
NotificationInterruptStateProvider notificationInterruptStateProvider,
NotificationRemoteInputManager remoteInputManager,
ConfigurationController configurationController,
- NotifPipelineFlags notifPipelineFlags) {
+ NotifPipelineFlags notifPipelineFlags,
+ NotificationRemoteInputManager.Callback remoteInputManagerCallback,
+ NotificationListContainer notificationListContainer) {
+ mActivityStarter = activityStarter;
mKeyguardStateController = keyguardStateController;
mNotificationPanel = panel;
mHeadsUpManager = headsUp;
mDynamicPrivacyController = dynamicPrivacyController;
mKeyguardIndicationController = keyguardIndicationController;
// TODO: use KeyguardStateController#isOccluded to remove this dependency
- mStatusBar = statusBar;
+ mCentralSurfaces = centralSurfaces;
mShadeController = shadeController;
mShadeTransitionController = shadeTransitionController;
mCommandQueue = commandQueue;
@@ -175,6 +186,7 @@
mKeyguardManager = context.getSystemService(KeyguardManager.class);
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+ mNotifListContainer = notificationListContainer;
IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService(
Context.VR_SERVICE));
@@ -186,14 +198,14 @@
}
}
remoteInputManager.setUpWithCallback(
- Dependency.get(NotificationRemoteInputManager.Callback.class),
+ remoteInputManagerCallback,
mNotificationPanel.createRemoteInputDelegate());
initController.addPostInitTask(() -> {
mKeyguardIndicationController.init();
mViewHierarchyManager.setUpWithPresenter(this,
stackScrollerController.getNotifStackController(),
- stackScrollerController.getNotificationListContainer());
+ mNotifListContainer);
mNotifShadeEventSource.setShadeEmptiedCallback(this::maybeClosePanelForShadeEmptied);
mNotifShadeEventSource.setNotifRemovedByUserCallback(this::maybeEndAmbientPulse);
if (!mNotifPipelineFlags.isNewPipelineEnabled()) {
@@ -206,9 +218,8 @@
notificationInterruptStateProvider.addSuppressor(mInterruptSuppressor);
mLockscreenUserManager.setUpWithPresenter(this);
mMediaManager.setUpWithPresenter(this);
- mGutsManager.setUpWithPresenter(this,
- stackScrollerController.getNotificationListContainer(), mCheckSaveListener,
- mOnSettingsClickListener);
+ mGutsManager.setUpWithPresenter(
+ this, mNotifListContainer, mCheckSaveListener, mOnSettingsClickListener);
// ForegroundServiceNotificationListener adds its listener in its constructor
// but we need to request it here in order for it to be instantiated.
// TODO: figure out how to do this correctly once Dependency.get() is gone.
@@ -341,7 +352,7 @@
updateNotificationViews("user switched");
}
mMediaManager.clearCurrentMediaNotification();
- mStatusBar.setLockscreenUser(newUserId);
+ mCentralSurfaces.setLockscreenUser(newUserId);
updateMediaMetaData(true, false);
}
@@ -395,7 +406,8 @@
public void onExpandClicked(NotificationEntry clickedEntry, View clickedView,
boolean nowExpanded) {
mHeadsUpManager.setExpanded(clickedEntry, nowExpanded);
- mStatusBar.wakeUpIfDozing(SystemClock.uptimeMillis(), clickedView, "NOTIFICATION_CLICK");
+ mCentralSurfaces.wakeUpIfDozing(
+ SystemClock.uptimeMillis(), clickedView, "NOTIFICATION_CLICK");
if (nowExpanded) {
if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
mShadeTransitionController.goToLockedShade(clickedEntry.getRow());
@@ -463,7 +475,7 @@
@Override
public boolean suppressAwakeHeadsUp(NotificationEntry entry) {
final StatusBarNotification sbn = entry.getSbn();
- if (mStatusBar.isOccluded()) {
+ if (mCentralSurfaces.isOccluded()) {
boolean devicePublic = mLockscreenUserManager
.isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId());
boolean userPublic = devicePublic
@@ -486,7 +498,7 @@
// we don't allow head-up on the lockscreen (unless there's a
// "showWhenLocked" activity currently showing) if
// the potential HUN has a fullscreen intent
- if (mKeyguardStateController.isShowing() && !mStatusBar.isOccluded()) {
+ if (mKeyguardStateController.isShowing() && !mCentralSurfaces.isOccluded()) {
if (DEBUG) {
Log.d(TAG, "No heads up: entry has fullscreen intent on lockscreen "
+ sbn.getKey());
@@ -511,7 +523,7 @@
@Override
public boolean suppressInterruptions(NotificationEntry entry) {
- return mStatusBar.areNotificationAlertsDisabled();
+ return mCentralSurfaces.areNotificationAlertsDisabled();
}
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterModule.java
new file mode 100644
index 0000000..26c483c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterModule.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import com.android.systemui.statusbar.NotificationPresenter;
+import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
+
+import dagger.Binds;
+import dagger.Module;
+
+@Module
+public abstract class StatusBarNotificationPresenterModule {
+ @Binds
+ abstract NotificationPresenter bindPresenter(StatusBarNotificationPresenter impl);
+
+ @Binds
+ abstract NotificationRowBinderImpl.BindRowCallback bindBindRowCallback(
+ StatusBarNotificationPresenter impl);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index b0206f0..ee242a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -44,7 +44,7 @@
import javax.inject.Inject;
-/** Controls the signal policies for icons shown in the StatusBar. **/
+/** Controls the signal policies for icons shown in the statusbar. **/
@SysUISingleton
public class StatusBarSignalPolicy implements SignalCallback,
SecurityController.SecurityControllerCallback, Tunable {
@@ -449,7 +449,7 @@
}
/**
- * Stores the StatusBar state for no Calling & SMS.
+ * Stores the statusbar state for no Calling & SMS.
*/
public static class CallIndicatorIconState {
public boolean isNoCalling;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
index b742394..95667cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
@@ -59,7 +59,7 @@
private boolean mIsStatusBarExpanded = false;
private boolean mShouldAdjustInsets = false;
- private StatusBar mStatusBar;
+ private CentralSurfaces mCentralSurfaces;
private View mNotificationShadeWindowView;
private View mNotificationPanelView;
private boolean mForceCollapsedUntilLayout = false;
@@ -119,9 +119,9 @@
}
protected void setup(
- @NonNull StatusBar statusBar,
+ @NonNull CentralSurfaces centralSurfaces,
@NonNull View notificationShadeWindowView) {
- mStatusBar = statusBar;
+ mCentralSurfaces = centralSurfaces;
mNotificationShadeWindowView = notificationShadeWindowView;
mNotificationPanelView = mNotificationShadeWindowView.findViewById(R.id.notification_panel);
}
@@ -246,7 +246,7 @@
new OnComputeInternalInsetsListener() {
@Override
public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
- if (mIsStatusBarExpanded || mStatusBar.isBouncerShowing()) {
+ if (mIsStatusBarExpanded || mCentralSurfaces.isBouncerShowing()) {
// The touchable region is always the full area when expanded
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainViewController.java
index 26ba31c..582afb1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainViewController.java
@@ -20,7 +20,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.util.ViewController;
@@ -32,7 +32,7 @@
/**
* Controller for {@link TapAgainView}.
*/
-@StatusBarComponent.StatusBarScope
+@CentralSurfacesComponent.CentralSurfacesScope
public class TapAgainViewController extends ViewController<TapAgainView> {
private final DelayableExecutor mDelayableExecutor;
private final ConfigurationController mConfigurationController;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index 0abe8e4..8b0eaec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -60,14 +60,21 @@
private val handler: Handler = Handler()
) : WakefulnessLifecycle.Observer, ScreenOffAnimation {
- private lateinit var statusBar: StatusBar
+ private lateinit var mCentralSurfaces: CentralSurfaces
+
+ /**
+ * Whether or not [initialize] has been called to provide us with the StatusBar,
+ * NotificationPanelViewController, and LightRevealSrim so that we can run the unlocked screen
+ * off animation.
+ */
+ private var initialized = false
+
private lateinit var lightRevealScrim: LightRevealScrim
private var animatorDurationScale = 1f
private var shouldAnimateInKeyguard = false
private var lightRevealAnimationPlaying = false
private var aodUiAnimationPlaying = false
- private var callbacks = HashSet<Callback>()
/**
* The result of our decision whether to play the screen off animation in
@@ -81,9 +88,6 @@
interpolator = Interpolators.LINEAR
addUpdateListener {
lightRevealScrim.revealAmount = it.animatedValue as Float
- sendUnlockedScreenOffProgressUpdate(
- 1f - (it.animatedFraction as Float),
- 1f - (it.animatedValue as Float))
if (lightRevealScrim.isScrimAlmostOccludes &&
interactionJankMonitor.isInstrumenting(CUJ_SCREEN_OFF)) {
// ends the instrument when the scrim almost occludes the screen.
@@ -95,7 +99,6 @@
override fun onAnimationCancel(animation: Animator?) {
lightRevealScrim.revealAmount = 1f
lightRevealAnimationPlaying = false
- sendUnlockedScreenOffProgressUpdate(0f, 0f)
interactionJankMonitor.cancel(CUJ_SCREEN_OFF)
}
@@ -105,7 +108,8 @@
}
override fun onAnimationStart(animation: Animator?) {
- interactionJankMonitor.begin(statusBar.notificationShadeWindowView, CUJ_SCREEN_OFF)
+ interactionJankMonitor.begin(
+ mCentralSurfaces.notificationShadeWindowView, CUJ_SCREEN_OFF)
}
})
}
@@ -117,11 +121,12 @@
}
override fun initialize(
- statusBar: StatusBar,
+ centralSurfaces: CentralSurfaces,
lightRevealScrim: LightRevealScrim
) {
+ this.initialized = true
this.lightRevealScrim = lightRevealScrim
- this.statusBar = statusBar
+ this.mCentralSurfaces = centralSurfaces
updateAnimatorDurationScale()
globalSettings.registerContentObserver(
@@ -176,9 +181,9 @@
// Lock the keyguard if it was waiting for the screen off animation to end.
keyguardViewMediatorLazy.get().maybeHandlePendingLock()
- // Tell the StatusBar to become keyguard for real - we waited on that since
- // it is slow and would have caused the animation to jank.
- statusBar.updateIsKeyguard()
+ // Tell the CentralSurfaces to become keyguard for real - we waited on that
+ // since it is slow and would have caused the animation to jank.
+ mCentralSurfaces.updateIsKeyguard()
// Run the callback given to us by the KeyguardVisibilityHelper.
after.run()
@@ -196,7 +201,8 @@
override fun onAnimationStart(animation: Animator?) {
interactionJankMonitor.begin(
- statusBar.notificationShadeWindowView, CUJ_SCREEN_OFF_SHOW_AOD)
+ mCentralSurfaces.notificationShadeWindowView,
+ CUJ_SCREEN_OFF_SHOW_AOD)
}
})
.start()
@@ -213,12 +219,12 @@
override fun onFinishedWakingUp() {
// Set this to false in onFinishedWakingUp rather than onStartedWakingUp so that other
- // observers (such as StatusBar) can ask us whether we were playing the screen off animation
- // and reset accordingly.
+ // observers (such as CentralSurfaces) can ask us whether we were playing the screen off
+ // animation and reset accordingly.
aodUiAnimationPlaying = false
- // If we can't control the screen off animation, we shouldn't mess with the StatusBar's
- // keyguard state unnecessarily.
+ // If we can't control the screen off animation, we shouldn't mess with the
+ // CentralSurfaces's keyguard state unnecessarily.
if (dozeParameters.get().canControlUnlockedScreenOff()) {
// Make sure the status bar is in the correct keyguard state, forcing it if necessary.
// This is required if the screen off animation is cancelled, since it might be
@@ -227,7 +233,7 @@
// even if we're going from SHADE to SHADE or KEYGUARD to KEYGUARD, since we might have
// changed parts of the UI (such as showing AOD in the shade) without actually changing
// the StatusBarState. This ensures that the UI definitely reflects the desired state.
- statusBar.updateIsKeyguard(true /* forceStateChange */)
+ mCentralSurfaces.updateIsKeyguard(true /* forceStateChange */)
}
}
@@ -249,7 +255,7 @@
// Show AOD. That'll cause the KeyguardVisibilityHelper to call
// #animateInKeyguard.
- statusBar.notificationPanelViewController.showAodUi()
+ mCentralSurfaces.notificationPanelViewController.showAodUi()
}
}, (ANIMATE_IN_KEYGUARD_DELAY * animatorDurationScale).toLong())
@@ -265,6 +271,18 @@
* on the current state of the device.
*/
fun shouldPlayUnlockedScreenOffAnimation(): Boolean {
+ // If we haven't been initialized yet, we don't have a StatusBar/LightRevealScrim yet, so we
+ // can't perform the animation.
+ if (!initialized) {
+ return false
+ }
+
+ // If the device isn't in a state where we can control unlocked screen off (no AOD enabled,
+ // power save, etc.) then we shouldn't try to do so.
+ if (!dozeParameters.get().canControlUnlockedScreenOff()) {
+ return false
+ }
+
// If we explicitly already decided not to play the screen off animation, then never change
// our mind.
if (decidedToAnimateGoingToSleep == false) {
@@ -285,8 +303,8 @@
// We currently draw both the light reveal scrim, and the AOD UI, in the shade. If it's
// already expanded and showing notifications/QS, the animation looks really messy. For now,
// disable it if the notification panel is not fully collapsed.
- if ((!this::statusBar.isInitialized ||
- !statusBar.notificationPanelViewController.isFullyCollapsed) &&
+ if ((!this::mCentralSurfaces.isInitialized ||
+ !mCentralSurfaces.notificationPanelViewController.isFullyCollapsed) &&
// Status bar might be expanded because we have started
// playing the animation already
!isAnimationPlaying()
@@ -307,21 +325,7 @@
}
override fun shouldDelayDisplayDozeTransition(): Boolean =
- dozeParameters.get().shouldControlUnlockedScreenOff()
-
- fun addCallback(callback: Callback) {
- callbacks.add(callback)
- }
-
- fun removeCallback(callback: Callback) {
- callbacks.remove(callback)
- }
-
- private fun sendUnlockedScreenOffProgressUpdate(linear: Float, eased: Float) {
- callbacks.forEach {
- it.onUnlockedScreenOffProgressUpdate(linear, eased)
- }
- }
+ shouldPlayUnlockedScreenOffAnimation()
/**
* Whether we're doing the light reveal animation or we're done with that and animating in the
@@ -356,8 +360,4 @@
fun isScreenOffLightRevealAnimationPlaying(): Boolean {
return lightRevealAnimationPlaying
}
-
- interface Callback {
- fun onUnlockedScreenOffProgressUpdate(linear: Float, eased: Float)
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java
similarity index 69%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java
index ad8e79e..59c9d0b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java
@@ -22,16 +22,23 @@
import com.android.keyguard.LockIconViewController;
import com.android.systemui.biometrics.AuthRippleController;
+import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.NotificationShelfController;
import com.android.systemui.statusbar.core.StatusBarInitializer;
+import com.android.systemui.statusbar.notification.NotificationActivityStarter;
+import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
import com.android.systemui.statusbar.notification.collection.render.StatusBarNotifPanelEventSourceModule;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutListContainerModule;
+import com.android.systemui.statusbar.phone.CentralSurfacesCommandQueueCallbacks;
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
import com.android.systemui.statusbar.phone.NotificationShadeWindowViewController;
import com.android.systemui.statusbar.phone.SplitShadeHeaderController;
-import com.android.systemui.statusbar.phone.StatusBarCommandQueueCallbacks;
import com.android.systemui.statusbar.phone.StatusBarHeadsUpChangeListener;
+import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarterModule;
+import com.android.systemui.statusbar.phone.StatusBarNotificationPresenterModule;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import java.lang.annotation.Documented;
@@ -45,7 +52,7 @@
/**
* Dagger subcomponent for classes (semi-)related to the status bar. The component is created once
- * inside {@link com.android.systemui.statusbar.phone.StatusBar} and never re-created.
+ * inside {@link com.android.systemui.statusbar.phone.CentralSurfaces} and never re-created.
*
* TODO(b/197137564): This should likely be re-factored a bit. It includes classes that aren't
* directly related to status bar functionality, like multiple notification classes. And, the fact
@@ -53,81 +60,82 @@
* outside the component. Should more items be moved *into* this component to avoid so many getters?
*/
@Subcomponent(modules = {
+ NotificationStackScrollLayoutListContainerModule.class,
StatusBarNotifPanelEventSourceModule.class,
- StatusBarViewModule.class
+ StatusBarViewModule.class,
+ StatusBarNotificationActivityStarterModule.class,
+ StatusBarNotificationPresenterModule.class,
})
-@StatusBarComponent.StatusBarScope
-public interface StatusBarComponent {
+@CentralSurfacesComponent.CentralSurfacesScope
+public interface CentralSurfacesComponent {
/**
- * Builder for {@link StatusBarComponent}.
+ * Builder for {@link CentralSurfacesComponent}.
*/
@Subcomponent.Factory
interface Factory {
- StatusBarComponent create();
+ CentralSurfacesComponent create();
}
/**
- * Scope annotation for singleton items within the StatusBarComponent.
+ * Scope annotation for singleton items within the CentralSurfacesComponent.
*/
@Documented
@Retention(RUNTIME)
@Scope
- @interface StatusBarScope {}
+ @interface CentralSurfacesScope {}
+
+ /**
+ * Performs initialization logic after {@link CentralSurfacesComponent} has been constructed.
+ */
+ interface Startable {
+ void start();
+ void stop();
+ }
/**
* Creates a {@link NotificationShadeWindowView}.
*/
- @StatusBarScope
NotificationShadeWindowView getNotificationShadeWindowView();
/** */
- @StatusBarScope
NotificationShelfController getNotificationShelfController();
/** */
- @StatusBarScope
NotificationStackScrollLayoutController getNotificationStackScrollLayoutController();
/**
* Creates a NotificationShadeWindowViewController.
*/
- @StatusBarScope
NotificationShadeWindowViewController getNotificationShadeWindowViewController();
/**
* Creates a NotificationPanelViewController.
*/
- @StatusBarScope
NotificationPanelViewController getNotificationPanelViewController();
/**
* Creates a LockIconViewController. Must be init after creation.
*/
- @StatusBarScope
LockIconViewController getLockIconViewController();
/**
* Creates an AuthRippleViewController. Must be init after creation.
*/
- @StatusBarScope
AuthRippleController getAuthRippleController();
/**
* Creates a StatusBarHeadsUpChangeListener.
*/
- @StatusBarScope
StatusBarHeadsUpChangeListener getStatusBarHeadsUpChangeListener();
/**
- * Creates a StatusBarCommandQueueCallbacks.
+ * Creates a CentralSurfacesCommandQueueCallbacks.
*/
- @StatusBarScope
- StatusBarCommandQueueCallbacks getStatusBarCommandQueueCallbacks();
+ CentralSurfacesCommandQueueCallbacks getCentralSurfacesCommandQueueCallbacks();
/**
* Creates a SplitShadeHeaderController.
*/
- @StatusBarScope
SplitShadeHeaderController getSplitShadeHeaderController();
/**
@@ -140,20 +148,18 @@
/**
* Creates a StatusBarInitializer
*/
- @StatusBarScope
StatusBarInitializer getStatusBarInitializer();
/**
- * Set of startables to be run after a StatusBarComponent has been constructed.
+ * Set of startables to be run after a CentralSurfacesComponent has been constructed.
*/
- @StatusBarScope
Set<Startable> getStartables();
- /**
- * Performs initialization logic after {@link StatusBarComponent} has been constructed.
- */
- interface Startable {
- void start();
- void stop();
- }
+ NotificationActivityStarter getNotificationActivityStarter();
+
+ NotificationPresenter getNotificationPresenter();
+
+ NotificationRowBinderImpl.BindRowCallback getBindRowCallback();
+
+ NotificationListContainer getNotificationListContainer();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index e6c239f..c024c72 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -92,10 +92,9 @@
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy;
import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
@@ -127,16 +126,16 @@
import dagger.Provides;
/**
- * Dagger Module providing {@link StatusBar}.
+ * Dagger Module providing {@link CentralSurfaces}.
*/
@Module
public interface StatusBarPhoneModule {
/**
- * Provides our instance of StatusBar which is considered optional.
+ * Provides our instance of CentralSurfaces which is considered optional.
*/
@Provides
@SysUISingleton
- static StatusBar provideStatusBar(
+ static CentralSurfaces provideCentralSurfaces(
Context context,
NotificationsController notificationsController,
FragmentService fragmentService,
@@ -196,10 +195,8 @@
DozeScrimController dozeScrimController,
VolumeComponent volumeComponent,
CommandQueue commandQueue,
- StatusBarComponent.Factory statusBarComponentFactory,
+ CentralSurfacesComponent.Factory statusBarComponentFactory,
PluginManager pluginManager,
- StatusBarNotificationActivityStarter.Builder
- statusBarNotificationActivityStarterBuilder,
ShadeController shadeController,
StatusBarKeyguardViewManager statusBarKeyguardViewManager,
ViewMediatorCallback viewMediatorCallback,
@@ -234,7 +231,7 @@
DeviceStateManager deviceStateManager,
DreamOverlayStateController dreamOverlayStateController,
WiredChargingRippleController wiredChargingRippleController) {
- return new StatusBar(
+ return new CentralSurfaces(
context,
notificationsController,
fragmentService,
@@ -296,7 +293,6 @@
commandQueue,
statusBarComponentFactory,
pluginManager,
- statusBarNotificationActivityStarterBuilder,
shadeController,
statusBarKeyguardViewManager,
viewMediatorCallback,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index ebd58fb..79fe700 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -61,6 +61,9 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.tuner.TunerService;
+import com.android.systemui.util.settings.SecureSettings;
+
+import java.util.concurrent.Executor;
import javax.inject.Named;
@@ -77,7 +80,7 @@
/** */
@Provides
- @StatusBarComponent.StatusBarScope
+ @CentralSurfacesComponent.CentralSurfacesScope
public static NotificationShadeWindowView providesNotificationShadeWindowView(
LayoutInflater layoutInflater) {
NotificationShadeWindowView notificationShadeWindowView = (NotificationShadeWindowView)
@@ -92,7 +95,7 @@
/** */
@Provides
- @StatusBarComponent.StatusBarScope
+ @CentralSurfacesComponent.CentralSurfacesScope
public static NotificationStackScrollLayout providesNotificationStackScrollLayout(
NotificationShadeWindowView notificationShadeWindowView) {
return notificationShadeWindowView.findViewById(R.id.notification_stack_scroller);
@@ -100,7 +103,7 @@
/** */
@Provides
- @StatusBarComponent.StatusBarScope
+ @CentralSurfacesComponent.CentralSurfacesScope
public static NotificationShelf providesNotificationShelf(LayoutInflater layoutInflater,
NotificationStackScrollLayout notificationStackScrollLayout) {
NotificationShelf view = (NotificationShelf) layoutInflater.inflate(
@@ -115,7 +118,7 @@
/** */
@Provides
- @StatusBarComponent.StatusBarScope
+ @CentralSurfacesComponent.CentralSurfacesScope
public static NotificationShelfController providesStatusBarWindowView(
NotificationShelfComponent.Builder notificationShelfComponentBuilder,
NotificationShelf notificationShelf) {
@@ -131,7 +134,7 @@
/** */
@Provides
- @StatusBarComponent.StatusBarScope
+ @CentralSurfacesComponent.CentralSurfacesScope
public static NotificationPanelView getNotificationPanelView(
NotificationShadeWindowView notificationShadeWindowView) {
return notificationShadeWindowView.getNotificationPanelView();
@@ -139,7 +142,7 @@
/** */
@Provides
- @StatusBarComponent.StatusBarScope
+ @CentralSurfacesComponent.CentralSurfacesScope
public static LockIconView getLockIconView(
NotificationShadeWindowView notificationShadeWindowView) {
return notificationShadeWindowView.findViewById(R.id.lock_icon_view);
@@ -147,7 +150,7 @@
/** */
@Provides
- @StatusBarComponent.StatusBarScope
+ @CentralSurfacesComponent.CentralSurfacesScope
@Nullable
public static AuthRippleView getAuthRippleView(
NotificationShadeWindowView notificationShadeWindowView) {
@@ -157,7 +160,7 @@
/** */
@Provides
@Named(SPLIT_SHADE_HEADER)
- @StatusBarComponent.StatusBarScope
+ @CentralSurfacesComponent.CentralSurfacesScope
public static View getSplitShadeStatusBarView(
NotificationShadeWindowView notificationShadeWindowView,
FeatureFlags featureFlags) {
@@ -172,7 +175,7 @@
/** */
@Provides
- @StatusBarComponent.StatusBarScope
+ @CentralSurfacesComponent.CentralSurfacesScope
public static OngoingPrivacyChip getSplitShadeOngoingPrivacyChip(
@Named(SPLIT_SHADE_HEADER) View header) {
return header.findViewById(R.id.privacy_chip);
@@ -180,21 +183,21 @@
/** */
@Provides
- @StatusBarComponent.StatusBarScope
+ @CentralSurfacesComponent.CentralSurfacesScope
static StatusIconContainer providesStatusIconContainer(@Named(SPLIT_SHADE_HEADER) View header) {
return header.findViewById(R.id.statusIcons);
}
/** */
@Provides
- @StatusBarComponent.StatusBarScope
+ @CentralSurfacesComponent.CentralSurfacesScope
@Named(SPLIT_SHADE_BATTERY_VIEW)
static BatteryMeterView getBatteryMeterView(@Named(SPLIT_SHADE_HEADER) View view) {
return view.findViewById(R.id.batteryRemainingIcon);
}
@Provides
- @StatusBarComponent.StatusBarScope
+ @CentralSurfacesComponent.CentralSurfacesScope
@Named(SPLIT_SHADE_BATTERY_CONTROLLER)
static BatteryMeterViewController getBatteryMeterViewController(
@Named(SPLIT_SHADE_BATTERY_VIEW) BatteryMeterView batteryMeterView,
@@ -218,14 +221,14 @@
/** */
@Provides
- @StatusBarComponent.StatusBarScope
+ @CentralSurfacesComponent.CentralSurfacesScope
public static TapAgainView getTapAgainView(NotificationPanelView npv) {
return npv.getTapAgainView();
}
/** */
@Provides
- @StatusBarComponent.StatusBarScope
+ @CentralSurfacesComponent.CentralSurfacesScope
public static NotificationsQuickSettingsContainer getNotificationsQuickSettingsContainer(
NotificationShadeWindowView notificationShadeWindowView) {
return notificationShadeWindowView.findViewById(R.id.notification_container_parent);
@@ -235,9 +238,9 @@
* Creates a new {@link CollapsedStatusBarFragment}.
*
* **IMPORTANT**: This method intentionally does not have
- * {@link StatusBarComponent.StatusBarScope}, which means a new fragment *will* be created each
- * time this method is called. This is intentional because we need fragments to re-created in
- * certain lifecycle scenarios.
+ * {@link CentralSurfacesComponent.CentralSurfacesScope}, which means a new fragment *will* be
+ * created each time this method is called. This is intentional because we need fragments to
+ * re-created in certain lifecycle scenarios.
*
* This provider is {@link Named} such that it does not conflict with the provider inside of
* {@link StatusBarFragmentComponent}.
@@ -260,7 +263,9 @@
StatusBarStateController statusBarStateController,
CommandQueue commandQueue,
CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger,
- OperatorNameViewController.Factory operatorNameViewControllerFactory
+ OperatorNameViewController.Factory operatorNameViewControllerFactory,
+ SecureSettings secureSettings,
+ @Main Executor mainExecutor
) {
return new CollapsedStatusBarFragment(statusBarFragmentComponentFactory,
ongoingCallController,
@@ -277,6 +282,8 @@
statusBarStateController,
commandQueue,
collapsedStatusBarFragmentLogger,
- operatorNameViewControllerFactory);
+ operatorNameViewControllerFactory,
+ secureSettings,
+ mainExecutor);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index 2af0772..2c84219 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -30,8 +30,10 @@
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.Fragment;
+import android.database.ContentObserver;
import android.os.Bundle;
import android.os.Parcelable;
+import android.provider.Settings;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
@@ -39,8 +41,11 @@
import android.view.ViewStub;
import android.widget.LinearLayout;
+import androidx.annotation.VisibleForTesting;
+
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
@@ -66,9 +71,11 @@
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.EncryptionHelper;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.settings.SecureSettings;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.Executor;
/**
* Contains the collapsed status bar and handles hiding/showing based on disable flags
@@ -110,6 +117,8 @@
private final PanelExpansionStateManager mPanelExpansionStateManager;
private final StatusBarIconController mStatusBarIconController;
private final StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
+ private final SecureSettings mSecureSettings;
+ private final Executor mMainExecutor;
private List<String> mBlockedIcons = new ArrayList<>();
@@ -145,7 +154,9 @@
StatusBarStateController statusBarStateController,
CommandQueue commandQueue,
CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger,
- OperatorNameViewController.Factory operatorNameViewControllerFactory
+ OperatorNameViewController.Factory operatorNameViewControllerFactory,
+ SecureSettings secureSettings,
+ @Main Executor mainExecutor
) {
mStatusBarFragmentComponentFactory = statusBarFragmentComponentFactory;
mOngoingCallController = ongoingCallController;
@@ -163,6 +174,8 @@
mCommandQueue = commandQueue;
mCollapsedStatusBarFragmentLogger = collapsedStatusBarFragmentLogger;
mOperatorNameViewControllerFactory = operatorNameViewControllerFactory;
+ mSecureSettings = secureSettings;
+ mMainExecutor = mainExecutor;
}
@Override
@@ -187,10 +200,7 @@
}
mDarkIconManager = new DarkIconManager(view.findViewById(R.id.statusIcons), mFeatureFlags);
mDarkIconManager.setShouldLog(true);
- mBlockedIcons.add(getString(com.android.internal.R.string.status_bar_volume));
- mBlockedIcons.add(getString(com.android.internal.R.string.status_bar_alarm_clock));
- mBlockedIcons.add(getString(com.android.internal.R.string.status_bar_call_strength));
- mDarkIconManager.setBlockList(mBlockedIcons);
+ updateBlockedIcons();
mStatusBarIconController.addIconGroup(mDarkIconManager);
mSystemIconArea = mStatusBar.findViewById(R.id.system_icon_area);
mClockView = mStatusBar.findViewById(R.id.clock);
@@ -203,6 +213,24 @@
mAnimationScheduler.addCallback(this);
}
+ @VisibleForTesting
+ void updateBlockedIcons() {
+ mBlockedIcons.clear();
+
+ if (mSecureSettings.getInt(Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON, 0) == 0) {
+ mBlockedIcons.add(getString(com.android.internal.R.string.status_bar_volume));
+ }
+ mBlockedIcons.add(getString(com.android.internal.R.string.status_bar_alarm_clock));
+ mBlockedIcons.add(getString(com.android.internal.R.string.status_bar_call_strength));
+
+ mMainExecutor.execute(() -> mDarkIconManager.setBlockList(mBlockedIcons));
+ }
+
+ @VisibleForTesting
+ List<String> getBlockedIcons() {
+ return mBlockedIcons;
+ }
+
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
@@ -217,6 +245,11 @@
mCommandQueue.addCallback(this);
mStatusBarStateController.addCallback(this);
initOngoingCallChip();
+
+ mSecureSettings.registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON),
+ false,
+ mVolumeSettingObserver);
}
@Override
@@ -225,6 +258,7 @@
mCommandQueue.removeCallback(this);
mStatusBarStateController.removeCallback(this);
mOngoingCallController.removeCallback(mOngoingCallListener);
+ mSecureSettings.unregisterContentObserver(mVolumeSettingObserver);
}
@Override
@@ -584,6 +618,13 @@
mLocationPublisher.updateStatusBarMargin(leftMargin, rightMargin);
}
+ private final ContentObserver mVolumeSettingObserver = new ContentObserver(null) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updateBlockedIcons();
+ }
+ };
+
// Listen for view end changes of PhoneStatusBarView and publish that to the privacy dot
private View.OnLayoutChangeListener mStatusBarLayoutListener =
(view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
index 22b7f64..2eba325 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java
@@ -18,12 +18,14 @@
import com.android.systemui.battery.BatteryMeterViewController;
import com.android.systemui.dagger.qualifiers.RootView;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.LightsOutNotifController;
import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions;
import com.android.systemui.statusbar.phone.PhoneStatusBarView;
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
import com.android.systemui.statusbar.phone.StatusBarDemoMode;
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import dagger.BindsInstance;
@@ -39,12 +41,6 @@
*
* Anything that depends on {@link CollapsedStatusBarFragment} or {@link PhoneStatusBarView}
* should be included here or in {@link StatusBarFragmentModule}.
- *
- * Note that this is completely separate from
- * {@link com.android.systemui.statusbar.phone.dagger.StatusBarComponent}. This component gets
- * re-created on each new fragment creation, whereas
- * {@link com.android.systemui.statusbar.phone.dagger.StatusBarComponent} is only created once in
- * {@link com.android.systemui.statusbar.phone.StatusBar} and never re-created.
*/
@Subcomponent(modules = {StatusBarFragmentModule.class})
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index 6e7231e..982ccfd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -21,6 +21,7 @@
import android.app.IUidObserver
import android.app.Notification
import android.app.Notification.CallStyle.CALL_TYPE_ONGOING
+import android.content.Context
import android.content.Intent
import android.util.Log
import android.view.View
@@ -52,6 +53,7 @@
*/
@SysUISingleton
class OngoingCallController @Inject constructor(
+ private val context: Context,
private val notifCollection: CommonNotifCollection,
private val ongoingCallFlags: OngoingCallFlags,
private val systemClock: SystemClock,
@@ -67,13 +69,10 @@
private var isFullscreen: Boolean = false
/** Non-null if there's an active call notification. */
private var callNotificationInfo: CallNotificationInfo? = null
- /** True if the application managing the call is visible to the user. */
- private var isCallAppVisible: Boolean = false
private var chipView: View? = null
- private var uidObserver: IUidObserver.Stub? = null
private val mListeners: MutableList<OngoingCallListener> = mutableListOf()
-
+ private val uidObserver = CallAppUidObserver()
private val notifListener = object : NotifCollectionListener {
// Temporary workaround for b/178406514 for testing purposes.
//
@@ -158,7 +157,7 @@
fun hasOngoingCall(): Boolean {
return callNotificationInfo?.isOngoing == true &&
// When the user is in the phone app, don't show the chip.
- !isCallAppVisible
+ !uidObserver.isCallAppVisible
}
override fun addCallback(listener: OngoingCallListener) {
@@ -194,7 +193,7 @@
}
updateChipClickListener()
- setUpUidObserver(currentCallNotificationInfo)
+ uidObserver.registerWithUid(currentCallNotificationInfo.uid)
if (!currentCallNotificationInfo.statusBarSwipedAway) {
statusBarWindowController.ifPresent {
it.setOngoingProcessRequiresStatusBarVisible(true)
@@ -238,62 +237,6 @@
}
}
- /**
- * Sets up an [IUidObserver] to monitor the status of the application managing the ongoing call.
- */
- private fun setUpUidObserver(currentCallNotificationInfo: CallNotificationInfo) {
- try {
- isCallAppVisible = isProcessVisibleToUser(
- iActivityManager.getUidProcessState(currentCallNotificationInfo.uid, null)
- )
- } catch (se: SecurityException) {
- Log.e(TAG, "Security exception when trying to get process state: $se")
- return
- }
-
- if (uidObserver != null) {
- iActivityManager.unregisterUidObserver(uidObserver)
- }
-
- uidObserver = object : IUidObserver.Stub() {
- override fun onUidStateChanged(
- uid: Int,
- procState: Int,
- procStateSeq: Long,
- capability: Int
- ) {
- if (uid == currentCallNotificationInfo.uid) {
- val oldIsCallAppVisible = isCallAppVisible
- isCallAppVisible = isProcessVisibleToUser(procState)
- if (oldIsCallAppVisible != isCallAppVisible) {
- // Animations may be run as a result of the call's state change, so ensure
- // the listener is notified on the main thread.
- mainExecutor.execute {
- mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
- }
- }
- }
- }
-
- override fun onUidGone(uid: Int, disabled: Boolean) {}
- override fun onUidActive(uid: Int) {}
- override fun onUidIdle(uid: Int, disabled: Boolean) {}
- override fun onUidCachedChanged(uid: Int, cached: Boolean) {}
- }
-
- try {
- iActivityManager.registerUidObserver(
- uidObserver,
- ActivityManager.UID_OBSERVER_PROCSTATE,
- ActivityManager.PROCESS_STATE_UNKNOWN,
- null
- )
- } catch (se: SecurityException) {
- Log.e(TAG, "Security exception when trying to register uid observer: $se")
- return
- }
- }
-
/** Returns true if the given [procState] represents a process that's visible to the user. */
private fun isProcessVisibleToUser(procState: Int): Boolean {
return procState <= ActivityManager.PROCESS_STATE_TOP
@@ -306,7 +249,7 @@
swipeStatusBarAwayGestureHandler.ifPresent { it.removeOnGestureDetectedCallback(TAG) }
} else {
swipeStatusBarAwayGestureHandler.ifPresent {
- it.addOnGestureDetectedCallback(TAG, this::onSwipeAwayGestureDetected)
+ it.addOnGestureDetectedCallback(TAG) { _ -> onSwipeAwayGestureDetected() }
}
}
}
@@ -317,9 +260,7 @@
statusBarWindowController.ifPresent { it.setOngoingProcessRequiresStatusBarVisible(false) }
swipeStatusBarAwayGestureHandler.ifPresent { it.removeOnGestureDetectedCallback(TAG) }
mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
- if (uidObserver != null) {
- iActivityManager.unregisterUidObserver(uidObserver)
- }
+ uidObserver.unregister()
}
/** Tear down anything related to the chip view to prevent leaks. */
@@ -376,7 +317,84 @@
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
pw.println("Active call notification: $callNotificationInfo")
- pw.println("Call app visible: $isCallAppVisible")
+ pw.println("Call app visible: ${uidObserver.isCallAppVisible}")
+ }
+
+ /** Our implementation of a [IUidObserver]. */
+ inner class CallAppUidObserver : IUidObserver.Stub() {
+ /** True if the application managing the call is visible to the user. */
+ var isCallAppVisible: Boolean = false
+ private set
+
+ /** The UID of the application managing the call. Null if there is no active call. */
+ private var callAppUid: Int? = null
+
+ /**
+ * True if this observer is currently registered with the activity manager and false
+ * otherwise.
+ */
+ private var isRegistered = false
+
+
+ /** Register this observer with the activity manager and the given [uid]. */
+ fun registerWithUid(uid: Int) {
+ if (callAppUid == uid) {
+ return
+ }
+ callAppUid = uid
+
+ try {
+ isCallAppVisible = isProcessVisibleToUser(
+ iActivityManager.getUidProcessState(uid, context.opPackageName)
+ )
+ if (isRegistered) {
+ return
+ }
+ iActivityManager.registerUidObserver(
+ uidObserver,
+ ActivityManager.UID_OBSERVER_PROCSTATE,
+ ActivityManager.PROCESS_STATE_UNKNOWN,
+ context.opPackageName
+ )
+ isRegistered = true
+ } catch (se: SecurityException) {
+ Log.e(TAG, "Security exception when trying to set up uid observer: $se")
+ }
+ }
+
+ /** Unregister this observer with the activity manager. */
+ fun unregister() {
+ callAppUid = null
+ isRegistered = false
+ iActivityManager.unregisterUidObserver(uidObserver)
+ }
+
+ override fun onUidStateChanged(
+ uid: Int,
+ procState: Int,
+ procStateSeq: Long,
+ capability: Int
+ ) {
+ val currentCallAppUid = callAppUid ?: return
+ if (uid != currentCallAppUid) {
+ return
+ }
+
+ val oldIsCallAppVisible = isCallAppVisible
+ isCallAppVisible = isProcessVisibleToUser(procState)
+ if (oldIsCallAppVisible != isCallAppVisible) {
+ // Animations may be run as a result of the call's state change, so ensure
+ // the listener is notified on the main thread.
+ mainExecutor.execute {
+ mListeners.forEach { l -> l.onOngoingCallStateChanged(animate = true) }
+ }
+ }
+ }
+
+ override fun onUidGone(uid: Int, disabled: Boolean) {}
+ override fun onUidActive(uid: Int) {}
+ override fun onUidIdle(uid: Int, disabled: Boolean) {}
+ override fun onUidCachedChanged(uid: Int, cached: Boolean) {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateController.kt
index 3a14914..60f6df6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateController.kt
@@ -25,7 +25,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.DisplayId
import com.android.systemui.statusbar.CommandQueue
-import com.android.systemui.statusbar.phone.StatusBar
+import com.android.systemui.statusbar.phone.CentralSurfaces
import javax.inject.Inject
/**
@@ -80,8 +80,8 @@
}
windowState = state
- if (StatusBar.DEBUG_WINDOW_STATE) {
- Log.d(StatusBar.TAG, "Status bar " + windowStateToString(state))
+ if (CentralSurfaces.DEBUG_WINDOW_STATE) {
+ Log.d(CentralSurfaces.TAG, "Status bar " + windowStateToString(state))
}
listeners.forEach { it.onStatusBarWindowStateChanged(state) }
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
index e2374ad..d6dfcea 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
@@ -26,7 +26,7 @@
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.phone.ScreenOffAnimation
-import com.android.systemui.statusbar.phone.StatusBar
+import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.policy.CallbackController
import com.android.systemui.unfold.FoldAodAnimationController.FoldAodAnimationStatus
import com.android.systemui.util.settings.GlobalSettings
@@ -50,7 +50,7 @@
private val globalSettings: GlobalSettings
) : CallbackController<FoldAodAnimationStatus>, ScreenOffAnimation, WakefulnessLifecycle.Observer {
- private lateinit var statusBar: StatusBar
+ private lateinit var mCentralSurfaces: CentralSurfaces
private var isFolded = false
private var isFoldHandled = true
@@ -66,14 +66,14 @@
private val statusListeners = arrayListOf<FoldAodAnimationStatus>()
private val startAnimationRunnable = Runnable {
- statusBar.notificationPanelViewController.startFoldToAodAnimation {
+ mCentralSurfaces.notificationPanelViewController.startFoldToAodAnimation {
// End action
setAnimationState(playing = false)
}
}
- override fun initialize(statusBar: StatusBar, lightRevealScrim: LightRevealScrim) {
- this.statusBar = statusBar
+ override fun initialize(centralSurfaces: CentralSurfaces, lightRevealScrim: LightRevealScrim) {
+ this.mCentralSurfaces = centralSurfaces
deviceStateManager.registerCallback(executor, FoldListener())
wakefulnessLifecycle.addObserver(this)
@@ -88,7 +88,7 @@
globalSettings.getString(Settings.Global.ANIMATOR_DURATION_SCALE) != "0"
) {
setAnimationState(playing = true)
- statusBar.notificationPanelViewController.prepareFoldToAodAnimation()
+ mCentralSurfaces.notificationPanelViewController.prepareFoldToAodAnimation()
true
} else {
setAnimationState(playing = false)
@@ -98,7 +98,7 @@
override fun onStartedWakingUp() {
if (isAnimationPlaying) {
handler.removeCallbacks(startAnimationRunnable)
- statusBar.notificationPanelViewController.cancelFoldToAodAnimation()
+ mCentralSurfaces.notificationPanelViewController.cancelFoldToAodAnimation()
}
setAnimationState(playing = false)
@@ -131,12 +131,14 @@
// We should play the folding to AOD animation
setAnimationState(playing = true)
- statusBar.notificationPanelViewController.prepareFoldToAodAnimation()
+ mCentralSurfaces.notificationPanelViewController.prepareFoldToAodAnimation()
// We don't need to wait for the scrim as it is already displayed
// but we should wait for the initial animation preparations to be drawn
// (setting initial alpha/translation)
- OneShotPreDrawListener.add(statusBar.notificationPanelViewController.view, onReady)
+ OneShotPreDrawListener.add(
+ mCentralSurfaces.notificationPanelViewController.view, onReady
+ )
} else {
// No animation, call ready callback immediately
onReady.run()
diff --git a/packages/SystemUI/src/com/android/systemui/util/view/ViewUtil.kt b/packages/SystemUI/src/com/android/systemui/util/view/ViewUtil.kt
new file mode 100644
index 0000000..613a797
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/view/ViewUtil.kt
@@ -0,0 +1,26 @@
+package com.android.systemui.util.view
+
+import android.view.View
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+/**
+ * A class with generic view utility methods.
+ *
+ * Doesn't use static methods so that it can be easily mocked out in tests.
+ */
+@SysUISingleton
+class ViewUtil @Inject constructor() {
+ /**
+ * Returns true if the given (x, y) point (in screen coordinates) is within the status bar
+ * view's range and false otherwise.
+ */
+ fun touchIsWithinView(view: View, x: Float, y: Float): Boolean {
+ val left = view.locationOnScreen[0]
+ val top = view.locationOnScreen[1]
+ return left <= x &&
+ x <= left + view.width &&
+ top <= y &&
+ y <= top + view.height
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
index 5128ccc..ec2c1de 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
@@ -32,7 +32,7 @@
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.KeyguardBypassController
-import com.android.systemui.statusbar.phone.StatusBar
+import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.leak.RotationUtils
@@ -61,7 +61,7 @@
private lateinit var staticMockSession: MockitoSession
private lateinit var controller: AuthRippleController
- @Mock private lateinit var statusBar: StatusBar
+ @Mock private lateinit var mCentralSurfaces: CentralSurfaces
@Mock private lateinit var rippleView: AuthRippleView
@Mock private lateinit var commandRegistry: CommandRegistry
@Mock private lateinit var configurationController: ConfigurationController
@@ -89,7 +89,7 @@
`when`(udfpsControllerProvider.get()).thenReturn(udfpsController)
controller = AuthRippleController(
- statusBar,
+ mCentralSurfaces,
context,
authController,
configurationController,
@@ -105,7 +105,7 @@
rippleView
)
controller.init()
- `when`(statusBar.lightRevealScrim).thenReturn(lightRevealScrim)
+ `when`(mCentralSurfaces.lightRevealScrim).thenReturn(lightRevealScrim)
}
@After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 35e838b..1856fda 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -65,7 +65,7 @@
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
@@ -121,7 +121,7 @@
@Mock
private StatusBarStateController mStatusBarStateController;
@Mock
- private StatusBar mStatusBar;
+ private CentralSurfaces mCentralSurfaces;
@Mock
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
index e9a4e15..7fb42b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -166,7 +166,7 @@
mController.onViewAttached();
verify(mView, atLeast(1)).setPauseAuth(true);
- verify(mView).onDozeAmountChanged(dozeAmount, dozeAmount);
+ verify(mView).onDozeAmountChanged(dozeAmount, dozeAmount, true);
}
@Test
@@ -193,7 +193,7 @@
final float eased = .65f;
mStatusBarStateListener.onDozeAmountChanged(linear, eased);
- verify(mView).onDozeAmountChanged(linear, eased);
+ verify(mView).onDozeAmountChanged(linear, eased, true);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
index 9908d44..da25c62 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
@@ -16,15 +16,22 @@
package com.android.systemui.controls.ui
+import android.database.ContentObserver
+import android.net.Uri
+import android.os.Handler
+import android.provider.Settings
+import android.test.suitebuilder.annotation.SmallTest
import android.testing.AndroidTestingRunner
-import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.ControlsMetricsLogger
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.settings.SecureSettings
import com.android.wm.shell.TaskViewFactory
+import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -44,7 +51,6 @@
@SmallTest
@RunWith(AndroidTestingRunner::class)
class ControlActionCoordinatorImplTest : SysuiTestCase() {
-
@Mock
private lateinit var vibratorHelper: VibratorHelper
@Mock
@@ -61,6 +67,10 @@
private lateinit var cvh: ControlViewHolder
@Mock
private lateinit var metricsLogger: ControlsMetricsLogger
+ @Mock
+ private lateinit var secureSettings: SecureSettings
+ @Mock
+ private lateinit var mainHandler: Handler
companion object {
fun <T> any(): T = Mockito.any<T>()
@@ -75,16 +85,24 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
+ `when`(secureSettings.getUriFor(Settings.Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS))
+ .thenReturn(Settings.Secure
+ .getUriFor(Settings.Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS))
+
coordinator = spy(ControlActionCoordinatorImpl(
- mContext,
- bgExecutor,
- uiExecutor,
- activityStarter,
- keyguardStateController,
- taskViewFactory,
- metricsLogger,
- vibratorHelper
- ))
+ mContext,
+ bgExecutor,
+ uiExecutor,
+ activityStarter,
+ keyguardStateController,
+ taskViewFactory,
+ metricsLogger,
+ vibratorHelper,
+ secureSettings,
+ mainHandler))
+
+ verify(secureSettings).registerContentObserver(any(Uri::class.java),
+ anyBoolean(), any(ContentObserver::class.java))
`when`(cvh.cws.ci.controlId).thenReturn(ID)
`when`(cvh.cws.control?.isAuthRequired()).thenReturn(true)
@@ -126,10 +144,23 @@
fun testToggleRunsWhenLockedAndAuthNotRequired() {
`when`(keyguardStateController.isShowing()).thenReturn(true)
`when`(keyguardStateController.isUnlocked()).thenReturn(false)
- `when`(cvh.cws.control?.isAuthRequired()).thenReturn(false)
+ doReturn(false).`when`(coordinator).isAuthRequired(
+ any(), anyBoolean())
coordinator.toggle(cvh, "", true)
+
verify(coordinator).bouncerOrRun(action, false /* authRequired */)
verify(action).invoke()
}
+
+ @Test
+ fun testIsAuthRequired() {
+ `when`(cvh.cws.control?.isAuthRequired).thenReturn(true)
+ assertThat(coordinator.isAuthRequired(cvh, false)).isTrue()
+
+ `when`(cvh.cws.control?.isAuthRequired).thenReturn(false)
+ assertThat(coordinator.isAuthRequired(cvh, false)).isTrue()
+
+ assertThat(coordinator.isAuthRequired(cvh, true)).isFalse()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
index 5684429..3340f2f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
@@ -143,8 +143,8 @@
}
@Test
- public void testInitialize_dozeSuppressed_alwaysOnDisabled_goesToDoze() {
- when(mHost.isDozeSuppressed()).thenReturn(true);
+ public void testInitialize_alwaysOnSuppressed_alwaysOnDisabled_goesToDoze() {
+ when(mHost.isAlwaysOnSuppressed()).thenReturn(true);
when(mConfigMock.alwaysOnEnabled(anyInt())).thenReturn(false);
mMachine.requestState(INITIALIZED);
@@ -154,8 +154,8 @@
}
@Test
- public void testInitialize_dozeSuppressed_alwaysOnEnabled_goesToDoze() {
- when(mHost.isDozeSuppressed()).thenReturn(true);
+ public void testInitialize_alwaysOnSuppressed_alwaysOnEnabled_goesToDoze() {
+ when(mHost.isAlwaysOnSuppressed()).thenReturn(true);
when(mConfigMock.alwaysOnEnabled(anyInt())).thenReturn(true);
mMachine.requestState(INITIALIZED);
@@ -165,8 +165,8 @@
}
@Test
- public void testInitialize_dozeSuppressed_afterDocked_goesToDoze() {
- when(mHost.isDozeSuppressed()).thenReturn(true);
+ public void testInitialize_alwaysOnSuppressed_afterDocked_goesToDoze() {
+ when(mHost.isAlwaysOnSuppressed()).thenReturn(true);
when(mDockManager.isDocked()).thenReturn(true);
mMachine.requestState(INITIALIZED);
@@ -176,8 +176,8 @@
}
@Test
- public void testInitialize_dozeSuppressed_alwaysOnDisabled_afterDockPaused_goesToDoze() {
- when(mHost.isDozeSuppressed()).thenReturn(true);
+ public void testInitialize_alwaysOnSuppressed_alwaysOnDisabled_afterDockPaused_goesToDoze() {
+ when(mHost.isAlwaysOnSuppressed()).thenReturn(true);
when(mConfigMock.alwaysOnEnabled(anyInt())).thenReturn(false);
when(mDockManager.isDocked()).thenReturn(true);
when(mDockManager.isHidden()).thenReturn(true);
@@ -189,8 +189,8 @@
}
@Test
- public void testInitialize_dozeSuppressed_alwaysOnEnabled_afterDockPaused_goesToDoze() {
- when(mHost.isDozeSuppressed()).thenReturn(true);
+ public void testInitialize_alwaysOnSuppressed_alwaysOnEnabled_afterDockPaused_goesToDoze() {
+ when(mHost.isAlwaysOnSuppressed()).thenReturn(true);
when(mConfigMock.alwaysOnEnabled(anyInt())).thenReturn(true);
when(mDockManager.isDocked()).thenReturn(true);
when(mDockManager.isHidden()).thenReturn(true);
@@ -228,8 +228,8 @@
}
@Test
- public void testPulseDone_dozeSuppressed_goesToSuppressed() {
- when(mHost.isDozeSuppressed()).thenReturn(true);
+ public void testPulseDone_alwaysOnSuppressed_goesToSuppressed() {
+ when(mHost.isAlwaysOnSuppressed()).thenReturn(true);
when(mConfigMock.alwaysOnEnabled(anyInt())).thenReturn(true);
mMachine.requestState(INITIALIZED);
mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
@@ -266,8 +266,8 @@
}
@Test
- public void testPulseDone_dozeSuppressed_afterDocked_goesToDoze() {
- when(mHost.isDozeSuppressed()).thenReturn(true);
+ public void testPulseDone_alwaysOnSuppressed_afterDocked_goesToDoze() {
+ when(mHost.isAlwaysOnSuppressed()).thenReturn(true);
when(mDockManager.isDocked()).thenReturn(true);
mMachine.requestState(INITIALIZED);
mMachine.requestPulse(DozeLog.PULSE_REASON_NOTIFICATION);
@@ -295,8 +295,8 @@
}
@Test
- public void testPulseDone_dozeSuppressed_afterDockPaused_goesToDoze() {
- when(mHost.isDozeSuppressed()).thenReturn(true);
+ public void testPulseDone_alwaysOnSuppressed_afterDockPaused_goesToDoze() {
+ when(mHost.isAlwaysOnSuppressed()).thenReturn(true);
when(mConfigMock.alwaysOnEnabled(anyInt())).thenReturn(true);
when(mDockManager.isDocked()).thenReturn(true);
when(mDockManager.isHidden()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressorTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressorTest.java
new file mode 100644
index 0000000..aa0a909
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressorTest.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions andatest
+ * limitations under the License.
+ */
+
+package com.android.systemui.doze;
+
+import static com.android.systemui.doze.DozeMachine.State.DOZE;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD;
+import static com.android.systemui.doze.DozeMachine.State.FINISH;
+import static com.android.systemui.doze.DozeMachine.State.INITIALIZED;
+import static com.android.systemui.doze.DozeMachine.State.UNINITIALIZED;
+
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.UiModeManager;
+import android.content.BroadcastReceiver;
+import android.content.res.Configuration;
+import android.hardware.display.AmbientDisplayConfiguration;
+import android.testing.AndroidTestingRunner;
+import android.testing.UiThreadTest;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.statusbar.phone.BiometricUnlockController;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import dagger.Lazy;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@UiThreadTest
+public class DozeSuppressorTest extends SysuiTestCase {
+
+ DozeSuppressor mDozeSuppressor;
+ @Mock
+ private DozeLog mDozeLog;
+ @Mock
+ private DozeHost mDozeHost;
+ @Mock
+ private AmbientDisplayConfiguration mConfig;
+ @Mock
+ private BroadcastDispatcher mBroadcastDispatcher;
+ @Mock
+ private UiModeManager mUiModeManager;
+ @Mock
+ private Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
+ @Mock
+ private BiometricUnlockController mBiometricUnlockController;
+
+ @Mock
+ private DozeMachine mDozeMachine;
+
+ @Captor
+ private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor;
+ private BroadcastReceiver mBroadcastReceiver;
+
+ @Captor
+ private ArgumentCaptor<DozeHost.Callback> mDozeHostCaptor;
+ private DozeHost.Callback mDozeHostCallback;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ // setup state for NOT ending doze immediately
+ when(mBiometricUnlockControllerLazy.get()).thenReturn(mBiometricUnlockController);
+ when(mBiometricUnlockController.hasPendingAuthentication()).thenReturn(false);
+ when(mDozeHost.isProvisioned()).thenReturn(true);
+
+ mDozeSuppressor = new DozeSuppressor(
+ mDozeHost,
+ mConfig,
+ mDozeLog,
+ mBroadcastDispatcher,
+ mUiModeManager,
+ mBiometricUnlockControllerLazy);
+
+ mDozeSuppressor.setDozeMachine(mDozeMachine);
+ }
+
+ @After
+ public void tearDown() {
+ mDozeSuppressor.destroy();
+ }
+
+ @Test
+ public void testRegistersListenersOnInitialized_unregisteredOnFinish() {
+ // check that receivers and callbacks registered
+ mDozeSuppressor.transitionTo(UNINITIALIZED, INITIALIZED);
+ captureBroadcastReceiver();
+ captureDozeHostCallback();
+
+ // check that receivers and callbacks are unregistered
+ mDozeSuppressor.transitionTo(INITIALIZED, FINISH);
+ verify(mBroadcastDispatcher).unregisterReceiver(mBroadcastReceiver);
+ verify(mDozeHost).removeCallback(mDozeHostCallback);
+ }
+
+ @Test
+ public void testEndDoze_carMode() {
+ // GIVEN car mode
+ when(mUiModeManager.getCurrentModeType()).thenReturn(Configuration.UI_MODE_TYPE_CAR);
+
+ // WHEN dozing begins
+ mDozeSuppressor.transitionTo(UNINITIALIZED, INITIALIZED);
+
+ // THEN doze immediately ends
+ verify(mDozeMachine).requestState(FINISH);
+ }
+
+ @Test
+ public void testEndDoze_unprovisioned() {
+ // GIVEN device unprovisioned
+ when(mDozeHost.isProvisioned()).thenReturn(false);
+
+ // WHEN dozing begins
+ mDozeSuppressor.transitionTo(UNINITIALIZED, INITIALIZED);
+
+ // THEN doze immediately ends
+ verify(mDozeMachine).requestState(FINISH);
+ }
+
+ @Test
+ public void testEndDoze_hasPendingUnlock() {
+ // GIVEN device unprovisioned
+ when(mBiometricUnlockController.hasPendingAuthentication()).thenReturn(true);
+
+ // WHEN dozing begins
+ mDozeSuppressor.transitionTo(UNINITIALIZED, INITIALIZED);
+
+ // THEN doze immediately ends
+ verify(mDozeMachine).requestState(FINISH);
+ }
+
+ @Test
+ public void testPowerSaveChanged_active() {
+ // GIVEN AOD power save is active and doze is initialized
+ when(mDozeHost.isPowerSaveActive()).thenReturn(true);
+ mDozeSuppressor.transitionTo(UNINITIALIZED, INITIALIZED);
+ captureDozeHostCallback();
+
+ // WHEN power save change gets triggered (even if active = false, since it
+ // may differ from the aodPowerSaveActive state reported by DostHost)
+ mDozeHostCallback.onPowerSaveChanged(false);
+
+ // THEN the state changes to DOZE
+ verify(mDozeMachine).requestState(DOZE);
+ }
+
+ @Test
+ public void testPowerSaveChanged_notActive() {
+ // GIVEN DOZE (not showing aod content)
+ when(mConfig.alwaysOnEnabled(anyInt())).thenReturn(true);
+ mDozeSuppressor.transitionTo(UNINITIALIZED, INITIALIZED);
+ when(mDozeMachine.getState()).thenReturn(DOZE);
+ captureDozeHostCallback();
+
+ // WHEN power save mode is no longer active
+ when(mDozeHost.isPowerSaveActive()).thenReturn(false);
+ mDozeHostCallback.onPowerSaveChanged(false);
+
+ // THEN the state changes to DOZE_AOD
+ verify(mDozeMachine).requestState(DOZE_AOD);
+ }
+
+ @Test
+ public void testAlwaysOnSuppressedChanged_nowSuppressed() {
+ // GIVEN DOZE_AOD
+ when(mConfig.alwaysOnEnabled(anyInt())).thenReturn(true);
+ mDozeSuppressor.transitionTo(UNINITIALIZED, INITIALIZED);
+ when(mDozeMachine.getState()).thenReturn(DOZE_AOD);
+ captureDozeHostCallback();
+
+ // WHEN alwaysOnSuppressedChanged to suppressed=true
+ mDozeHostCallback.onAlwaysOnSuppressedChanged(true);
+
+ // THEN DOZE requested
+ verify(mDozeMachine).requestState(DOZE);
+ }
+
+ @Test
+ public void testAlwaysOnSuppressedChanged_notSuppressed() {
+ // GIVEN DOZE
+ when(mConfig.alwaysOnEnabled(anyInt())).thenReturn(true);
+ mDozeSuppressor.transitionTo(UNINITIALIZED, INITIALIZED);
+ when(mDozeMachine.getState()).thenReturn(DOZE);
+ captureDozeHostCallback();
+
+ // WHEN alwaysOnSuppressedChanged to suppressed=false
+ mDozeHostCallback.onAlwaysOnSuppressedChanged(false);
+
+ // THEN DOZE_AOD requested
+ verify(mDozeMachine).requestState(DOZE_AOD);
+ }
+
+ private void captureDozeHostCallback() {
+ verify(mDozeHost).addCallback(mDozeHostCaptor.capture());
+ mDozeHostCallback = mDozeHostCaptor.getValue();
+ }
+
+ private void captureBroadcastReceiver() {
+ verify(mBroadcastDispatcher).registerReceiver(mBroadcastReceiverCaptor.capture(),
+ anyObject());
+ mBroadcastReceiver = mBroadcastReceiverCaptor.getValue();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
index 21768ed..2860b50 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
@@ -152,19 +152,19 @@
}
@Test
- public void testShouldShowComplicationsTrueByDefault() {
+ public void testShouldShowComplicationsFalseByDefault() {
mService.onBind(new Intent());
- assertThat(mService.shouldShowComplications()).isTrue();
+ assertThat(mService.shouldShowComplications()).isFalse();
}
@Test
public void testShouldShowComplicationsSetByIntentExtra() {
final Intent intent = new Intent();
- intent.putExtra(DreamService.EXTRA_SHOW_COMPLICATIONS, false);
+ intent.putExtra(DreamService.EXTRA_SHOW_COMPLICATIONS, true);
mService.onBind(intent);
- assertThat(mService.shouldShowComplications()).isFalse();
+ assertThat(mService.shouldShowComplications()).isTrue();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
index 49da4bd..3ce9889 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
@@ -158,6 +158,7 @@
public void testComplicationFilteringWhenShouldShowComplications() {
final DreamOverlayStateController stateController =
new DreamOverlayStateController(mExecutor);
+ stateController.setShouldShowComplications(true);
final Complication alwaysAvailableComplication = Mockito.mock(Complication.class);
final Complication weatherComplication = Mockito.mock(Complication.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
index ad8d44d..a32ff80 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
@@ -17,19 +17,34 @@
package com.android.systemui.dreams;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.AlarmManager;
+import android.content.res.Resources;
+import android.hardware.SensorPrivacyManager;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
+import android.provider.Settings;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner;
import androidx.test.filters.SmallTest;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
+import com.android.systemui.statusbar.policy.NextAlarmController;
+import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.touch.TouchInsetManager;
+import com.android.systemui.util.time.DateFormatUtil;
import org.junit.Before;
import org.junit.Test;
@@ -41,6 +56,8 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
+ private static final String NOTIFICATION_INDICATOR_FORMATTER_STRING =
+ "{count, plural, =1 {# notification} other {# notifications}}";
@Mock
DreamOverlayStatusBarView mView;
@@ -52,14 +69,55 @@
Network mNetwork;
@Mock
TouchInsetManager.TouchInsetSession mTouchSession;
+ @Mock
+ Resources mResources;
+ @Mock
+ AlarmManager mAlarmManager;
+ @Mock
+ AlarmManager.AlarmClockInfo mAlarmClockInfo;
+ @Mock
+ NextAlarmController mNextAlarmController;
+ @Mock
+ DateFormatUtil mDateFormatUtil;
+ @Mock
+ IndividualSensorPrivacyController mSensorPrivacyController;
+ @Mock
+ StatusBarNotification mStatusBarNotification;
+ @Mock
+ NotificationListenerService.RankingMap mRankingMap;
+ @Mock
+ NotificationListener mNotificationListener;
+ @Mock
+ ZenModeController mZenModeController;
DreamOverlayStatusBarViewController mController;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mController = new DreamOverlayStatusBarViewController(mView, mConnectivityManager,
- mTouchSession);
+
+ when(mResources.getString(R.string.dream_overlay_status_bar_notification_indicator))
+ .thenReturn(NOTIFICATION_INDICATOR_FORMATTER_STRING);
+
+ mController = new DreamOverlayStatusBarViewController(
+ mView,
+ mResources,
+ mConnectivityManager,
+ mTouchSession,
+ mAlarmManager,
+ mNextAlarmController,
+ mDateFormatUtil,
+ mSensorPrivacyController,
+ mNotificationListener,
+ mZenModeController);
+ }
+
+ @Test
+ public void testOnViewAttachedAddsCallbacks() {
+ mController.onViewAttached();
+ verify(mNextAlarmController).addCallback(any());
+ verify(mSensorPrivacyController).addCallback(any());
+ verify(mZenModeController).addCallback(any());
}
@Test
@@ -71,28 +129,97 @@
}
@Test
- public void testOnViewAttachedShowsWifiStatusWhenWifiUnavailable() {
+ public void testOnViewAttachedShowsWifiIconWhenWifiUnavailable() {
when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
.thenReturn(false);
when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities);
mController.onViewAttached();
- verify(mView).showWifiStatus(true);
+ verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, true);
}
@Test
- public void testOnViewAttachedHidesWifiStatusWhenWifiAvailable() {
+ public void testOnViewAttachedHidesWifiIconWhenWifiAvailable() {
when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
.thenReturn(true);
when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities);
mController.onViewAttached();
- verify(mView).showWifiStatus(false);
+ verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, false);
}
@Test
- public void testOnViewAttachedShowsWifiStatusWhenNetworkCapabilitiesUnavailable() {
+ public void testOnViewAttachedShowsWifiIconWhenNetworkCapabilitiesUnavailable() {
when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(null);
mController.onViewAttached();
- verify(mView).showWifiStatus(true);
+ }
+
+ @Test
+ public void testOnViewAttachedShowsAlarmIconWhenAlarmExists() {
+ when(mAlarmClockInfo.getTriggerTime()).thenReturn(1L);
+ when(mAlarmManager.getNextAlarmClock(anyInt())).thenReturn(mAlarmClockInfo);
+ mController.onViewAttached();
+ verify(mView).showIcon(
+ eq(DreamOverlayStatusBarView.STATUS_ICON_ALARM_SET), eq(true), any());
+ }
+
+ @Test
+ public void testOnViewAttachedHidesAlarmIconWhenNoAlarmExists() {
+ when(mAlarmManager.getNextAlarmClock(anyInt())).thenReturn(null);
+ mController.onViewAttached();
+ verify(mView).showIcon(
+ eq(DreamOverlayStatusBarView.STATUS_ICON_ALARM_SET), eq(false), isNull());
+ }
+
+ @Test
+ public void testOnViewAttachedShowsMicCameraIconWhenDisabled() {
+ when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.MICROPHONE))
+ .thenReturn(true);
+ when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA))
+ .thenReturn(true);
+ mController.onViewAttached();
+ verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true);
+ }
+
+ @Test
+ public void testOnViewAttachedHidesMicCameraIconWhenEnabled() {
+ when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.MICROPHONE))
+ .thenReturn(false);
+ when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA))
+ .thenReturn(false);
+ mController.onViewAttached();
+ verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, false);
+ }
+
+ @Test
+ public void testOnViewAttachedShowsNotificationsIconWhenNotificationsExist() {
+ StatusBarNotification[] notifications = { mStatusBarNotification };
+ when(mNotificationListener.getActiveNotifications()).thenReturn(notifications);
+ mController.onViewAttached();
+ verify(mView).showIcon(
+ eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any());
+ }
+
+ @Test
+ public void testOnViewAttachedHidesNotificationsIconWhenNoNotificationsExist() {
+ when(mNotificationListener.getActiveNotifications()).thenReturn(null);
+ mController.onViewAttached();
+ verify(mView).showIcon(
+ eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(false), isNull());
+ }
+
+ @Test
+ public void testOnViewAttachedShowsPriorityModeIconWhenEnabled() {
+ when(mZenModeController.getZen()).thenReturn(
+ Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+ mController.onViewAttached();
+ verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, true);
+ }
+
+ @Test
+ public void testOnViewAttachedHidesPriorityModeIconWhenDisabled() {
+ when(mZenModeController.getZen()).thenReturn(
+ Settings.Global.ZEN_MODE_OFF);
+ mController.onViewAttached();
+ verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, false);
}
@Test
@@ -103,10 +230,19 @@
}
@Test
- public void testWifiStatusHiddenWhenWifiBecomesAvailable() {
- // Make sure wifi starts out unavailable when onViewAttached is called.
+ public void testOnViewDetachedRemovesCallbacks() {
+ mController.onViewDetached();
+ verify(mNextAlarmController).removeCallback(any());
+ verify(mSensorPrivacyController).removeCallback(any());
+ verify(mZenModeController).removeCallback(any());
+ }
+
+ @Test
+ public void testWifiIconHiddenWhenWifiBecomesAvailable() {
+ // Make sure wifi starts out unavailable when onViewAttached is called, and then returns
+ // true on the second query.
when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
- .thenReturn(false);
+ .thenReturn(false).thenReturn(true);
when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities);
mController.onViewAttached();
@@ -114,14 +250,15 @@
ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
verify(mConnectivityManager).registerNetworkCallback(any(), callbackCapture.capture());
callbackCapture.getValue().onAvailable(mNetwork);
- verify(mView).showWifiStatus(false);
+ verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, false);
}
@Test
- public void testWifiStatusShownWhenWifiBecomesUnavailable() {
- // Make sure wifi starts out available when onViewAttached is called.
+ public void testWifiIconShownWhenWifiBecomesUnavailable() {
+ // Make sure wifi starts out available when onViewAttached is called, then returns false
+ // on the second query.
when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
- .thenReturn(true);
+ .thenReturn(true).thenReturn(false);
when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities);
mController.onViewAttached();
@@ -129,11 +266,11 @@
ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
verify(mConnectivityManager).registerNetworkCallback(any(), callbackCapture.capture());
callbackCapture.getValue().onLost(mNetwork);
- verify(mView).showWifiStatus(true);
+ verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, true);
}
@Test
- public void testWifiStatusHiddenWhenCapabilitiesChange() {
+ public void testWifiIconHiddenWhenCapabilitiesChange() {
// Make sure wifi starts out unavailable when onViewAttached is called.
when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
.thenReturn(false);
@@ -146,6 +283,102 @@
when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))
.thenReturn(true);
callbackCapture.getValue().onCapabilitiesChanged(mNetwork, mNetworkCapabilities);
- verify(mView).showWifiStatus(false);
+ verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, false);
+ }
+
+ @Test
+ public void testNotificationsIconShownWhenNotificationAdded() {
+ mController.onViewAttached();
+
+ StatusBarNotification[] notifications = { mStatusBarNotification };
+ when(mNotificationListener.getActiveNotifications()).thenReturn(notifications);
+
+ final ArgumentCaptor<NotificationListener.NotificationHandler> callbackCapture =
+ ArgumentCaptor.forClass(NotificationListener.NotificationHandler.class);
+ verify(mNotificationListener).addNotificationHandler(callbackCapture.capture());
+ callbackCapture.getValue().onNotificationPosted(mStatusBarNotification, mRankingMap);
+
+ verify(mView).showIcon(
+ eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any());
+ }
+
+ @Test
+ public void testNotificationsIconHiddenWhenLastNotificationRemoved() {
+ StatusBarNotification[] notifications = { mStatusBarNotification };
+ when(mNotificationListener.getActiveNotifications()).thenReturn(notifications)
+ .thenReturn(null);
+ mController.onViewAttached();
+
+ final ArgumentCaptor<NotificationListener.NotificationHandler> callbackCapture =
+ ArgumentCaptor.forClass(NotificationListener.NotificationHandler.class);
+ verify(mNotificationListener).addNotificationHandler(callbackCapture.capture());
+ callbackCapture.getValue().onNotificationPosted(mStatusBarNotification, mRankingMap);
+
+ verify(mView).showIcon(
+ eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(false), any());
+ }
+
+ @Test
+ public void testMicCameraIconShownWhenSensorsBlocked() {
+ mController.onViewAttached();
+
+ when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.MICROPHONE))
+ .thenReturn(true);
+ when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA))
+ .thenReturn(true);
+
+ final ArgumentCaptor<IndividualSensorPrivacyController.Callback> callbackCapture =
+ ArgumentCaptor.forClass(IndividualSensorPrivacyController.Callback.class);
+ verify(mSensorPrivacyController).addCallback(callbackCapture.capture());
+ callbackCapture.getValue().onSensorBlockedChanged(
+ SensorPrivacyManager.Sensors.MICROPHONE, true);
+
+ verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true);
+ }
+
+ @Test
+ public void testMicCameraIconHiddenWhenSensorsNotBlocked() {
+ when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.MICROPHONE))
+ .thenReturn(true).thenReturn(false);
+ when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA))
+ .thenReturn(true).thenReturn(false);
+ mController.onViewAttached();
+
+ final ArgumentCaptor<IndividualSensorPrivacyController.Callback> callbackCapture =
+ ArgumentCaptor.forClass(IndividualSensorPrivacyController.Callback.class);
+ verify(mSensorPrivacyController).addCallback(callbackCapture.capture());
+ callbackCapture.getValue().onSensorBlockedChanged(
+ SensorPrivacyManager.Sensors.MICROPHONE, false);
+
+ verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, false);
+ }
+
+ @Test
+ public void testPriorityModeIconShownWhenZenModeEnabled() {
+ mController.onViewAttached();
+
+ when(mZenModeController.getZen()).thenReturn(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS);
+
+ final ArgumentCaptor<ZenModeController.Callback> callbackCapture =
+ ArgumentCaptor.forClass(ZenModeController.Callback.class);
+ verify(mZenModeController).addCallback(callbackCapture.capture());
+ callbackCapture.getValue().onZenChanged(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS);
+
+ verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, true);
+ }
+
+ @Test
+ public void testPriorityModeIconHiddenWhenZenModeDisabled() {
+ when(mZenModeController.getZen()).thenReturn(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS)
+ .thenReturn(Settings.Global.ZEN_MODE_OFF);
+ mController.onViewAttached();
+
+
+ final ArgumentCaptor<ZenModeController.Callback> callbackCapture =
+ ArgumentCaptor.forClass(ZenModeController.Callback.class);
+ verify(mZenModeController).addCallback(callbackCapture.capture());
+ callbackCapture.getValue().onZenChanged(Settings.Global.ZEN_MODE_OFF);
+
+ verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, false);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
index cad98f4..6e01541 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
@@ -18,7 +18,6 @@
import static com.google.common.truth.Truth.assertThat;
-
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyFloat;
@@ -27,19 +26,21 @@
import static org.mockito.Mockito.when;
import android.animation.ValueAnimator;
+import android.graphics.Rect;
+import android.graphics.Region;
import android.testing.AndroidTestingRunner;
+import android.util.DisplayMetrics;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.VelocityTracker;
-import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.wm.shell.animation.FlingAnimationUtils;
@@ -51,8 +52,6 @@
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-import java.util.Random;
-
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class BouncerSwipeTouchHandlerTest extends SysuiTestCase {
@@ -60,7 +59,7 @@
StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@Mock
- StatusBar mStatusBar;
+ CentralSurfaces mCentralSurfaces;
@Mock
NotificationShadeWindowController mNotificationShadeWindowController;
@@ -89,38 +88,51 @@
@Mock
VelocityTracker mVelocityTracker;
+ final DisplayMetrics mDisplayMetrics = new DisplayMetrics();
+
private static final float TOUCH_REGION = .3f;
- private static final float SCREEN_HEIGHT_PX = 100;
+ private static final int SCREEN_WIDTH_PX = 1024;
+ private static final int SCREEN_HEIGHT_PX = 100;
@Before
public void setup() {
+ mDisplayMetrics.widthPixels = SCREEN_WIDTH_PX;
+ mDisplayMetrics.heightPixels = SCREEN_HEIGHT_PX;
+
MockitoAnnotations.initMocks(this);
mTouchHandler = new BouncerSwipeTouchHandler(
+ mDisplayMetrics,
mStatusBarKeyguardViewManager,
- mStatusBar,
+ mCentralSurfaces,
mNotificationShadeWindowController,
mValueAnimatorCreator,
mVelocityTrackerFactory,
mFlingAnimationUtils,
mFlingAnimationUtilsClosing,
TOUCH_REGION);
- when(mStatusBar.getDisplayHeight()).thenReturn(SCREEN_HEIGHT_PX);
+
+ when(mCentralSurfaces.getDisplayHeight()).thenReturn((float) SCREEN_HEIGHT_PX);
when(mValueAnimatorCreator.create(anyFloat(), anyFloat())).thenReturn(mValueAnimator);
when(mVelocityTrackerFactory.obtain()).thenReturn(mVelocityTracker);
}
- private static void beginValidSwipe(GestureDetector.OnGestureListener listener) {
- listener.onDown(MotionEvent.obtain(0, 0,
- MotionEvent.ACTION_DOWN, 0,
- SCREEN_HEIGHT_PX - (.5f * TOUCH_REGION * SCREEN_HEIGHT_PX), 0));
- }
-
/**
* Ensures expansion only happens when touch down happens in valid part of the screen.
*/
- @FlakyTest
@Test
public void testSessionStart() {
+ final Region region = Region.obtain();
+ mTouchHandler.getTouchInitiationRegion(region);
+
+ final Rect bounds = region.getBounds();
+
+ final Rect expected = new Rect();
+
+ expected.set(0, Math.round(SCREEN_HEIGHT_PX * (1 - TOUCH_REGION)), SCREEN_WIDTH_PX,
+ SCREEN_HEIGHT_PX);
+
+ assertThat(bounds).isEqualTo(expected);
+
mTouchHandler.onSessionStart(mTouchSession);
verify(mNotificationShadeWindowController).setForcePluginOpen(eq(true), any());
ArgumentCaptor<InputChannelCompat.InputEventListener> eventListenerCaptor =
@@ -130,34 +142,12 @@
verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture());
verify(mTouchSession).registerInputListener(eventListenerCaptor.capture());
- final Random random = new Random(System.currentTimeMillis());
-
- // If an initial touch down meeting criteria has been met, scroll behavior should be
- // ignored.
- assertThat(gestureListenerCaptor.getValue()
- .onScroll(Mockito.mock(MotionEvent.class),
- Mockito.mock(MotionEvent.class),
- random.nextFloat(),
- random.nextFloat())).isFalse();
-
- // A touch at the top of the screen should also not trigger listening.
- final MotionEvent touchDownEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN,
- 0, 0, 0);
-
- gestureListenerCaptor.getValue().onDown(touchDownEvent);
- assertThat(gestureListenerCaptor.getValue()
- .onScroll(Mockito.mock(MotionEvent.class),
- Mockito.mock(MotionEvent.class),
- random.nextFloat(),
- random.nextFloat())).isFalse();
-
// A touch within range at the bottom of the screen should trigger listening
- beginValidSwipe(gestureListenerCaptor.getValue());
assertThat(gestureListenerCaptor.getValue()
.onScroll(Mockito.mock(MotionEvent.class),
Mockito.mock(MotionEvent.class),
- random.nextFloat(),
- random.nextFloat())).isTrue();
+ 1,
+ 2)).isTrue();
}
/**
@@ -170,8 +160,6 @@
ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class);
verify(mTouchSession).registerGestureListener(gestureListenerCaptor.capture());
- beginValidSwipe(gestureListenerCaptor.getValue());
-
final float scrollAmount = .3f;
final float distanceY = SCREEN_HEIGHT_PX * scrollAmount;
@@ -203,8 +191,6 @@
when(mVelocityTracker.getYVelocity()).thenReturn(velocityY);
- beginValidSwipe(gestureListenerCaptor.getValue());
-
final float distanceY = SCREEN_HEIGHT_PX * position;
final MotionEvent event1 = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java
index 74b217b..29f56e0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java
@@ -21,10 +21,13 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.graphics.Rect;
+import android.graphics.Region;
import android.testing.AndroidTestingRunner;
import android.util.Pair;
import android.view.GestureDetector;
@@ -137,6 +140,81 @@
}
@Test
+ public void testEntryTouchZone() {
+ final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);
+ final Rect touchArea = new Rect(4, 4, 8 , 8);
+
+ doAnswer(invocation -> {
+ final Region region = (Region) invocation.getArguments()[0];
+ region.set(touchArea);
+ return null;
+ }).when(touchHandler).getTouchInitiationRegion(any());
+
+ final Environment environment = new Environment(Stream.of(touchHandler)
+ .collect(Collectors.toCollection(HashSet::new)));
+
+ // Ensure touch outside specified region is not delivered.
+ final MotionEvent initialEvent = Mockito.mock(MotionEvent.class);
+ when(initialEvent.getX()).thenReturn(0.0f);
+ when(initialEvent.getY()).thenReturn(1.0f);
+ environment.publishInputEvent(initialEvent);
+ verify(touchHandler, never()).onSessionStart(any());
+
+ // Make sure touch inside region causes session start.
+ when(initialEvent.getX()).thenReturn(5.0f);
+ when(initialEvent.getY()).thenReturn(5.0f);
+ environment.publishInputEvent(initialEvent);
+ verify(touchHandler).onSessionStart(any());
+ }
+
+ @Test
+ public void testSessionCount() {
+ final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);
+ final Rect touchArea = new Rect(4, 4, 8 , 8);
+
+ final DreamTouchHandler unzonedTouchHandler = Mockito.mock(DreamTouchHandler.class);
+ doAnswer(invocation -> {
+ final Region region = (Region) invocation.getArguments()[0];
+ region.set(touchArea);
+ return null;
+ }).when(touchHandler).getTouchInitiationRegion(any());
+
+ final Environment environment = new Environment(Stream.of(touchHandler, unzonedTouchHandler)
+ .collect(Collectors.toCollection(HashSet::new)));
+
+ // Ensure touch outside specified region is delivered to unzoned touch handler.
+ final MotionEvent initialEvent = Mockito.mock(MotionEvent.class);
+ when(initialEvent.getX()).thenReturn(0.0f);
+ when(initialEvent.getY()).thenReturn(1.0f);
+ environment.publishInputEvent(initialEvent);
+
+ ArgumentCaptor<DreamTouchHandler.TouchSession> touchSessionCaptor = ArgumentCaptor.forClass(
+ DreamTouchHandler.TouchSession.class);
+
+ // Make sure only one active session.
+ {
+ verify(unzonedTouchHandler).onSessionStart(touchSessionCaptor.capture());
+ final DreamTouchHandler.TouchSession touchSession = touchSessionCaptor.getValue();
+ assertThat(touchSession.getActiveSessionCount()).isEqualTo(1);
+ touchSession.pop();
+ environment.executeAll();
+ }
+
+ // Make sure touch inside the touch region.
+ when(initialEvent.getX()).thenReturn(5.0f);
+ when(initialEvent.getY()).thenReturn(5.0f);
+ environment.publishInputEvent(initialEvent);
+
+ // Make sure there are two active sessions.
+ {
+ verify(touchHandler).onSessionStart(touchSessionCaptor.capture());
+ final DreamTouchHandler.TouchSession touchSession = touchSessionCaptor.getValue();
+ assertThat(touchSession.getActiveSessionCount()).isEqualTo(2);
+ touchSession.pop();
+ }
+ }
+
+ @Test
public void testInputEventPropagation() {
final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
index 953be7d..6fead9e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -60,7 +60,7 @@
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.telephony.TelephonyListenerManager;
@@ -113,7 +113,7 @@
@Mock private Handler mHandler;
@Mock private UserContextProvider mUserContextProvider;
@Mock private VibratorHelper mVibratorHelper;
- @Mock private StatusBar mStatusBar;
+ @Mock private CentralSurfaces mCentralSurfaces;
@Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock private DialogLaunchAnimator mDialogLaunchAnimator;
@@ -158,7 +158,7 @@
mRingerModeTracker,
mHandler,
mPackageManager,
- Optional.of(mStatusBar),
+ Optional.of(mCentralSurfaces),
mKeyguardUpdateMonitor,
mDialogLaunchAnimator);
mGlobalActionsDialogLite.setZeroDialogPressDelayForTesting();
@@ -227,7 +227,7 @@
doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
- doReturn(true).when(mStatusBar).isKeyguardShowing();
+ doReturn(true).when(mCentralSurfaces).isKeyguardShowing();
String[] actions = {
GlobalActionsDialogLite.GLOBAL_ACTION_KEY_EMERGENCY,
GlobalActionsDialogLite.GLOBAL_ACTION_KEY_LOCKDOWN,
@@ -242,7 +242,7 @@
MotionEvent end = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 500, 0);
gestureListener.onFling(start, end, 0, 1000);
verifyLogPosted(GlobalActionsDialogLite.GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
- verify(mStatusBar).animateExpandSettingsPanel(null);
+ verify(mCentralSurfaces).animateExpandSettingsPanel(null);
}
@Test
@@ -251,7 +251,7 @@
doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
- doReturn(false).when(mStatusBar).isKeyguardShowing();
+ doReturn(false).when(mCentralSurfaces).isKeyguardShowing();
String[] actions = {
GlobalActionsDialogLite.GLOBAL_ACTION_KEY_EMERGENCY,
GlobalActionsDialogLite.GLOBAL_ACTION_KEY_LOCKDOWN,
@@ -266,7 +266,7 @@
MotionEvent end = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 500, 0);
gestureListener.onFling(start, end, 0, 1000);
verifyLogPosted(GlobalActionsDialogLite.GlobalActionsEvent.GA_CLOSE_TAP_OUTSIDE);
- verify(mStatusBar).animateExpandNotificationsPanel();
+ verify(mCentralSurfaces).animateExpandNotificationsPanel();
}
@Test
@@ -432,7 +432,7 @@
doReturn(4).when(mGlobalActionsDialogLite).getMaxShownPowerItems();
doReturn(true).when(mGlobalActionsDialogLite).shouldDisplayLockdown(any());
doReturn(true).when(mGlobalActionsDialogLite).shouldShowAction(any());
- doReturn(false).when(mStatusBar).isKeyguardShowing();
+ doReturn(false).when(mCentralSurfaces).isKeyguardShowing();
String[] actions = {
GlobalActionsDialogLite.GLOBAL_ACTION_KEY_EMERGENCY,
GlobalActionsDialogLite.GLOBAL_ACTION_KEY_LOCKDOWN,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
index e606be1..b359ae5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaHierarchyManagerTest.kt
@@ -33,10 +33,11 @@
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.phone.KeyguardBypassController
-import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.FakeConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.animation.UniqueObjectHostView
-import junit.framework.Assert
+import com.android.systemui.util.mockito.any
+import com.google.common.truth.Truth.assertThat
import org.junit.Assert.assertNotNull
import org.junit.Before
import org.junit.Rule
@@ -44,16 +45,16 @@
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyLong
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.`when`
-import org.mockito.Mockito.any
-import org.mockito.Mockito.anyBoolean
-import org.mockito.Mockito.anyLong
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
+import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -83,8 +84,6 @@
@Mock
private lateinit var keyguardViewController: KeyguardViewController
@Mock
- private lateinit var configurationController: ConfigurationController
- @Mock
private lateinit var uniqueObjectHostView: UniqueObjectHostView
@Mock
private lateinit var dreamOverlayStateController: DreamOverlayStateController
@@ -97,6 +96,7 @@
val mockito = MockitoJUnit.rule()
private lateinit var mediaHiearchyManager: MediaHierarchyManager
private lateinit var mediaFrame: ViewGroup
+ private val configurationController = FakeConfigurationController()
@Before
fun setup() {
@@ -176,12 +176,7 @@
@Test
fun testGoingToFullShade() {
- // Let's set it onto Lock screen
- `when`(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
- `when`(notificationLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn(
- true)
- statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD)
- clearInvocations(mediaCarouselController)
+ goToLockscreen()
// Let's transition all the way to full shade
mediaHiearchyManager.setTransitionToFullShadeAmount(100000f)
@@ -204,41 +199,48 @@
// Let's make sure alpha is set
mediaHiearchyManager.setTransitionToFullShadeAmount(2.0f)
- Assert.assertTrue("alpha should not be 1.0f when cross fading", mediaFrame.alpha != 1.0f)
+ assertThat(mediaFrame.alpha).isNotEqualTo(1.0f)
}
@Test
fun testTransformationOnLockScreenIsFading() {
- // Let's set it onto Lock screen
- `when`(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
- `when`(notificationLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn(
- true)
- statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD)
- clearInvocations(mediaCarouselController)
+ goToLockscreen()
+ expandQS()
- // Let's transition from lockscreen to qs
- mediaHiearchyManager.qsExpansion = 1.0f
val transformType = mediaHiearchyManager.calculateTransformationType()
- Assert.assertTrue("media isn't transforming to qs with a fade",
- transformType == MediaHierarchyManager.TRANSFORMATION_TYPE_FADE)
+ assertThat(transformType).isEqualTo(MediaHierarchyManager.TRANSFORMATION_TYPE_FADE)
+ }
+
+ @Test
+ fun calculateTransformationType_onLockShade_inSplitShade_goingToFullShade_returnsTransition() {
+ enableSplitShade()
+ goToLockscreen()
+ expandQS()
+ mediaHiearchyManager.setTransitionToFullShadeAmount(10000f)
+
+ val transformType = mediaHiearchyManager.calculateTransformationType()
+ assertThat(transformType).isEqualTo(MediaHierarchyManager.TRANSFORMATION_TYPE_TRANSITION)
+ }
+
+ @Test
+ fun calculateTransformationType_onLockShade_inSplitShade_notExpanding_returnsFade() {
+ enableSplitShade()
+ goToLockscreen()
+ goToLockedShade()
+ expandQS()
+ mediaHiearchyManager.setTransitionToFullShadeAmount(0f)
+
+ val transformType = mediaHiearchyManager.calculateTransformationType()
+ assertThat(transformType).isEqualTo(MediaHierarchyManager.TRANSFORMATION_TYPE_FADE)
}
@Test
fun testTransformationOnLockScreenToQQSisFading() {
- // Let's set it onto Lock screen
- `when`(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
- `when`(notificationLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn(
- true)
- statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD)
- clearInvocations(mediaCarouselController)
+ goToLockscreen()
+ goToLockedShade()
- // Let's transition from lockscreen to qs
- `when`(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
- statusBarCallback.value.onStatePreChange(StatusBarState.KEYGUARD,
- StatusBarState.SHADE_LOCKED)
val transformType = mediaHiearchyManager.calculateTransformationType()
- Assert.assertTrue("media isn't transforming to qqswith a fade",
- transformType == MediaHierarchyManager.TRANSFORMATION_TYPE_FADE)
+ assertThat(transformType).isEqualTo(MediaHierarchyManager.TRANSFORMATION_TYPE_FADE)
}
@Test
@@ -254,4 +256,32 @@
verify(mediaCarouselController).closeGuts()
}
-}
\ No newline at end of file
+
+ private fun enableSplitShade() {
+ context.getOrCreateTestableResources().addOverride(
+ R.bool.config_use_split_notification_shade, true
+ )
+ configurationController.notifyConfigurationChanged()
+ }
+
+ private fun goToLockscreen() {
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+ whenever(notificationLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn(
+ true
+ )
+ statusBarCallback.value.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD)
+ clearInvocations(mediaCarouselController)
+ }
+
+ private fun goToLockedShade() {
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
+ statusBarCallback.value.onStatePreChange(
+ StatusBarState.KEYGUARD,
+ StatusBarState.SHADE_LOCKED
+ )
+ }
+
+ private fun expandQS() {
+ mediaHiearchyManager.qsExpansion = 1.0f
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
index 28de176..809b890 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt
@@ -18,6 +18,7 @@
import android.content.Context
import android.graphics.drawable.Drawable
+import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
@@ -30,7 +31,10 @@
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.argumentCaptor
+import com.android.systemui.util.mockito.capture
import com.android.systemui.util.time.FakeSystemClock
+import com.android.systemui.util.view.ViewUtil
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -39,6 +43,7 @@
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@SmallTest
@@ -52,6 +57,8 @@
@Mock
private lateinit var windowManager: WindowManager
@Mock
+ private lateinit var viewUtil: ViewUtil
+ @Mock
private lateinit var tapGestureDetector: TapGestureDetector
@Before
@@ -62,7 +69,7 @@
fakeExecutor = FakeExecutor(fakeClock)
controllerCommon = TestControllerCommon(
- context, windowManager, fakeExecutor, tapGestureDetector
+ context, windowManager, viewUtil, fakeExecutor, tapGestureDetector
)
}
@@ -169,6 +176,40 @@
.isEqualTo(state.getAppName(context))
}
+ @Test
+ fun tapGestureDetected_outsideViewBounds_viewHidden() {
+ controllerCommon.displayChip(getState())
+ whenever(viewUtil.touchIsWithinView(any(), any(), any())).thenReturn(false)
+ val gestureCallbackCaptor = argumentCaptor<(MotionEvent) -> Unit>()
+ verify(tapGestureDetector).addOnGestureDetectedCallback(
+ any(), capture(gestureCallbackCaptor)
+ )
+ val callback = gestureCallbackCaptor.value!!
+
+ callback.invoke(
+ MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
+ )
+
+ verify(windowManager).removeView(any())
+ }
+
+ @Test
+ fun tapGestureDetected_insideViewBounds_viewNotHidden() {
+ controllerCommon.displayChip(getState())
+ whenever(viewUtil.touchIsWithinView(any(), any(), any())).thenReturn(true)
+ val gestureCallbackCaptor = argumentCaptor<(MotionEvent) -> Unit>()
+ verify(tapGestureDetector).addOnGestureDetectedCallback(
+ any(), capture(gestureCallbackCaptor)
+ )
+ val callback = gestureCallbackCaptor.value!!
+
+ callback.invoke(
+ MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
+ )
+
+ verify(windowManager, never()).removeView(any())
+ }
+
private fun getState() = MediaTttChipState(PACKAGE_NAME)
private fun getChipView(): ViewGroup {
@@ -182,10 +223,11 @@
inner class TestControllerCommon(
context: Context,
windowManager: WindowManager,
+ viewUtil: ViewUtil,
@Main mainExecutor: DelayableExecutor,
tapGestureDetector: TapGestureDetector,
) : MediaTttChipControllerCommon<MediaTttChipState>(
- context, windowManager, mainExecutor, tapGestureDetector, R.layout.media_ttt_chip
+ context, windowManager, viewUtil, mainExecutor, tapGestureDetector, R.layout.media_ttt_chip
) {
override fun updateChipView(chipState: MediaTttChipState, currentChipView: ViewGroup) {
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index e5f4df6..86d4c1b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -37,6 +37,7 @@
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
+import com.android.systemui.util.view.ViewUtil
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -61,6 +62,8 @@
@Mock
private lateinit var windowManager: WindowManager
@Mock
+ private lateinit var viewUtil: ViewUtil
+ @Mock
private lateinit var commandQueue: CommandQueue
private lateinit var commandQueueCallback: CommandQueue.Callbacks
private lateinit var fakeAppIconDrawable: Drawable
@@ -81,6 +84,7 @@
commandQueue,
context,
windowManager,
+ viewUtil,
FakeExecutor(FakeSystemClock()),
TapGestureDetector(context),
Handler.getMain(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
index e5ba3f3..88888f0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
@@ -38,6 +38,8 @@
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
+import com.android.systemui.util.view.ViewUtil
+
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -62,6 +64,8 @@
@Mock
private lateinit var windowManager: WindowManager
@Mock
+ private lateinit var viewUtil: ViewUtil
+ @Mock
private lateinit var commandQueue: CommandQueue
private lateinit var commandQueueCallback: CommandQueue.Callbacks
private lateinit var fakeAppIconDrawable: Drawable
@@ -82,6 +86,7 @@
commandQueue,
context,
windowManager,
+ viewUtil,
FakeExecutor(FakeSystemClock()),
TapGestureDetector(context)
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
index 3a95ed3..634d9e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
@@ -37,8 +37,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import org.junit.Before;
import org.junit.Test;
@@ -91,7 +90,7 @@
mNavBarHelper = new NavBarHelper(mContext, mAccessibilityManager,
mAccessibilityButtonModeObserver, mAccessibilityButtonTargetObserver,
mSystemActions, mOverviewProxyService, mAssistManagerLazy,
- () -> Optional.of(mock(StatusBar.class)),
+ () -> Optional.of(mock(CentralSurfaces.class)),
mNavigationModeController, mUserTracker, mDumpManager);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 5a652fc..eb1e1a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -94,7 +94,7 @@
import com.android.systemui.statusbar.phone.LightBarController;
import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.utils.leaks.LeakCheckedTest;
@@ -160,7 +160,7 @@
@Mock
private AssistManager mAssistManager;
@Mock
- private StatusBar mStatusBar;
+ private CentralSurfaces mCentralSurfaces;
@Rule
public final LeakCheckedTest.SysuiLeakCheck mLeakCheck = new LeakCheckedTest.SysuiLeakCheck();
@@ -189,7 +189,7 @@
mock(AccessibilityButtonModeObserver.class),
mock(AccessibilityButtonTargetsObserver.class),
mSystemActions, mOverviewProxyService,
- () -> mock(AssistManager.class), () -> Optional.of(mStatusBar),
+ () -> mock(AssistManager.class), () -> Optional.of(mCentralSurfaces),
mock(NavigationModeController.class), mock(UserTracker.class),
mock(DumpManager.class)));
mNavigationBar = createNavBar(mContext);
@@ -282,7 +282,7 @@
NotificationShadeWindowView mockShadeWindowView = mock(NotificationShadeWindowView.class);
WindowInsets windowInsets = new WindowInsets.Builder().setVisible(ime(), false).build();
doReturn(windowInsets).when(mockShadeWindowView).getRootWindowInsets();
- doReturn(mockShadeWindowView).when(mStatusBar).getNotificationShadeWindowView();
+ doReturn(mockShadeWindowView).when(mCentralSurfaces).getNotificationShadeWindowView();
doReturn(true).when(mockShadeWindowView).isAttachedToWindow();
doNothing().when(defaultNavBar).checkNavBarModes();
doNothing().when(externalNavBar).checkNavBarModes();
@@ -318,7 +318,7 @@
@Test
public void testSetImeWindowStatusWhenKeyguardLockingAndImeInsetsChange() {
NotificationShadeWindowView mockShadeWindowView = mock(NotificationShadeWindowView.class);
- doReturn(mockShadeWindowView).when(mStatusBar).getNotificationShadeWindowView();
+ doReturn(mockShadeWindowView).when(mCentralSurfaces).getNotificationShadeWindowView();
doReturn(true).when(mockShadeWindowView).isAttachedToWindow();
doNothing().when(mNavigationBar).checkNavBarModes();
mNavigationBar.createView(null, /* initialVisibility= */ true);
@@ -335,7 +335,7 @@
// Verify navbar didn't alter and showing back icon when the keyguard is showing without
// requesting IME insets visible.
- doReturn(true).when(mStatusBar).isKeyguardShowing();
+ doReturn(true).when(mCentralSurfaces).isKeyguardShowing();
mNavigationBar.setImeWindowStatus(DEFAULT_DISPLAY, null, IME_VISIBLE,
BACK_DISPOSITION_DEFAULT, true);
assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
@@ -401,7 +401,7 @@
mCommandQueue,
Optional.of(mock(Pip.class)),
Optional.of(mock(Recents.class)),
- () -> Optional.of(mStatusBar),
+ () -> Optional.of(mCentralSurfaces),
mock(ShadeController.class),
mock(NotificationRemoteInputManager.class),
mock(NotificationShadeDepthController.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index 721809c..91f8a40 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -48,7 +48,7 @@
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.power.PowerUI.WarningsUI;
import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import org.junit.Before;
import org.junit.Test;
@@ -89,14 +89,14 @@
private IThermalEventListener mSkinThermalEventListener;
@Mock private BroadcastDispatcher mBroadcastDispatcher;
@Mock private CommandQueue mCommandQueue;
- @Mock private Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
- @Mock private StatusBar mStatusBar;
+ @Mock private Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
+ @Mock private CentralSurfaces mCentralSurfaces;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- when(mStatusBarOptionalLazy.get()).thenReturn(Optional.of(mStatusBar));
+ when(mCentralSurfacesOptionalLazy.get()).thenReturn(Optional.of(mCentralSurfaces));
createPowerUi();
mSkinThermalEventListener = mPowerUI.new SkinThermalEventListener();
@@ -430,12 +430,6 @@
state.mIsPowerSaver = true;
shouldShow = mPowerUI.shouldShowHybridWarning(state.get());
assertThat(shouldShow).isFalse();
-
- state.mIsPowerSaver = false;
- // if disabled we should not show the low warning.
- state.mIsLowLevelWarningEnabled = false;
- shouldShow = mPowerUI.shouldShowHybridWarning(state.get());
- assertThat(shouldShow).isFalse();
}
@Test
@@ -685,7 +679,7 @@
private void createPowerUi() {
mPowerUI = new PowerUI(
- mContext, mBroadcastDispatcher, mCommandQueue, mStatusBarOptionalLazy,
+ mContext, mBroadcastDispatcher, mCommandQueue, mCentralSurfacesOptionalLazy,
mMockWarnings, mEnhancedEstimates, mPowerManager);
mPowerUI.mThermalService = mThermalServiceMock;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index 4ab3926..534c7e7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -56,7 +56,7 @@
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.AutoTileManager;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.policy.Clock;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -142,7 +142,7 @@
mock(QSFactoryImpl.class), new Handler(), Looper.myLooper(),
mock(PluginManager.class), mock(TunerService.class),
() -> mock(AutoTileManager.class), mock(DumpManager.class),
- mock(BroadcastDispatcher.class), Optional.of(mock(StatusBar.class)),
+ mock(BroadcastDispatcher.class), Optional.of(mock(CentralSurfaces.class)),
mock(QSLogger.class), mock(UiEventLogger.class), mock(UserTracker.class),
mock(SecureSettings.class), mock(CustomTileStatePersister.class),
mTileServiceRequestControllerBuilder, mock(TileLifecycleManager.Factory.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
new file mode 100644
index 0000000..ac1e86f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
@@ -0,0 +1,136 @@
+package com.android.systemui.qs
+
+import android.test.suitebuilder.annotation.SmallTest
+import android.testing.AndroidTestingRunner
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.media.MediaFlags
+import com.android.systemui.media.MediaHost
+import com.android.systemui.media.MediaHostState
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.qs.customize.QSCustomizerController
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.settings.brightness.BrightnessController
+import com.android.systemui.settings.brightness.BrightnessSliderController
+import com.android.systemui.tuner.TunerService
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class QSPanelControllerTest : SysuiTestCase() {
+
+ @Mock private lateinit var qsPanel: QSPanel
+ @Mock private lateinit var qsFgsManagerFooter: QSFgsManagerFooter
+ @Mock private lateinit var qsSecurityFooter: QSSecurityFooter
+ @Mock private lateinit var tunerService: TunerService
+ @Mock private lateinit var qsTileHost: QSTileHost
+ @Mock private lateinit var qsCustomizerController: QSCustomizerController
+ @Mock private lateinit var qsTileRevealControllerFactory: QSTileRevealController.Factory
+ @Mock private lateinit var dumpManager: DumpManager
+ @Mock private lateinit var metricsLogger: MetricsLogger
+ @Mock private lateinit var uiEventLogger: UiEventLogger
+ @Mock private lateinit var qsLogger: QSLogger
+ @Mock private lateinit var brightnessControllerFactory: BrightnessController.Factory
+ @Mock private lateinit var brightnessController: BrightnessController
+ @Mock private lateinit var brightnessSlider: BrightnessSliderController
+ @Mock private lateinit var brightnessSliderFactory: BrightnessSliderController.Factory
+ @Mock private lateinit var falsingManager: FalsingManager
+ @Mock private lateinit var featureFlags: FeatureFlags
+ @Mock private lateinit var mediaFlags: MediaFlags
+ @Mock private lateinit var mediaHost: MediaHost
+
+ private lateinit var controller: QSPanelController
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ whenever(brightnessSliderFactory.create(any(), any())).thenReturn(brightnessSlider)
+ whenever(brightnessControllerFactory.create(any())).thenReturn(brightnessController)
+ whenever(qsPanel.resources).thenReturn(mContext.orCreateTestableResources.resources)
+
+ controller = QSPanelController(
+ qsPanel,
+ qsFgsManagerFooter,
+ qsSecurityFooter,
+ tunerService,
+ qsTileHost,
+ qsCustomizerController,
+ /* usingMediaPlayer= */ true,
+ mediaHost,
+ qsTileRevealControllerFactory,
+ dumpManager,
+ metricsLogger,
+ uiEventLogger,
+ qsLogger,
+ brightnessControllerFactory,
+ brightnessSliderFactory,
+ falsingManager,
+ featureFlags,
+ mediaFlags
+ )
+ }
+
+ @After
+ fun tearDown() {
+ reset(mediaHost)
+ }
+
+ @Test
+ fun onInit_notSplitShade_newMediaLayoutAvailable_setsMediaAsExpanded() {
+ setSplitShadeEnabled(false)
+ whenever(mediaFlags.useMediaSessionLayout()).thenReturn(true)
+
+ controller.onInit()
+
+ verify(mediaHost).expansion = MediaHostState.EXPANDED
+ }
+
+ @Test
+ fun onInit_notSplitShade_newMediaLayoutNotAvailable_setsMediaAsExpanded() {
+ setSplitShadeEnabled(false)
+ whenever(mediaFlags.useMediaSessionLayout()).thenReturn(false)
+
+ controller.onInit()
+
+ verify(mediaHost).expansion = MediaHostState.EXPANDED
+ }
+
+ @Test
+ fun onInit_inSplitShade_newMediaLayoutAvailable_setsMediaAsExpanded() {
+ setSplitShadeEnabled(true)
+ whenever(mediaFlags.useMediaSessionLayout()).thenReturn(true)
+
+ controller.onInit()
+
+ verify(mediaHost).expansion = MediaHostState.EXPANDED
+ }
+
+ @Test
+ fun onInit_inSplitShade_newMediaLayoutNotAvailable_setsMediaAsCollapsed() {
+ setSplitShadeEnabled(true)
+ whenever(mediaFlags.useMediaSessionLayout()).thenReturn(false)
+
+ controller.onInit()
+
+ verify(mediaHost).expansion = MediaHostState.COLLAPSED
+ }
+
+ private fun setSplitShadeEnabled(enabled: Boolean) {
+ mContext.orCreateTestableResources
+ .addOverride(R.bool.config_use_split_notification_shade, enabled)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index 8872e28..e67d37ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -65,7 +65,7 @@
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.phone.AutoTileManager;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.settings.FakeSettings;
@@ -112,7 +112,7 @@
@Mock
private QSTile.State mMockState;
@Mock
- private StatusBar mStatusBar;
+ private CentralSurfaces mCentralSurfaces;
@Mock
private QSLogger mQSLogger;
@Mock
@@ -152,7 +152,7 @@
QSTileHost.TILES_SETTING, "", "", false, mUserTracker.getUserId(), false);
mQSTileHost = new TestQSTileHost(mContext, mIconController, mDefaultFactory, mHandler,
mLooper.getLooper(), mPluginManager, mTunerService, mAutoTiles, mDumpManager,
- mBroadcastDispatcher, mStatusBar, mQSLogger, mUiEventLogger, mUserTracker,
+ mBroadcastDispatcher, mCentralSurfaces, mQSLogger, mUiEventLogger, mUserTracker,
mSecureSettings, mCustomTileStatePersister, mTileServiceRequestControllerBuilder,
mTileLifecycleManagerFactory);
setUpTileFactory();
@@ -437,15 +437,15 @@
QSFactory defaultFactory, Handler mainHandler, Looper bgLooper,
PluginManager pluginManager, TunerService tunerService,
Provider<AutoTileManager> autoTiles, DumpManager dumpManager,
- BroadcastDispatcher broadcastDispatcher, StatusBar statusBar, QSLogger qsLogger,
- UiEventLogger uiEventLogger, UserTracker userTracker,
+ BroadcastDispatcher broadcastDispatcher, CentralSurfaces centralSurfaces,
+ QSLogger qsLogger, UiEventLogger uiEventLogger, UserTracker userTracker,
SecureSettings secureSettings, CustomTileStatePersister customTileStatePersister,
TileServiceRequestController.Builder tileServiceRequestControllerBuilder,
TileLifecycleManager.Factory tileLifecycleManagerFactory) {
super(context, iconController, defaultFactory, mainHandler, bgLooper, pluginManager,
tunerService, autoTiles, dumpManager, broadcastDispatcher,
- Optional.of(statusBar), qsLogger, uiEventLogger, userTracker, secureSettings,
- customTileStatePersister, tileServiceRequestControllerBuilder,
+ Optional.of(centralSurfaces), qsLogger, uiEventLogger, userTracker,
+ secureSettings, customTileStatePersister, tileServiceRequestControllerBuilder,
tileLifecycleManagerFactory);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
index e39d6a1..19a9863 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
@@ -50,7 +50,7 @@
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.phone.AutoTileManager;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.policy.BluetoothController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -91,7 +91,7 @@
@Mock
private DumpManager mDumpManager;
@Mock
- private StatusBar mStatusBar;
+ private CentralSurfaces mCentralSurfaces;
@Mock
private QSLogger mQSLogger;
@Mock
@@ -132,7 +132,7 @@
() -> mAutoTileManager,
mDumpManager,
mock(BroadcastDispatcher.class),
- Optional.of(mStatusBar),
+ Optional.of(mCentralSurfaces),
mQSLogger,
mUiEventLogger,
mUserTracker,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
index bfe875c..7ab49584f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java
@@ -19,7 +19,7 @@
import static com.android.systemui.screenshot.ScreenshotController.ACTION_TYPE_SHARE;
import static com.android.systemui.screenshot.ScreenshotController.EXTRA_ID;
import static com.android.systemui.screenshot.ScreenshotController.EXTRA_SMART_ACTIONS_ENABLED;
-import static com.android.systemui.statusbar.phone.StatusBar.SYSTEM_DIALOG_REASON_SCREENSHOT;
+import static com.android.systemui.statusbar.phone.CentralSurfaces.SYSTEM_DIALOG_REASON_SCREENSHOT;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -41,7 +41,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import org.junit.Before;
import org.junit.Test;
@@ -59,7 +59,7 @@
public class ActionProxyReceiverTest extends SysuiTestCase {
@Mock
- private StatusBar mMockStatusBar;
+ private CentralSurfaces mMockCentralSurfaces;
@Mock
private ActivityManagerWrapper mMockActivityManagerWrapper;
@Mock
@@ -83,7 +83,7 @@
actionProxyReceiver.onReceive(mContext, mIntent);
verify(mMockActivityManagerWrapper).closeSystemWindows(SYSTEM_DIALOG_REASON_SCREENSHOT);
- verify(mMockStatusBar, never()).executeRunnableDismissingKeyguard(
+ verify(mMockCentralSurfaces, never()).executeRunnableDismissingKeyguard(
any(Runnable.class), any(Runnable.class), anyBoolean(), anyBoolean(), anyBoolean());
verify(mMockPendingIntent).send(
eq(mContext), anyInt(), isNull(), isNull(), isNull(), isNull(), any(Bundle.class));
@@ -96,13 +96,13 @@
doAnswer((Answer<Object>) invocation -> {
((Runnable) invocation.getArgument(0)).run();
return null;
- }).when(mMockStatusBar).executeRunnableDismissingKeyguard(
+ }).when(mMockCentralSurfaces).executeRunnableDismissingKeyguard(
any(Runnable.class), isNull(), anyBoolean(), anyBoolean(), anyBoolean());
actionProxyReceiver.onReceive(mContext, mIntent);
verify(mMockActivityManagerWrapper).closeSystemWindows(SYSTEM_DIALOG_REASON_SCREENSHOT);
- verify(mMockStatusBar).executeRunnableDismissingKeyguard(
+ verify(mMockCentralSurfaces).executeRunnableDismissingKeyguard(
any(Runnable.class), isNull(), eq(true), eq(true), eq(true));
verify(mMockPendingIntent).send(
eq(mContext), anyInt(), isNull(), isNull(), isNull(), isNull(), any(Bundle.class));
@@ -135,7 +135,7 @@
private ActionProxyReceiver constructActionProxyReceiver(boolean withStatusBar) {
if (withStatusBar) {
return new ActionProxyReceiver(
- Optional.of(mMockStatusBar), mMockActivityManagerWrapper,
+ Optional.of(mMockCentralSurfaces), mMockActivityManagerWrapper,
mMockScreenshotSmartActions);
} else {
return new ActionProxyReceiver(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 9076e16..25a5b90 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -22,8 +22,8 @@
import com.android.systemui.statusbar.phone.LSShadeTransitionLogger
import com.android.systemui.statusbar.phone.NotificationPanelViewController
import com.android.systemui.statusbar.phone.ScrimController
-import com.android.systemui.statusbar.phone.StatusBar
-import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.phone.CentralSurfaces
+import com.android.systemui.statusbar.policy.FakeConfigurationController
import org.junit.After
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotNull
@@ -66,17 +66,18 @@
@Mock lateinit var wakefulnessLifecycle: WakefulnessLifecycle
@Mock lateinit var mediaHierarchyManager: MediaHierarchyManager
@Mock lateinit var scrimController: ScrimController
- @Mock lateinit var configurationController: ConfigurationController
@Mock lateinit var falsingManager: FalsingManager
@Mock lateinit var notificationPanelController: NotificationPanelViewController
@Mock lateinit var nsslController: NotificationStackScrollLayoutController
@Mock lateinit var depthController: NotificationShadeDepthController
@Mock lateinit var stackscroller: NotificationStackScrollLayout
@Mock lateinit var expandHelperCallback: ExpandHelper.Callback
- @Mock lateinit var statusbar: StatusBar
+ @Mock lateinit var mCentralSurfaces: CentralSurfaces
@Mock lateinit var qS: QS
@JvmField @Rule val mockito = MockitoJUnit.rule()
+ private val configurationController = FakeConfigurationController()
+
@Before
fun setup() {
val helper = NotificationTestHelper(
@@ -105,7 +106,7 @@
whenever(nsslController.view).thenReturn(stackscroller)
whenever(nsslController.expandHelperCallback).thenReturn(expandHelperCallback)
transitionController.notificationPanelController = notificationPanelController
- transitionController.statusbar = statusbar
+ transitionController.centralSurfaces = mCentralSurfaces
transitionController.qS = qS
transitionController.setStackScroller(nsslController)
whenever(statusbarStateController.state).thenReturn(StatusBarState.KEYGUARD)
@@ -117,7 +118,7 @@
whenever(lockScreenUserManager.isLockscreenPublicMode(anyInt())).thenReturn(true)
whenever(falsingCollector.shouldEnforceBouncer()).thenReturn(false)
whenever(keyguardBypassController.bypassEnabled).thenReturn(false)
- clearInvocations(statusbar)
+ clearInvocations(mCentralSurfaces)
}
@After
@@ -174,7 +175,7 @@
@Test
fun testDontGoWhenShadeDisabled() {
- whenever(statusbar.isShadeDisabled).thenReturn(true)
+ whenever(mCentralSurfaces.isShadeDisabled).thenReturn(true)
transitionController.goToLockedShade(null)
verify(statusbarStateController, never()).setState(anyInt())
}
@@ -193,7 +194,7 @@
transitionController.goToLockedShade(null)
verify(statusbarStateController, never()).setState(anyInt())
verify(statusbarStateController).setLeaveOpenOnKeyguardHide(true)
- verify(statusbar).showBouncerWithDimissAndCancelIfKeyguard(anyObject(), anyObject())
+ verify(mCentralSurfaces).showBouncerWithDimissAndCancelIfKeyguard(anyObject(), anyObject())
}
@Test
@@ -202,7 +203,7 @@
transitionController.goToLockedShade(null)
verify(statusbarStateController, never()).setState(anyInt())
verify(statusbarStateController).setLeaveOpenOnKeyguardHide(true)
- verify(statusbar).showBouncerWithDimissAndCancelIfKeyguard(anyObject(), anyObject())
+ verify(mCentralSurfaces).showBouncerWithDimissAndCancelIfKeyguard(anyObject(), anyObject())
}
@Test
@@ -244,4 +245,27 @@
verify(qS).setTransitionToFullShadeAmount(anyFloat(), anyFloat())
verify(depthController).transitionToFullShadeProgress = anyFloat()
}
+
+ @Test
+ fun setDragDownAmount_setsValueOnMediaHierarchyManager() {
+ transitionController.dragDownAmount = 10f
+
+ verify(mediaHierarchyManager).setTransitionToFullShadeAmount(10f)
+ }
+
+ @Test
+ fun setDragDownAmount_inSplitShade_setsValueOnMediaHierarchyManager() {
+ enableSplitShade()
+
+ transitionController.dragDownAmount = 10f
+
+ verify(mediaHierarchyManager).setTransitionToFullShadeAmount(10f)
+ }
+
+ private fun enableSplitShade() {
+ context.getOrCreateTestableResources().addOverride(
+ R.bool.config_use_split_notification_shade, true
+ )
+ configurationController.notifyConfigurationChanged()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
index 4213b07..a4ce9cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NonPhoneDependencyTest.java
@@ -47,8 +47,8 @@
/**
* Verifies that particular sets of dependencies don't have dependencies on others. For example,
- * code managing notifications shouldn't directly depend on StatusBar, since there are platforms
- * which want to manage notifications, but don't use StatusBar.
+ * code managing notifications shouldn't directly depend on CentralSurfaces, since there are
+ * platforms which want to manage notifications, but don't use CentralSurfaces.
*/
@SmallTest
@RunWith(AndroidTestingRunner.class)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
index bd9f91f..2691ff9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java
@@ -49,7 +49,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
import com.google.android.collect.Sets;
@@ -105,7 +105,7 @@
mVisibilityProvider,
mEntryManager,
mock(RemoteInputNotificationRebuilder.class),
- () -> Optional.of(mock(StatusBar.class)),
+ () -> Optional.of(mock(CentralSurfaces.class)),
mStateController,
Handler.createAsync(Looper.myLooper()),
mRemoteInputUriController,
@@ -196,7 +196,7 @@
NotificationVisibilityProvider visibilityProvider,
NotificationEntryManager notificationEntryManager,
RemoteInputNotificationRebuilder rebuilder,
- Lazy<Optional<StatusBar>> statusBarOptionalLazy,
+ Lazy<Optional<CentralSurfaces>> centralSurfacesOptionalLazy,
StatusBarStateController statusBarStateController,
Handler mainHandler,
RemoteInputUriController remoteInputUriController,
@@ -211,7 +211,7 @@
visibilityProvider,
notificationEntryManager,
rebuilder,
- statusBarOptionalLazy,
+ centralSurfacesOptionalLazy,
statusBarStateController,
mainHandler,
remoteInputUriController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
index 7fafb24..407044b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationViewHierarchyManagerTest.java
@@ -54,7 +54,6 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
-import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -121,7 +120,6 @@
mock(KeyguardBypassController.class),
Optional.of(mock(Bubbles.class)),
mock(DynamicPrivacyController.class),
- mock(ForegroundServiceSectionController.class),
mock(DynamicChildBindController.class),
mock(LowPriorityInflationHelper.class),
mock(AssistantFeedbackController.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
index e0689f3..3500e4d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java
@@ -45,7 +45,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.policy.RemoteInputUriController;
import org.junit.Before;
@@ -103,7 +103,7 @@
mVisibilityProvider,
mNotificationEntryManager,
new RemoteInputNotificationRebuilder(mContext),
- () -> Optional.of(mock(StatusBar.class)),
+ () -> Optional.of(mock(CentralSurfaces.class)),
mStatusBarStateController,
Handler.createAsync(Looper.myLooper()),
mRemoteInputUriController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/gesture/GenericGestureDetectorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/gesture/GenericGestureDetectorTest.kt
index c038903..ea06647 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/gesture/GenericGestureDetectorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/gesture/GenericGestureDetectorTest.kt
@@ -111,7 +111,7 @@
override fun onInputEvent(ev: InputEvent) {
if (ev is MotionEvent && ev.x == CORRECT_X) {
- onGestureDetected()
+ onGestureDetected(ev)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index f2b7bf5..0fff5f5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -201,7 +201,6 @@
() -> mNotificationRowBinder,
() -> mRemoteInputManager,
mLeakDetector,
- mock(ForegroundServiceDismissalFeatureController.class),
mock(IStatusBarService.class),
NotifLiveDataStoreMocksKt.createNotifLiveDataStoreImplMock(),
mock(DumpManager.class)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
index 52189e4..7fc5ece 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
@@ -61,7 +61,6 @@
import com.android.systemui.statusbar.SbnBuilder;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.notification.ConversationNotificationProcessor;
-import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationClicker;
import com.android.systemui.statusbar.notification.NotificationEntryListener;
@@ -191,7 +190,6 @@
() -> mRowBinder,
() -> mRemoteInputManager,
mLeakDetector,
- mock(ForegroundServiceDismissalFeatureController.class),
mock(IStatusBarService.class),
NotifLiveDataStoreMocksKt.createNotifLiveDataStoreImplMock(),
mock(DumpManager.class)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index 7d8e0d2..e9d8f58 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -82,8 +82,8 @@
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager.OnSettingsClickListener;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.wmshell.BubblesManager;
@@ -124,7 +124,7 @@
@Mock private NotificationInfo.CheckSaveListener mCheckSaveListener;
@Mock private OnSettingsClickListener mOnSettingsClickListener;
@Mock private DeviceProvisionedController mDeviceProvisionedController;
- @Mock private StatusBar mStatusBar;
+ @Mock private CentralSurfaces mCentralSurfaces;
@Mock private AccessibilityManager mAccessibilityManager;
@Mock private HighPriorityProvider mHighPriorityProvider;
@Mock private INotificationManager mINotificationManager;
@@ -155,7 +155,7 @@
when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false);
mGutsManager = new NotificationGutsManager(mContext,
- () -> Optional.of(mStatusBar), mHandler, mHandler, mAccessibilityManager,
+ () -> Optional.of(mCentralSurfaces), mHandler, mHandler, mAccessibilityManager,
mHighPriorityProvider, mINotificationManager, mNotificationEntryManager,
mPeopleSpaceWidgetManager, mLauncherApps, mShortcutManager,
mChannelEditorDialogController, mContextTracker, mAssistantFeedbackController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index c4f954f..1561b5a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -58,7 +58,6 @@
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
@@ -68,14 +67,13 @@
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
-import com.android.systemui.statusbar.notification.row.ForegroundServiceDungeonView;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController.NotificationPanelEvent;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -118,7 +116,7 @@
@Mock(answer = Answers.RETURNS_SELF)
private NotificationSwipeHelper.Builder mNotificationSwipeHelperBuilder;
@Mock private NotificationSwipeHelper mNotificationSwipeHelper;
- @Mock private StatusBar mStatusBar;
+ @Mock private CentralSurfaces mCentralSurfaces;
@Mock private ScrimController mScrimController;
@Mock private NotificationGroupManagerLegacy mLegacyGroupManager;
@Mock private SectionHeaderController mSilentHeaderController;
@@ -129,9 +127,6 @@
@Mock private IStatusBarService mIStatusBarService;
@Mock private UiEventLogger mUiEventLogger;
@Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
- @Mock private ForegroundServiceDismissalFeatureController mFgFeatureController;
- @Mock private ForegroundServiceSectionController mFgServicesSectionController;
- @Mock private ForegroundServiceDungeonView mForegroundServiceDungeonView;
@Mock private LayoutInflater mLayoutInflater;
@Mock private NotificationRemoteInputManager mRemoteInputManager;
@Mock private VisualStabilityManager mVisualStabilityManager;
@@ -151,8 +146,6 @@
when(mNotificationSwipeHelperBuilder.build()).thenReturn(mNotificationSwipeHelper);
when(mNotifPipelineFlags.isNewPipelineEnabled()).thenReturn(false);
- when(mFgServicesSectionController.createView(mLayoutInflater))
- .thenReturn(mForegroundServiceDungeonView);
mController = new NotificationStackScrollLayoutController(
true,
@@ -175,7 +168,7 @@
new FalsingManagerFake(),
mResources,
mNotificationSwipeHelperBuilder,
- mStatusBar,
+ mCentralSurfaces,
mScrimController,
mLegacyGroupManager,
mLegacyGroupManager,
@@ -187,8 +180,6 @@
mLockscreenShadeTransitionController,
mIStatusBarService,
mUiEventLogger,
- mFgFeatureController,
- mFgServicesSectionController,
mLayoutInflater,
mRemoteInputManager,
mVisualStabilityManager,
@@ -399,22 +390,6 @@
}
@Test
- public void testForegroundDismissEnabled() {
- when(mFgFeatureController.isForegroundServiceDismissalEnabled()).thenReturn(true);
- mController.attach(mNotificationStackScrollLayout);
- verify(mNotificationStackScrollLayout).initializeForegroundServiceSection(
- mForegroundServiceDungeonView);
- }
-
- @Test
- public void testForegroundDismissaDisabled() {
- when(mFgFeatureController.isForegroundServiceDismissalEnabled()).thenReturn(false);
- mController.attach(mNotificationStackScrollLayout);
- verify(mNotificationStackScrollLayout, never()).initializeForegroundServiceSection(
- any(ForegroundServiceDungeonView.class));
- }
-
- @Test
public void testUpdateFooter_remoteInput() {
ArgumentCaptor<RemoteInputController.Callback> callbackCaptor =
ArgumentCaptor.forClass(RemoteInputController.Callback.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 4f731ed..eafcc35 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -61,9 +61,9 @@
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.FooterView;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ShadeController;
-import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
import org.junit.Assert;
@@ -89,7 +89,7 @@
private AmbientState mAmbientState;
@Rule public MockitoRule mockito = MockitoJUnit.rule();
- @Mock private StatusBar mBar;
+ @Mock private CentralSurfaces mCentralSurfaces;
@Mock private SysuiStatusBarStateController mBarState;
@Mock private NotificationGroupManagerLegacy mGroupMembershipManger;
@Mock private NotificationGroupManagerLegacy mGroupExpansionManager;
@@ -141,7 +141,7 @@
mStackScrollerInternal.initView(getContext(), mNotificationSwipeHelper);
mStackScroller = spy(mStackScrollerInternal);
mStackScroller.setShelfController(notificationShelfController);
- mStackScroller.setStatusBar(mBar);
+ mStackScroller.setCentralSurfaces(mCentralSurfaces);
mStackScroller.setEmptyShadeView(mEmptyShadeView);
when(mStackScrollLayoutController.isHistoryEnabled()).thenReturn(true);
when(mStackScrollLayoutController.getNoticationRoundessManager())
@@ -149,7 +149,7 @@
mStackScroller.setController(mStackScrollLayoutController);
// Stub out functionality that isn't necessary to test.
- doNothing().when(mBar)
+ doNothing().when(mCentralSurfaces)
.executeRunnableDismissingKeyguard(any(Runnable.class),
any(Runnable.class),
anyBoolean(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
similarity index 87%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacksTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
index 25950f2..9bfb2c4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarCommandQueueCallbacksTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
@@ -57,8 +57,8 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
-public class StatusBarCommandQueueCallbacksTest extends SysuiTestCase {
- @Mock private StatusBar mStatusBar;
+public class CentralSurfacesCommandQueueCallbacksTest extends SysuiTestCase {
+ @Mock private CentralSurfaces mCentralSurfaces;
@Mock private ShadeController mShadeController;
@Mock private CommandQueue mCommandQueue;
@Mock private NotificationPanelViewController mNotificationPanelViewController;
@@ -81,14 +81,14 @@
@Mock private LightBarController mLightBarController;
@Mock private StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
- StatusBarCommandQueueCallbacks mSbcqCallbacks;
+ CentralSurfacesCommandQueueCallbacks mSbcqCallbacks;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mSbcqCallbacks = new StatusBarCommandQueueCallbacks(
- mStatusBar,
+ mSbcqCallbacks = new CentralSurfacesCommandQueueCallbacks(
+ mCentralSurfaces,
mContext,
mContext.getResources(),
mShadeController,
@@ -122,13 +122,13 @@
@Test
public void testDisableNotificationShade() {
- when(mStatusBar.getDisabled1()).thenReturn(StatusBarManager.DISABLE_NONE);
- when(mStatusBar.getDisabled2()).thenReturn(StatusBarManager.DISABLE_NONE);
+ when(mCentralSurfaces.getDisabled1()).thenReturn(StatusBarManager.DISABLE_NONE);
+ when(mCentralSurfaces.getDisabled2()).thenReturn(StatusBarManager.DISABLE_NONE);
when(mCommandQueue.panelsEnabled()).thenReturn(false);
mSbcqCallbacks.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE,
StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false);
- verify(mStatusBar).updateQsExpansionEnabled();
+ verify(mCentralSurfaces).updateQsExpansionEnabled();
verify(mShadeController).animateCollapsePanels();
// Trying to open it does nothing.
@@ -140,12 +140,13 @@
@Test
public void testEnableNotificationShade() {
- when(mStatusBar.getDisabled1()).thenReturn(StatusBarManager.DISABLE_NONE);
- when(mStatusBar.getDisabled2()).thenReturn(StatusBarManager.DISABLE2_NOTIFICATION_SHADE);
+ when(mCentralSurfaces.getDisabled1()).thenReturn(StatusBarManager.DISABLE_NONE);
+ when(mCentralSurfaces.getDisabled2())
+ .thenReturn(StatusBarManager.DISABLE2_NOTIFICATION_SHADE);
when(mCommandQueue.panelsEnabled()).thenReturn(true);
mSbcqCallbacks.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE,
StatusBarManager.DISABLE2_NONE, false);
- verify(mStatusBar).updateQsExpansionEnabled();
+ verify(mCentralSurfaces).updateQsExpansionEnabled();
verify(mShadeController, never()).animateCollapsePanels();
// Can now be opened.
@@ -158,13 +159,13 @@
@Test
public void testSuppressAmbientDisplay_suppress() {
mSbcqCallbacks.suppressAmbientDisplay(true);
- verify(mDozeServiceHost).setDozeSuppressed(true);
+ verify(mDozeServiceHost).setAlwaysOnSuppressed(true);
}
@Test
public void testSuppressAmbientDisplay_unsuppress() {
mSbcqCallbacks.suppressAmbientDisplay(false);
- verify(mDozeServiceHost).setDozeSuppressed(false);
+ verify(mDozeServiceHost).setAlwaysOnSuppressed(false);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesTest.java
similarity index 90%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesTest.java
index 0979439..953a330 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesTest.java
@@ -139,7 +139,7 @@
import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -177,12 +177,12 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper(setAsMainLooper = true)
-public class StatusBarTest extends SysuiTestCase {
+public class CentralSurfacesTest extends SysuiTestCase {
private static final int FOLD_STATE_FOLDED = 0;
private static final int FOLD_STATE_UNFOLDED = 1;
- private StatusBar mStatusBar;
+ private CentralSurfaces mCentralSurfaces;
private FakeMetricsLogger mMetricsLogger;
private PowerManager mPowerManager;
private TestableNotificationInterruptStateProviderImpl mNotificationInterruptStateProvider;
@@ -252,14 +252,12 @@
@Mock private ViewMediatorCallback mKeyguardVieMediatorCallback;
@Mock private VolumeComponent mVolumeComponent;
@Mock private CommandQueue mCommandQueue;
- @Mock private StatusBarComponent.Factory mStatusBarComponentFactory;
- @Mock private StatusBarComponent mStatusBarComponent;
+ @Mock private CentralSurfacesComponent.Factory mStatusBarComponentFactory;
+ @Mock private CentralSurfacesComponent mCentralSurfacesComponent;
@Mock private PluginManager mPluginManager;
@Mock private ViewMediatorCallback mViewMediatorCallback;
@Mock private StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
@Mock private ScreenPinningRequest mScreenPinningRequest;
- @Mock private StatusBarNotificationActivityStarter.Builder
- mStatusBarNotificationActivityStarterBuilder;
@Mock private PluginDependencyProvider mPluginDependencyProvider;
@Mock private KeyguardDismissUtil mKeyguardDismissUtil;
@Mock private ExtensionController mExtensionController;
@@ -336,8 +334,6 @@
mContext.setTheme(R.style.Theme_SystemUI_LightWallpaper);
when(mStackScrollerController.getView()).thenReturn(mStackScroller);
- when(mStackScrollerController.getNotificationListContainer()).thenReturn(
- mNotificationListContainer);
when(mStackScroller.generateLayoutParams(any())).thenReturn(new LayoutParams(0, 0));
when(mNotificationPanelViewController.getView()).thenReturn(mNotificationPanelView);
when(mNotificationPanelView.getLayoutParams()).thenReturn(new LayoutParams(0, 0));
@@ -368,8 +364,8 @@
when(mLockscreenWallpaperLazy.get()).thenReturn(mLockscreenWallpaper);
when(mBiometricUnlockControllerLazy.get()).thenReturn(mBiometricUnlockController);
- when(mStatusBarComponentFactory.create()).thenReturn(mStatusBarComponent);
- when(mStatusBarComponent.getNotificationShadeWindowViewController()).thenReturn(
+ when(mStatusBarComponentFactory.create()).thenReturn(mCentralSurfacesComponent);
+ when(mCentralSurfacesComponent.getNotificationShadeWindowViewController()).thenReturn(
mNotificationShadeWindowViewController);
doAnswer(invocation -> {
((Runnable) invocation.getArgument(0)).run();
@@ -379,12 +375,12 @@
mShadeController = new ShadeControllerImpl(mCommandQueue,
mStatusBarStateController, mNotificationShadeWindowController,
mStatusBarKeyguardViewManager, mContext.getSystemService(WindowManager.class),
- () -> Optional.of(mStatusBar), () -> mAssistManager);
+ () -> Optional.of(mCentralSurfaces), () -> mAssistManager);
when(mOperatorNameViewControllerFactory.create(any()))
.thenReturn(mOperatorNameViewController);
- mStatusBar = new StatusBar(
+ mCentralSurfaces = new CentralSurfaces(
mContext,
mNotificationsController,
mock(FragmentService.class),
@@ -445,7 +441,6 @@
mCommandQueue,
mStatusBarComponentFactory,
mPluginManager,
- mStatusBarNotificationActivityStarterBuilder,
mShadeController,
mStatusBarKeyguardViewManager,
mViewMediatorCallback,
@@ -480,8 +475,8 @@
mDeviceStateManager,
mDreamOverlayStateController,
mWiredChargingRippleController);
- when(mKeyguardViewMediator.registerStatusBar(
- any(StatusBar.class),
+ when(mKeyguardViewMediator.registerCentralSurfaces(
+ any(CentralSurfaces.class),
any(NotificationPanelViewController.class),
any(PanelExpansionStateManager.class),
any(BiometricUnlockController.class),
@@ -492,23 +487,23 @@
when(mKeyguardViewMediator.getViewMediatorCallback()).thenReturn(
mKeyguardVieMediatorCallback);
- // TODO: we should be able to call mStatusBar.start() and have all the below values
+ // TODO: we should be able to call mCentralSurfaces.start() and have all the below values
// initialized automatically.
- mStatusBar.mNotificationShadeWindowView = mNotificationShadeWindowView;
- mStatusBar.mNotificationPanelViewController = mNotificationPanelViewController;
- mStatusBar.mDozeScrimController = mDozeScrimController;
- mStatusBar.mPresenter = mNotificationPresenter;
- mStatusBar.mKeyguardIndicationController = mKeyguardIndicationController;
- mStatusBar.mBarService = mBarService;
- mStatusBar.mStackScroller = mStackScroller;
- mStatusBar.startKeyguard();
+ mCentralSurfaces.mNotificationShadeWindowView = mNotificationShadeWindowView;
+ mCentralSurfaces.mNotificationPanelViewController = mNotificationPanelViewController;
+ mCentralSurfaces.mDozeScrimController = mDozeScrimController;
+ mCentralSurfaces.mPresenter = mNotificationPresenter;
+ mCentralSurfaces.mKeyguardIndicationController = mKeyguardIndicationController;
+ mCentralSurfaces.mBarService = mBarService;
+ mCentralSurfaces.mStackScroller = mStackScroller;
+ mCentralSurfaces.startKeyguard();
mInitController.executePostInitTasks();
notificationLogger.setUpWithContainer(mNotificationListContainer);
}
@Test
public void testSetBouncerShowing_noCrash() {
- mStatusBar.setBouncerShowing(true);
+ mCentralSurfaces.setBouncerShowing(true);
}
@Test
@@ -516,7 +511,7 @@
when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(true);
- mStatusBar.executeRunnableDismissingKeyguard(null, null, false, false, false);
+ mCentralSurfaces.executeRunnableDismissingKeyguard(null, null, false, false, false);
}
@Test
@@ -524,7 +519,7 @@
when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(false);
- mStatusBar.executeRunnableDismissingKeyguard(null, null, false, false, false);
+ mCentralSurfaces.executeRunnableDismissingKeyguard(null, null, false, false, false);
}
@Test
@@ -532,7 +527,7 @@
when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(false);
when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(false);
- mStatusBar.executeRunnableDismissingKeyguard(null, null, false, false, false);
+ mCentralSurfaces.executeRunnableDismissingKeyguard(null, null, false, false, false);
}
@Test
@@ -544,7 +539,7 @@
when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(false);
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
when(mKeyguardStateController.isMethodSecure()).thenReturn(false);
- mStatusBar.onKeyguardViewManagerStatesUpdated();
+ mCentralSurfaces.onKeyguardViewManagerStatesUpdated();
MetricsAsserts.assertHasLog("missing hidden insecure lockscreen log",
mMetricsLogger.getLogs(),
@@ -563,7 +558,7 @@
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
when(mKeyguardStateController.isMethodSecure()).thenReturn(true);
- mStatusBar.onKeyguardViewManagerStatesUpdated();
+ mCentralSurfaces.onKeyguardViewManagerStatesUpdated();
MetricsAsserts.assertHasLog("missing hidden secure lockscreen log",
mMetricsLogger.getLogs(),
@@ -582,7 +577,7 @@
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
when(mKeyguardStateController.isMethodSecure()).thenReturn(false);
- mStatusBar.onKeyguardViewManagerStatesUpdated();
+ mCentralSurfaces.onKeyguardViewManagerStatesUpdated();
MetricsAsserts.assertHasLog("missing insecure lockscreen showing",
mMetricsLogger.getLogs(),
@@ -601,7 +596,7 @@
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false);
when(mKeyguardStateController.isMethodSecure()).thenReturn(true);
- mStatusBar.onKeyguardViewManagerStatesUpdated();
+ mCentralSurfaces.onKeyguardViewManagerStatesUpdated();
MetricsAsserts.assertHasLog("missing secure lockscreen showing log",
mMetricsLogger.getLogs(),
@@ -620,7 +615,7 @@
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true);
when(mKeyguardStateController.isMethodSecure()).thenReturn(true);
- mStatusBar.onKeyguardViewManagerStatesUpdated();
+ mCentralSurfaces.onKeyguardViewManagerStatesUpdated();
MetricsAsserts.assertHasLog("missing bouncer log",
mMetricsLogger.getLogs(),
@@ -721,7 +716,7 @@
@Test
public void testLogHidden() {
try {
- mStatusBar.handleVisibleToUserChanged(false);
+ mCentralSurfaces.handleVisibleToUserChanged(false);
mUiBgExecutor.runAllReady();
verify(mBarService, times(1)).onPanelHidden();
verify(mBarService, never()).onPanelRevealed(anyBoolean(), anyInt());
@@ -736,10 +731,10 @@
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true);
when(mNotificationsController.getActiveNotificationsCount()).thenReturn(5);
when(mNotificationPresenter.isPresenterFullyCollapsed()).thenReturn(true);
- mStatusBar.setBarStateForTest(StatusBarState.SHADE);
+ mCentralSurfaces.setBarStateForTest(StatusBarState.SHADE);
try {
- mStatusBar.handleVisibleToUserChanged(true);
+ mCentralSurfaces.handleVisibleToUserChanged(true);
mUiBgExecutor.runAllReady();
verify(mBarService, never()).onPanelHidden();
verify(mBarService, times(1)).onPanelRevealed(false, 1);
@@ -755,10 +750,10 @@
when(mNotificationsController.getActiveNotificationsCount()).thenReturn(5);
when(mNotificationPresenter.isPresenterFullyCollapsed()).thenReturn(false);
- mStatusBar.setBarStateForTest(StatusBarState.SHADE);
+ mCentralSurfaces.setBarStateForTest(StatusBarState.SHADE);
try {
- mStatusBar.handleVisibleToUserChanged(true);
+ mCentralSurfaces.handleVisibleToUserChanged(true);
mUiBgExecutor.runAllReady();
verify(mBarService, never()).onPanelHidden();
verify(mBarService, times(1)).onPanelRevealed(true, 5);
@@ -773,10 +768,10 @@
when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false);
when(mNotificationsController.getActiveNotificationsCount()).thenReturn(5);
when(mNotificationPresenter.isPresenterFullyCollapsed()).thenReturn(false);
- mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
+ mCentralSurfaces.setBarStateForTest(StatusBarState.KEYGUARD);
try {
- mStatusBar.handleVisibleToUserChanged(true);
+ mCentralSurfaces.handleVisibleToUserChanged(true);
mUiBgExecutor.runAllReady();
verify(mBarService, never()).onPanelHidden();
verify(mBarService, times(1)).onPanelRevealed(false, 5);
@@ -788,18 +783,18 @@
@Test
public void testDump_DoesNotCrash() {
- mStatusBar.dump(null, new PrintWriter(new ByteArrayOutputStream()), null);
+ mCentralSurfaces.dump(null, new PrintWriter(new ByteArrayOutputStream()), null);
}
@Test
public void testDumpBarTransitions_DoesNotCrash() {
- StatusBar.dumpBarTransitions(
+ CentralSurfaces.dumpBarTransitions(
new PrintWriter(new ByteArrayOutputStream()), "var", /* transitions= */ null);
}
@Test
public void testFingerprintNotification_UpdatesScrims() {
- mStatusBar.notifyBiometricAuthModeChanged();
+ mCentralSurfaces.notifyBiometricAuthModeChanged();
verify(mScrimController).transitionTo(any(), any());
}
@@ -808,81 +803,81 @@
// Simulate unlocking from AoD with fingerprint.
when(mBiometricUnlockController.getMode())
.thenReturn(BiometricUnlockController.MODE_WAKE_AND_UNLOCK);
- mStatusBar.updateScrimController();
+ mCentralSurfaces.updateScrimController();
verify(mScrimController).transitionTo(eq(ScrimState.UNLOCKED), any());
}
@Test
public void testTransitionLaunch_goesToUnlocked() {
- mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
- mStatusBar.showKeyguardImpl();
+ mCentralSurfaces.setBarStateForTest(StatusBarState.KEYGUARD);
+ mCentralSurfaces.showKeyguardImpl();
// Starting a pulse should change the scrim controller to the pulsing state
when(mNotificationPanelViewController.isLaunchTransitionRunning()).thenReturn(true);
when(mNotificationPanelViewController.isLaunchingAffordanceWithPreview()).thenReturn(true);
- mStatusBar.updateScrimController();
+ mCentralSurfaces.updateScrimController();
verify(mScrimController).transitionTo(eq(ScrimState.UNLOCKED), any());
}
@Test
public void testSetExpansionAffectsAlpha_whenKeyguardShowingButGoingAwayForAnyReason() {
- mStatusBar.updateScrimController();
+ mCentralSurfaces.updateScrimController();
verify(mScrimController).setExpansionAffectsAlpha(eq(true));
clearInvocations(mScrimController);
when(mKeyguardStateController.isShowing()).thenReturn(true);
when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(false);
- mStatusBar.updateScrimController();
+ mCentralSurfaces.updateScrimController();
verify(mScrimController).setExpansionAffectsAlpha(eq(true));
clearInvocations(mScrimController);
when(mKeyguardStateController.isShowing()).thenReturn(true);
when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(true);
- mStatusBar.updateScrimController();
+ mCentralSurfaces.updateScrimController();
verify(mScrimController).setExpansionAffectsAlpha(eq(false));
clearInvocations(mScrimController);
when(mKeyguardStateController.isShowing()).thenReturn(true);
when(mKeyguardStateController.isKeyguardFadingAway()).thenReturn(true);
- mStatusBar.updateScrimController();
+ mCentralSurfaces.updateScrimController();
verify(mScrimController).setExpansionAffectsAlpha(eq(false));
}
@Test
public void testTransitionLaunch_noPreview_doesntGoUnlocked() {
- mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
- mStatusBar.showKeyguardImpl();
+ mCentralSurfaces.setBarStateForTest(StatusBarState.KEYGUARD);
+ mCentralSurfaces.showKeyguardImpl();
// Starting a pulse should change the scrim controller to the pulsing state
when(mNotificationPanelViewController.isLaunchTransitionRunning()).thenReturn(true);
when(mNotificationPanelViewController.isLaunchingAffordanceWithPreview()).thenReturn(false);
- mStatusBar.updateScrimController();
+ mCentralSurfaces.updateScrimController();
verify(mScrimController).transitionTo(eq(ScrimState.KEYGUARD));
}
@Test
public void testSetOccluded_propagatesToScrimController() {
- mStatusBar.setOccluded(true);
+ mCentralSurfaces.setOccluded(true);
verify(mScrimController).setKeyguardOccluded(eq(true));
reset(mScrimController);
- mStatusBar.setOccluded(false);
+ mCentralSurfaces.setOccluded(false);
verify(mScrimController).setKeyguardOccluded(eq(false));
}
@Test
public void testPulseWhileDozing_updatesScrimController() {
- mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
- mStatusBar.showKeyguardImpl();
+ mCentralSurfaces.setBarStateForTest(StatusBarState.KEYGUARD);
+ mCentralSurfaces.showKeyguardImpl();
// Starting a pulse should change the scrim controller to the pulsing state
when(mDozeServiceHost.isPulsing()).thenReturn(true);
- mStatusBar.updateScrimController();
+ mCentralSurfaces.updateScrimController();
verify(mScrimController).transitionTo(eq(ScrimState.PULSING), any());
// Ending a pulse should take it back to keyguard state
when(mDozeServiceHost.isPulsing()).thenReturn(false);
- mStatusBar.updateScrimController();
+ mCentralSurfaces.updateScrimController();
verify(mScrimController).transitionTo(eq(ScrimState.KEYGUARD));
}
@@ -890,45 +885,45 @@
public void testShowKeyguardImplementation_setsState() {
when(mLockscreenUserManager.getCurrentProfiles()).thenReturn(new SparseArray<>());
- mStatusBar.setBarStateForTest(StatusBarState.SHADE);
+ mCentralSurfaces.setBarStateForTest(StatusBarState.SHADE);
// By default, showKeyguardImpl sets state to KEYGUARD.
- mStatusBar.showKeyguardImpl();
+ mCentralSurfaces.showKeyguardImpl();
verify(mStatusBarStateController).setState(
eq(StatusBarState.KEYGUARD), eq(false) /* force */);
}
@Test
public void testOnStartedWakingUp_isNotDozing() {
- mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
+ mCentralSurfaces.setBarStateForTest(StatusBarState.KEYGUARD);
when(mStatusBarStateController.isKeyguardRequested()).thenReturn(true);
when(mDozeServiceHost.getDozingRequested()).thenReturn(true);
- mStatusBar.updateIsKeyguard();
+ mCentralSurfaces.updateIsKeyguard();
// TODO: mNotificationPanelView.expand(false) gets called twice. Should be once.
verify(mNotificationPanelViewController, times(2)).expand(eq(false));
clearInvocations(mNotificationPanelViewController);
- mStatusBar.mWakefulnessObserver.onStartedWakingUp();
+ mCentralSurfaces.mWakefulnessObserver.onStartedWakingUp();
verify(mDozeServiceHost).stopDozing();
verify(mNotificationPanelViewController).expand(eq(false));
}
@Test
public void testOnStartedWakingUp_doesNotDismissBouncer_whenPulsing() {
- mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
+ mCentralSurfaces.setBarStateForTest(StatusBarState.KEYGUARD);
when(mStatusBarStateController.isKeyguardRequested()).thenReturn(true);
when(mDozeServiceHost.getDozingRequested()).thenReturn(true);
- mStatusBar.updateIsKeyguard();
+ mCentralSurfaces.updateIsKeyguard();
clearInvocations(mNotificationPanelViewController);
- mStatusBar.setBouncerShowing(true);
- mStatusBar.mWakefulnessObserver.onStartedWakingUp();
+ mCentralSurfaces.setBouncerShowing(true);
+ mCentralSurfaces.mWakefulnessObserver.onStartedWakingUp();
verify(mNotificationPanelViewController, never()).expand(anyBoolean());
}
@Test
public void testRegisterBroadcastsonDispatcher() {
- mStatusBar.registerBroadcastReceiver();
+ mCentralSurfaces.registerBroadcastReceiver();
verify(mBroadcastDispatcher).registerReceiver(
any(BroadcastReceiver.class),
any(IntentFilter.class),
@@ -938,7 +933,7 @@
@Test
public void testUpdateResources_updatesBouncer() {
- mStatusBar.updateResources();
+ mCentralSurfaces.updateResources();
verify(mStatusBarKeyguardViewManager).updateResources();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
index 5f2bbd3..077b41a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java
@@ -126,6 +126,12 @@
setAodEnabledForTest(true);
setShouldControlUnlockedScreenOffForTest(true);
setDisplayNeedsBlankingForTest(false);
+
+ // Default to false here (with one test to make sure that when it returns true, we respect
+ // that). We'll test the specific conditions for this to return true/false in the
+ // UnlockedScreenOffAnimationController's tests.
+ when(mUnlockedScreenOffAnimationController.shouldPlayUnlockedScreenOffAnimation())
+ .thenReturn(false);
}
@Test
@@ -174,9 +180,12 @@
*/
@Test
public void testControlUnlockedScreenOffAnimation_dozeAfterScreenOff_false() {
+ mDozeParameters.mKeyguardVisibilityCallback.onKeyguardVisibilityChanged(true);
+
// If AOD is disabled, we shouldn't want to control screen off. Also, let's double check
// that when that value is updated, we called through to PowerManager.
setAodEnabledForTest(false);
+
assertFalse(mDozeParameters.shouldControlScreenOff());
assertTrue(mPowerManagerDozeAfterScreenOff);
@@ -188,7 +197,6 @@
@Test
public void testControlUnlockedScreenOffAnimationDisabled_dozeAfterScreenOff() {
- setShouldControlUnlockedScreenOffForTest(true);
when(mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ANIMATIONS)).thenReturn(false);
assertFalse(mDozeParameters.shouldControlUnlockedScreenOff());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
index 6ce3b4b..26ac70c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
@@ -82,7 +82,7 @@
@Mock private NotificationShadeWindowController mNotificationShadeWindowController;
@Mock private PowerManager mPowerManager;
@Mock private WakefulnessLifecycle mWakefullnessLifecycle;
- @Mock private StatusBar mStatusBar;
+ @Mock private CentralSurfaces mCentralSurfaces;
@Mock private NotificationIconAreaController mNotificationIconAreaController;
@Mock private NotificationShadeWindowViewController mNotificationShadeWindowViewController;
@Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@@ -103,7 +103,7 @@
mAuthController, mNotificationIconAreaController);
mDozeServiceHost.initialize(
- mStatusBar,
+ mCentralSurfaces,
mStatusBarKeyguardViewManager,
mNotificationShadeWindowViewController,
mNotificationPanel,
@@ -119,7 +119,7 @@
mDozeServiceHost.startDozing();
verify(mStatusBarStateController).setIsDozing(eq(true));
- verify(mStatusBar).updateIsKeyguard();
+ verify(mCentralSurfaces).updateIsKeyguard();
mDozeServiceHost.stopDozing();
verify(mStatusBarStateController).setIsDozing(eq(false));
@@ -128,8 +128,8 @@
@Test
public void testPulseWhileDozing_updatesScrimController() {
- mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
- mStatusBar.showKeyguardImpl();
+ mCentralSurfaces.setBarStateForTest(StatusBarState.KEYGUARD);
+ mCentralSurfaces.showKeyguardImpl();
// Keep track of callback to be able to stop the pulse
// DozeHost.PulseCallback[] pulseCallback = new DozeHost.PulseCallback[1];
@@ -154,12 +154,12 @@
verify(mDozeScrimController).pulse(
pulseCallbackArgumentCaptor.capture(), eq(DozeLog.PULSE_REASON_NOTIFICATION));
- verify(mStatusBar).updateScrimController();
- reset(mStatusBar);
+ verify(mCentralSurfaces).updateScrimController();
+ reset(mCentralSurfaces);
pulseCallbackArgumentCaptor.getValue().onPulseFinished();
assertFalse(mDozeScrimController.isPulsing());
- verify(mStatusBar).updateScrimController();
+ verify(mCentralSurfaces).updateScrimController();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
index 421d8f6a..db5741c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhoneTest.java
@@ -63,7 +63,6 @@
@Mock private NotificationGroupManagerLegacy mGroupManager;
@Mock private View mNotificationShadeWindowView;
@Mock private VisualStabilityProvider mVSProvider;
- @Mock private StatusBar mBar;
@Mock private StatusBarStateController mStatusBarStateController;
@Mock private KeyguardBypassController mBypassController;
@Mock private ConfigurationControllerImpl mConfigurationController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt
index 3257a84..31465f4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt
@@ -25,7 +25,7 @@
class KeyguardBottomAreaTest : SysuiTestCase() {
@Mock
- private lateinit var mStatusBar: StatusBar
+ private lateinit var mCentralSurfaces: CentralSurfaces
private lateinit var mKeyguardBottomArea: KeyguardBottomAreaView
@Before
@@ -42,7 +42,7 @@
mKeyguardBottomArea = LayoutInflater.from(mContext).inflate(
R.layout.keyguard_bottom_area, null, false) as KeyguardBottomAreaView
- mKeyguardBottomArea.setStatusBar(mStatusBar)
+ mKeyguardBottomArea.setCentralSurfaces(mCentralSurfaces)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
index 479c271..1af7035 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
@@ -130,6 +130,7 @@
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy;
import com.android.systemui.statusbar.notification.stack.AmbientState;
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
@@ -168,7 +169,7 @@
private static final int NOTIFICATION_SCRIM_TOP_PADDING_IN_SPLIT_SHADE = 50;
@Mock
- private StatusBar mStatusBar;
+ private CentralSurfaces mCentralSurfaces;
@Mock
private NotificationStackScrollLayout mNotificationStackScrollLayout;
@Mock
@@ -358,6 +359,8 @@
private NotificationShadeWindowController mNotificationShadeWindowController;
@Mock
private SysUiState mSysUiState;
+ @Mock
+ private NotificationListContainer mNotificationListContainer;
private Optional<SysUIUnfoldComponent> mSysUIUnfoldComponent = Optional.empty();
private SysuiStatusBarStateController mStatusBarStateController;
private NotificationPanelViewController mNotificationPanelViewController;
@@ -387,8 +390,8 @@
when(mResources.getBoolean(R.bool.config_enableNotificationShadeDrag)).thenReturn(true);
when(mResources.getDimensionPixelSize(R.dimen.notifications_top_padding_split_shade))
.thenReturn(NOTIFICATION_SCRIM_TOP_PADDING_IN_SPLIT_SHADE);
- when(mResources.getDimensionPixelSize(R.dimen.qs_panel_width)).thenReturn(400);
- when(mResources.getDimensionPixelSize(R.dimen.notification_panel_width)).thenReturn(400);
+ when(mResources.getDimensionPixelSize(R.dimen.notification_panel_margin_horizontal))
+ .thenReturn(10);
when(mView.getContext()).thenReturn(getContext());
when(mView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar);
when(mView.findViewById(R.id.keyguard_user_switcher_view)).thenReturn(mUserSwitcherView);
@@ -542,9 +545,10 @@
mInteractionJankMonitor,
mQsFrameTranslateController,
mSysUiState,
- mKeyguardUnlockAnimationController);
+ mKeyguardUnlockAnimationController,
+ mNotificationListContainer);
mNotificationPanelViewController.initDependencies(
- mStatusBar,
+ mCentralSurfaces,
() -> {},
mNotificationShelfController);
mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
@@ -786,25 +790,31 @@
}
@Test
- public void testSinglePaneShadeLayout_childrenHaveConstantWidth() {
- enableSplitShade(/* enabled= */ 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() {
+ public void testSplitShadeLayout_childrenHaveInsideMarginsOfZero() {
enableSplitShade(/* enabled= */ true);
mNotificationPanelViewController.updateResources();
- assertThat(getConstraintSetLayout(R.id.qs_frame).mWidth).isEqualTo(0);
- assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).mWidth).isEqualTo(0);
+ assertThat(getConstraintSetLayout(R.id.qs_frame).startMargin).isEqualTo(10);
+ assertThat(getConstraintSetLayout(R.id.qs_frame).endMargin).isEqualTo(0);
+ assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).startMargin)
+ .isEqualTo(0);
+ assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).endMargin)
+ .isEqualTo(10);
+ }
+
+ @Test
+ public void testSinglePaneLayout_childrenHaveEqualMargins() {
+ enableSplitShade(/* enabled= */ false);
+
+ mNotificationPanelViewController.updateResources();
+
+ assertThat(getConstraintSetLayout(R.id.qs_frame).startMargin).isEqualTo(10);
+ assertThat(getConstraintSetLayout(R.id.qs_frame).endMargin).isEqualTo(10);
+ assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).startMargin)
+ .isEqualTo(10);
+ assertThat(getConstraintSetLayout(R.id.notification_stack_scroller).endMargin)
+ .isEqualTo(10);
}
@Test
@@ -904,6 +914,7 @@
public void testSwitchesToCorrectClockInSplitShade() {
mStatusBarStateController.setState(KEYGUARD);
enableSplitShade(/* enabled= */ true);
+ clearInvocations(mKeyguardStatusViewController);
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
triggerPositionClockAndNotifications();
@@ -918,11 +929,56 @@
}
@Test
+ public void testHasNotifications_switchesToLargeClockWhenEnteringSplitShade() {
+ mStatusBarStateController.setState(KEYGUARD);
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
+
+ enableSplitShade(/* enabled= */ true);
+
+ verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true);
+ }
+
+ @Test
+ public void testNoNotifications_switchesToLargeClockWhenEnteringSplitShade() {
+ mStatusBarStateController.setState(KEYGUARD);
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+
+ enableSplitShade(/* enabled= */ true);
+
+ verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true);
+ }
+
+ @Test
+ public void testHasNotifications_switchesToSmallClockWhenExitingSplitShade() {
+ mStatusBarStateController.setState(KEYGUARD);
+ enableSplitShade(/* enabled= */ true);
+ clearInvocations(mKeyguardStatusViewController);
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
+
+ enableSplitShade(/* enabled= */ false);
+
+ verify(mKeyguardStatusViewController).displayClock(SMALL, /* animate */ true);
+ }
+
+ @Test
+ public void testNoNotifications_switchesToLargeClockWhenExitingSplitShade() {
+ mStatusBarStateController.setState(KEYGUARD);
+ enableSplitShade(/* enabled= */ true);
+ clearInvocations(mKeyguardStatusViewController);
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
+
+ enableSplitShade(/* enabled= */ false);
+
+ verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true);
+ }
+
+ @Test
public void testSwitchesToBigClockInSplitShadeOnAod() {
mStatusBarStateController.setState(KEYGUARD);
enableSplitShade(/* enabled= */ true);
when(mMediaDataManager.hasActiveMedia()).thenReturn(true);
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+ clearInvocations(mKeyguardStatusViewController);
mNotificationPanelViewController.setDozing(true, false, null);
@@ -934,6 +990,7 @@
when(mScreenOffAnimationController.shouldAnimateClockChange()).thenReturn(false);
mStatusBarStateController.setState(KEYGUARD);
enableSplitShade(/* enabled= */ true);
+ clearInvocations(mKeyguardStatusViewController);
when(mMediaDataManager.hasActiveMedia()).thenReturn(true);
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
@@ -946,6 +1003,7 @@
public void testDisplaysSmallClockOnLockscreenInSplitShadeWhenMediaIsPlaying() {
mStatusBarStateController.setState(KEYGUARD);
enableSplitShade(/* enabled= */ true);
+ clearInvocations(mKeyguardStatusViewController);
when(mMediaDataManager.hasActiveMedia()).thenReturn(true);
// one notification + media player visible
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewControllerTest.kt
index 34a5d4b..093f926 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewControllerTest.kt
@@ -61,7 +61,7 @@
@Mock
private lateinit var mStatusBarStateController: SysuiStatusBarStateController
@Mock
- private lateinit var mStatusBar: StatusBar
+ private lateinit var mCentralSurfaces: CentralSurfaces
@Mock
private lateinit var mDockManager: DockManager
@Mock
@@ -107,10 +107,11 @@
mStatusBarKeyguardViewManager,
mStatusBarWindowStateController,
mLockIconViewController,
- Optional.of(mLowLightClockController)
+ Optional.of(mLowLightClockController),
+ mCentralSurfaces,
+ mNotificationShadeWindowController
)
mController.setupExpandedStatusBar()
- mController.setService(mStatusBar, mNotificationShadeWindowController)
mInteractionEventHandlerCaptor =
ArgumentCaptor.forClass(InteractionEventHandler::class.java)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
index 6c33113..62a1bcd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java
@@ -71,7 +71,7 @@
@Mock private DragDownHelper mDragDownHelper;
@Mock private SysuiStatusBarStateController mStatusBarStateController;
@Mock private ShadeController mShadeController;
- @Mock private StatusBar mStatusBar;
+ @Mock private CentralSurfaces mCentralSurfaces;
@Mock private DockManager mDockManager;
@Mock private NotificationPanelViewController mNotificationPanelViewController;
@Mock private NotificationStackScrollLayout mNotificationStackScrollLayout;
@@ -115,9 +115,10 @@
mStatusBarKeyguardViewManager,
mStatusBarWindowStateController,
mLockIconViewController,
- Optional.of(mLowLightClockController));
+ Optional.of(mLowLightClockController),
+ mCentralSurfaces,
+ mNotificationShadeWindowController);
mController.setupExpandedStatusBar();
- mController.setService(mStatusBar, mNotificationShadeWindowController);
mController.setDragDownHelper(mDragDownHelper);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index 5891161..9ab88dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -31,6 +31,7 @@
import com.android.systemui.unfold.SysUIUnfoldComponent
import com.android.systemui.unfold.config.UnfoldTransitionConfig
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
+import com.android.systemui.util.view.ViewUtil
import com.android.systemui.util.mockito.any
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -63,6 +64,8 @@
private lateinit var configurationController: ConfigurationController
@Mock
private lateinit var userSwitcherController: StatusBarUserSwitcherController
+ @Mock
+ private lateinit var viewUtil: ViewUtil
private lateinit var view: PhoneStatusBarView
private lateinit var controller: PhoneStatusBarViewController
@@ -80,7 +83,6 @@
val parent = FrameLayout(mContext) // add parent to keep layout params
view = LayoutInflater.from(mContext)
.inflate(R.layout.status_bar, parent, false) as PhoneStatusBarView
- view.setLeftTopRightBottom(VIEW_LEFT, VIEW_TOP, VIEW_RIGHT, VIEW_BOTTOM)
}
controller = createAndInitController(view)
@@ -111,64 +113,6 @@
verify(moveFromCenterAnimation).onViewsReady(any())
}
- @Test
- fun touchIsWithinView_inBounds_returnsTrue() {
- val view = createViewMockWithScreenLocation()
- controller = createAndInitController(view)
-
- assertThat(controller.touchIsWithinView(VIEW_LEFT + 1f, VIEW_TOP + 1f)).isTrue()
- }
-
- @Test
- fun touchIsWithinView_onTopLeftCorner_returnsTrue() {
- val view = createViewMockWithScreenLocation()
- controller = createAndInitController(view)
-
- assertThat(controller.touchIsWithinView(VIEW_LEFT.toFloat(), VIEW_TOP.toFloat())).isTrue()
- }
-
- @Test
- fun touchIsWithinView_onBottomRightCorner_returnsTrue() {
- val view = createViewMockWithScreenLocation()
- controller = createAndInitController(view)
-
- assertThat(controller.touchIsWithinView(
- VIEW_RIGHT.toFloat(), VIEW_BOTTOM.toFloat())
- ).isTrue()
- }
-
- @Test
- fun touchIsWithinView_xTooSmall_returnsFalse() {
- val view = createViewMockWithScreenLocation()
- controller = createAndInitController(view)
-
- assertThat(controller.touchIsWithinView(VIEW_LEFT - 1f, VIEW_TOP + 1f)).isFalse()
- }
-
- @Test
- fun touchIsWithinView_xTooLarge_returnsFalse() {
- val view = createViewMockWithScreenLocation()
- controller = createAndInitController(view)
-
- assertThat(controller.touchIsWithinView(VIEW_RIGHT + 1f, VIEW_TOP + 1f)).isFalse()
- }
-
- @Test
- fun touchIsWithinView_yTooSmall_returnsFalse() {
- val view = createViewMockWithScreenLocation()
- controller = createAndInitController(view)
-
- assertThat(controller.touchIsWithinView(VIEW_LEFT + 1f, VIEW_TOP - 1f)).isFalse()
- }
-
- @Test
- fun touchIsWithinView_yTooLarge_returnsFalse() {
- val view = createViewMockWithScreenLocation()
- controller = createAndInitController(view)
-
- assertThat(controller.touchIsWithinView(VIEW_LEFT + 1f, VIEW_BOTTOM + 1f)).isFalse()
- }
-
private fun createViewMock(): PhoneStatusBarView {
val view = spy(view)
val viewTreeObserver = mock(ViewTreeObserver::class.java)
@@ -177,20 +121,12 @@
return view
}
- private fun createViewMockWithScreenLocation(): PhoneStatusBarView {
- val view = spy(view)
- val location = IntArray(2)
- location[0] = VIEW_LEFT
- location[1] = VIEW_TOP
- `when`(view.locationOnScreen).thenReturn(location)
- return view
- }
-
private fun createAndInitController(view: PhoneStatusBarView): PhoneStatusBarViewController {
return PhoneStatusBarViewController.Factory(
Optional.of(sysuiUnfoldComponent),
Optional.of(progressProvider),
userSwitcherController,
+ viewUtil,
configurationController
).create(view, touchEventHandler).also {
it.init()
@@ -215,8 +151,3 @@
}
}
}
-
-private const val VIEW_LEFT = 30
-private const val VIEW_RIGHT = 100
-private const val VIEW_TOP = 40
-private const val VIEW_BOTTOM = 100
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 8b93de5..f4f55cc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -75,7 +75,7 @@
@Mock
private KeyguardStateController mKeyguardStateController;
@Mock
- private StatusBar mStatusBar;
+ private CentralSurfaces mCentralSurfaces;
@Mock
private ViewGroup mContainer;
@Mock
@@ -118,7 +118,7 @@
any(ViewGroup.class),
any(KeyguardBouncer.BouncerExpansionCallback.class)))
.thenReturn(mBouncer);
- when(mStatusBar.getBouncerContainer()).thenReturn(mContainer);
+ when(mCentralSurfaces.getBouncerContainer()).thenReturn(mContainer);
when(mContainer.findViewById(anyInt())).thenReturn(mKeyguardMessageArea);
mStatusBarKeyguardViewManager = new StatusBarKeyguardViewManager(
getContext(),
@@ -138,8 +138,8 @@
Optional.of(mSysUiUnfoldComponent),
() -> mShadeController,
mLatencyTracker);
- mStatusBarKeyguardViewManager.registerStatusBar(
- mStatusBar,
+ mStatusBarKeyguardViewManager.registerCentralSurfaces(
+ mCentralSurfaces,
mNotificationPanelView,
new PanelExpansionStateManager(),
mBiometricUnlockController,
@@ -261,7 +261,7 @@
@Test
public void onPanelExpansionChanged_neverTranslatesBouncerWhenLaunchingApp() {
- when(mStatusBar.isInLaunchTransition()).thenReturn(true);
+ when(mCentralSurfaces.isInLaunchTransition()).thenReturn(true);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(
/* fraction= */ KeyguardBouncer.EXPANSION_VISIBLE,
/* expanded= */ true,
@@ -272,12 +272,12 @@
@Test
public void setOccluded_animatesPanelExpansion_onlyIfBouncerHidden() {
mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, true /* animated */);
- verify(mStatusBar).animateKeyguardUnoccluding();
+ verify(mCentralSurfaces).animateKeyguardUnoccluding();
when(mBouncer.isShowing()).thenReturn(true);
- clearInvocations(mStatusBar);
+ clearInvocations(mCentralSurfaces);
mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, true /* animated */);
- verify(mStatusBar, never()).animateKeyguardUnoccluding();
+ verify(mCentralSurfaces, never()).animateKeyguardUnoccluding();
}
@Test
@@ -303,7 +303,7 @@
@Test
public void setOccluded_isInLaunchTransition_onKeyguardOccludedChangedCalled() {
- when(mStatusBar.isInLaunchTransition()).thenReturn(true);
+ when(mCentralSurfaces.isInLaunchTransition()).thenReturn(true);
mStatusBarKeyguardViewManager.show(null);
mStatusBarKeyguardViewManager.setOccluded(true /* occluded */, false /* animated */);
@@ -312,7 +312,7 @@
@Test
public void setOccluded_isLaunchingActivityOverLockscreen_onKeyguardOccludedChangedCalled() {
- when(mStatusBar.isLaunchingActivityOverLockscreen()).thenReturn(true);
+ when(mCentralSurfaces.isLaunchingActivityOverLockscreen()).thenReturn(true);
mStatusBarKeyguardViewManager.show(null);
mStatusBarKeyguardViewManager.setOccluded(true /* occluded */, false /* animated */);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 743311f..ace7415 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -113,7 +113,7 @@
@Mock
private NotificationRemoteInputManager mRemoteInputManager;
@Mock
- private StatusBar mStatusBar;
+ private CentralSurfaces mCentralSurfaces;
@Mock
private KeyguardStateController mKeyguardStateController;
@Mock
@@ -203,7 +203,7 @@
mJankMonitor);
mNotificationActivityStarter =
- new StatusBarNotificationActivityStarter.Builder(
+ new StatusBarNotificationActivityStarter(
getContext(),
mock(CommandQueue.class),
mHandler,
@@ -229,18 +229,16 @@
mock(LockPatternUtils.class),
mock(StatusBarRemoteInputCallback.class),
mActivityIntentHelper,
-
mNotifPipelineFlags,
mock(MetricsLogger.class),
mock(StatusBarNotificationActivityStarterLogger.class),
- mOnUserInteractionCallback)
- .setStatusBar(mStatusBar)
- .setNotificationPresenter(mock(NotificationPresenter.class))
- .setNotificationPanelViewController(
- mock(NotificationPanelViewController.class))
- .setActivityLaunchAnimator(mActivityLaunchAnimator)
- .setNotificationAnimatorControllerProvider(notificationAnimationProvider)
- .build();
+ mOnUserInteractionCallback,
+ mCentralSurfaces,
+ mock(NotificationPresenter.class),
+ mock(NotificationPanelViewController.class),
+ mActivityLaunchAnimator,
+ notificationAnimationProvider
+ );
// set up dismissKeyguardThenExecute to synchronously invoke the OnDismissAction arg
doAnswer(mCallOnDismiss).when(mActivityStarter).dismissKeyguardThenExecute(
@@ -269,7 +267,7 @@
sbn.getNotification().flags |= Notification.FLAG_AUTO_CANCEL;
when(mKeyguardStateController.isShowing()).thenReturn(true);
- when(mStatusBar.isOccluded()).thenReturn(true);
+ when(mCentralSurfaces.isOccluded()).thenReturn(true);
// When
mNotificationActivityStarter.onNotificationClicked(sbn, mNotificationRow);
@@ -328,7 +326,7 @@
// Given
sbn.getNotification().contentIntent = null;
when(mKeyguardStateController.isShowing()).thenReturn(true);
- when(mStatusBar.isOccluded()).thenReturn(true);
+ when(mCentralSurfaces.isOccluded()).thenReturn(true);
// When
mNotificationActivityStarter.onNotificationClicked(sbn, mBubbleNotificationRow);
@@ -358,7 +356,7 @@
// Given
sbn.getNotification().contentIntent = mContentIntent;
when(mKeyguardStateController.isShowing()).thenReturn(true);
- when(mStatusBar.isOccluded()).thenReturn(true);
+ when(mCentralSurfaces.isOccluded()).thenReturn(true);
// When
mNotificationActivityStarter.onNotificationClicked(sbn, mBubbleNotificationRow);
@@ -402,6 +400,6 @@
mNotificationActivityStarter.handleFullScreenIntent(entry);
// THEN display should try wake up for the full screen intent
- verify(mStatusBar).wakeUpForFullScreenIntent();
+ verify(mCentralSurfaces).wakeUpForFullScreenIntent();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index 7d9e6b4..1a3dd3a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -37,6 +37,7 @@
import com.android.systemui.ForegroundServiceNotificationListener;
import com.android.systemui.InitController;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyguardIndicationController;
@@ -79,7 +80,7 @@
private CommandQueue mCommandQueue;
private FakeMetricsLogger mMetricsLogger;
private ShadeController mShadeController = mock(ShadeController.class);
- private StatusBar mStatusBar = mock(StatusBar.class);
+ private CentralSurfaces mCentralSurfaces = mock(CentralSurfaces.class);
private InitController mInitController = new InitController();
@Before
@@ -101,19 +102,24 @@
mock(NotificationStackScrollLayoutController.class);
when(stackScrollLayoutController.getView()).thenReturn(
mock(NotificationStackScrollLayout.class));
- when(stackScrollLayoutController.getNotificationListContainer()).thenReturn(
- mock(NotificationListContainer.class));
when(notificationShadeWindowView.getResources()).thenReturn(mContext.getResources());
- mStatusBarNotificationPresenter = new StatusBarNotificationPresenter(mContext,
- mock(NotificationPanelViewController.class), mock(HeadsUpManagerPhone.class),
- notificationShadeWindowView, stackScrollLayoutController,
- mock(DozeScrimController.class), mock(ScrimController.class),
- mock(NotificationShadeWindowController.class), mock(DynamicPrivacyController.class),
+ mStatusBarNotificationPresenter = new StatusBarNotificationPresenter(
+ mContext,
+ mock(NotificationPanelViewController.class),
+ mock(HeadsUpManagerPhone.class),
+ notificationShadeWindowView,
+ mock(ActivityStarter.class),
+ stackScrollLayoutController,
+ mock(DozeScrimController.class),
+ mock(ScrimController.class),
+ mock(NotificationShadeWindowController.class),
+ mock(DynamicPrivacyController.class),
mock(KeyguardStateController.class),
mock(KeyguardIndicationController.class),
- mStatusBar,
- mock(ShadeControllerImpl.class), mock(LockscreenShadeTransitionController.class),
+ mCentralSurfaces,
+ mock(ShadeControllerImpl.class),
+ mock(LockscreenShadeTransitionController.class),
mCommandQueue,
mock(NotificationViewHierarchyManager.class),
mock(NotificationLockscreenUserManager.class),
@@ -128,7 +134,9 @@
mNotificationInterruptStateProvider,
mock(NotificationRemoteInputManager.class),
mock(ConfigurationController.class),
- mock(NotifPipelineFlags.class));
+ mock(NotifPipelineFlags.class),
+ mock(NotificationRemoteInputManager.Callback.class),
+ mock(NotificationListContainer.class));
mInitController.executePostInitTasks();
ArgumentCaptor<NotificationInterruptSuppressor> suppressorCaptor =
ArgumentCaptor.forClass(NotificationInterruptSuppressor.class);
@@ -195,9 +203,9 @@
.setTag("a")
.setNotification(n)
.build();
- when(mStatusBar.areNotificationAlertsDisabled()).thenReturn(true);
+ when(mCentralSurfaces.areNotificationAlertsDisabled()).thenReturn(true);
- assertTrue("StatusBar alerts disabled shouldn't allow interruptions",
+ assertTrue("CentralSurfaces alerts disabled shouldn't allow interruptions",
mInterruptSuppressor.suppressInterruptions(entry));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
index 71b32c0..0936b77 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
@@ -31,6 +31,7 @@
import com.android.systemui.statusbar.StatusBarStateControllerImpl
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.settings.GlobalSettings
+import junit.framework.Assert.assertFalse
import org.junit.After
import org.junit.Before
import org.junit.Test
@@ -61,7 +62,7 @@
@Mock
private lateinit var globalSettings: GlobalSettings
@Mock
- private lateinit var statusBar: StatusBar
+ private lateinit var mCentralSurfaces: CentralSurfaces
@Mock
private lateinit var notificationPanelViewController: NotificationPanelViewController
@Mock
@@ -93,8 +94,8 @@
powerManager,
handler = handler
)
- controller.initialize(statusBar, lightRevealScrim)
- `when`(statusBar.notificationPanelViewController).thenReturn(
+ controller.initialize(mCentralSurfaces, lightRevealScrim)
+ `when`(mCentralSurfaces.notificationPanelViewController).thenReturn(
notificationPanelViewController)
// Screen off does not run if the panel is expanded, so we should say it's collapsed to test
@@ -133,7 +134,7 @@
*/
@Test
fun testAodUiShownIfNotInteractive() {
- `when`(dozeParameters.shouldControlUnlockedScreenOff()).thenReturn(true)
+ `when`(dozeParameters.canControlUnlockedScreenOff()).thenReturn(true)
`when`(powerManager.isInteractive).thenReturn(false)
val callbackCaptor = ArgumentCaptor.forClass(Runnable::class.java)
@@ -156,7 +157,7 @@
*/
@Test
fun testAodUiNotShownIfInteractive() {
- `when`(dozeParameters.shouldControlUnlockedScreenOff()).thenReturn(true)
+ `when`(dozeParameters.canControlUnlockedScreenOff()).thenReturn(true)
`when`(powerManager.isInteractive).thenReturn(true)
val callbackCaptor = ArgumentCaptor.forClass(Runnable::class.java)
@@ -167,4 +168,13 @@
verify(notificationPanelViewController, never()).showAodUi()
}
+
+ @Test
+ fun testNoAnimationPlaying_dozeParamsCanNotControlScreenOff() {
+ `when`(dozeParameters.canControlUnlockedScreenOff()).thenReturn(false)
+
+ assertFalse(controller.shouldPlayUnlockedScreenOffAnimation())
+ controller.startAnimation()
+ assertFalse(controller.isAnimationPlaying())
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index 5861617..5095094 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -17,6 +17,8 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.atLeast;
@@ -27,6 +29,7 @@
import android.app.StatusBarManager;
import android.content.Context;
import android.os.Bundle;
+import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
@@ -56,6 +59,9 @@
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.settings.SecureSettings;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -82,6 +88,8 @@
private final CommandQueue mCommandQueue = mock(CommandQueue.class);
private OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
private OperatorNameViewController mOperatorNameViewController;
+ private SecureSettings mSecureSettings;
+ private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
@Mock
private StatusBarFragmentComponent.Factory mStatusBarFragmentComponentFactory;
@@ -298,6 +306,40 @@
assertEquals(mStatusBarFragmentComponent, fragment.getStatusBarFragmentComponent());
}
+ @Test
+ public void testBlockedIcons_obeysSettingForVibrateIcon_settingOff() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+ String str = mContext.getString(com.android.internal.R.string.status_bar_volume);
+
+ // GIVEN the setting is off
+ when(mSecureSettings.getInt(Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON, 0))
+ .thenReturn(0);
+
+ // WHEN CollapsedStatusBarFragment builds the blocklist
+ fragment.updateBlockedIcons();
+
+ // THEN status_bar_volume SHOULD be present in the list
+ boolean contains = fragment.getBlockedIcons().contains(str);
+ assertTrue(contains);
+ }
+
+ @Test
+ public void testBlockedIcons_obeysSettingForVibrateIcon_settingOn() {
+ CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+ String str = mContext.getString(com.android.internal.R.string.status_bar_volume);
+
+ // GIVEN the setting is ON
+ when(mSecureSettings.getInt(Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON, 0))
+ .thenReturn(1);
+
+ // WHEN CollapsedStatusBarFragment builds the blocklist
+ fragment.updateBlockedIcons();
+
+ // THEN status_bar_volume SHOULD NOT be present in the list
+ boolean contains = fragment.getBlockedIcons().contains(str);
+ assertFalse(contains);
+ }
+
@Override
protected Fragment instantiate(Context context, String className, Bundle arguments) {
MockitoAnnotations.initMocks(this);
@@ -313,6 +355,7 @@
mOperatorNameViewControllerFactory = mock(OperatorNameViewController.Factory.class);
when(mOperatorNameViewControllerFactory.create(any()))
.thenReturn(mOperatorNameViewController);
+ mSecureSettings = mock(SecureSettings.class);
setUpNotificationIconAreaController();
return new CollapsedStatusBarFragment(
@@ -334,7 +377,9 @@
new LogBuffer("TEST", 1, 1, mock(LogcatEchoTracker.class)),
new DisableFlagsLogger()
),
- mOperatorNameViewControllerFactory);
+ mOperatorNameViewControllerFactory,
+ mSecureSettings,
+ mExecutor);
}
private void setUpDaggerComponent() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index 807664d..1c48eca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -105,6 +105,7 @@
val notificationCollection = mock(CommonNotifCollection::class.java)
controller = OngoingCallController(
+ context,
notificationCollection,
mockOngoingCallFlags,
clock,
@@ -204,17 +205,15 @@
/** Regression test for b/194731244. */
@Test
- fun onEntryUpdated_calledManyTimes_uidObserverUnregisteredManyTimes() {
- val numCalls = 4
-
- for (i in 0 until numCalls) {
+ fun onEntryUpdated_calledManyTimes_uidObserverOnlyRegisteredOnce() {
+ for (i in 0 until 4) {
// Re-create the notification each time so that it's considered a different object and
- // observers will get re-registered (and hopefully unregistered).
+ // will re-trigger the whole flow.
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
}
- // There should be 1 observer still registered, so we should unregister n-1 times.
- verify(mockIActivityManager, times(numCalls - 1)).unregisterUidObserver(any())
+ verify(mockIActivityManager, times(1))
+ .registerUidObserver(any(), any(), any(), any())
}
/** Regression test for b/216248574. */
@@ -238,6 +237,18 @@
notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
}
+ /** Regression test for b/216248574. */
+ @Test
+ fun entryUpdated_packageNameProvidedToActivityManager() {
+ notifCollectionListener.onEntryUpdated(createOngoingCallNotifEntry())
+
+ val packageNameCaptor = ArgumentCaptor.forClass(String::class.java)
+ verify(mockIActivityManager).registerUidObserver(
+ any(), any(), any(), packageNameCaptor.capture()
+ )
+ assertThat(packageNameCaptor.value).isNotNull()
+ }
+
/**
* If a call notification is never added before #onEntryRemoved is called, then the listener
* should never be notified.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt
new file mode 100644
index 0000000..3a5d9ee
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt
@@ -0,0 +1,31 @@
+package com.android.systemui.statusbar.policy
+
+import android.content.res.Configuration
+
+/** Fake implementation of [ConfigurationController] for tests. */
+class FakeConfigurationController : ConfigurationController {
+
+ private var listener: ConfigurationController.ConfigurationListener? = null
+
+ override fun addCallback(listener: ConfigurationController.ConfigurationListener) {
+ this.listener = listener
+ }
+
+ override fun removeCallback(listener: ConfigurationController.ConfigurationListener) {
+ this.listener = null
+ }
+
+ override fun onConfigurationChanged(newConfiguration: Configuration?) {
+ listener?.onConfigChanged(newConfiguration)
+ }
+
+ override fun notifyThemeChanged() {
+ listener?.onThemeChanged()
+ }
+
+ fun notifyConfigurationChanged() {
+ onConfigurationChanged(newConfiguration = null)
+ }
+
+ override fun isLayoutRtl(): Boolean = false
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/view/ViewUtilTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/view/ViewUtilTest.kt
new file mode 100644
index 0000000..dead159
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/view/ViewUtilTest.kt
@@ -0,0 +1,72 @@
+package com.android.systemui.util.view
+
+import android.view.View
+import android.widget.TextView
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.`when`
+
+@SmallTest
+class ViewUtilTest : SysuiTestCase() {
+ private val viewUtil = ViewUtil()
+ private lateinit var view: View
+
+ @Before
+ fun setUp() {
+ view = TextView(context)
+ view.setLeftTopRightBottom(VIEW_LEFT, VIEW_TOP, VIEW_RIGHT, VIEW_BOTTOM)
+
+ view = spy(view)
+ val location = IntArray(2)
+ location[0] = VIEW_LEFT
+ location[1] = VIEW_TOP
+ `when`(view.locationOnScreen).thenReturn(location)
+ }
+
+ @Test
+ fun touchIsWithinView_inBounds_returnsTrue() {
+ assertThat(viewUtil.touchIsWithinView(view, VIEW_LEFT + 1f, VIEW_TOP + 1f)).isTrue()
+ }
+
+ @Test
+ fun touchIsWithinView_onTopLeftCorner_returnsTrue() {
+ assertThat(viewUtil.touchIsWithinView(
+ view, VIEW_LEFT.toFloat(), VIEW_TOP.toFloat())
+ ).isTrue()
+ }
+
+ @Test
+ fun touchIsWithinView_onBottomRightCorner_returnsTrue() {
+ assertThat(viewUtil.touchIsWithinView(view, VIEW_RIGHT.toFloat(), VIEW_BOTTOM.toFloat()))
+ .isTrue()
+ }
+
+ @Test
+ fun touchIsWithinView_xTooSmall_returnsFalse() {
+ assertThat(viewUtil.touchIsWithinView(view, VIEW_LEFT - 1f, VIEW_TOP + 1f)).isFalse()
+ }
+
+ @Test
+ fun touchIsWithinView_xTooLarge_returnsFalse() {
+ assertThat(viewUtil.touchIsWithinView(view, VIEW_RIGHT + 1f, VIEW_TOP + 1f)).isFalse()
+ }
+
+ @Test
+ fun touchIsWithinView_yTooSmall_returnsFalse() {
+ assertThat(viewUtil.touchIsWithinView(view, VIEW_LEFT + 1f, VIEW_TOP - 1f)).isFalse()
+ }
+
+ @Test
+ fun touchIsWithinView_yTooLarge_returnsFalse() {
+ assertThat(viewUtil.touchIsWithinView(view, VIEW_LEFT + 1f, VIEW_BOTTOM + 1f)).isFalse()
+ }
+}
+
+private const val VIEW_LEFT = 30
+private const val VIEW_RIGHT = 100
+private const val VIEW_TOP = 40
+private const val VIEW_BOTTOM = 100
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
index 8ad6271..2be67ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java
@@ -43,6 +43,10 @@
}
@Override
+ public void refreshIconGroup(IconManager iconManager) {
+ }
+
+ @Override
public void setExternalIcon(String slot) {
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 593b97e..82880fe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -1221,7 +1221,7 @@
final Context userContext = setUpContextWithPackageManager(workPkg,
mock(ApplicationInfo.class));
- // If things are working correctly, StatusBar.getPackageManagerForUser will call this
+ // If things are working correctly, CentralSurfaces.getPackageManagerForUser will call this
when(context.createPackageContextAsUser(eq(workPkg), anyInt(), eq(workUser)))
.thenReturn(userContext);
diff --git a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerService.java b/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerService.java
index daead0a..b1f572d 100644
--- a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerService.java
+++ b/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerService.java
@@ -43,6 +43,8 @@
import com.android.server.wm.ActivityTaskManagerInternal;
import java.io.FileDescriptor;
+import java.util.ArrayList;
+import java.util.List;
import java.util.function.Consumer;
/**
@@ -62,7 +64,7 @@
public CloudSearchManagerService(Context context) {
super(context, new FrameworkResourcesServiceNameResolver(context,
- R.string.config_defaultCloudSearchService), null,
+ R.array.config_defaultCloudSearchServices, true), null,
PACKAGE_UPDATE_POLICY_NO_REFRESH | PACKAGE_RESTART_POLICY_NO_REFRESH);
mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
mContext = context;
@@ -70,7 +72,25 @@
@Override
protected CloudSearchPerUserService newServiceLocked(int resolvedUserId, boolean disabled) {
- return new CloudSearchPerUserService(this, mLock, resolvedUserId);
+ return new CloudSearchPerUserService(this, mLock, resolvedUserId, "");
+ }
+
+ @Override
+ protected List<CloudSearchPerUserService> newServiceListLocked(int resolvedUserId,
+ boolean disabled, String[] serviceNames) {
+ if (serviceNames == null) {
+ return new ArrayList<>();
+ }
+ List<CloudSearchPerUserService> serviceList =
+ new ArrayList<>(serviceNames.length);
+ for (int i = 0; i < serviceNames.length; i++) {
+ if (serviceNames[i] == null) {
+ continue;
+ }
+ serviceList.add(new CloudSearchPerUserService(this, mLock, resolvedUserId,
+ serviceNames[i]));
+ }
+ return serviceList;
}
@Override
@@ -111,19 +131,28 @@
@NonNull ICloudSearchManagerCallback callBack) {
searchRequest.setSource(
mContext.getPackageManager().getNameForUid(Binder.getCallingUid()));
- runForUserLocked("search", searchRequest.getRequestId(), (service) ->
- service.onSearchLocked(searchRequest, callBack));
+ runForUser("search", (service) -> {
+ synchronized (service.mLock) {
+ service.onSearchLocked(searchRequest, callBack);
+ }
+ });
}
@Override
public void returnResults(IBinder token, String requestId, SearchResponse response) {
- runForUserLocked("returnResults", requestId, (service) ->
- service.onReturnResultsLocked(token, requestId, response));
+ runForUser("returnResults", (service) -> {
+ synchronized (service.mLock) {
+ service.onReturnResultsLocked(token, requestId, response);
+ }
+ });
}
public void destroy(@NonNull SearchRequest searchRequest) {
- runForUserLocked("destroyCloudSearchSession", searchRequest.getRequestId(),
- (service) -> service.onDestroyLocked(searchRequest.getRequestId()));
+ runForUser("destroyCloudSearchSession", (service) -> {
+ synchronized (service.mLock) {
+ service.onDestroyLocked(searchRequest.getRequestId());
+ }
+ });
}
public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
@@ -134,8 +163,7 @@
.exec(this, in, out, err, args, callback, resultReceiver);
}
- private void runForUserLocked(@NonNull final String func,
- @NonNull final String requestId,
+ private void runForUser(@NonNull final String func,
@NonNull final Consumer<CloudSearchPerUserService> c) {
ActivityManagerInternal am = LocalServices.getService(ActivityManagerInternal.class);
final int userId = am.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
@@ -143,7 +171,7 @@
null, null);
if (DEBUG) {
- Slog.d(TAG, "runForUserLocked:" + func + " from pid=" + Binder.getCallingPid()
+ Slog.d(TAG, "runForUser:" + func + " from pid=" + Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid());
}
Context ctx = getContext();
@@ -160,8 +188,11 @@
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- final CloudSearchPerUserService service = getServiceForUserLocked(userId);
- c.accept(service);
+ final List<CloudSearchPerUserService> services =
+ getServiceListForUserLocked(userId);
+ for (int i = 0; i < services.size(); i++) {
+ c.accept(services.get(i));
+ }
}
} finally {
Binder.restoreCallingIdentity(origId);
diff --git a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerServiceShellCommand.java b/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerServiceShellCommand.java
index 51f5fd9..c64982d 100644
--- a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerServiceShellCommand.java
+++ b/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerServiceShellCommand.java
@@ -54,7 +54,12 @@
return 0;
}
final int duration = Integer.parseInt(getNextArgRequired());
- mService.setTemporaryService(userId, serviceName, duration);
+ String[] services = serviceName.split(";");
+ if (services.length == 0) {
+ return 0;
+ } else {
+ mService.setTemporaryServices(userId, services, duration);
+ }
pw.println("CloudSearchService temporarily set to " + serviceName
+ " for " + duration + "ms");
break;
diff --git a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchPerUserService.java b/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchPerUserService.java
index 32d66af..116c739 100644
--- a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchPerUserService.java
+++ b/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchPerUserService.java
@@ -49,6 +49,8 @@
@GuardedBy("mLock")
private final CircularQueue<String, CloudSearchCallbackInfo> mCallbackQueue =
new CircularQueue<>(QUEUE_SIZE);
+ private final String mServiceName;
+ private final ComponentName mRemoteComponentName;
@Nullable
@GuardedBy("mLock")
private RemoteCloudSearchService mRemoteService;
@@ -60,8 +62,10 @@
private boolean mZombie;
protected CloudSearchPerUserService(CloudSearchManagerService master,
- Object lock, int userId) {
+ Object lock, int userId, String serviceName) {
super(master, lock, userId);
+ mServiceName = serviceName;
+ mRemoteComponentName = ComponentName.unflattenFromString(mServiceName);
}
@Override // from PerUserSystemService
@@ -108,7 +112,7 @@
? searchRequest.getSearchConstraints().getString(
SearchRequest.CONSTRAINT_SEARCH_PROVIDER_FILTER) : "";
- String remoteServicePackageName = getServiceComponentName().getPackageName();
+ String remoteServicePackageName = mRemoteComponentName.getPackageName();
// By default, all providers are marked as wanted.
boolean wantedProvider = true;
if (filterList.length() > 0) {
@@ -150,11 +154,19 @@
/**
* Used to return results back to the clients.
*/
+ @GuardedBy("mLock")
public void onReturnResultsLocked(@NonNull IBinder token,
@NonNull String requestId,
@NonNull SearchResponse response) {
+ if (mRemoteService == null) {
+ return;
+ }
+ ICloudSearchService serviceInterface = mRemoteService.getServiceInterface();
+ if (serviceInterface == null || token != serviceInterface.asBinder()) {
+ return;
+ }
if (mCallbackQueue.containsKey(requestId)) {
- response.setSource(mRemoteService.getComponentName().getPackageName());
+ response.setSource(mServiceName);
final CloudSearchCallbackInfo sessionInfo = mCallbackQueue.getElement(requestId);
try {
if (response.getStatusCode() == SearchResponse.SEARCH_STATUS_OK) {
@@ -163,6 +175,10 @@
sessionInfo.mCallback.onSearchFailed(response);
}
} catch (RemoteException e) {
+ if (mMaster.debug) {
+ Slog.e(TAG, "Exception in posting results");
+ e.printStackTrace();
+ }
onDestroyLocked(requestId);
}
}
@@ -297,7 +313,7 @@
@Nullable
private RemoteCloudSearchService getRemoteServiceLocked() {
if (mRemoteService == null) {
- final String serviceName = getComponentNameLocked();
+ final String serviceName = getComponentNameForMultipleLocked(mServiceName);
if (serviceName == null) {
if (mMaster.verbose) {
Slog.v(TAG, "getRemoteServiceLocked(): not set");
diff --git a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
index 2b7b977..bf8b18c 100644
--- a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
+++ b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java
@@ -50,6 +50,7 @@
import android.os.Parcel;
import android.os.RemoteException;
import android.os.ResultReceiver;
+import android.os.UserHandle;
import android.util.PackageUtils;
import android.util.Slog;
@@ -201,8 +202,10 @@
// requests at the same time.
// If the application already has a pending association request, that PendingIntent
// will be cancelled.
- pendingIntent = PendingIntent.getActivity(mContext, /*requestCode */ packageUid, intent,
- FLAG_ONE_SHOT | FLAG_CANCEL_CURRENT | FLAG_IMMUTABLE);
+ pendingIntent = PendingIntent.getActivityAsUser(
+ mContext, /*requestCode */ packageUid, intent,
+ FLAG_ONE_SHOT | FLAG_CANCEL_CURRENT | FLAG_IMMUTABLE,
+ /* options= */ null, UserHandle.CURRENT);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/companion/java/com/android/server/companion/virtual/CameraAccessController.java b/services/companion/java/com/android/server/companion/virtual/CameraAccessController.java
index 2c42c91..adc8459 100644
--- a/services/companion/java/com/android/server/companion/virtual/CameraAccessController.java
+++ b/services/companion/java/com/android/server/companion/virtual/CameraAccessController.java
@@ -33,17 +33,19 @@
/**
* Handles blocking access to the camera for apps running on virtual devices.
*/
-class CameraAccessController extends CameraManager.AvailabilityCallback {
+class CameraAccessController extends CameraManager.AvailabilityCallback implements AutoCloseable {
private static final String TAG = "CameraAccessController";
private final Object mLock = new Object();
private final Context mContext;
- private VirtualDeviceManagerInternal mVirtualDeviceManagerInternal;
- CameraAccessBlockedCallback mBlockedCallback;
- private CameraManager mCameraManager;
- private boolean mListeningForCameraEvents;
- private PackageManager mPackageManager;
+ private final VirtualDeviceManagerInternal mVirtualDeviceManagerInternal;
+ private final CameraAccessBlockedCallback mBlockedCallback;
+ private final CameraManager mCameraManager;
+ private final PackageManager mPackageManager;
+
+ @GuardedBy("mLock")
+ private int mObserverCount = 0;
@GuardedBy("mLock")
private ArrayMap<String, InjectionSessionData> mPackageToSessionData = new ArrayMap<>();
@@ -77,23 +79,38 @@
*/
public void startObservingIfNeeded() {
synchronized (mLock) {
- if (!mListeningForCameraEvents) {
+ if (mObserverCount == 0) {
mCameraManager.registerAvailabilityCallback(mContext.getMainExecutor(), this);
- mListeningForCameraEvents = true;
}
+ mObserverCount++;
}
}
/**
* Stop watching for camera access.
*/
- public void stopObserving() {
+ public void stopObservingIfNeeded() {
synchronized (mLock) {
- mCameraManager.unregisterAvailabilityCallback(this);
- mListeningForCameraEvents = false;
+ mObserverCount--;
+ if (mObserverCount <= 0) {
+ close();
+ }
}
}
+
+ @Override
+ public void close() {
+ synchronized (mLock) {
+ if (mObserverCount < 0) {
+ Slog.wtf(TAG, "Unexpected negative mObserverCount: " + mObserverCount);
+ } else if (mObserverCount > 0) {
+ Slog.w(TAG, "Unexpected close with observers remaining: " + mObserverCount);
+ }
+ }
+ mCameraManager.unregisterAvailabilityCallback(this);
+ }
+
@Override
public void onCameraOpened(@NonNull String cameraId, @NonNull String packageName) {
synchronized (mLock) {
diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
index bc1f28d..b991ba8 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -24,6 +24,8 @@
import android.annotation.Nullable;
import android.app.compat.CompatChanges;
import android.companion.virtual.VirtualDeviceManager.ActivityListener;
+import android.companion.virtual.VirtualDeviceParams;
+import android.companion.virtual.VirtualDeviceParams.ActivityPolicy;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.content.ComponentName;
@@ -77,7 +79,9 @@
@Nullable
private final ArraySet<ComponentName> mBlockedActivities;
private final Object mGenericWindowPolicyControllerLock = new Object();
- private Consumer<ActivityInfo> mActivityBlockedCallback;
+ @ActivityPolicy
+ private final int mDefaultActivityPolicy;
+ private final Consumer<ActivityInfo> mActivityBlockedCallback;
@NonNull
@GuardedBy("mGenericWindowPolicyControllerLock")
@@ -95,18 +99,30 @@
* @param windowFlags The window flags that this controller is interested in.
* @param systemWindowFlags The system window flags that this controller is interested in.
* @param allowedUsers The set of users that are allowed to stream in this display.
+ * @param allowedActivities The set of activities explicitly allowed to stream on this device.
+ * Used only if the {@code activityPolicy} is
+ * {@link VirtualDeviceParams#ACTIVITY_POLICY_DEFAULT_BLOCKED}.
+ * @param blockedActivities The set of activities explicitly blocked from streaming on this
+ * device. Used only if the {@code activityPolicy} is
+ * {@link VirtualDeviceParams#ACTIVITY_POLICY_DEFAULT_ALLOWED}
+ * @param defaultActivityPolicy Whether activities are default allowed to be displayed or
+ * blocked.
* @param activityListener Activity listener to listen for activity changes. The display ID
* is not populated in this callback and is always {@link Display#INVALID_DISPLAY}.
+ * @param activityBlockedCallback Callback that is called when an activity is blocked from
+ * launching.
*/
public GenericWindowPolicyController(int windowFlags, int systemWindowFlags,
@NonNull ArraySet<UserHandle> allowedUsers,
- @Nullable Set<ComponentName> allowedActivities,
- @Nullable Set<ComponentName> blockedActivities,
+ @NonNull Set<ComponentName> allowedActivities,
+ @NonNull Set<ComponentName> blockedActivities,
+ @ActivityPolicy int defaultActivityPolicy,
@NonNull ActivityListener activityListener,
@NonNull Consumer<ActivityInfo> activityBlockedCallback) {
mAllowedUsers = allowedUsers;
- mAllowedActivities = allowedActivities == null ? null : new ArraySet<>(allowedActivities);
- mBlockedActivities = blockedActivities == null ? null : new ArraySet<>(blockedActivities);
+ mAllowedActivities = new ArraySet<>(allowedActivities);
+ mBlockedActivities = new ArraySet<>(blockedActivities);
+ mDefaultActivityPolicy = defaultActivityPolicy;
mActivityBlockedCallback = activityBlockedCallback;
setInterestedWindowFlags(windowFlags, systemWindowFlags);
mActivityListener = activityListener;
@@ -191,11 +207,13 @@
Slog.d(TAG, "Virtual device activity not allowed from user " + activityUser);
return false;
}
- if (mBlockedActivities != null && mBlockedActivities.contains(activityComponent)) {
+ if (mDefaultActivityPolicy == VirtualDeviceParams.ACTIVITY_POLICY_DEFAULT_ALLOWED
+ && mBlockedActivities.contains(activityComponent)) {
Slog.d(TAG, "Virtual device blocking launch of " + activityComponent);
return false;
}
- if (mAllowedActivities != null && !mAllowedActivities.contains(activityComponent)) {
+ if (mDefaultActivityPolicy == VirtualDeviceParams.ACTIVITY_POLICY_DEFAULT_BLOCKED
+ && !mAllowedActivities.contains(activityComponent)) {
Slog.d(TAG, activityComponent + " is not in the allowed list.");
return false;
}
diff --git a/services/companion/java/com/android/server/companion/virtual/PermissionUtils.java b/services/companion/java/com/android/server/companion/virtual/PermissionUtils.java
index c397ea2..77b880f 100644
--- a/services/companion/java/com/android/server/companion/virtual/PermissionUtils.java
+++ b/services/companion/java/com/android/server/companion/virtual/PermissionUtils.java
@@ -18,6 +18,8 @@
import android.content.Context;
import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.UserHandle;
import android.util.Slog;
/**
@@ -32,13 +34,15 @@
*
* @param context the context
* @param callingPackage the calling application package name
- * @param callingUid the calling application uid
- * @return {@code true} if the package name matches the calling app uid, {@code false} otherwise
+ * @return {@code true} if the package name matches {@link Binder#getCallingUid()}, or
+ * {@code false} otherwise
*/
- public static boolean validatePackageName(Context context, String callingPackage,
- int callingUid) {
+ public static boolean validateCallingPackageName(Context context, String callingPackage) {
+ final int callingUid = Binder.getCallingUid();
+ final long token = Binder.clearCallingIdentity();
try {
- int packageUid = context.getPackageManager().getPackageUid(callingPackage, 0);
+ int packageUid = context.getPackageManager()
+ .getPackageUidAsUser(callingPackage, UserHandle.getUserId(callingUid));
if (packageUid != callingUid) {
Slog.e(LOG_TAG, "validatePackageName: App with package name " + callingPackage
+ " is UID " + packageUid + " but caller is " + callingUid);
@@ -48,6 +52,8 @@
Slog.e(LOG_TAG, "validatePackageName: App with package name " + callingPackage
+ " does not exist");
return false;
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
return true;
}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index c0a904f..b05a7db 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -140,7 +140,8 @@
int ownerUid, InputController inputController, OnDeviceCloseListener listener,
PendingTrampolineCallback pendingTrampolineCallback,
IVirtualDeviceActivityListener activityListener, VirtualDeviceParams params) {
- mContext = context;
+ UserHandle ownerUserHandle = UserHandle.getUserHandleForUid(ownerUid);
+ mContext = context.createContextAsUser(ownerUserHandle, 0);
mAssociationInfo = associationInfo;
mPendingTrampolineCallback = pendingTrampolineCallback;
mActivityListener = activityListener;
@@ -505,6 +506,7 @@
getAllowedUserHandles(),
mParams.getAllowedActivities(),
mParams.getBlockedActivities(),
+ mParams.getDefaultActivityPolicy(),
createListenerAdapter(displayId),
activityInfo -> onActivityBlocked(displayId, activityInfo));
mWindowPolicyControllers.put(displayId, dwpc);
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index c7d8daa..9f252d7 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -36,6 +36,7 @@
import android.os.Looper;
import android.os.Parcel;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.ExceptionUtils;
import android.util.Slog;
import android.util.SparseArray;
@@ -66,7 +67,12 @@
private VirtualDeviceManagerInternal mLocalService;
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final PendingTrampolineMap mPendingTrampolines = new PendingTrampolineMap(mHandler);
- private final CameraAccessController mCameraAccessController;
+ /**
+ * Mapping from user IDs to CameraAccessControllers.
+ */
+ @GuardedBy("mVirtualDeviceManagerLock")
+ private final SparseArray<CameraAccessController> mCameraAccessControllers =
+ new SparseArray<>();
/**
* Mapping from CDM association IDs to virtual devices. Only one virtual device is allowed for
@@ -94,8 +100,6 @@
super(context);
mImpl = new VirtualDeviceManagerImpl();
mLocalService = new LocalService();
- mCameraAccessController = new CameraAccessController(getContext(), mLocalService,
- this::onCameraAccessBlocked);
}
private final ActivityInterceptorCallback mActivityInterceptorCallback =
@@ -144,16 +148,19 @@
@Override
public void onUserStarting(@NonNull TargetUser user) {
super.onUserStarting(user);
+ Context userContext = getContext().createContextAsUser(user.getUserHandle(), 0);
synchronized (mVirtualDeviceManagerLock) {
- final CompanionDeviceManager cdm = getContext()
- .createContextAsUser(user.getUserHandle(), 0)
- .getSystemService(CompanionDeviceManager.class);
+ final CompanionDeviceManager cdm =
+ userContext.getSystemService(CompanionDeviceManager.class);
final int userId = user.getUserIdentifier();
mAllAssociations.put(userId, cdm.getAllAssociations());
OnAssociationsChangedListener listener =
associations -> mAllAssociations.put(userId, associations);
mOnAssociationsChangedListeners.put(userId, listener);
cdm.addOnAssociationsChangedListener(Runnable::run, listener);
+ CameraAccessController cameraAccessController = new CameraAccessController(
+ userContext, mLocalService, this::onCameraAccessBlocked);
+ mCameraAccessControllers.put(user.getUserIdentifier(), cameraAccessController);
}
}
@@ -171,6 +178,14 @@
cdm.removeOnAssociationsChangedListener(listener);
mOnAssociationsChangedListeners.remove(userId);
}
+ CameraAccessController cameraAccessController = mCameraAccessControllers.get(
+ user.getUserIdentifier());
+ if (cameraAccessController != null) {
+ cameraAccessController.close();
+ mCameraAccessControllers.remove(user.getUserIdentifier());
+ } else {
+ Slog.w(TAG, "Cannot unregister cameraAccessController for user " + user);
+ }
}
}
@@ -198,7 +213,7 @@
android.Manifest.permission.CREATE_VIRTUAL_DEVICE,
"createVirtualDevice");
final int callingUid = getCallingUid();
- if (!PermissionUtils.validatePackageName(getContext(), packageName, callingUid)) {
+ if (!PermissionUtils.validateCallingPackageName(getContext(), packageName)) {
throw new SecurityException(
"Package name " + packageName + " does not belong to calling uid "
+ callingUid);
@@ -213,6 +228,9 @@
"Virtual device for association ID " + associationId
+ " already exists");
}
+ final int userId = UserHandle.getUserId(callingUid);
+ final CameraAccessController cameraAccessController =
+ mCameraAccessControllers.get(userId);
VirtualDeviceImpl virtualDevice = new VirtualDeviceImpl(getContext(),
associationInfo, token, callingUid,
new VirtualDeviceImpl.OnDeviceCloseListener() {
@@ -220,14 +238,21 @@
public void onClose(int associationId) {
synchronized (mVirtualDeviceManagerLock) {
mVirtualDevices.remove(associationId);
- if (mVirtualDevices.size() == 0) {
- mCameraAccessController.stopObserving();
+ if (cameraAccessController != null) {
+ cameraAccessController.stopObservingIfNeeded();
+ } else {
+ Slog.w(TAG, "cameraAccessController not found for user "
+ + userId);
}
}
}
},
this, activityListener, params);
- mCameraAccessController.startObservingIfNeeded();
+ if (cameraAccessController != null) {
+ cameraAccessController.startObservingIfNeeded();
+ } else {
+ Slog.w(TAG, "cameraAccessController not found for user " + userId);
+ }
mVirtualDevices.put(associationInfo.getId(), virtualDevice);
return virtualDevice;
}
diff --git a/services/core/java/com/android/server/NetworkTimeUpdateService.java b/services/core/java/com/android/server/NetworkTimeUpdateService.java
index ff2308c..a0f239d 100644
--- a/services/core/java/com/android/server/NetworkTimeUpdateService.java
+++ b/services/core/java/com/android/server/NetworkTimeUpdateService.java
@@ -16,6 +16,7 @@
package com.android.server;
+import android.annotation.NonNull;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.timedetector.NetworkTimeSuggestion;
@@ -38,6 +39,7 @@
import android.os.SystemClock;
import android.os.TimestampedValue;
import android.provider.Settings;
+import android.util.LocalLog;
import android.util.Log;
import android.util.NtpTrustedTime;
import android.util.TimeUtils;
@@ -95,6 +97,13 @@
// connection to happen.
private int mTryAgainCounter;
+ /**
+ * A log that records the decisions to fetch a network time update.
+ * This is logged in bug reports to assist with debugging issues with network time suggestions.
+ */
+ @NonNull
+ private final LocalLog mLocalLog = new LocalLog(30, false /* useLocalTimestamps */);
+
public NetworkTimeUpdateService(Context context) {
mContext = context;
mTime = NtpTrustedTime.getInstance(context);
@@ -155,15 +164,29 @@
}
private void onPollNetworkTimeUnderWakeLock(int event) {
+ long currentElapsedRealtimeMillis = SystemClock.elapsedRealtime();
// Force an NTP fix when outdated
NtpTrustedTime.TimeResult cachedNtpResult = mTime.getCachedTimeResult();
- if (cachedNtpResult == null || cachedNtpResult.getAgeMillis() >= mPollingIntervalMs) {
+ if (cachedNtpResult == null || cachedNtpResult.getAgeMillis(currentElapsedRealtimeMillis)
+ >= mPollingIntervalMs) {
if (DBG) Log.d(TAG, "Stale NTP fix; forcing refresh");
- mTime.forceRefresh();
+ boolean isSuccessful = mTime.forceRefresh();
+ if (!isSuccessful) {
+ String logMsg = "forceRefresh() returned false: cachedNtpResult=" + cachedNtpResult
+ + ", currentElapsedRealtimeMillis=" + currentElapsedRealtimeMillis;
+
+ if (DBG) {
+ Log.d(TAG, logMsg);
+ }
+ mLocalLog.log(logMsg);
+ }
+
cachedNtpResult = mTime.getCachedTimeResult();
}
- if (cachedNtpResult != null && cachedNtpResult.getAgeMillis() < mPollingIntervalMs) {
+ if (cachedNtpResult != null
+ && cachedNtpResult.getAgeMillis(currentElapsedRealtimeMillis)
+ < mPollingIntervalMs) {
// Obtained fresh fix; schedule next normal update
resetAlarm(mPollingIntervalMs);
@@ -180,6 +203,11 @@
resetAlarm(mPollingIntervalShorterMs);
} else {
// Try much later
+ String logMsg = "mTryAgainTimesMax exceeded, cachedNtpResult=" + cachedNtpResult;
+ if (DBG) {
+ Log.d(TAG, logMsg);
+ }
+ mLocalLog.log(logMsg);
mTryAgainCounter = 0;
resetAlarm(mPollingIntervalMs);
}
@@ -285,6 +313,8 @@
if (ntpResult != null) {
pw.println("NTP result age: " + ntpResult.getAgeMillis());
}
+ pw.println("Local logs:");
+ mLocalLog.dump(fd, pw, args);
pw.println();
}
}
diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java
index 4d5384c..d07590f 100644
--- a/services/core/java/com/android/server/am/AppRestrictionController.java
+++ b/services/core/java/com/android/server/am/AppRestrictionController.java
@@ -191,7 +191,7 @@
// No lock is needed, as it's immutable after initialization in constructor.
private final ArrayList<BaseAppStateTracker> mAppStateTrackers = new ArrayList<>();
- @GuardedBy("mLock")
+ @GuardedBy("mSettingsLock")
private final RestrictionSettings mRestrictionSettings = new RestrictionSettings();
private final CopyOnWriteArraySet<AppBackgroundRestrictionListener> mRestrictionListeners =
@@ -201,7 +201,7 @@
* A mapping between the UID/Pkg and its pending work which should be triggered on inactive;
* an active UID/pkg pair should have an entry here, although its pending work could be null.
*/
- @GuardedBy("mLock")
+ @GuardedBy("mSettingsLock")
private final SparseArrayMap<String, Runnable> mActiveUids = new SparseArrayMap<>();
// No lock is needed as it's accessed in bg handler thread only.
@@ -218,6 +218,7 @@
private int[] mDeviceIdleExceptIdleAllowlist = new int[0]; // No lock is needed.
private final Object mLock = new Object();
+ private final Object mSettingsLock = new Object();
private final Injector mInjector;
private final NotificationHelper mNotificationHelper;
@@ -344,7 +345,7 @@
* {@link android.app.ActivityManager.RESTRICTION_LEVEL_*}.
*/
final class RestrictionSettings {
- @GuardedBy("mLock")
+ @GuardedBy("mSettingsLock")
final SparseArrayMap<String, PkgSettings> mRestrictionLevels = new SparseArrayMap();
final class PkgSettings {
@@ -365,6 +366,7 @@
mCurrentRestrictionLevel = mLastRestrictionLevel = RESTRICTION_LEVEL_UNKNOWN;
}
+ @GuardedBy("mSettingsLock")
@RestrictionLevel int update(@RestrictionLevel int level, int reason, int subReason) {
if (level != mCurrentRestrictionLevel) {
mLastRestrictionLevel = mCurrentRestrictionLevel;
@@ -378,6 +380,7 @@
}
@Override
+ @GuardedBy("mSettingsLock")
public String toString() {
final StringBuilder sb = new StringBuilder(128);
sb.append("RestrictionLevel{");
@@ -396,21 +399,23 @@
}
void dump(PrintWriter pw, @ElapsedRealtimeLong long nowElapsed) {
- pw.print(toString());
- if (mLastRestrictionLevel != RESTRICTION_LEVEL_UNKNOWN) {
- pw.print('/');
- pw.print(ActivityManager.restrictionLevelToName(mLastRestrictionLevel));
- }
- pw.print(" levelChange=");
- TimeUtils.formatDuration(mLevelChangeTimeElapsed - nowElapsed, pw);
- if (mLastNotificationShownTimeElapsed != null) {
- for (int i = 0; i < mLastNotificationShownTimeElapsed.length; i++) {
- if (mLastNotificationShownTimeElapsed[i] > 0) {
- pw.print(" lastNoti(");
- pw.print(mNotificationHelper.notificationTypeToString(i));
- pw.print(")=");
- TimeUtils.formatDuration(
- mLastNotificationShownTimeElapsed[i] - nowElapsed, pw);
+ synchronized (mSettingsLock) {
+ pw.print(toString());
+ if (mLastRestrictionLevel != RESTRICTION_LEVEL_UNKNOWN) {
+ pw.print('/');
+ pw.print(ActivityManager.restrictionLevelToName(mLastRestrictionLevel));
+ }
+ pw.print(" levelChange=");
+ TimeUtils.formatDuration(mLevelChangeTimeElapsed - nowElapsed, pw);
+ if (mLastNotificationShownTimeElapsed != null) {
+ for (int i = 0; i < mLastNotificationShownTimeElapsed.length; i++) {
+ if (mLastNotificationShownTimeElapsed[i] > 0) {
+ pw.print(" lastNoti(");
+ pw.print(mNotificationHelper.notificationTypeToString(i));
+ pw.print(")=");
+ TimeUtils.formatDuration(
+ mLastNotificationShownTimeElapsed[i] - nowElapsed, pw);
+ }
}
}
}
@@ -426,18 +431,22 @@
return mUid;
}
+ @GuardedBy("mSettingsLock")
@RestrictionLevel int getCurrentRestrictionLevel() {
return mCurrentRestrictionLevel;
}
+ @GuardedBy("mSettingsLock")
@RestrictionLevel int getLastRestrictionLevel() {
return mLastRestrictionLevel;
}
+ @GuardedBy("mSettingsLock")
int getReason() {
return mReason;
}
+ @GuardedBy("mSettingsLock")
@ElapsedRealtimeLong long getLastNotificationTime(
@NotificationHelper.NotificationType int notificationType) {
if (mLastNotificationShownTimeElapsed == null) {
@@ -446,6 +455,7 @@
return mLastNotificationShownTimeElapsed[notificationType];
}
+ @GuardedBy("mSettingsLock")
void setLastNotificationTime(@NotificationHelper.NotificationType int notificationType,
@ElapsedRealtimeLong long timestamp) {
if (mLastNotificationShownTimeElapsed == null) {
@@ -455,6 +465,7 @@
mLastNotificationShownTimeElapsed[notificationType] = timestamp;
}
+ @GuardedBy("mSettingsLock")
int getNotificationId(@NotificationHelper.NotificationType int notificationType) {
if (mNotificationId == null) {
return 0;
@@ -462,6 +473,7 @@
return mNotificationId[notificationType];
}
+ @GuardedBy("mSettingsLock")
void setNotificationId(@NotificationHelper.NotificationType int notificationType,
int notificationId) {
if (mNotificationId == null) {
@@ -478,7 +490,7 @@
*/
@RestrictionLevel int update(String packageName, int uid, @RestrictionLevel int level,
int reason, int subReason) {
- synchronized (mLock) {
+ synchronized (mSettingsLock) {
PkgSettings settings = getRestrictionSettingsLocked(uid, packageName);
if (settings == null) {
settings = new PkgSettings(packageName, uid);
@@ -492,7 +504,7 @@
* @return The reason of why it's in this level.
*/
int getReason(String packageName, int uid) {
- synchronized (mLock) {
+ synchronized (mSettingsLock) {
final PkgSettings settings = mRestrictionLevels.get(uid, packageName);
return settings != null ? settings.getReason()
: (REASON_MAIN_DEFAULT | REASON_SUB_DEFAULT_UNDEFINED);
@@ -500,7 +512,7 @@
}
@RestrictionLevel int getRestrictionLevel(int uid) {
- synchronized (mLock) {
+ synchronized (mSettingsLock) {
final int uidKeyIndex = mRestrictionLevels.indexOfKey(uid);
if (uidKeyIndex < 0) {
return RESTRICTION_LEVEL_UNKNOWN;
@@ -522,7 +534,7 @@
}
@RestrictionLevel int getRestrictionLevel(int uid, String packageName) {
- synchronized (mLock) {
+ synchronized (mSettingsLock) {
final PkgSettings settings = getRestrictionSettingsLocked(uid, packageName);
return settings == null
? getRestrictionLevel(uid) : settings.getCurrentRestrictionLevel();
@@ -536,14 +548,14 @@
}
private @RestrictionLevel int getLastRestrictionLevel(int uid, String packageName) {
- synchronized (mLock) {
+ synchronized (mSettingsLock) {
final PkgSettings settings = mRestrictionLevels.get(uid, packageName);
return settings == null
- ? RESTRICTION_LEVEL_UNKNOWN : settings.mLastRestrictionLevel;
+ ? RESTRICTION_LEVEL_UNKNOWN : settings.getLastRestrictionLevel();
}
}
- @GuardedBy("mLock")
+ @GuardedBy("mSettingsLock")
void forEachPackageInUidLocked(int uid,
@NonNull TriConsumer<String, Integer, Integer> consumer) {
final int uidKeyIndex = mRestrictionLevels.indexOfKey(uid);
@@ -558,20 +570,20 @@
}
}
- @GuardedBy("mLock")
+ @GuardedBy("mSettingsLock")
void forEachUidLocked(@NonNull Consumer<Integer> consumer) {
for (int i = mRestrictionLevels.numMaps() - 1; i >= 0; i--) {
consumer.accept(mRestrictionLevels.keyAt(i));
}
}
- @GuardedBy("mLock")
+ @GuardedBy("mSettingsLock")
PkgSettings getRestrictionSettingsLocked(int uid, String packageName) {
return mRestrictionLevels.get(uid, packageName);
}
void removeUser(@UserIdInt int userId) {
- synchronized (mLock) {
+ synchronized (mSettingsLock) {
for (int i = mRestrictionLevels.numMaps() - 1; i >= 0; i--) {
final int uid = mRestrictionLevels.keyAt(i);
if (UserHandle.getUserId(uid) != userId) {
@@ -583,28 +595,29 @@
}
void removePackage(String pkgName, int uid) {
- synchronized (mLock) {
+ synchronized (mSettingsLock) {
mRestrictionLevels.delete(uid, pkgName);
}
}
void removeUid(int uid) {
- synchronized (mLock) {
+ synchronized (mSettingsLock) {
mRestrictionLevels.delete(uid);
}
}
@VisibleForTesting
void reset() {
- synchronized (mLock) {
+ synchronized (mSettingsLock) {
mRestrictionLevels.clear();
}
}
- @GuardedBy("mLock")
- void dumpLocked(PrintWriter pw, String prefix) {
+ void dump(PrintWriter pw, String prefix) {
final ArrayList<PkgSettings> settings = new ArrayList<>();
- mRestrictionLevels.forEach(setting -> settings.add(setting));
+ synchronized (mSettingsLock) {
+ mRestrictionLevels.forEach(setting -> settings.add(setting));
+ }
Collections.sort(settings, Comparator.comparingInt(PkgSettings::getUid));
final long nowElapsed = SystemClock.elapsedRealtime();
for (int i = 0, size = settings.size(); i < size; i++) {
@@ -908,7 +921,9 @@
@VisibleForTesting
void resetRestrictionSettings() {
- mRestrictionSettings.reset();
+ synchronized (mSettingsLock) {
+ mRestrictionSettings.reset();
+ }
initRestrictionStates();
}
@@ -1309,9 +1324,7 @@
prefix = " " + prefix;
pw.print(prefix);
pw.println("BACKGROUND RESTRICTION LEVEL SETTINGS");
- synchronized (mLock) {
- mRestrictionSettings.dumpLocked(pw, " " + prefix);
- }
+ mRestrictionSettings.dump(pw, " " + prefix);
mConstantsObserver.dump(pw, " " + prefix);
for (int i = 0, size = mAppStateTrackers.size(); i < size; i++) {
pw.println();
@@ -1323,7 +1336,7 @@
int curBucket, boolean allowUpdateBucket, int reason, int subReason) {
int curLevel;
int prevReason;
- synchronized (mLock) {
+ synchronized (mSettingsLock) {
curLevel = getRestrictionLevel(uid, pkgName);
if (curLevel == level) {
// Nothing to do.
@@ -1361,7 +1374,7 @@
|| level == RESTRICTION_LEVEL_RESTRICTED_BUCKET)) {
// restrict the app if it hasn't done so.
boolean doIt = true;
- synchronized (mLock) {
+ synchronized (mSettingsLock) {
final int index = mActiveUids.indexOfKey(uid, pkgName);
if (index >= 0) {
// It's currently active, enqueue it.
@@ -1379,7 +1392,7 @@
&& level < RESTRICTION_LEVEL_RESTRICTED_BUCKET) {
// Moved out of the background-restricted state.
if (curBucket != STANDBY_BUCKET_RARE) {
- synchronized (mLock) {
+ synchronized (mSettingsLock) {
final int index = mActiveUids.indexOfKey(uid, pkgName);
if (index >= 0) {
mActiveUids.add(uid, pkgName, null);
@@ -1437,7 +1450,7 @@
private void dispatchAutoRestrictedBucketFeatureFlagChanged(boolean newValue) {
final AppStandbyInternal appStandbyInternal = mInjector.getAppStandbyInternal();
final ArrayList<Runnable> pendingTasks = new ArrayList<>();
- synchronized (mLock) {
+ synchronized (mSettingsLock) {
mRestrictionSettings.forEachUidLocked(uid -> {
mRestrictionSettings.forEachPackageInUidLocked(uid, (pkgName, level, reason) -> {
if (level == RESTRICTION_LEVEL_BACKGROUND_RESTRICTED) {
@@ -1531,6 +1544,7 @@
private final NotificationManager mNotificationManager;
private final Injector mInjector;
private final Object mLock;
+ private final Object mSettingsLock;
private final Context mContext;
private final BroadcastReceiver mActionButtonReceiver = new BroadcastReceiver() {
@@ -1551,7 +1565,7 @@
}
};
- @GuardedBy("mLock")
+ @GuardedBy("mSettingsLock")
private int mNotificationIDStepper = SUMMARY_NOTIFICATION_ID + 1;
NotificationHelper(AppRestrictionController controller) {
@@ -1559,6 +1573,7 @@
mInjector = controller.mInjector;
mNotificationManager = mInjector.getNotificationManager();
mLock = controller.mLock;
+ mSettingsLock = controller.mSettingsLock;
mContext = mInjector.getContext();
}
@@ -1637,7 +1652,7 @@
int getNotificationIdIfNecessary(@NotificationType int notificationType,
String packageName, int uid) {
- synchronized (mLock) {
+ synchronized (mSettingsLock) {
final RestrictionSettings.PkgSettings settings = mBgController.mRestrictionSettings
.getRestrictionSettingsLocked(uid, packageName);
if (settings == null) {
@@ -1741,7 +1756,7 @@
}
void cancelRequestBgRestrictedIfNecessary(String packageName, int uid) {
- synchronized (mLock) {
+ synchronized (mSettingsLock) {
final RestrictionSettings.PkgSettings settings = mBgController.mRestrictionSettings
.getRestrictionSettingsLocked(uid, packageName);
if (settings != null) {
@@ -1755,7 +1770,7 @@
}
void cancelLongRunningFGSNotificationIfNecessary(String packageName, int uid) {
- synchronized (mLock) {
+ synchronized (mSettingsLock) {
final RestrictionSettings.PkgSettings settings = mBgController.mRestrictionSettings
.getRestrictionSettingsLocked(uid, packageName);
if (settings != null) {
@@ -1771,7 +1786,7 @@
void handleUidInactive(int uid, boolean disabled) {
final ArrayList<Runnable> pendingTasks = mTmpRunnables;
- synchronized (mLock) {
+ synchronized (mSettingsLock) {
final int index = mActiveUids.indexOfKey(uid);
if (index < 0) {
return;
@@ -1792,7 +1807,7 @@
}
void handleUidActive(int uid) {
- synchronized (mLock) {
+ synchronized (mSettingsLock) {
final AppStandbyInternal appStandbyInternal = mInjector.getAppStandbyInternal();
final int userId = UserHandle.getUserId(uid);
mRestrictionSettings.forEachPackageInUidLocked(uid, (pkgName, level, reason) -> {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index daf3561..ff7557a 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -396,7 +396,8 @@
AudioAttributes attr =
AudioProductStrategy.getAudioAttributesForStrategyWithLegacyStreamType(
AudioSystem.STREAM_VOICE_CALL);
- List<AudioDeviceAttributes> devices = AudioSystem.getDevicesForAttributes(attr);
+ List<AudioDeviceAttributes> devices = AudioSystem.getDevicesForAttributes(
+ attr, false /* forVolume */);
if (devices.isEmpty()) {
if (mAudioService.isPlatformVoice()) {
Log.w(TAG,
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 0b9fb1a..0ff8a93 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1808,6 +1808,10 @@
* @param caller caller of this method
*/
private void updateVolumeStates(int device, int streamType, String caller) {
+ // Handle device volume aliasing of SPEAKER_SAFE.
+ if (device == AudioSystem.DEVICE_OUT_SPEAKER_SAFE) {
+ device = AudioSystem.DEVICE_OUT_SPEAKER;
+ }
if (!mStreamStates[streamType].hasIndexForDevice(device)) {
// set the default value, if device is affected by a full/fix/abs volume rule, it
// will taken into account in checkFixedVolumeDevices()
@@ -1819,7 +1823,8 @@
// Check if device to be updated is routed for the given audio stream
List<AudioDeviceAttributes> devicesForAttributes = getDevicesForAttributesInt(
- new AudioAttributes.Builder().setInternalLegacyStreamType(streamType).build());
+ new AudioAttributes.Builder().setInternalLegacyStreamType(streamType).build(),
+ true /* forVolume */);
for (AudioDeviceAttributes deviceAttributes : devicesForAttributes) {
if (deviceAttributes.getType() == AudioDeviceInfo.convertInternalDeviceToDeviceType(
device)) {
@@ -2687,7 +2692,7 @@
public @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributes(
@NonNull AudioAttributes attributes) {
enforceQueryStateOrModifyRoutingPermission();
- return getDevicesForAttributesInt(attributes);
+ return getDevicesForAttributesInt(attributes, false /* forVolume */);
}
/** @see AudioManager#getAudioDevicesForAttributes(AudioAttributes)
@@ -2697,7 +2702,7 @@
*/
public @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributesUnprotected(
@NonNull AudioAttributes attributes) {
- return getDevicesForAttributesInt(attributes);
+ return getDevicesForAttributesInt(attributes, false /* forVolume */);
}
/**
@@ -2719,9 +2724,9 @@
}
protected @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributesInt(
- @NonNull AudioAttributes attributes) {
+ @NonNull AudioAttributes attributes, boolean forVolume) {
Objects.requireNonNull(attributes);
- return mAudioSystem.getDevicesForAttributes(attributes);
+ return mAudioSystem.getDevicesForAttributes(attributes, forVolume);
}
/** Indicates no special treatment in the handling of the volume adjustement */
@@ -6490,7 +6495,8 @@
.setUsage(AudioAttributes.USAGE_MEDIA)
.build();
// calling getDevice*Int to bypass permission check
- final List<AudioDeviceAttributes> devices = getDevicesForAttributesInt(attributes);
+ final List<AudioDeviceAttributes> devices =
+ getDevicesForAttributesInt(attributes, true /* forVolume */);
for (AudioDeviceAttributes device : devices) {
if (getDeviceVolumeBehaviorInt(device) == AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED) {
return true;
@@ -11273,6 +11279,7 @@
@Override
public void setActiveAssistantServiceUids(int [] activeAssistantUids) {
enforceModifyAudioRoutingPermission();
+ Objects.requireNonNull(activeAssistantUids);
synchronized (mSettingsLock) {
mActiveAssistantServiceUids = activeAssistantUids;
}
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index a70b470..6ef8e87 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -24,12 +24,14 @@
import android.media.audiopolicy.AudioMix;
import android.os.SystemClock;
import android.util.Log;
+import android.util.Pair;
import com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
@@ -59,7 +61,7 @@
private static final boolean USE_CACHE_FOR_GETDEVICES = true;
private ConcurrentHashMap<Integer, Integer> mDevicesForStreamCache;
- private ConcurrentHashMap<AudioAttributes, ArrayList<AudioDeviceAttributes>>
+ private ConcurrentHashMap<Pair<AudioAttributes, Boolean>, ArrayList<AudioDeviceAttributes>>
mDevicesForAttrCache;
private int[] mMethodCacheHit;
private static final Object sRoutingListenerLock = new Object();
@@ -201,26 +203,28 @@
* @return the devices that the stream with the given attributes would be routed to
*/
public @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributes(
- @NonNull AudioAttributes attributes) {
+ @NonNull AudioAttributes attributes, boolean forVolume) {
if (!ENABLE_GETDEVICES_STATS) {
- return getDevicesForAttributesImpl(attributes);
+ return getDevicesForAttributesImpl(attributes, forVolume);
}
mMethodCallCounter[METHOD_GETDEVICESFORATTRIBUTES]++;
final long startTime = SystemClock.uptimeNanos();
- final ArrayList<AudioDeviceAttributes> res = getDevicesForAttributesImpl(attributes);
+ final ArrayList<AudioDeviceAttributes> res = getDevicesForAttributesImpl(
+ attributes, forVolume);
mMethodTimeNs[METHOD_GETDEVICESFORATTRIBUTES] += SystemClock.uptimeNanos() - startTime;
return res;
}
private @NonNull ArrayList<AudioDeviceAttributes> getDevicesForAttributesImpl(
- @NonNull AudioAttributes attributes) {
+ @NonNull AudioAttributes attributes, boolean forVolume) {
if (USE_CACHE_FOR_GETDEVICES) {
ArrayList<AudioDeviceAttributes> res;
+ final Pair<AudioAttributes, Boolean> key = new Pair(attributes, forVolume);
synchronized (mDevicesForAttrCache) {
- res = mDevicesForAttrCache.get(attributes);
+ res = mDevicesForAttrCache.get(key);
if (res == null) {
- res = AudioSystem.getDevicesForAttributes(attributes);
- mDevicesForAttrCache.put(attributes, res);
+ res = AudioSystem.getDevicesForAttributes(attributes, forVolume);
+ mDevicesForAttrCache.put(key, res);
if (DEBUG_CACHE) {
Log.d(TAG, mMethodNames[METHOD_GETDEVICESFORATTRIBUTES]
+ attrDeviceToDebugString(attributes, res));
@@ -231,7 +235,7 @@
mMethodCacheHit[METHOD_GETDEVICESFORATTRIBUTES]++;
if (DEBUG_CACHE) {
final ArrayList<AudioDeviceAttributes> real =
- AudioSystem.getDevicesForAttributes(attributes);
+ AudioSystem.getDevicesForAttributes(attributes, forVolume);
if (res.equals(real)) {
Log.d(TAG, mMethodNames[METHOD_GETDEVICESFORATTRIBUTES]
+ attrDeviceToDebugString(attributes, res) + " CACHE");
@@ -245,7 +249,7 @@
return res;
}
// not using cache
- return AudioSystem.getDevicesForAttributes(attributes);
+ return AudioSystem.getDevicesForAttributes(attributes, forVolume);
}
private static String attrDeviceToDebugString(@NonNull AudioAttributes attr,
@@ -523,9 +527,10 @@
}
pw.println(" mDevicesForAttrCache:");
if (mDevicesForAttrCache != null) {
- for (AudioAttributes attr : mDevicesForAttrCache.keySet()) {
- pw.println("\t" + attr);
- for (AudioDeviceAttributes devAttr : mDevicesForAttrCache.get(attr)) {
+ for (Map.Entry<Pair<AudioAttributes, Boolean>, ArrayList<AudioDeviceAttributes>>
+ entry : mDevicesForAttrCache.entrySet()) {
+ pw.println("\t" + entry.getKey().first + " forVolume: " + entry.getKey().second);
+ for (AudioDeviceAttributes devAttr : entry.getValue()) {
pw.println("\t\t" + devAttr);
}
}
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 63b27d8..193cc5f 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -264,7 +264,8 @@
return;
}
mState = STATE_DISABLED_UNAVAILABLE;
- mASA.getDevicesForAttributes(DEFAULT_ATTRIBUTES).toArray(ROUTING_DEVICES);
+ mASA.getDevicesForAttributes(
+ DEFAULT_ATTRIBUTES, false /* forVolume */).toArray(ROUTING_DEVICES);
// note at this point mSpat is still not instantiated
}
@@ -298,7 +299,8 @@
case STATE_DISABLED_AVAILABLE:
break;
}
- mASA.getDevicesForAttributes(DEFAULT_ATTRIBUTES).toArray(ROUTING_DEVICES);
+ mASA.getDevicesForAttributes(
+ DEFAULT_ATTRIBUTES, false /* forVolume */).toArray(ROUTING_DEVICES);
// is media routed to a new device?
if (isWireless(ROUTING_DEVICES[0].getType())) {
@@ -865,7 +867,8 @@
}
AudioDeviceAttributes[] devices = new AudioDeviceAttributes[1];
// going through adapter to take advantage of routing cache
- mASA.getDevicesForAttributes(attributes).toArray(devices);
+ mASA.getDevicesForAttributes(
+ attributes, false /* forVolume */).toArray(devices);
final boolean able = canBeSpatializedOnDevice(attributes, format, devices);
logd("canBeSpatialized returning " + able);
return able;
diff --git a/services/core/java/com/android/server/audio/TEST_MAPPING b/services/core/java/com/android/server/audio/TEST_MAPPING
index 5a6c6a5..f3a73f0 100644
--- a/services/core/java/com/android/server/audio/TEST_MAPPING
+++ b/services/core/java/com/android/server/audio/TEST_MAPPING
@@ -8,6 +8,9 @@
},
{
"include-filter": "android.media.audio.cts.AudioFocusTest"
+ },
+ {
+ "include-filter": "android.media.audio.cts.SpatializerTest"
}
]
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
index 812ca8a..15f0cad 100644
--- a/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
+++ b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java
@@ -84,6 +84,8 @@
private final BaseClientMonitor mClientMonitor;
@Nullable
private final ClientMonitorCallback mClientCallback;
+ @Nullable
+ private ClientMonitorCallback mOnStartCallback;
@OperationState
private int mState;
@VisibleForTesting
@@ -108,7 +110,8 @@
mCancelWatchdog = () -> {
if (!isFinished()) {
Slog.e(TAG, "[Watchdog Triggered]: " + this);
- getWrappedCallback().onClientFinished(mClientMonitor, false /* success */);
+ getWrappedCallback(mOnStartCallback)
+ .onClientFinished(mClientMonitor, false /* success */);
}
};
}
@@ -174,6 +177,7 @@
}
private boolean doStart(@NonNull ClientMonitorCallback callback) {
+ mOnStartCallback = callback;
final ClientMonitorCallback cb = getWrappedCallback(callback);
if (mState == STATE_WAITING_IN_QUEUE_CANCELING) {
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index a7b49f1..9bfdd68 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2463,7 +2463,7 @@
pw.println(" mMinimumBrightnessCurve=" + mMinimumBrightnessCurve);
if (mUserPreferredMode != null) {
- pw.println(mUserPreferredMode);
+ pw.println(" mUserPreferredMode=" + mUserPreferredMode);
}
pw.println();
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 2e80efb..7a0cf4b 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -884,20 +884,26 @@
public void setUserPreferredDisplayModeLocked(Display.Mode mode) {
final int oldModeId = getPreferredModeId();
mUserPreferredMode = mode;
- if (mode != null && (mode.isRefreshRateSet() ^ mode.isResolutionSet())) {
- mUserPreferredMode = findMode(mode.getPhysicalWidth(),
+ if (mode != null && (mode.isRefreshRateSet() || mode.isResolutionSet())) {
+ Display.Mode matchingSupportedMode;
+ matchingSupportedMode = findMode(mode.getPhysicalWidth(),
mode.getPhysicalHeight(), mode.getRefreshRate());
+ if (matchingSupportedMode != null) {
+ mUserPreferredMode = matchingSupportedMode;
+ }
}
- mUserPreferredModeId = findUserPreferredModeIdLocked(mode);
- if (oldModeId != getPreferredModeId()) {
- updateDeviceInfoLocked();
+ mUserPreferredModeId = findUserPreferredModeIdLocked(mUserPreferredMode);
+
+ if (oldModeId == getPreferredModeId()) {
+ return;
}
+ updateDeviceInfoLocked();
if (!mSurfaceControlProxy.getBootDisplayModeSupport()) {
return;
}
- if (mUserPreferredMode == null) {
+ if (mUserPreferredModeId == INVALID_MODE_ID) {
mSurfaceControlProxy.clearBootDisplayMode(getDisplayTokenLocked());
} else {
int preferredSfDisplayModeId = findSfDisplayModeIdLocked(
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
index fb36dc7..d04b5a2 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
@@ -346,6 +346,7 @@
writer.println("DisplayWhiteBalanceController");
writer.println(" mLoggingEnabled=" + mLoggingEnabled);
writer.println(" mEnabled=" + mEnabled);
+ writer.println(" mStrongModeEnabled=" + mStrongModeEnabled);
writer.println(" mDisplayPowerControllerCallbacks=" + mDisplayPowerControllerCallbacks);
mBrightnessSensor.dump(writer);
mBrightnessFilter.dump(writer);
diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
index 9e00f95..29e5f92 100644
--- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
@@ -48,6 +48,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@@ -168,10 +169,10 @@
private final SparseBooleanArray mDisabledByUserRestriction;
/**
- * Cache of services per user id.
+ * Cache of service list per user id.
*/
@GuardedBy("mLock")
- private final SparseArray<S> mServicesCache = new SparseArray<>();
+ private final SparseArray<List<S>> mServicesCacheList = new SparseArray<>();
/**
* Value that determines whether the per-user service should be removed from the cache when its
@@ -252,8 +253,7 @@
mServiceNameResolver = serviceNameResolver;
if (mServiceNameResolver != null) {
mServiceNameResolver.setOnTemporaryServiceNameChangedCallback(
- (u, s, t) -> onServiceNameChanged(u, s, t));
-
+ this::onServiceNameChanged);
}
if (disallowProperty == null) {
mDisabledByUserRestriction = null;
@@ -308,7 +308,7 @@
@Override // from SystemService
public void onUserStopped(@NonNull TargetUser user) {
synchronized (mLock) {
- removeCachedServiceLocked(user.getUserIdentifier());
+ removeCachedServiceListLocked(user.getUserIdentifier());
}
}
@@ -386,21 +386,58 @@
synchronized (mLock) {
final S oldService = peekServiceForUserLocked(userId);
if (oldService != null) {
- oldService.removeSelfFromCacheLocked();
+ oldService.removeSelfFromCache();
}
mServiceNameResolver.setTemporaryService(userId, componentName, durationMs);
}
}
/**
+ * Temporarily sets the service implementation.
+ *
+ * <p>Typically used by Shell command and/or CTS tests.
+ *
+ * @param componentNames list of the names of the new component
+ * @param durationMs how long the change will be valid (the service will be automatically
+ * reset
+ * to the default component after this timeout expires).
+ * @throws SecurityException if caller is not allowed to manage this service's settings.
+ * @throws IllegalArgumentException if value of {@code durationMs} is higher than
+ * {@link #getMaximumTemporaryServiceDurationMs()}.
+ */
+ public final void setTemporaryServices(@UserIdInt int userId, @NonNull String[] componentNames,
+ int durationMs) {
+ Slog.i(mTag, "setTemporaryService(" + userId + ") to " + Arrays.toString(componentNames)
+ + " for " + durationMs + "ms");
+ if (mServiceNameResolver == null) {
+ return;
+ }
+ enforceCallingPermissionForManagement();
+
+ Objects.requireNonNull(componentNames);
+ final int maxDurationMs = getMaximumTemporaryServiceDurationMs();
+ if (durationMs > maxDurationMs) {
+ throw new IllegalArgumentException(
+ "Max duration is " + maxDurationMs + " (called with " + durationMs + ")");
+ }
+
+ synchronized (mLock) {
+ final S oldService = peekServiceForUserLocked(userId);
+ if (oldService != null) {
+ oldService.removeSelfFromCache();
+ }
+ mServiceNameResolver.setTemporaryServices(userId, componentNames, durationMs);
+ }
+ }
+
+ /**
* Sets whether the default service should be used.
*
* <p>Typically used during CTS tests to make sure only the default service doesn't interfere
* with the test results.
*
- * @throws SecurityException if caller is not allowed to manage this service's settings.
- *
* @return whether the enabled state changed.
+ * @throws SecurityException if caller is not allowed to manage this service's settings.
*/
public final boolean setDefaultServiceEnabled(@UserIdInt int userId, boolean enabled) {
Slog.i(mTag, "setDefaultServiceEnabled() for userId " + userId + ": " + enabled);
@@ -420,7 +457,7 @@
final S oldService = peekServiceForUserLocked(userId);
if (oldService != null) {
- oldService.removeSelfFromCacheLocked();
+ oldService.removeSelfFromCache();
}
// Must update the service on cache so its initialization code is triggered
@@ -501,6 +538,21 @@
protected abstract S newServiceLocked(@UserIdInt int resolvedUserId, boolean disabled);
/**
+ * Creates a new service list that will be added to the cache.
+ *
+ * @param resolvedUserId the resolved user id for the service.
+ * @param disabled whether the service is currently disabled (due to {@link UserManager}
+ * restrictions).
+ * @return a new instance.
+ */
+ @Nullable
+ @GuardedBy("mLock")
+ protected List<S> newServiceListLocked(@UserIdInt int resolvedUserId, boolean disabled,
+ String[] serviceNames) {
+ throw new UnsupportedOperationException("newServiceListLocked not implemented. ");
+ }
+
+ /**
* Register the service for extra Settings changes (i.e., other than
* {@link android.provider.Settings.Secure#USER_SETUP_COMPLETE} or
* {@link #getServiceSettingsProperty()}, which are automatically handled).
@@ -516,7 +568,6 @@
* <p><b>NOTE: </p>it doesn't need to register for
* {@link android.provider.Settings.Secure#USER_SETUP_COMPLETE} or
* {@link #getServiceSettingsProperty()}.
- *
*/
@SuppressWarnings("unused")
protected void registerForExtraSettingsChanges(@NonNull ContentResolver resolver,
@@ -527,7 +578,7 @@
* Callback for Settings changes that were registered though
* {@link #registerForExtraSettingsChanges(ContentResolver, ContentObserver)}.
*
- * @param userId user associated with the change
+ * @param userId user associated with the change
* @param property Settings property changed.
*/
protected void onSettingsChanged(@UserIdInt int userId, @NonNull String property) {
@@ -539,18 +590,38 @@
@GuardedBy("mLock")
@NonNull
protected S getServiceForUserLocked(@UserIdInt int userId) {
+ List<S> services = getServiceListForUserLocked(userId);
+ return services == null || services.size() == 0 ? null : services.get(0);
+ }
+
+ /**
+ * Gets the service instance list for a user, creating instances if not present in the cache.
+ */
+ @GuardedBy("mLock")
+ protected List<S> getServiceListForUserLocked(@UserIdInt int userId) {
final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), userId, false, false, null, null);
- S service = mServicesCache.get(resolvedUserId);
- if (service == null) {
+ List<S> services = mServicesCacheList.get(resolvedUserId);
+ if (services == null || services.size() == 0) {
final boolean disabled = isDisabledLocked(userId);
- service = newServiceLocked(resolvedUserId, disabled);
- if (!disabled) {
- onServiceEnabledLocked(service, resolvedUserId);
+ if (mServiceNameResolver == null) {
+ return null;
}
- mServicesCache.put(userId, service);
+ if (mServiceNameResolver.isConfiguredInMultipleMode()) {
+ services = newServiceListLocked(resolvedUserId, disabled,
+ mServiceNameResolver.getServiceNameList(userId));
+ } else {
+ services = new ArrayList<>();
+ services.add(newServiceLocked(resolvedUserId, disabled));
+ }
+ if (!disabled) {
+ for (int i = 0; i < services.size(); i++) {
+ onServiceEnabledLocked(services.get(i), resolvedUserId);
+ }
+ }
+ mServicesCacheList.put(userId, services);
}
- return service;
+ return services;
}
/**
@@ -560,9 +631,20 @@
@GuardedBy("mLock")
@Nullable
protected S peekServiceForUserLocked(@UserIdInt int userId) {
+ List<S> serviceList = peekServiceListForUserLocked(userId);
+ return serviceList == null || serviceList.size() == 0 ? null : serviceList.get(0);
+ }
+
+ /**
+ * Gets the <b>existing</b> service instance for a user, returning {@code null} if not already
+ * present in the cache.
+ */
+ @GuardedBy("mLock")
+ @Nullable
+ protected List<S> peekServiceListForUserLocked(@UserIdInt int userId) {
final int resolvedUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), userId, false, false, null, null);
- return mServicesCache.get(resolvedUserId);
+ return mServicesCacheList.get(resolvedUserId);
}
/**
@@ -570,36 +652,59 @@
*/
@GuardedBy("mLock")
protected void updateCachedServiceLocked(@UserIdInt int userId) {
- updateCachedServiceLocked(userId, isDisabledLocked(userId));
+ updateCachedServiceListLocked(userId, isDisabledLocked(userId));
}
/**
* Checks whether the service is disabled (through {@link UserManager} restrictions) for the
* given user.
*/
+ @GuardedBy("mLock")
protected boolean isDisabledLocked(@UserIdInt int userId) {
- return mDisabledByUserRestriction == null ? false : mDisabledByUserRestriction.get(userId);
+ return mDisabledByUserRestriction != null && mDisabledByUserRestriction.get(userId);
}
/**
* Updates a cached service for a given user.
*
- * @param userId user handle.
+ * @param userId user handle.
* @param disabled whether the user is disabled.
* @return service for the user.
*/
@GuardedBy("mLock")
protected S updateCachedServiceLocked(@UserIdInt int userId, boolean disabled) {
final S service = getServiceForUserLocked(userId);
- if (service != null) {
- service.updateLocked(disabled);
- if (!service.isEnabledLocked()) {
- removeCachedServiceLocked(userId);
- } else {
- onServiceEnabledLocked(service, userId);
+ updateCachedServiceListLocked(userId, disabled);
+ return service;
+ }
+
+ /**
+ * Updates a cached service for a given user.
+ *
+ * @param userId user handle.
+ * @param disabled whether the user is disabled.
+ * @return service for the user.
+ */
+ @GuardedBy("mLock")
+ protected List<S> updateCachedServiceListLocked(@UserIdInt int userId, boolean disabled) {
+ final List<S> services = getServiceListForUserLocked(userId);
+ if (services == null) {
+ return null;
+ }
+ for (int i = 0; i < services.size(); i++) {
+ S service = services.get(i);
+ if (service != null) {
+ synchronized (service.mLock) {
+ service.updateLocked(disabled);
+ if (!service.isEnabledLocked()) {
+ removeCachedServiceListLocked(userId);
+ } else {
+ onServiceEnabledLocked(services.get(i), userId);
+ }
+ }
}
}
- return service;
+ return services;
}
/**
@@ -619,28 +724,32 @@
* <p>By default doesn't do anything, but can be overridden by subclasses.
*/
@SuppressWarnings("unused")
+ @GuardedBy("mLock")
protected void onServiceEnabledLocked(@NonNull S service, @UserIdInt int userId) {
}
/**
- * Removes a cached service for a given user.
+ * Removes a cached service list for a given user.
*
* @return the removed service.
*/
@GuardedBy("mLock")
@NonNull
- protected final S removeCachedServiceLocked(@UserIdInt int userId) {
- final S service = peekServiceForUserLocked(userId);
- if (service != null) {
- mServicesCache.delete(userId);
- onServiceRemoved(service, userId);
+ protected final List<S> removeCachedServiceListLocked(@UserIdInt int userId) {
+ final List<S> services = peekServiceListForUserLocked(userId);
+ if (services != null) {
+ mServicesCacheList.delete(userId);
+ for (int i = 0; i < services.size(); i++) {
+ onServiceRemoved(services.get(i), userId);
+ }
}
- return service;
+ return services;
}
/**
* Called before the package that provides the service for the given user is being updated.
*/
+ @GuardedBy("mLock")
protected void onServicePackageUpdatingLocked(@UserIdInt int userId) {
if (verbose) Slog.v(mTag, "onServicePackageUpdatingLocked(" + userId + ")");
}
@@ -648,6 +757,7 @@
/**
* Called after the package that provides the service for the given user is being updated.
*/
+ @GuardedBy("mLock")
protected void onServicePackageUpdatedLocked(@UserIdInt int userId) {
if (verbose) Slog.v(mTag, "onServicePackageUpdated(" + userId + ")");
}
@@ -655,6 +765,7 @@
/**
* Called after the package data that provides the service for the given user is cleared.
*/
+ @GuardedBy("mLock")
protected void onServicePackageDataClearedLocked(@UserIdInt int userId) {
if (verbose) Slog.v(mTag, "onServicePackageDataCleared(" + userId + ")");
}
@@ -662,6 +773,7 @@
/**
* Called after the package that provides the service for the given user is restarted.
*/
+ @GuardedBy("mLock")
protected void onServicePackageRestartedLocked(@UserIdInt int userId) {
if (verbose) Slog.v(mTag, "onServicePackageRestarted(" + userId + ")");
}
@@ -679,14 +791,31 @@
* <p>By default, it calls {@link #updateCachedServiceLocked(int)}; subclasses must either call
* that same method, or {@code super.onServiceNameChanged()}.
*
- * @param userId user handle.
+ * @param userId user handle.
* @param serviceName the new service name.
* @param isTemporary whether the new service is temporary.
*/
protected void onServiceNameChanged(@UserIdInt int userId, @Nullable String serviceName,
boolean isTemporary) {
synchronized (mLock) {
- updateCachedServiceLocked(userId);
+ updateCachedServiceListLocked(userId, isDisabledLocked(userId));
+ }
+ }
+
+ /**
+ * Called when the service name list has changed (typically when using temporary services).
+ *
+ * <p>By default, it calls {@link #updateCachedServiceLocked(int)}; subclasses must either call
+ * that same method, or {@code super.onServiceNameChanged()}.
+ *
+ * @param userId user handle.
+ * @param serviceNames the new service name list.
+ * @param isTemporary whether the new service is temporary.
+ */
+ protected void onServiceNameListChanged(@UserIdInt int userId, @Nullable String[] serviceNames,
+ boolean isTemporary) {
+ synchronized (mLock) {
+ updateCachedServiceListLocked(userId, isDisabledLocked(userId));
}
}
@@ -695,9 +824,12 @@
*/
@GuardedBy("mLock")
protected void visitServicesLocked(@NonNull Visitor<S> visitor) {
- final int size = mServicesCache.size();
+ final int size = mServicesCacheList.size();
for (int i = 0; i < size; i++) {
- visitor.visit(mServicesCache.valueAt(i));
+ List<S> services = mServicesCacheList.valueAt(i);
+ for (int j = 0; j < services.size(); j++) {
+ visitor.visit(services.get(j));
+ }
}
}
@@ -706,7 +838,7 @@
*/
@GuardedBy("mLock")
protected void clearCacheLocked() {
- mServicesCache.clear();
+ mServicesCacheList.clear();
}
/**
@@ -757,6 +889,7 @@
}
// TODO(b/117779333): support proto
+ @GuardedBy("mLock")
protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
boolean realDebug = debug;
boolean realVerbose = verbose;
@@ -765,40 +898,64 @@
try {
// Temporarily turn on full logging;
debug = verbose = true;
- final int size = mServicesCache.size();
- pw.print(prefix); pw.print("Debug: "); pw.print(realDebug);
- pw.print(" Verbose: "); pw.println(realVerbose);
- pw.print("Package policy flags: "); pw.println(mServicePackagePolicyFlags);
+ final int size = mServicesCacheList.size();
+ pw.print(prefix);
+ pw.print("Debug: ");
+ pw.print(realDebug);
+ pw.print(" Verbose: ");
+ pw.println(realVerbose);
+ pw.print("Package policy flags: ");
+ pw.println(mServicePackagePolicyFlags);
if (mUpdatingPackageNames != null) {
- pw.print("Packages being updated: "); pw.println(mUpdatingPackageNames);
+ pw.print("Packages being updated: ");
+ pw.println(mUpdatingPackageNames);
}
dumpSupportedUsers(pw, prefix);
if (mServiceNameResolver != null) {
- pw.print(prefix); pw.print("Name resolver: ");
- mServiceNameResolver.dumpShort(pw); pw.println();
+ pw.print(prefix);
+ pw.print("Name resolver: ");
+ mServiceNameResolver.dumpShort(pw);
+ pw.println();
final List<UserInfo> users = getSupportedUsers();
for (int i = 0; i < users.size(); i++) {
final int userId = users.get(i).id;
- pw.print(prefix2); pw.print(userId); pw.print(": ");
- mServiceNameResolver.dumpShort(pw, userId); pw.println();
+ pw.print(prefix2);
+ pw.print(userId);
+ pw.print(": ");
+ mServiceNameResolver.dumpShort(pw, userId);
+ pw.println();
}
}
- pw.print(prefix); pw.print("Users disabled by restriction: ");
+ pw.print(prefix);
+ pw.print("Users disabled by restriction: ");
pw.println(mDisabledByUserRestriction);
- pw.print(prefix); pw.print("Allow instant service: "); pw.println(mAllowInstantService);
+ pw.print(prefix);
+ pw.print("Allow instant service: ");
+ pw.println(mAllowInstantService);
final String settingsProperty = getServiceSettingsProperty();
if (settingsProperty != null) {
- pw.print(prefix); pw.print("Settings property: "); pw.println(settingsProperty);
+ pw.print(prefix);
+ pw.print("Settings property: ");
+ pw.println(settingsProperty);
}
- pw.print(prefix); pw.print("Cached services: ");
+ pw.print(prefix);
+ pw.print("Cached services: ");
if (size == 0) {
pw.println("none");
} else {
pw.println(size);
for (int i = 0; i < size; i++) {
- pw.print(prefix); pw.print("Service at "); pw.print(i); pw.println(": ");
- final S service = mServicesCache.valueAt(i);
- service.dumpLocked(prefix2, pw);
+ pw.print(prefix);
+ pw.print("Service at ");
+ pw.print(i);
+ pw.println(": ");
+ final List<S> services = mServicesCacheList.valueAt(i);
+ for (int j = 0; j < services.size(); j++) {
+ S service = services.get(i);
+ synchronized (service.mLock) {
+ service.dumpLocked(prefix2, pw);
+ }
+ }
pw.println();
}
}
@@ -820,7 +977,7 @@
final int userId = getChangingUserId();
synchronized (mLock) {
if (mUpdatingPackageNames == null) {
- mUpdatingPackageNames = new SparseArray<String>(mServicesCache.size());
+ mUpdatingPackageNames = new SparseArray<String>(mServicesCacheList.size());
}
mUpdatingPackageNames.put(userId, packageName);
onServicePackageUpdatingLocked(userId);
@@ -835,7 +992,7 @@
+ " because package " + activePackageName
+ " is being updated");
}
- removeCachedServiceLocked(userId);
+ removeCachedServiceListLocked(userId);
if ((mServicePackagePolicyFlags & PACKAGE_UPDATE_POLICY_REFRESH_EAGER)
!= 0) {
@@ -901,7 +1058,7 @@
if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
handleActiveServiceRestartedLocked(activePackageName, userId);
} else {
- removeCachedServiceLocked(userId);
+ removeCachedServiceListLocked(userId);
}
} else {
handlePackageUpdateLocked(pkg);
@@ -930,7 +1087,7 @@
private void handleActiveServiceRemoved(@UserIdInt int userId) {
synchronized (mLock) {
- removeCachedServiceLocked(userId);
+ removeCachedServiceListLocked(userId);
}
final String serviceSettingsProperty = getServiceSettingsProperty();
if (serviceSettingsProperty != null) {
@@ -939,6 +1096,7 @@
}
}
+ @GuardedBy("mLock")
private void handleActiveServiceRestartedLocked(String activePackageName,
@UserIdInt int userId) {
if ((mServicePackagePolicyFlags & PACKAGE_RESTART_POLICY_NO_REFRESH) != 0) {
@@ -952,7 +1110,7 @@
+ " because package " + activePackageName
+ " is being restarted");
}
- removeCachedServiceLocked(userId);
+ removeCachedServiceListLocked(userId);
if ((mServicePackagePolicyFlags & PACKAGE_RESTART_POLICY_REFRESH_EAGER) != 0) {
if (debug) {
@@ -966,14 +1124,27 @@
@Override
public void onPackageModified(String packageName) {
- if (verbose) Slog.v(mTag, "onPackageModified(): " + packageName);
+ synchronized (mLock) {
+ if (verbose) Slog.v(mTag, "onPackageModified(): " + packageName);
- if (mServiceNameResolver == null) {
- return;
+ if (mServiceNameResolver == null) {
+ return;
+ }
+
+ final int userId = getChangingUserId();
+ final String[] serviceNames = mServiceNameResolver.getDefaultServiceNameList(
+ userId);
+ if (serviceNames != null) {
+ for (int i = 0; i < serviceNames.length; i++) {
+ peekAndUpdateCachedServiceLocked(packageName, userId, serviceNames[i]);
+ }
+ }
}
+ }
- final int userId = getChangingUserId();
- final String serviceName = mServiceNameResolver.getDefaultServiceName(userId);
+ @GuardedBy("mLock")
+ private void peekAndUpdateCachedServiceLocked(String packageName, int userId,
+ String serviceName) {
if (serviceName == null) {
return;
}
@@ -997,6 +1168,7 @@
}
}
+ @GuardedBy("mLock")
private String getActiveServicePackageNameLocked() {
final int userId = getChangingUserId();
final S service = peekServiceForUserLocked(userId);
@@ -1017,7 +1189,7 @@
};
// package changes
- monitor.register(getContext(), null, UserHandle.ALL, true);
+ monitor.register(getContext(), null, UserHandle.ALL, true);
}
/**
diff --git a/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java b/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
index 757a5cc..58413c9 100644
--- a/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractPerUserSystemService.java
@@ -43,14 +43,13 @@
*
* @param <M> "main" service class.
* @param <S> "real" service class.
- *
* @hide
*/
public abstract class AbstractPerUserSystemService<S extends AbstractPerUserSystemService<S, M>,
M extends AbstractMasterSystemService<M, S>> {
- protected final @UserIdInt int mUserId;
- protected final Object mLock;
+ @UserIdInt protected final int mUserId;
+ public final Object mLock;
protected final String mTag = getClass().getSimpleName();
protected final M mMaster;
@@ -91,14 +90,14 @@
* <p><b>MUST</b> be overridden by subclasses that bind to an
* {@link com.android.internal.infra.AbstractRemoteService}.
*
- * @throws NameNotFoundException if the service does not exist.
- * @throws SecurityException if the service does not have the proper permissions to be bound to.
- * @throws UnsupportedOperationException if subclass binds to a remote service but does not
- * overrides it.
- *
* @return new {@link ServiceInfo},
+ * @throws NameNotFoundException if the service does not exist.
+ * @throws SecurityException if the service does not have the proper permissions to
+ * be bound to.
+ * @throws UnsupportedOperationException if subclass binds to a remote service but does not
+ * overrides it.
*/
- protected @NonNull ServiceInfo newServiceInfoLocked(
+ @NonNull protected ServiceInfo newServiceInfoLocked(
@SuppressWarnings("unused") @NonNull ComponentName serviceComponent)
throws NameNotFoundException {
throw new UnsupportedOperationException("not overridden");
@@ -137,7 +136,6 @@
* previous state.
*
* @param disabled whether the service is disabled (due to {@link UserManager} restrictions).
- *
* @return whether the disabled state changed.
*/
@GuardedBy("mLock")
@@ -154,18 +152,48 @@
updateIsSetupComplete(mUserId);
mDisabled = disabled;
- updateServiceInfoLocked();
+ if (mMaster.mServiceNameResolver.isConfiguredInMultipleMode()) {
+ updateServiceInfoListLocked();
+ } else {
+ updateServiceInfoLocked();
+ }
return wasEnabled != isEnabledLocked();
}
/**
* Updates the internal reference to the service info, and returns the service's component.
*/
+ @GuardedBy("mLock")
protected final ComponentName updateServiceInfoLocked() {
- ComponentName serviceComponent = null;
- if (mMaster.mServiceNameResolver != null) {
- ServiceInfo serviceInfo = null;
+ ComponentName[] componentNames = updateServiceInfoListLocked();
+ return componentNames == null || componentNames.length == 0 ? null : componentNames[0];
+ }
+
+ /**
+ * Updates the internal reference to the service info, and returns the service's component.
+ */
+ @GuardedBy("mLock")
+ protected final ComponentName[] updateServiceInfoListLocked() {
+ if (mMaster.mServiceNameResolver == null) {
+ return null;
+ }
+ if (!mMaster.mServiceNameResolver.isConfiguredInMultipleMode()) {
final String componentName = getComponentNameLocked();
+ return new ComponentName[] { getServiceComponent(componentName) };
+ }
+ final String[] componentNames = mMaster.mServiceNameResolver.getServiceNameList(
+ mUserId);
+ ComponentName[] serviceComponents = new ComponentName[componentNames.length];
+ for (int i = 0; i < componentNames.length; i++) {
+ serviceComponents[i] = getServiceComponent(componentNames[i]);
+ }
+ return serviceComponents;
+ }
+
+ private ComponentName getServiceComponent(String componentName) {
+ synchronized (mLock) {
+ ServiceInfo serviceInfo = null;
+ ComponentName serviceComponent = null;
if (!TextUtils.isEmpty(componentName)) {
try {
serviceComponent = ComponentName.unflattenFromString(componentName);
@@ -196,14 +224,14 @@
Slog.e(mTag, "Bad ServiceInfo for '" + componentName + "': " + e);
mServiceInfo = null;
}
+ return serviceComponent;
}
- return serviceComponent;
}
/**
* Gets the user associated with this service.
*/
- public final @UserIdInt int getUserId() {
+ @UserIdInt public final int getUserId() {
return mUserId;
}
@@ -229,15 +257,34 @@
/**
* Gets the current name of the service, which is either the default service or the
- * {@link AbstractMasterSystemService#setTemporaryService(int, String, int) temporary one}.
+ * {@link AbstractMasterSystemService#setTemporaryService(int, String, int) temporary one}.
*/
- protected final @Nullable String getComponentNameLocked() {
+ @Nullable
+ @GuardedBy("mLock")
+ protected final String getComponentNameLocked() {
return mMaster.mServiceNameResolver.getServiceName(mUserId);
}
/**
+ * Gets the current name of the service, which is either the default service or the
+ * {@link AbstractMasterSystemService#setTemporaryService(int, String, int) temporary one}.
+ */
+ @Nullable
+ @GuardedBy("mLock")
+ protected final String getComponentNameForMultipleLocked(String serviceName) {
+ String[] services = mMaster.mServiceNameResolver.getServiceNameList(mUserId);
+ for (int i = 0; i < services.length; i++) {
+ if (serviceName.equals(services[i])) {
+ return services[i];
+ }
+ }
+ return null;
+ }
+
+ /**
* Checks whether the current service for the user was temporarily set.
*/
+ @GuardedBy("mLock")
public final boolean isTemporaryServiceSetLocked() {
return mMaster.mServiceNameResolver.isTemporary(mUserId);
}
@@ -245,6 +292,7 @@
/**
* Resets the temporary service implementation to the default component.
*/
+ @GuardedBy("mLock")
protected final void resetTemporaryServiceLocked() {
mMaster.mServiceNameResolver.resetTemporaryService(mUserId);
}
@@ -268,6 +316,7 @@
return mServiceInfo == null ? null : mServiceInfo.getComponentName();
}
}
+
/**
* Gets the name of the of the app this service binds to, or {@code null} if the service is
* disabled.
@@ -303,8 +352,10 @@
/**
* Removes the service from the main service's cache.
*/
- protected final void removeSelfFromCacheLocked() {
- mMaster.removeCachedServiceLocked(mUserId);
+ protected final void removeSelfFromCache() {
+ synchronized (mMaster.mLock) {
+ mMaster.removeCachedServiceListLocked(mUserId);
+ }
}
/**
@@ -327,6 +378,7 @@
* Gets the target SDK level of the service this service binds to,
* or {@code 0} if the service is disabled.
*/
+ @GuardedBy("mLock")
public final int getTargedSdkLocked() {
return mServiceInfo == null ? 0 : mServiceInfo.applicationInfo.targetSdkVersion;
}
@@ -334,6 +386,7 @@
/**
* Gets whether the device already finished setup.
*/
+ @GuardedBy("mLock")
protected final boolean isSetupCompletedLocked() {
return mSetupComplete;
}
@@ -348,19 +401,32 @@
// TODO(b/117779333): support proto
@GuardedBy("mLock")
protected void dumpLocked(@NonNull String prefix, @NonNull PrintWriter pw) {
- pw.print(prefix); pw.print("User: "); pw.println(mUserId);
+ pw.print(prefix);
+ pw.print("User: ");
+ pw.println(mUserId);
if (mServiceInfo != null) {
- pw.print(prefix); pw.print("Service Label: "); pw.println(getServiceLabelLocked());
- pw.print(prefix); pw.print("Target SDK: "); pw.println(getTargedSdkLocked());
+ pw.print(prefix);
+ pw.print("Service Label: ");
+ pw.println(getServiceLabelLocked());
+ pw.print(prefix);
+ pw.print("Target SDK: ");
+ pw.println(getTargedSdkLocked());
}
if (mMaster.mServiceNameResolver != null) {
- pw.print(prefix); pw.print("Name resolver: ");
- mMaster.mServiceNameResolver.dumpShort(pw, mUserId); pw.println();
+ pw.print(prefix);
+ pw.print("Name resolver: ");
+ mMaster.mServiceNameResolver.dumpShort(pw, mUserId);
+ pw.println();
}
- pw.print(prefix); pw.print("Disabled by UserManager: "); pw.println(mDisabled);
- pw.print(prefix); pw.print("Setup complete: "); pw.println(mSetupComplete);
+ pw.print(prefix);
+ pw.print("Disabled by UserManager: ");
+ pw.println(mDisabled);
+ pw.print(prefix);
+ pw.print("Setup complete: ");
+ pw.println(mSetupComplete);
if (mServiceInfo != null) {
- pw.print(prefix); pw.print("Service UID: ");
+ pw.print(prefix);
+ pw.print("Service UID: ");
pw.println(mServiceInfo.applicationInfo.uid);
}
pw.println();
diff --git a/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java b/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
index 35d5956..db2cb52 100644
--- a/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
+++ b/services/core/java/com/android/server/infra/FrameworkResourcesServiceNameResolver.java
@@ -15,6 +15,7 @@
*/
package com.android.server.infra;
+import android.annotation.ArrayRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringRes;
@@ -33,6 +34,7 @@
import com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter;
+import java.util.Arrays;
/**
* Gets the service name using a framework resources, temporarily changing the service if necessary
@@ -47,20 +49,20 @@
/** Handler message to {@link #resetTemporaryService(int)} */
private static final int MSG_RESET_TEMPORARY_SERVICE = 0;
- private final @NonNull Context mContext;
- private final @NonNull Object mLock = new Object();
- private final @StringRes int mResourceId;
- private @Nullable NameResolverListener mOnSetCallback;
-
+ @NonNull private final Context mContext;
+ @NonNull private final Object mLock = new Object();
+ @StringRes private final int mStringResourceId;
+ @ArrayRes private final int mArrayResourceId;
+ private final boolean mIsMultiple;
/**
- * Map of temporary service name set by {@link #setTemporaryService(int, String, int)},
+ * Map of temporary service name list set by {@link #setTemporaryServices(int, String[], int)},
* keyed by {@code userId}.
*
- * <p>Typically used by Shell command and/or CTS tests.
+ * <p>Typically used by Shell command and/or CTS tests to configure temporary services if
+ * mIsMultiple is true.
*/
@GuardedBy("mLock")
- private final SparseArray<String> mTemporaryServiceNames = new SparseArray<>();
-
+ private final SparseArray<String[]> mTemporaryServiceNamesList = new SparseArray<>();
/**
* Map of default services that have been disabled by
* {@link #setDefaultServiceEnabled(int, boolean)},keyed by {@code userId}.
@@ -69,7 +71,7 @@
*/
@GuardedBy("mLock")
private final SparseBooleanArray mDefaultServicesDisabled = new SparseBooleanArray();
-
+ @Nullable private NameResolverListener mOnSetCallback;
/**
* When the temporary service will expire (and reset back to the default).
*/
@@ -85,7 +87,22 @@
public FrameworkResourcesServiceNameResolver(@NonNull Context context,
@StringRes int resourceId) {
mContext = context;
- mResourceId = resourceId;
+ mStringResourceId = resourceId;
+ mArrayResourceId = -1;
+ mIsMultiple = false;
+ }
+
+ public FrameworkResourcesServiceNameResolver(@NonNull Context context,
+ @ArrayRes int resourceId, boolean isMultiple) {
+ if (!isMultiple) {
+ throw new UnsupportedOperationException("Please use "
+ + "FrameworkResourcesServiceNameResolver(context, @StringRes int) constructor "
+ + "if single service mode is requested.");
+ }
+ mContext = context;
+ mStringResourceId = -1;
+ mArrayResourceId = resourceId;
+ mIsMultiple = true;
}
@Override
@@ -96,22 +113,31 @@
}
@Override
- public String getDefaultServiceName(@UserIdInt int userId) {
- synchronized (mLock) {
- final String name = mContext.getString(mResourceId);
- return TextUtils.isEmpty(name) ? null : name;
- }
+ public String getServiceName(@UserIdInt int userId) {
+ String[] serviceNames = getServiceNameList(userId);
+ return (serviceNames == null || serviceNames.length == 0) ? null : serviceNames[0];
}
@Override
- public String getServiceName(@UserIdInt int userId) {
+ public String getDefaultServiceName(@UserIdInt int userId) {
+ String[] serviceNames = getDefaultServiceNameList(userId);
+ return (serviceNames == null || serviceNames.length == 0) ? null : serviceNames[0];
+ }
+
+ /**
+ * Gets the default list of the service names for the given user.
+ *
+ * <p>Typically implemented by services which want to provide multiple backends.
+ */
+ @Override
+ public String[] getServiceNameList(int userId) {
synchronized (mLock) {
- final String temporaryName = mTemporaryServiceNames.get(userId);
- if (temporaryName != null) {
+ String[] temporaryNames = mTemporaryServiceNamesList.get(userId);
+ if (temporaryNames != null) {
// Always log it, as it should only be used on CTS or during development
- Slog.w(TAG, "getServiceName(): using temporary name " + temporaryName
- + " for user " + userId);
- return temporaryName;
+ Slog.w(TAG, "getServiceName(): using temporary name "
+ + Arrays.toString(temporaryNames) + " for user " + userId);
+ return temporaryNames;
}
final boolean disabled = mDefaultServicesDisabled.get(userId);
if (disabled) {
@@ -120,22 +146,50 @@
+ "user " + userId);
return null;
}
- return getDefaultServiceName(userId);
+ return getDefaultServiceNameList(userId);
+
}
}
+ /**
+ * Gets the default list of the service names for the given user.
+ *
+ * <p>Typically implemented by services which want to provide multiple backends.
+ */
+ @Override
+ public String[] getDefaultServiceNameList(int userId) {
+ synchronized (mLock) {
+ if (mIsMultiple) {
+ return mContext.getResources().getStringArray(mArrayResourceId);
+ } else {
+ final String name = mContext.getString(mStringResourceId);
+ return TextUtils.isEmpty(name) ? new String[0] : new String[] { name };
+ }
+ }
+ }
+
+ @Override
+ public boolean isConfiguredInMultipleMode() {
+ return mIsMultiple;
+ }
+
@Override
public boolean isTemporary(@UserIdInt int userId) {
synchronized (mLock) {
- return mTemporaryServiceNames.get(userId) != null;
+ return mTemporaryServiceNamesList.get(userId) != null;
}
}
@Override
public void setTemporaryService(@UserIdInt int userId, @NonNull String componentName,
int durationMs) {
+ setTemporaryServices(userId, new String[]{componentName}, durationMs);
+ }
+
+ @Override
+ public void setTemporaryServices(int userId, @NonNull String[] componentNames, int durationMs) {
synchronized (mLock) {
- mTemporaryServiceNames.put(userId, componentName);
+ mTemporaryServiceNamesList.put(userId, componentNames);
if (mTemporaryHandler == null) {
mTemporaryHandler = new Handler(Looper.getMainLooper(), null, true) {
@@ -155,8 +209,10 @@
}
mTemporaryServiceExpiration = SystemClock.elapsedRealtime() + durationMs;
mTemporaryHandler.sendEmptyMessageDelayed(MSG_RESET_TEMPORARY_SERVICE, durationMs);
- notifyTemporaryServiceNameChangedLocked(userId, componentName,
- /* isTemporary= */ true);
+ for (int i = 0; i < componentNames.length; i++) {
+ notifyTemporaryServiceNameChangedLocked(userId, componentNames[i],
+ /* isTemporary= */ true);
+ }
}
}
@@ -164,8 +220,8 @@
public void resetTemporaryService(@UserIdInt int userId) {
synchronized (mLock) {
Slog.i(TAG, "resetting temporary service for user " + userId + " from "
- + mTemporaryServiceNames.get(userId));
- mTemporaryServiceNames.remove(userId);
+ + Arrays.toString(mTemporaryServiceNamesList.get(userId)));
+ mTemporaryServiceNamesList.remove(userId);
if (mTemporaryHandler != null) {
mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_SERVICE);
mTemporaryHandler = null;
@@ -207,16 +263,21 @@
@Override
public String toString() {
- return "FrameworkResourcesServiceNamer[temps=" + mTemporaryServiceNames + "]";
+ synchronized (mLock) {
+ return "FrameworkResourcesServiceNamer[temps=" + mTemporaryServiceNamesList + "]";
+ }
}
// TODO(b/117779333): support proto
@Override
public void dumpShort(@NonNull PrintWriter pw) {
synchronized (mLock) {
- pw.print("FrameworkResourcesServiceNamer: resId="); pw.print(mResourceId);
- pw.print(", numberTemps="); pw.print(mTemporaryServiceNames.size());
- pw.print(", enabledDefaults="); pw.print(mDefaultServicesDisabled.size());
+ pw.print("FrameworkResourcesServiceNamer: resId=");
+ pw.print(mStringResourceId);
+ pw.print(", numberTemps=");
+ pw.print(mTemporaryServiceNamesList.size());
+ pw.print(", enabledDefaults=");
+ pw.print(mDefaultServicesDisabled.size());
}
}
@@ -224,13 +285,17 @@
@Override
public void dumpShort(@NonNull PrintWriter pw, @UserIdInt int userId) {
synchronized (mLock) {
- final String temporaryName = mTemporaryServiceNames.get(userId);
- if (temporaryName != null) {
- pw.print("tmpName="); pw.print(temporaryName);
+ final String[] temporaryNames = mTemporaryServiceNamesList.get(userId);
+ if (temporaryNames != null) {
+ pw.print("tmpName=");
+ pw.print(Arrays.toString(temporaryNames));
final long ttl = mTemporaryServiceExpiration - SystemClock.elapsedRealtime();
- pw.print(" (expires in "); TimeUtils.formatDuration(ttl, pw); pw.print("), ");
+ pw.print(" (expires in ");
+ TimeUtils.formatDuration(ttl, pw);
+ pw.print("), ");
}
- pw.print("defaultName="); pw.print(getDefaultServiceName(userId));
+ pw.print("defaultName=");
+ pw.print(getDefaultServiceName(userId));
final boolean disabled = mDefaultServicesDisabled.get(userId);
pw.println(disabled ? " (disabled)" : " (enabled)");
}
diff --git a/services/core/java/com/android/server/infra/OWNERS b/services/core/java/com/android/server/infra/OWNERS
new file mode 100644
index 0000000..0466d8a
--- /dev/null
+++ b/services/core/java/com/android/server/infra/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 655446
+
+include /core/java/android/service/cloudsearch/OWNERS
diff --git a/services/core/java/com/android/server/infra/ServiceNameResolver.java b/services/core/java/com/android/server/infra/ServiceNameResolver.java
index e20c459..7d85fdb4 100644
--- a/services/core/java/com/android/server/infra/ServiceNameResolver.java
+++ b/services/core/java/com/android/server/infra/ServiceNameResolver.java
@@ -34,7 +34,7 @@
/**
* Listener for name changes.
*/
- public interface NameResolverListener {
+ interface NameResolverListener {
/**
* The name change callback.
@@ -64,6 +64,30 @@
String getDefaultServiceName(@UserIdInt int userId);
/**
+ * Gets the default list of names of the services for the given user.
+ *
+ * <p>Typically implemented by reading a Settings property or framework resource.
+ */
+ @Nullable
+ default String[] getDefaultServiceNameList(@UserIdInt int userId) {
+ if (isConfiguredInMultipleMode()) {
+ throw new UnsupportedOperationException("getting default service list not supported");
+ } else {
+ return new String[] { getDefaultServiceName(userId) };
+ }
+ }
+
+ /**
+ * Returns whether the resolver is configured to connect to multiple backend services.
+ * The default return type is false.
+ *
+ * <p>Typically implemented by reading a Settings property or framework resource.
+ */
+ default boolean isConfiguredInMultipleMode() {
+ return false;
+ }
+
+ /**
* Gets the current name of the service for the given user
*
* @return either the temporary name (set by
@@ -76,6 +100,18 @@
}
/**
+ * Gets the current name of the service for the given user
+ *
+ * @return either the temporary name (set by
+ * {@link #setTemporaryService(int, String, int)}, or the
+ * {@link #getDefaultServiceName(int) default name}.
+ */
+ @Nullable
+ default String[] getServiceNameList(@UserIdInt int userId) {
+ return getDefaultServiceNameList(userId);
+ }
+
+ /**
* Checks whether the current service is temporary for the given user.
*/
default boolean isTemporary(@SuppressWarnings("unused") @UserIdInt int userId) {
@@ -85,11 +121,11 @@
/**
* Temporarily sets the service implementation for the given user.
*
- * @param userId user handle
+ * @param userId user handle
* @param componentName name of the new component
- * @param durationMs how long the change will be valid (the service will be automatically reset
- * to the default component after this timeout expires).
- *
+ * @param durationMs how long the change will be valid (the service will be automatically
+ * reset
+ * to the default component after this timeout expires).
* @throws UnsupportedOperationException if not implemented.
*/
default void setTemporaryService(@UserIdInt int userId, @NonNull String componentName,
@@ -98,10 +134,24 @@
}
/**
+ * Temporarily sets the service implementation for the given user.
+ *
+ * @param userId user handle
+ * @param componentNames list of the names of the new component
+ * @param durationMs how long the change will be valid (the service will be automatically
+ * reset
+ * to the default component after this timeout expires).
+ * @throws UnsupportedOperationException if not implemented.
+ */
+ default void setTemporaryServices(@UserIdInt int userId, @NonNull String[] componentNames,
+ int durationMs) {
+ throw new UnsupportedOperationException("temporary user not supported");
+ }
+
+ /**
* Resets the temporary service implementation to the default component for the given user.
*
* @param userId user handle
- *
* @throws UnsupportedOperationException if not implemented.
*/
default void resetTemporaryService(@UserIdInt int userId) {
@@ -114,11 +164,11 @@
* <p>Typically used during CTS tests to make sure only the default service doesn't interfere
* with the test results.
*
- * @param userId user handle
+ * @param userId user handle
* @param enabled whether the default service should be used when the temporary service is not
- * set. If the service enabled state is already that value, the command is ignored and this
- * method return {@code false}.
- *
+ * set. If the service enabled state is already that value, the command is
+ * ignored and this
+ * method return {@code false}.
* @return whether the enabled state changed.
* @throws UnsupportedOperationException if not implemented.
*/
@@ -133,7 +183,6 @@
* with the test results.
*
* @param userId user handle
- *
* @throws UnsupportedOperationException if not implemented.
*/
default boolean isDefaultServiceEnabled(@UserIdInt int userId) {
diff --git a/services/core/java/com/android/server/locales/LocaleManagerService.java b/services/core/java/com/android/server/locales/LocaleManagerService.java
index c427705..176c08c 100644
--- a/services/core/java/com/android/server/locales/LocaleManagerService.java
+++ b/services/core/java/com/android/server/locales/LocaleManagerService.java
@@ -78,10 +78,20 @@
Process.THREAD_PRIORITY_BACKGROUND);
broadcastHandlerThread.start();
+ SystemAppUpdateTracker systemAppUpdateTracker =
+ new SystemAppUpdateTracker(this);
+ broadcastHandlerThread.getThreadHandler().postAtFrontOfQueue(new Runnable() {
+ @Override
+ public void run() {
+ systemAppUpdateTracker.init();
+ }
+ });
+
mBackupHelper = new LocaleManagerBackupHelper(this,
mPackageManagerInternal, broadcastHandlerThread);
- mPackageMonitor = new LocaleManagerServicePackageMonitor(mBackupHelper);
+ mPackageMonitor = new LocaleManagerServicePackageMonitor(mBackupHelper,
+ systemAppUpdateTracker);
mPackageMonitor.register(context, broadcastHandlerThread.getLooper(),
UserHandle.ALL,
true);
@@ -246,7 +256,7 @@
* <p><b>Note:</b> This is can be used by installers to deal with cases such as
* language-based APK Splits.
*/
- private void notifyInstallerOfAppWhoseLocaleChanged(String appPackageName, int userId,
+ void notifyInstallerOfAppWhoseLocaleChanged(String appPackageName, int userId,
LocaleList locales) {
String installingPackageName = getInstallingPackageName(appPackageName);
if (installingPackageName != null) {
@@ -271,7 +281,7 @@
mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
}
- private static Intent createBaseIntent(String intentAction, String appPackageName,
+ static Intent createBaseIntent(String intentAction, String appPackageName,
LocaleList locales) {
return new Intent(intentAction)
.putExtra(Intent.EXTRA_PACKAGE_NAME, appPackageName)
@@ -406,7 +416,7 @@
}
@Nullable
- private String getInstallingPackageName(String packageName) {
+ String getInstallingPackageName(String packageName) {
try {
return mContext.getPackageManager()
.getInstallSourceInfo(packageName).getInstallingPackageName();
diff --git a/services/core/java/com/android/server/locales/LocaleManagerServicePackageMonitor.java b/services/core/java/com/android/server/locales/LocaleManagerServicePackageMonitor.java
index b459be7..32080ef 100644
--- a/services/core/java/com/android/server/locales/LocaleManagerServicePackageMonitor.java
+++ b/services/core/java/com/android/server/locales/LocaleManagerServicePackageMonitor.java
@@ -23,13 +23,22 @@
*
* <p> These listeners forward the call to different aspects of locale service that
* handle the business logic.
- * <p> We're interested in package added, package data cleared and package removed events.
+ * <p> We're interested in the following events:
+ * <ul>
+ * <li> Package added
+ * <li> Package data cleared
+ * <li> Package removed
+ * <li> Package Updated
+ * </ul>
*/
final class LocaleManagerServicePackageMonitor extends PackageMonitor {
private LocaleManagerBackupHelper mBackupHelper;
+ private SystemAppUpdateTracker mSystemAppUpdateTracker;
- LocaleManagerServicePackageMonitor(LocaleManagerBackupHelper localeManagerBackupHelper) {
+ LocaleManagerServicePackageMonitor(LocaleManagerBackupHelper localeManagerBackupHelper,
+ SystemAppUpdateTracker systemAppUpdateTracker) {
mBackupHelper = localeManagerBackupHelper;
+ mSystemAppUpdateTracker = systemAppUpdateTracker;
}
@Override
@@ -46,4 +55,9 @@
public void onPackageRemoved(String packageName, int uid) {
mBackupHelper.onPackageRemoved();
}
+
+ @Override
+ public void onPackageUpdateFinished(String packageName, int uid) {
+ mSystemAppUpdateTracker.onPackageUpdateFinished(packageName, uid);
+ }
}
diff --git a/services/core/java/com/android/server/locales/SystemAppUpdateTracker.java b/services/core/java/com/android/server/locales/SystemAppUpdateTracker.java
new file mode 100644
index 0000000..d13b1f4
--- /dev/null
+++ b/services/core/java/com/android/server/locales/SystemAppUpdateTracker.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locales;
+
+import static com.android.server.locales.LocaleManagerService.DEBUG;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Environment;
+import android.os.LocaleList;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.XmlUtils;
+
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Track if a system app is being updated for the first time after the user completed device setup.
+ *
+ * <p> The entire operation is being done on a background thread from {@link LocaleManagerService}.
+ * If it is the first time that a system app is being updated, then it fetches the app-specific
+ * locales and sends a broadcast to the newly set installer of the app. It maintains a file to store
+ * the name of the apps that have been updated.
+ */
+public class SystemAppUpdateTracker {
+ private static final String TAG = "SystemAppUpdateTracker";
+ private static final String PACKAGE_XML_TAG = "package";
+ private static final String ATTR_NAME = "name";
+ private static final String SYSTEM_APPS_XML_TAG = "system_apps";
+
+ private final Context mContext;
+ private final LocaleManagerService mLocaleManagerService;
+ private final AtomicFile mUpdatedAppsFile;
+
+ // Lock used while writing to the file.
+ private final Object mFileLock = new Object();
+
+ // In-memory list of all the system apps that have been updated once after device setup.
+ // We do not need to store the userid->packages mapping because when updating a system app on
+ // one user updates for all users.
+ private final Set<String> mUpdatedApps = new HashSet<>();
+
+ SystemAppUpdateTracker(LocaleManagerService localeManagerService) {
+ this(localeManagerService.mContext, localeManagerService, new AtomicFile(
+ new File(Environment.getDataSystemDirectory(),
+ /* child = */ "locale_manager_service_updated_system_apps.xml")));
+ }
+
+ @VisibleForTesting
+ SystemAppUpdateTracker(Context context, LocaleManagerService localeManagerService,
+ AtomicFile file) {
+ mContext = context;
+ mLocaleManagerService = localeManagerService;
+ mUpdatedAppsFile = file;
+ }
+
+ /**
+ * Loads the info of updated system apps from the file.
+ *
+ * <p> Invoked once during device boot from {@link LocaleManagerService} by a background thread.
+ */
+ void init() {
+ if (DEBUG) {
+ Slog.d(TAG, "Loading the app info from storage. ");
+ }
+ loadUpdatedSystemApps();
+ }
+
+ /**
+ * Reads the XML stored in the {@link #mUpdatedAppsFile} and populates it in the in-memory list
+ * {@link #mUpdatedApps}.
+ */
+ private void loadUpdatedSystemApps() {
+ if (!mUpdatedAppsFile.getBaseFile().exists()) {
+ if (DEBUG) {
+ Slog.d(TAG, "loadUpdatedSystemApps: File does not exist.");
+ }
+ return;
+ }
+ InputStream updatedAppNamesInputStream = null;
+ try {
+ updatedAppNamesInputStream = mUpdatedAppsFile.openRead();
+ readFromXml(updatedAppNamesInputStream);
+ } catch (IOException | XmlPullParserException e) {
+ Slog.e(TAG, "loadUpdatedSystemApps: Could not parse storage file ", e);
+ } finally {
+ IoUtils.closeQuietly(updatedAppNamesInputStream);
+ }
+ }
+
+ /**
+ * Parses the update data from the serialized XML input stream.
+ */
+ private void readFromXml(InputStream updateInfoInputStream)
+ throws XmlPullParserException, IOException {
+ final TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(updateInfoInputStream, StandardCharsets.UTF_8.name());
+ XmlUtils.beginDocument(parser, SYSTEM_APPS_XML_TAG);
+ int depth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, depth)) {
+ if (parser.getName().equals(PACKAGE_XML_TAG)) {
+ String packageName = parser.getAttributeValue(/* namespace= */ null,
+ ATTR_NAME);
+ if (!TextUtils.isEmpty(packageName)) {
+ mUpdatedApps.add(packageName);
+ }
+ }
+ }
+ }
+
+ /**
+ * Sends a broadcast to the newly set installer with app-locales if it is a system app being
+ * updated for the first time.
+ *
+ * <p><b>Note:</b> Invoked by service's common monitor
+ * {@link LocaleManagerServicePackageMonitor#onPackageUpdateFinished} when a package updated.
+ */
+ void onPackageUpdateFinished(String packageName, int uid) {
+ try {
+ if ((!mUpdatedApps.contains(packageName)) && isUpdatedSystemApp(packageName)) {
+ // If a system app is updated, verify that it has an installer-on-record.
+ String installingPackageName = mLocaleManagerService.getInstallingPackageName(
+ packageName);
+ if (installingPackageName == null) {
+ // We want to broadcast the locales info to the installer.
+ // If this app does not have an installer then do nothing.
+ return;
+ }
+
+ try {
+ int userId = UserHandle.getUserId(uid);
+ // Fetch the app-specific locales.
+ // If non-empty then send the info to the installer.
+ LocaleList appLocales = mLocaleManagerService.getApplicationLocales(
+ packageName, userId);
+ if (!appLocales.isEmpty()) {
+ // The broadcast would be sent to the newly set installer of the
+ // updated system app.
+ mLocaleManagerService.notifyInstallerOfAppWhoseLocaleChanged(packageName,
+ userId, appLocales);
+ }
+ } catch (RemoteException e) {
+ if (DEBUG) {
+ Slog.d(TAG, "onPackageUpdateFinished: Error in fetching app locales");
+ }
+ }
+ updateBroadcastedAppsList(packageName);
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "Exception in onPackageUpdateFinished.", e);
+ }
+ }
+
+ /**
+ * Writes in-memory data {@link #mUpdatedApps} to the storage file in a synchronized manner.
+ */
+ private void updateBroadcastedAppsList(String packageName) {
+ synchronized (mFileLock) {
+ mUpdatedApps.add(packageName);
+ writeUpdatedAppsFileLocked();
+ }
+ }
+
+ private void writeUpdatedAppsFileLocked() {
+ FileOutputStream stream = null;
+ try {
+ stream = mUpdatedAppsFile.startWrite();
+ writeToXmlLocked(stream);
+ mUpdatedAppsFile.finishWrite(stream);
+ } catch (IOException e) {
+ mUpdatedAppsFile.failWrite(stream);
+ Slog.e(TAG, "Failed to persist the updated apps list", e);
+ }
+ }
+
+ /**
+ * Converts the list of updated app data into a serialized xml stream.
+ */
+ private void writeToXmlLocked(OutputStream stream) throws IOException {
+ final TypedXmlSerializer xml = Xml.newFastSerializer();
+ xml.setOutput(stream, StandardCharsets.UTF_8.name());
+ xml.startDocument(/* encoding= */ null, /* standalone= */ true);
+ xml.startTag(/* namespace= */ null, SYSTEM_APPS_XML_TAG);
+
+ for (String packageName : mUpdatedApps) {
+ xml.startTag(/* namespace= */ null, PACKAGE_XML_TAG);
+ xml.attribute(/* namespace= */ null, ATTR_NAME, packageName);
+ xml.endTag(/* namespace= */ null, PACKAGE_XML_TAG);
+ }
+
+ xml.endTag(null, SYSTEM_APPS_XML_TAG);
+ xml.endDocument();
+ }
+
+ private boolean isUpdatedSystemApp(String packageName) {
+ ApplicationInfo appInfo = null;
+ try {
+ appInfo = mContext.getPackageManager().getApplicationInfo(packageName,
+ PackageManager.ApplicationInfoFlags.of(PackageManager.MATCH_SYSTEM_ONLY));
+ } catch (PackageManager.NameNotFoundException e) {
+ if (DEBUG) {
+ Slog.d(TAG, "isUpdatedSystemApp: Package not found " + packageName);
+ }
+ }
+ if (appInfo == null) {
+ return false;
+ }
+ return (appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
+ }
+
+ @VisibleForTesting
+ Set<String> getUpdatedApps() {
+ return mUpdatedApps;
+ }
+}
diff --git a/services/core/java/com/android/server/location/geofence/GeofenceManager.java b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
index 5093f5d..b6342a4 100644
--- a/services/core/java/com/android/server/location/geofence/GeofenceManager.java
+++ b/services/core/java/com/android/server/location/geofence/GeofenceManager.java
@@ -19,7 +19,6 @@
import static android.location.LocationManager.FUSED_PROVIDER;
import static android.location.LocationManager.KEY_PROXIMITY_ENTERING;
-import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
import android.annotation.Nullable;
@@ -41,6 +40,7 @@
import android.util.ArraySet;
import com.android.internal.annotations.GuardedBy;
+import com.android.server.FgThread;
import com.android.server.PendingIntentUtils;
import com.android.server.location.LocationPermissions;
import com.android.server.location.injector.Injector;
@@ -396,7 +396,7 @@
protected boolean registerWithService(LocationRequest locationRequest,
Collection<GeofenceRegistration> registrations) {
getLocationManager().requestLocationUpdates(FUSED_PROVIDER, locationRequest,
- DIRECT_EXECUTOR, this);
+ FgThread.getExecutor(), this);
return true;
}
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index acbee11..721ef1e 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -201,18 +201,54 @@
@Override
public void deliverOnLocationChanged(LocationResult locationResult,
@Nullable IRemoteCallback onCompleteCallback) throws RemoteException {
- mListener.onLocationChanged(locationResult.asList(), onCompleteCallback);
+ try {
+ mListener.onLocationChanged(locationResult.asList(), onCompleteCallback);
+ } catch (RuntimeException e) {
+ // the only way a runtime exception can be thrown here is if the client is in the
+ // system server process (so that the binder call is executed directly, rather than
+ // asynchronously in another process), and the client is using a direct executor (so
+ // any client exceptions bubble directly back to us). we move any exception onto
+ // another thread so that it can't cause further problems
+ RuntimeException wrapper = new RuntimeException(e);
+ FgThread.getExecutor().execute(() -> {
+ throw wrapper;
+ });
+ }
}
@Override
public void deliverOnFlushComplete(int requestCode) throws RemoteException {
- mListener.onFlushComplete(requestCode);
+ try {
+ mListener.onFlushComplete(requestCode);
+ } catch (RuntimeException e) {
+ // the only way a runtime exception can be thrown here is if the client is in the
+ // system server process (so that the binder call is executed directly, rather than
+ // asynchronously in another process), and the client is using a direct executor (so
+ // any client exceptions bubble directly back to us). we move any exception onto
+ // another thread so that it can't cause further problems
+ RuntimeException wrapper = new RuntimeException(e);
+ FgThread.getExecutor().execute(() -> {
+ throw wrapper;
+ });
+ }
}
@Override
public void deliverOnProviderEnabledChanged(String provider, boolean enabled)
throws RemoteException {
- mListener.onProviderEnabledChanged(provider, enabled);
+ try {
+ mListener.onProviderEnabledChanged(provider, enabled);
+ } catch (RuntimeException e) {
+ // the only way a runtime exception can be thrown here is if the client is in the
+ // system server process (so that the binder call is executed directly, rather than
+ // asynchronously in another process), and the client is using a direct executor (so
+ // any client exceptions bubble directly back to us). we move any exception onto
+ // another thread so that it can't cause further problems
+ RuntimeException wrapper = new RuntimeException(e);
+ FgThread.getExecutor().execute(() -> {
+ throw wrapper;
+ });
+ }
}
}
@@ -294,10 +330,23 @@
throws RemoteException {
// ILocationCallback doesn't currently support completion callbacks
Preconditions.checkState(onCompleteCallback == null);
- if (locationResult != null) {
- mCallback.onLocation(locationResult.getLastLocation());
- } else {
- mCallback.onLocation(null);
+
+ try {
+ if (locationResult != null) {
+ mCallback.onLocation(locationResult.getLastLocation());
+ } else {
+ mCallback.onLocation(null);
+ }
+ } catch (RuntimeException e) {
+ // the only way a runtime exception can be thrown here is if the client is in the
+ // system server process (so that the binder call is executed directly, rather than
+ // asynchronously in another process), and the client is using a direct executor (so
+ // any client exceptions bubble directly back to us). we move any exception onto
+ // another thread so that it can't cause further problems
+ RuntimeException wrapper = new RuntimeException(e);
+ FgThread.getExecutor().execute(() -> {
+ throw wrapper;
+ });
}
}
diff --git a/services/core/java/com/android/server/notification/GroupHelper.java b/services/core/java/com/android/server/notification/GroupHelper.java
index 9cb8a01..4f26809 100644
--- a/services/core/java/com/android/server/notification/GroupHelper.java
+++ b/services/core/java/com/android/server/notification/GroupHelper.java
@@ -82,7 +82,7 @@
}
String combinedKey = generatePackageGroupKey(userId, sbn.getPackageName(), group);
boolean needsOngoingFlag = notifications.size() > 0;
- mCallback.updateAutogroupSummary(sbn.getKey(), needsOngoingFlag);
+ mCallback.updateAutogroupSummary(userId, sbn.getPackageName(), needsOngoingFlag);
}
public void onNotificationUpdated(StatusBarNotification childSbn,
@@ -211,6 +211,6 @@
void removeAutoGroup(String key);
void addAutoGroupSummary(int userId, String pkg, String triggeringKey);
void removeAutoGroupSummary(int user, String pkg);
- void updateAutogroupSummary(String key, boolean needsOngoingFlag);
+ void updateAutogroupSummary(int userId, String pkg, boolean needsOngoingFlag);
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index a711b44..050cfea 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2587,19 +2587,11 @@
}
@Override
- public void updateAutogroupSummary(String key, boolean needsOngoingFlag) {
- String pkg;
- synchronized (mNotificationLock) {
- NotificationRecord r = mNotificationsByKey.get(key);
- pkg = r != null && r.getSbn() != null ? r.getSbn().getPackageName() : null;
- }
+ public void updateAutogroupSummary(int userId, String pkg, boolean needsOngoingFlag) {
boolean isAppForeground = pkg != null
&& mActivityManager.getPackageImportance(pkg) == IMPORTANCE_FOREGROUND;
synchronized (mNotificationLock) {
- NotificationRecord r = mNotificationsByKey.get(key);
- if (r == null) return;
- updateAutobundledSummaryFlags(r.getUser().getIdentifier(),
- r.getSbn().getPackageName(), needsOngoingFlag, isAppForeground);
+ updateAutobundledSummaryFlags(userId, pkg, needsOngoingFlag, isAppForeground);
}
}
});
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 394c8fb..3e5ab1e 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -3906,13 +3906,17 @@
} else if (tagName.equals(TAG_PERMISSIONS)) {
final LegacyPermissionState legacyState;
if (packageSetting.hasSharedUser()) {
- legacyState = getSettingLPr(
- packageSetting.getSharedUserAppId()).getLegacyPermissionState();
+ final SettingBase sharedUserSettings = getSettingLPr(
+ packageSetting.getSharedUserAppId());
+ legacyState = sharedUserSettings != null
+ ? sharedUserSettings.getLegacyPermissionState() : null;
} else {
legacyState = packageSetting.getLegacyPermissionState();
}
- readInstallPermissionsLPr(parser, legacyState, users);
- packageSetting.setInstallPermissionsFixed(true);
+ if (legacyState != null) {
+ readInstallPermissionsLPr(parser, legacyState, users);
+ packageSetting.setInstallPermissionsFixed(true);
+ }
} else if (tagName.equals("proper-signing-keyset")) {
long id = parser.getAttributeLong(null, "identifier");
Integer refCt = mKeySetRefs.get(id);
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index b03db66..dcfb8b5 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -315,15 +315,15 @@
com.android.internal.R.string.reboot_to_update_reboot));
}
} else if (mReason != null && mReason.equals(PowerManager.REBOOT_RECOVERY)) {
- if (showSysuiReboot()) {
- return null;
- } else if (RescueParty.isAttemptingFactoryReset()) {
+ if (RescueParty.isAttemptingFactoryReset()) {
// We're not actually doing a factory reset yet; we're rebooting
// to ask the user if they'd like to reset, so give them a less
// scary dialog message.
pd.setTitle(context.getText(com.android.internal.R.string.power_off));
pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
pd.setIndeterminate(true);
+ } else if (showSysuiReboot()) {
+ return null;
} else {
// Factory reset path. Set the dialog message accordingly.
pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_reset_title));
diff --git a/services/core/java/com/android/server/security/AndroidKeystoreAttestationVerificationAttributes.java b/services/core/java/com/android/server/security/AndroidKeystoreAttestationVerificationAttributes.java
new file mode 100644
index 0000000..3543e93
--- /dev/null
+++ b/services/core/java/com/android/server/security/AndroidKeystoreAttestationVerificationAttributes.java
@@ -0,0 +1,468 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.security;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import com.android.framework.protobuf.ByteString;
+import com.android.internal.org.bouncycastle.asn1.ASN1Boolean;
+import com.android.internal.org.bouncycastle.asn1.ASN1Encodable;
+import com.android.internal.org.bouncycastle.asn1.ASN1Enumerated;
+import com.android.internal.org.bouncycastle.asn1.ASN1InputStream;
+import com.android.internal.org.bouncycastle.asn1.ASN1Integer;
+import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import com.android.internal.org.bouncycastle.asn1.ASN1OctetString;
+import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.internal.org.bouncycastle.asn1.ASN1Set;
+import com.android.internal.org.bouncycastle.asn1.ASN1TaggedObject;
+import com.android.internal.org.bouncycastle.asn1.x509.Certificate;
+
+import java.nio.charset.StandardCharsets;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Parsed {@link X509Certificate} attestation extension values for Android Keystore attestations.
+ *
+ * Pull fields out of the top-level sequence. A full description of this structure is at
+ * https://source.android.com/security/keystore/attestation.
+ *
+ * If a value is null or empty, then it was not set/found in the extension values.
+ *
+ */
+class AndroidKeystoreAttestationVerificationAttributes {
+ // The OID for the extension Android Keymaster puts into device-generated certificates.
+ private static final String ANDROID_KEYMASTER_KEY_DESCRIPTION_EXTENSION_OID =
+ "1.3.6.1.4.1.11129.2.1.17";
+
+ // ASN.1 sequence index values for the Android Keymaster extension.
+ private static final int ATTESTATION_VERSION_INDEX = 0;
+ private static final int ATTESTATION_SECURITY_LEVEL_INDEX = 1;
+ private static final int KEYMASTER_VERSION_INDEX = 2;
+ private static final int KEYMASTER_SECURITY_LEVEL_INDEX = 3;
+ private static final int ATTESTATION_CHALLENGE_INDEX = 4;
+ private static final int KEYMASTER_UNIQUE_ID_INDEX = 5;
+ private static final int SW_ENFORCED_INDEX = 6;
+ private static final int HW_ENFORCED_INDEX = 7;
+ private static final int VERIFIED_BOOT_KEY_INDEX = 0;
+ private static final int VERIFIED_BOOT_LOCKED_INDEX = 1;
+ private static final int VERIFIED_BOOT_STATE_INDEX = 2;
+ private static final int VERIFIED_BOOT_HASH_INDEX = 3;
+
+ // ASN.1 sequence index values for the Android Keystore application id.
+ private static final int PACKAGE_INFO_SET_INDEX = 0;
+ private static final int PACKAGE_SIGNATURE_SET_INDEX = 1;
+ private static final int PACKAGE_INFO_NAME_INDEX = 0;
+ private static final int PACKAGE_INFO_VERSION_INDEX = 1;
+
+ // See these AOSP files: hardware/libhardware/include/hardware/hw_auth_token.h
+ private static final int HW_AUTH_NONE = 0;
+
+ // Some keymaster constants. See this AOSP file:
+ // hardware/libhardware/include/hardware/keymaster_defs.h
+ private static final int KM_TAG_NO_AUTH_REQUIRED = 503;
+ private static final int KM_TAG_UNLOCKED_DEVICE_REQUIRED = 509;
+ private static final int KM_TAG_ALL_APPLICATIONS = 600;
+ private static final int KM_TAG_ROOT_OF_TRUST = 704;
+ private static final int KM_TAG_OS_VERSION = 705;
+ private static final int KM_TAG_OS_PATCHLEVEL = 706;
+ private static final int KM_TAG_ATTESTATION_APPLICATION_ID = 709;
+ private static final int KM_TAG_ATTESTATION_ID_BRAND = 710;
+ private static final int KM_TAG_ATTESTATION_ID_DEVICE = 711;
+ private static final int KM_TAG_ATTESTATION_ID_PRODUCT = 712;
+ private static final int KM_TAG_VENDOR_PATCHLEVEL = 718;
+ private static final int KM_TAG_BOOT_PATCHLEVEL = 719;
+
+ private static final int KM_SECURITY_LEVEL_SOFTWARE = 0;
+ private static final int KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT = 1;
+ private static final int KM_SECURITY_LEVEL_STRONG_BOX = 2;
+ private static final int KM_VERIFIED_BOOT_STATE_VERIFIED = 0;
+ private static final int KM_VERIFIED_BOOT_STATE_SELF_SIGNED = 1;
+ private static final int KM_VERIFIED_BOOT_STATE_UNVERIFIED = 2;
+ private static final int KM_VERIFIED_BOOT_STATE_FAILED = 3;
+
+ private Integer mAttestationVersion = null;
+ private SecurityLevel mAttestationSecurityLevel = null;
+ private boolean mAttestationHardwareBacked = false;
+ private Integer mKeymasterVersion = null;
+ private SecurityLevel mKeymasterSecurityLevel = null;
+ private boolean mKeymasterHardwareBacked = false;
+ private ByteString mAttestationChallenge = null;
+ private ByteString mKeymasterUniqueId = null;
+ private String mDeviceBrand = null;
+ private String mDeviceName = null;
+ private String mDeviceProductName = null;
+ private boolean mKeyAllowedForAllApplications = false;
+ private Integer mKeyAuthenticatorType = null;
+ private Integer mKeyBootPatchLevel = null;
+ private Integer mKeyOsPatchLevel = null;
+ private Integer mKeyOsVersion = null;
+ private Integer mKeyVendorPatchLevel = null;
+ private Boolean mKeyRequiresUnlockedDevice = null;
+ private ByteString mVerifiedBootHash = null;
+ private ByteString mVerifiedBootKey = null;
+ private Boolean mVerifiedBootLocked = null;
+ private VerifiedBootState mVerifiedBootState = null;
+ private Map<String, Long> mApplicationPackageNameVersion = null;
+ private List<ByteString> mApplicationCertificateDigests = null;
+
+ enum VerifiedBootState {
+ VERIFIED,
+ SELF_SIGNED,
+ UNVERIFIED,
+ FAILED
+ }
+
+ enum SecurityLevel {
+ SOFTWARE,
+ TRUSTED_ENVIRONMENT,
+ STRONG_BOX
+ }
+
+ /**
+ * Extracts attestation extension properties from {@link X509Certificate}
+ * and returns a {@link AndroidKeystoreAttestationVerificationAttributes} that encapsulates the
+ * properties.
+ */
+ @NonNull
+ static AndroidKeystoreAttestationVerificationAttributes fromCertificate(
+ @NonNull X509Certificate x509Certificate)
+ throws Exception {
+ return new AndroidKeystoreAttestationVerificationAttributes(x509Certificate);
+ }
+
+ int getAttestationVersion() {
+ return mAttestationVersion;
+ }
+
+ @Nullable
+ SecurityLevel getAttestationSecurityLevel() {
+ return mAttestationSecurityLevel;
+ }
+
+ boolean isAttestationHardwareBacked() {
+ return mAttestationHardwareBacked;
+ }
+
+ int getKeymasterVersion() {
+ return mKeymasterVersion;
+ }
+
+ @Nullable
+ SecurityLevel getKeymasterSecurityLevel() {
+ return mKeymasterSecurityLevel;
+ }
+
+ boolean isKeymasterHardwareBacked() {
+ return mKeymasterHardwareBacked;
+ }
+
+ @Nullable
+ ByteString getAttestationChallenge() {
+ return mAttestationChallenge;
+ }
+
+ @Nullable
+ ByteString getKeymasterUniqueId() {
+ return mKeymasterUniqueId;
+ }
+
+ @Nullable
+ String getDeviceBrand() {
+ return mDeviceBrand;
+ }
+
+ @Nullable
+ String getDeviceName() {
+ return mDeviceName;
+ }
+
+ @Nullable
+ String getDeviceProductName() {
+ return mDeviceProductName;
+ }
+
+ boolean isKeyAllowedForAllApplications() {
+ return mKeyAllowedForAllApplications;
+ }
+
+ int getKeyAuthenticatorType() {
+ if (mKeyAuthenticatorType == null) {
+ throw new IllegalStateException("KeyAuthenticatorType is not set.");
+ }
+ return mKeyAuthenticatorType;
+ }
+
+ int getKeyBootPatchLevel() {
+ if (mKeyBootPatchLevel == null) {
+ throw new IllegalStateException("KeyBootPatchLevel is not set.");
+ }
+ return mKeyBootPatchLevel;
+ }
+
+ int getKeyOsPatchLevel() {
+ if (mKeyOsPatchLevel == null) {
+ throw new IllegalStateException("KeyOsPatchLevel is not set.");
+ }
+ return mKeyOsPatchLevel;
+ }
+
+ int getKeyVendorPatchLevel() {
+ if (mKeyVendorPatchLevel == null) {
+ throw new IllegalStateException("KeyVendorPatchLevel is not set.");
+ }
+ return mKeyVendorPatchLevel;
+ }
+
+ int getKeyOsVersion() {
+ if (mKeyOsVersion == null) {
+ throw new IllegalStateException("KeyOsVersion is not set.");
+ }
+ return mKeyOsVersion;
+ }
+
+ boolean isKeyRequiresUnlockedDevice() {
+ if (mKeyRequiresUnlockedDevice == null) {
+ throw new IllegalStateException("KeyRequiresUnlockedDevice is not set.");
+ }
+ return mKeyRequiresUnlockedDevice;
+ }
+
+ @Nullable
+ ByteString getVerifiedBootHash() {
+ return mVerifiedBootHash;
+ }
+
+ @Nullable
+ ByteString getVerifiedBootKey() {
+ return mVerifiedBootKey;
+ }
+
+ boolean isVerifiedBootLocked() {
+ if (mVerifiedBootLocked == null) {
+ throw new IllegalStateException("VerifiedBootLocked is not set.");
+ }
+ return mVerifiedBootLocked;
+ }
+
+ @Nullable
+ VerifiedBootState getVerifiedBootState() {
+ return mVerifiedBootState;
+ }
+
+ @Nullable
+ Map<String, Long> getApplicationPackageNameVersion() {
+ return Collections.unmodifiableMap(mApplicationPackageNameVersion);
+ }
+
+ @Nullable
+ List<ByteString> getApplicationCertificateDigests() {
+ return Collections.unmodifiableList(mApplicationCertificateDigests);
+ }
+
+ private AndroidKeystoreAttestationVerificationAttributes(X509Certificate x509Certificate)
+ throws Exception {
+ Certificate certificate = Certificate.getInstance(
+ new ASN1InputStream(x509Certificate.getEncoded()).readObject());
+ ASN1Sequence keyAttributes = (ASN1Sequence) certificate.getTBSCertificate().getExtensions()
+ .getExtensionParsedValue(
+ new ASN1ObjectIdentifier(ANDROID_KEYMASTER_KEY_DESCRIPTION_EXTENSION_OID));
+ if (keyAttributes == null) {
+ throw new CertificateEncodingException(
+ "No attestation extension found in certificate.");
+ }
+ this.mAttestationVersion = getIntegerFromAsn1(
+ keyAttributes.getObjectAt(ATTESTATION_VERSION_INDEX));
+ this.mAttestationSecurityLevel = getSecurityLevelEnum(
+ keyAttributes.getObjectAt(ATTESTATION_SECURITY_LEVEL_INDEX));
+ this.mAttestationHardwareBacked =
+ this.mAttestationSecurityLevel == SecurityLevel.TRUSTED_ENVIRONMENT;
+ this.mAttestationChallenge = getOctetsFromAsn1(
+ keyAttributes.getObjectAt(ATTESTATION_CHALLENGE_INDEX));
+ this.mKeymasterVersion = getIntegerFromAsn1(
+ keyAttributes.getObjectAt(KEYMASTER_VERSION_INDEX));
+ this.mKeymasterUniqueId = getOctetsFromAsn1(
+ keyAttributes.getObjectAt(KEYMASTER_UNIQUE_ID_INDEX));
+ this.mKeymasterSecurityLevel = getSecurityLevelEnum(
+ keyAttributes.getObjectAt(KEYMASTER_SECURITY_LEVEL_INDEX));
+ this.mKeymasterHardwareBacked =
+ this.mKeymasterSecurityLevel == SecurityLevel.TRUSTED_ENVIRONMENT;
+
+ ASN1Encodable[] softwareEnforced = ((ASN1Sequence)
+ keyAttributes.getObjectAt(SW_ENFORCED_INDEX)).toArray();
+ for (ASN1Encodable entry : softwareEnforced) {
+ ASN1TaggedObject taggedEntry = (ASN1TaggedObject) entry;
+ switch (taggedEntry.getTagNo()) {
+ case KM_TAG_ATTESTATION_APPLICATION_ID:
+ parseAttestationApplicationId(
+ getOctetsFromAsn1(taggedEntry.getObject()).toByteArray());
+ break;
+ case KM_TAG_UNLOCKED_DEVICE_REQUIRED:
+ this.mKeyRequiresUnlockedDevice = getBoolFromAsn1(taggedEntry.getObject());
+ break;
+ default:
+ break;
+ }
+ }
+
+ ASN1Encodable[] hardwareEnforced = ((ASN1Sequence)
+ keyAttributes.getObjectAt(HW_ENFORCED_INDEX)).toArray();
+ for (ASN1Encodable entry : hardwareEnforced) {
+ ASN1TaggedObject taggedEntry = (ASN1TaggedObject) entry;
+ switch (taggedEntry.getTagNo()) {
+ case KM_TAG_NO_AUTH_REQUIRED:
+ this.mKeyAuthenticatorType = HW_AUTH_NONE;
+ break;
+ case KM_TAG_ALL_APPLICATIONS:
+ this.mKeyAllowedForAllApplications = true;
+ break;
+ case KM_TAG_ROOT_OF_TRUST:
+ ASN1Sequence rootOfTrust = (ASN1Sequence) taggedEntry.getObject();
+ this.mVerifiedBootKey =
+ getOctetsFromAsn1(rootOfTrust.getObjectAt(VERIFIED_BOOT_KEY_INDEX));
+ this.mVerifiedBootLocked =
+ getBoolFromAsn1(rootOfTrust.getObjectAt(VERIFIED_BOOT_LOCKED_INDEX));
+ this.mVerifiedBootState =
+ getVerifiedBootStateEnum(
+ rootOfTrust.getObjectAt(VERIFIED_BOOT_STATE_INDEX));
+ // The verified boot hash was added in structure version 3 (Keymaster 4.0).
+ if (mAttestationVersion >= 3) {
+ this.mVerifiedBootHash =
+ getOctetsFromAsn1(
+ rootOfTrust.getObjectAt(VERIFIED_BOOT_HASH_INDEX));
+ }
+ break;
+ case KM_TAG_OS_VERSION:
+ this.mKeyOsVersion = getIntegerFromAsn1(taggedEntry.getObject());
+ break;
+ case KM_TAG_OS_PATCHLEVEL:
+ this.mKeyOsPatchLevel = getIntegerFromAsn1(taggedEntry.getObject());
+ break;
+ case KM_TAG_ATTESTATION_ID_BRAND:
+ this.mDeviceBrand = getUtf8FromOctetsFromAsn1(taggedEntry.getObject());
+ break;
+ case KM_TAG_ATTESTATION_ID_DEVICE:
+ this.mDeviceName = getUtf8FromOctetsFromAsn1(taggedEntry.getObject());
+ break;
+ case KM_TAG_ATTESTATION_ID_PRODUCT:
+ this.mDeviceProductName = getUtf8FromOctetsFromAsn1(taggedEntry.getObject());
+ break;
+ case KM_TAG_VENDOR_PATCHLEVEL:
+ this.mKeyVendorPatchLevel = getIntegerFromAsn1(taggedEntry.getObject());
+ break;
+ case KM_TAG_BOOT_PATCHLEVEL:
+ this.mKeyBootPatchLevel = getIntegerFromAsn1(taggedEntry.getObject());
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ private void parseAttestationApplicationId(byte [] attestationApplicationId)
+ throws Exception {
+ ASN1Sequence outerSequence = ASN1Sequence.getInstance(
+ new ASN1InputStream(attestationApplicationId).readObject());
+ Map<String, Long> packageNameVersion = new HashMap<>();
+ ASN1Set packageInfoSet = (ASN1Set) outerSequence.getObjectAt(PACKAGE_INFO_SET_INDEX);
+ for (ASN1Encodable packageInfoEntry : packageInfoSet.toArray()) {
+ ASN1Sequence packageInfoSequence = (ASN1Sequence) packageInfoEntry;
+ packageNameVersion.put(
+ getUtf8FromOctetsFromAsn1(
+ packageInfoSequence.getObjectAt(PACKAGE_INFO_NAME_INDEX)),
+ getLongFromAsn1(packageInfoSequence.getObjectAt(PACKAGE_INFO_VERSION_INDEX)));
+ }
+ List<ByteString> certificateDigests = new ArrayList<>();
+ ASN1Set certificateDigestSet =
+ (ASN1Set) outerSequence.getObjectAt(PACKAGE_SIGNATURE_SET_INDEX);
+ for (ASN1Encodable certificateDigestEntry : certificateDigestSet.toArray()) {
+ certificateDigests.add(getOctetsFromAsn1(certificateDigestEntry));
+ }
+ this.mApplicationPackageNameVersion = Collections.unmodifiableMap(packageNameVersion);
+ this.mApplicationCertificateDigests = Collections.unmodifiableList(certificateDigests);
+
+ }
+
+ private VerifiedBootState getVerifiedBootStateEnum(ASN1Encodable asn1) {
+ int verifiedBoot = getEnumFromAsn1(asn1);
+ switch (verifiedBoot) {
+ case KM_VERIFIED_BOOT_STATE_VERIFIED:
+ return VerifiedBootState.VERIFIED;
+ case KM_VERIFIED_BOOT_STATE_SELF_SIGNED:
+ return VerifiedBootState.SELF_SIGNED;
+ case KM_VERIFIED_BOOT_STATE_UNVERIFIED:
+ return VerifiedBootState.UNVERIFIED;
+ case KM_VERIFIED_BOOT_STATE_FAILED:
+ return VerifiedBootState.FAILED;
+ default:
+ throw new IllegalArgumentException("Invalid verified boot state.");
+ }
+ }
+
+ private SecurityLevel getSecurityLevelEnum(ASN1Encodable asn1) {
+ int securityLevel = getEnumFromAsn1(asn1);
+ switch (securityLevel) {
+ case KM_SECURITY_LEVEL_SOFTWARE:
+ return SecurityLevel.SOFTWARE;
+ case KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT:
+ return SecurityLevel.TRUSTED_ENVIRONMENT;
+ case KM_SECURITY_LEVEL_STRONG_BOX:
+ return SecurityLevel.STRONG_BOX;
+ default:
+ throw new IllegalArgumentException("Invalid security level.");
+ }
+ }
+
+ @NonNull
+ private ByteString getOctetsFromAsn1(ASN1Encodable asn1) {
+ return ByteString.copyFrom(((ASN1OctetString) asn1).getOctets());
+ }
+
+ @NonNull
+ private String getUtf8FromOctetsFromAsn1(ASN1Encodable asn1) {
+ return new String(((ASN1OctetString) asn1).getOctets(), StandardCharsets.UTF_8);
+ }
+
+ @NonNull
+ private int getIntegerFromAsn1(ASN1Encodable asn1) {
+ return ((ASN1Integer) asn1).getValue().intValueExact();
+ }
+
+ @NonNull
+ private long getLongFromAsn1(ASN1Encodable asn1) {
+ return ((ASN1Integer) asn1).getValue().longValueExact();
+ }
+
+ @NonNull
+ private int getEnumFromAsn1(ASN1Encodable asn1) {
+ return ((ASN1Enumerated) asn1).getValue().intValueExact();
+ }
+
+ @Nullable
+ private Boolean getBoolFromAsn1(ASN1Encodable asn1) {
+ if (asn1 instanceof ASN1Boolean) {
+ return ((ASN1Boolean) asn1).isTrue();
+ }
+ return null;
+ }
+}
diff --git a/services/core/java/com/android/server/security/AttestationVerificationManagerService.java b/services/core/java/com/android/server/security/AttestationVerificationManagerService.java
index 243efb5..863f2d1 100644
--- a/services/core/java/com/android/server/security/AttestationVerificationManagerService.java
+++ b/services/core/java/com/android/server/security/AttestationVerificationManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.security;
+import static android.security.attestationverification.AttestationVerificationManager.PROFILE_PEER_DEVICE;
import static android.security.attestationverification.AttestationVerificationManager.PROFILE_SELF_TRUSTED;
import static android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE;
import static android.security.attestationverification.AttestationVerificationManager.RESULT_UNKNOWN;
@@ -44,9 +45,11 @@
public class AttestationVerificationManagerService extends SystemService {
private static final String TAG = "AVF";
+ private final AttestationVerificationPeerDeviceVerifier mPeerDeviceVerifier;
- public AttestationVerificationManagerService(final Context context) {
+ public AttestationVerificationManagerService(final Context context) throws Exception {
super(context);
+ mPeerDeviceVerifier = new AttestationVerificationPeerDeviceVerifier(context);
}
private final IBinder mService = new IAttestationVerificationManagerService.Stub() {
@@ -83,7 +86,7 @@
result.token = null;
switch (profile.getAttestationProfileId()) {
case PROFILE_SELF_TRUSTED:
- Slog.d(TAG, "Verifying Self trusted profile.");
+ Slog.d(TAG, "Verifying Self Trusted profile.");
try {
result.resultCode =
AttestationVerificationSelfTrustedVerifierForTesting.getInstance()
@@ -92,6 +95,11 @@
result.resultCode = RESULT_FAILURE;
}
break;
+ case PROFILE_PEER_DEVICE:
+ Slog.d(TAG, "Verifying Peer Device profile.");
+ result.resultCode = mPeerDeviceVerifier.verifyAttestation(
+ localBindingType, requirements, attestation);
+ break;
default:
Slog.d(TAG, "No profile found, defaulting.");
result.resultCode = RESULT_UNKNOWN;
diff --git a/services/core/java/com/android/server/security/AttestationVerificationPeerDeviceVerifier.java b/services/core/java/com/android/server/security/AttestationVerificationPeerDeviceVerifier.java
new file mode 100644
index 0000000..0f8be5a
--- /dev/null
+++ b/services/core/java/com/android/server/security/AttestationVerificationPeerDeviceVerifier.java
@@ -0,0 +1,510 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.security;
+
+import static android.security.attestationverification.AttestationVerificationManager.PARAM_CHALLENGE;
+import static android.security.attestationverification.AttestationVerificationManager.PARAM_PUBLIC_KEY;
+import static android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE;
+import static android.security.attestationverification.AttestationVerificationManager.RESULT_SUCCESS;
+import static android.security.attestationverification.AttestationVerificationManager.TYPE_CHALLENGE;
+import static android.security.attestationverification.AttestationVerificationManager.TYPE_PUBLIC_KEY;
+
+import static com.android.server.security.AndroidKeystoreAttestationVerificationAttributes.VerifiedBootState.VERIFIED;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Build;
+import android.os.Bundle;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+
+import org.json.JSONObject;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.net.URL;
+import java.security.cert.CertPath;
+import java.security.cert.CertPathValidator;
+import java.security.cert.CertPathValidatorException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.PKIXCertPathChecker;
+import java.security.cert.PKIXParameters;
+import java.security.cert.TrustAnchor;
+import java.security.cert.X509Certificate;
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Verifies Android key attestation according to the {@code PROFILE_PEER_DEVICE} profile.
+ *
+ * Trust anchors are vendor-defined via the vendor_required_attestation_certificates.xml resource.
+ * The profile is satisfied by checking all the following:
+ * * TrustAnchor match
+ * * Certificate validity
+ * * Android OS 10 or higher
+ * * Hardware backed key store
+ * * Verified boot locked
+ * * Remote Patch level must be within 1 year of local patch if local patch is less than 1 year old.
+ *
+ */
+class AttestationVerificationPeerDeviceVerifier {
+ private static final String TAG = "AVF";
+ private static final boolean DEBUG = Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.VERBOSE);
+ private static final int MAX_PATCH_AGE_MONTHS = 12;
+
+ private final Context mContext;
+ private final Set<TrustAnchor> mTrustAnchors;
+ private final boolean mRevocationEnabled;
+ private final LocalDate mTestSystemDate;
+ private final LocalDate mTestLocalPatchDate;
+ private CertificateFactory mCertificateFactory;
+ private CertPathValidator mCertPathValidator;
+
+ private static void debugVerboseLog(String str, Throwable t) {
+ if (DEBUG) {
+ Slog.v(TAG, str, t);
+ }
+ }
+
+ private static void debugVerboseLog(String str) {
+ if (DEBUG) {
+ Slog.v(TAG, str);
+ }
+ }
+
+ AttestationVerificationPeerDeviceVerifier(@NonNull Context context) throws Exception {
+ mContext = Objects.requireNonNull(context);
+ mCertificateFactory = CertificateFactory.getInstance("X.509");
+ mCertPathValidator = CertPathValidator.getInstance("PKIX");
+ mTrustAnchors = getTrustAnchors();
+ mRevocationEnabled = true;
+ mTestSystemDate = null;
+ mTestLocalPatchDate = null;
+ }
+
+ // Use ONLY for hermetic unit testing.
+ @VisibleForTesting
+ AttestationVerificationPeerDeviceVerifier(@NonNull Context context,
+ Set<TrustAnchor> trustAnchors, boolean revocationEnabled,
+ LocalDate systemDate, LocalDate localPatchDate) throws Exception {
+ mContext = Objects.requireNonNull(context);
+ mCertificateFactory = CertificateFactory.getInstance("X.509");
+ mCertPathValidator = CertPathValidator.getInstance("PKIX");
+ mTrustAnchors = trustAnchors;
+ mRevocationEnabled = revocationEnabled;
+ mTestSystemDate = systemDate;
+ mTestLocalPatchDate = localPatchDate;
+ }
+
+ /**
+ * Verifies attestation for public key or challenge local binding.
+ *
+ * The attestations must be suitable for {@link java.security.cert.CertificateFactory}
+ * The certificates in the attestation provided must be DER-encoded and may be supplied in
+ * binary or printable (Base64) encoding. If the certificate is provided in Base64 encoding,
+ * it must be bounded at the beginning by -----BEGIN CERTIFICATE-----, and must be bounded at
+ * the end by -----END CERTIFICATE-----.
+ *
+ * @param localBindingType Only {@code TYPE_PUBLIC_KEY} and {@code TYPE_CHALLENGE} supported.
+ * @param requirements Only {@code PARAM_PUBLIC_KEY} and {@code PARAM_CHALLENGE} supported.
+ * @param attestation Certificates should be DER encoded with leaf certificate appended first.
+ */
+ int verifyAttestation(
+ int localBindingType, @NonNull Bundle requirements, @NonNull byte[] attestation) {
+ int status = RESULT_FAILURE;
+
+ if (mCertificateFactory == null) {
+ debugVerboseLog("Was unable to initialize CertificateFactory onCreate.");
+ return status;
+ }
+
+ if (mCertPathValidator == null) {
+ debugVerboseLog("Was unable to initialize CertPathValidator onCreate.");
+ return status;
+ }
+
+ List<X509Certificate> certificates;
+ try {
+ certificates = getCertificates(attestation);
+ } catch (CertificateException e) {
+ debugVerboseLog("Unable to parse attestation certificates.", e);
+ return status;
+ }
+
+ if (certificates.isEmpty()) {
+ debugVerboseLog("Attestation contains no certificates.");
+ return status;
+ }
+
+ X509Certificate leafNode = certificates.get(0);
+ if (validateRequirements(localBindingType, requirements)
+ && validateCertificateChain(certificates)
+ && checkCertificateAttributes(leafNode, localBindingType, requirements)) {
+ status = RESULT_SUCCESS;
+ } else {
+ status = RESULT_FAILURE;
+ }
+ return status;
+ }
+
+ @NonNull
+ private List<X509Certificate> getCertificates(byte[] attestation)
+ throws CertificateException {
+ List<X509Certificate> certificates = new ArrayList<>();
+ ByteArrayInputStream bis = new ByteArrayInputStream(attestation);
+ while (bis.available() > 0) {
+ certificates.add((X509Certificate) mCertificateFactory.generateCertificate(bis));
+ }
+
+ return certificates;
+ }
+
+ private boolean validateRequirements(int localBindingType, Bundle requirements) {
+ if (requirements.size() != 1) {
+ debugVerboseLog("Requirements does not contain exactly 1 key.");
+ return false;
+ }
+
+ if (localBindingType != TYPE_PUBLIC_KEY && localBindingType != TYPE_CHALLENGE) {
+ debugVerboseLog("Binding type is not supported: " + localBindingType);
+ return false;
+ }
+
+ if (localBindingType == TYPE_PUBLIC_KEY && !requirements.containsKey(PARAM_PUBLIC_KEY)) {
+ debugVerboseLog("Requirements does not contain key: " + PARAM_PUBLIC_KEY);
+ return false;
+ }
+
+ if (localBindingType == TYPE_CHALLENGE && !requirements.containsKey(PARAM_CHALLENGE)) {
+ debugVerboseLog("Requirements does not contain key: " + PARAM_CHALLENGE);
+ return false;
+ }
+
+ return true;
+ }
+
+ private boolean validateCertificateChain(List<X509Certificate> certificates) {
+ if (certificates.size() < 2) {
+ debugVerboseLog("Certificate chain less than 2 in size.");
+ return false;
+ }
+
+ try {
+ CertPath certificatePath = mCertificateFactory.generateCertPath(certificates);
+ PKIXParameters validationParams = new PKIXParameters(mTrustAnchors);
+ if (mRevocationEnabled) {
+ // Checks Revocation Status List based on
+ // https://developer.android.com/training/articles/security-key-attestation#certificate_status
+ PKIXCertPathChecker checker = new AndroidRevocationStatusListChecker();
+ validationParams.addCertPathChecker(checker);
+ }
+ // Do not use built-in revocation status checker.
+ validationParams.setRevocationEnabled(false);
+ mCertPathValidator.validate(certificatePath, validationParams);
+ } catch (Throwable t) {
+ debugVerboseLog("Invalid certificate chain.", t);
+ return false;
+ }
+ return true;
+ }
+
+ private Set<TrustAnchor> getTrustAnchors() throws CertPathValidatorException {
+ Set<TrustAnchor> modifiableSet = new HashSet<>();
+ try {
+ for (String certString: getTrustAnchorResources()) {
+ modifiableSet.add(
+ new TrustAnchor((X509Certificate) mCertificateFactory.generateCertificate(
+ new ByteArrayInputStream(getCertificateBytes(certString))), null));
+ }
+ } catch (CertificateException e) {
+ e.printStackTrace();
+ throw new CertPathValidatorException("Invalid trust anchor certificate.", e);
+ }
+ return Collections.unmodifiableSet(modifiableSet);
+ }
+
+ private byte[] getCertificateBytes(String certString) {
+ String formattedCertString = certString.replaceAll("\\s+", "\n");
+ formattedCertString = formattedCertString.replaceAll(
+ "-BEGIN\\nCERTIFICATE-", "-BEGIN CERTIFICATE-");
+ formattedCertString = formattedCertString.replaceAll(
+ "-END\\nCERTIFICATE-", "-END CERTIFICATE-");
+ return formattedCertString.getBytes(UTF_8);
+ }
+
+ private String[] getTrustAnchorResources() {
+ return mContext.getResources().getStringArray(
+ R.array.vendor_required_attestation_certificates);
+ }
+
+ private boolean checkCertificateAttributes(
+ X509Certificate leafCertificate, int localBindingType, Bundle requirements) {
+ AndroidKeystoreAttestationVerificationAttributes attestationAttributes;
+ try {
+ attestationAttributes =
+ AndroidKeystoreAttestationVerificationAttributes.fromCertificate(
+ leafCertificate);
+ } catch (Throwable t) {
+ debugVerboseLog("Could not get ParsedAttestationAttributes from Certificate.", t);
+ return false;
+ }
+
+ // Checks for support of Keymaster 4.
+ if (attestationAttributes.getAttestationVersion() < 3) {
+ debugVerboseLog("Attestation version is not at least 3 (Keymaster 4).");
+ return false;
+ }
+
+ // Checks for support of Keymaster 4.
+ if (attestationAttributes.getKeymasterVersion() < 4) {
+ debugVerboseLog("Keymaster version is not at least 4.");
+ return false;
+ }
+
+ // First two characters are Android OS version.
+ if (attestationAttributes.getKeyOsVersion() < 100000) {
+ debugVerboseLog("Android OS version is not 10+.");
+ return false;
+ }
+
+ if (!attestationAttributes.isAttestationHardwareBacked()) {
+ debugVerboseLog("Key is not HW backed.");
+ return false;
+ }
+
+ if (!attestationAttributes.isKeymasterHardwareBacked()) {
+ debugVerboseLog("Keymaster is not HW backed.");
+ return false;
+ }
+
+ if (attestationAttributes.getVerifiedBootState() != VERIFIED) {
+ debugVerboseLog("Boot state not Verified.");
+ return false;
+ }
+
+ try {
+ if (!attestationAttributes.isVerifiedBootLocked()) {
+ debugVerboseLog("Verified boot state is not locked.");
+ return false;
+ }
+ } catch (IllegalStateException e) {
+ debugVerboseLog("VerifiedBootLocked is not set.", e);
+ return false;
+ }
+
+ // Patch level integer YYYYMM is expected to be within 1 year of today.
+ if (!isValidPatchLevel(attestationAttributes.getKeyOsPatchLevel())) {
+ debugVerboseLog("OS patch level is not within valid range.");
+ return false;
+ }
+
+ // Patch level integer YYYYMMDD is expected to be within 1 year of today.
+ if (!isValidPatchLevel(attestationAttributes.getKeyBootPatchLevel())) {
+ debugVerboseLog("Boot patch level is not within valid range.");
+ return false;
+ }
+
+ if (!isValidPatchLevel(attestationAttributes.getKeyVendorPatchLevel())) {
+ debugVerboseLog("Vendor patch level is not within valid range.");
+ return false;
+ }
+
+ if (!isValidPatchLevel(attestationAttributes.getKeyBootPatchLevel())) {
+ debugVerboseLog("Boot patch level is not within valid range.");
+ return false;
+ }
+
+ // Verify leaf public key matches provided public key.
+ if (localBindingType == TYPE_PUBLIC_KEY
+ && !Arrays.equals(requirements.getByteArray(PARAM_PUBLIC_KEY),
+ leafCertificate.getPublicKey().getEncoded())) {
+ debugVerboseLog("Provided public key does not match leaf certificate public key.");
+ return false;
+ }
+
+ // Verify challenge matches provided challenge.
+ if (localBindingType == TYPE_CHALLENGE
+ && !Arrays.equals(requirements.getByteArray(PARAM_CHALLENGE),
+ attestationAttributes.getAttestationChallenge().toByteArray())) {
+ debugVerboseLog("Provided challenge does not match leaf certificate challenge.");
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Validates patchLevel passed is within range of the local device patch date if local patch is
+ * not over one year old. Since the time can be changed on device, just checking the patch date
+ * is not enough. Therefore, we also confirm the patch level for the remote and local device are
+ * similar.
+ */
+ private boolean isValidPatchLevel(int patchLevel) {
+ LocalDate currentDate = mTestSystemDate != null
+ ? mTestSystemDate : LocalDate.now(ZoneId.systemDefault());
+
+ // Convert local patch date to LocalDate.
+ LocalDate localPatchDate;
+ try {
+ if (mTestLocalPatchDate != null) {
+ localPatchDate = mTestLocalPatchDate;
+ } else {
+ localPatchDate = LocalDate.parse(Build.VERSION.SECURITY_PATCH);
+ }
+ } catch (Throwable t) {
+ debugVerboseLog("Build.VERSION.SECURITY_PATCH: "
+ + Build.VERSION.SECURITY_PATCH + " is not in format YYYY-MM-DD");
+ return false;
+ }
+
+ // Check local patch date is not in last year of system clock.
+ if (ChronoUnit.MONTHS.between(localPatchDate, currentDate) > MAX_PATCH_AGE_MONTHS) {
+ return true;
+ }
+
+ // Convert remote patch dates to LocalDate.
+ String remoteDeviceDateStr = String.valueOf(patchLevel);
+ if (remoteDeviceDateStr.length() != 6 && remoteDeviceDateStr.length() != 8) {
+ debugVerboseLog("Patch level is not in format YYYYMM or YYYYMMDD");
+ return false;
+ }
+
+ int patchYear = Integer.parseInt(remoteDeviceDateStr.substring(0, 4));
+ int patchMonth = Integer.parseInt(remoteDeviceDateStr.substring(4, 6));
+ LocalDate remotePatchDate = LocalDate.of(patchYear, patchMonth, 1);
+
+ // Check patch dates are within 1 year of each other
+ boolean IsRemotePatchWithinOneYearOfLocalPatch;
+ if (remotePatchDate.compareTo(localPatchDate) > 0) {
+ IsRemotePatchWithinOneYearOfLocalPatch = ChronoUnit.MONTHS.between(
+ localPatchDate, remotePatchDate) <= MAX_PATCH_AGE_MONTHS;
+ } else if (remotePatchDate.compareTo(localPatchDate) < 0) {
+ IsRemotePatchWithinOneYearOfLocalPatch = ChronoUnit.MONTHS.between(
+ remotePatchDate, localPatchDate) <= MAX_PATCH_AGE_MONTHS;
+ } else {
+ IsRemotePatchWithinOneYearOfLocalPatch = true;
+ }
+
+ return IsRemotePatchWithinOneYearOfLocalPatch;
+ }
+
+ /**
+ * Checks certificate revocation status.
+ *
+ * Queries status list from android.googleapis.com/attestation/status and checks for
+ * the existence of certificate's serial number. If serial number exists in map, then fail.
+ */
+ private final class AndroidRevocationStatusListChecker extends PKIXCertPathChecker {
+ private static final String TOP_LEVEL_JSON_PROPERTY_KEY = "entries";
+ private static final String STATUS_PROPERTY_KEY = "status";
+ private static final String REASON_PROPERTY_KEY = "reason";
+ private String mStatusUrl;
+ private JSONObject mJsonStatusMap;
+
+ @Override
+ public void init(boolean forward) throws CertPathValidatorException {
+ mStatusUrl = getRevocationListUrl();
+ if (mStatusUrl == null || mStatusUrl.isEmpty()) {
+ throw new CertPathValidatorException(
+ "R.string.vendor_required_attestation_revocation_list_url is empty.");
+ }
+ // TODO(b/221067843): Update to only pull status map on non critical path and if
+ // out of date (24hrs).
+ mJsonStatusMap = getStatusMap(mStatusUrl);
+ }
+
+ @Override
+ public boolean isForwardCheckingSupported() {
+ return false;
+ }
+
+ @Override
+ public Set<String> getSupportedExtensions() {
+ return null;
+ }
+
+ @Override
+ public void check(Certificate cert, Collection<String> unresolvedCritExts)
+ throws CertPathValidatorException {
+ X509Certificate x509Certificate = (X509Certificate) cert;
+ // The json key is the certificate's serial number converted to lowercase hex.
+ String serialNumber = x509Certificate.getSerialNumber().toString(16);
+
+ if (serialNumber == null) {
+ throw new CertPathValidatorException("Certificate serial number can not be null.");
+ }
+
+ if (mJsonStatusMap.has(serialNumber)) {
+ JSONObject revocationStatus;
+ String status;
+ String reason;
+ try {
+ revocationStatus = mJsonStatusMap.getJSONObject(serialNumber);
+ status = revocationStatus.getString(STATUS_PROPERTY_KEY);
+ reason = revocationStatus.getString(REASON_PROPERTY_KEY);
+ } catch (Throwable t) {
+ throw new CertPathValidatorException("Unable get properties for certificate "
+ + "with serial number " + serialNumber);
+ }
+ throw new CertPathValidatorException(
+ "Invalid certificate with serial number " + serialNumber
+ + " has status " + status
+ + " because reason " + reason);
+ }
+ }
+
+ private JSONObject getStatusMap(String stringUrl) throws CertPathValidatorException {
+ URL url;
+ try {
+ url = new URL(stringUrl);
+ } catch (Throwable t) {
+ throw new CertPathValidatorException(
+ "Unable to get revocation status from " + mStatusUrl, t);
+ }
+
+ try (InputStream inputStream = url.openStream()) {
+ JSONObject statusListJson = new JSONObject(
+ new String(inputStream.readAllBytes(), UTF_8));
+ return statusListJson.getJSONObject(TOP_LEVEL_JSON_PROPERTY_KEY);
+ } catch (Throwable t) {
+ throw new CertPathValidatorException(
+ "Unable to parse revocation status from " + mStatusUrl, t);
+ }
+ }
+
+ private String getRevocationListUrl() {
+ return mContext.getResources().getString(
+ R.string.vendor_required_attestation_revocation_list_url);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
index fa243c0..20cd8f5 100644
--- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java
+++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
@@ -671,6 +671,7 @@
mContext.unbindService(mConnection);
mBound = false;
mContext.unregisterReceiver(mBroadcastReceiver);
+ mContext.unregisterReceiver(mTrustableDowngradeReceiver);
mTrustAgentService = null;
mSetTrustAgentFeaturesToken = null;
mHandler.sendEmptyMessage(MSG_REVOKE_TRUST);
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index 6c5d952..aad59c6 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -347,24 +347,19 @@
* Return {@code true} if the device should vibrate for current ringer mode.
*
* <p>This checks the current {@link AudioManager#getRingerModeInternal()} against user settings
- * for touch and ringtone usages only. All other usages are allowed by this method.
+ * for ringtone usage only. All other usages are allowed by this method.
*/
@GuardedBy("mLock")
private boolean shouldVibrateForRingerModeLocked(@VibrationAttributes.Usage int usageHint) {
+ if (usageHint != USAGE_RINGTONE) {
+ // Only ringtone vibrations are disabled when phone is on silent mode.
+ return true;
+ }
// If audio manager was not loaded yet then assume most restrictive mode.
int ringerMode = (mAudioManager == null)
? AudioManager.RINGER_MODE_SILENT
: mAudioManager.getRingerModeInternal();
-
- switch (usageHint) {
- case USAGE_TOUCH:
- case USAGE_RINGTONE:
- // Touch feedback and ringtone disabled when phone is on silent mode.
- return ringerMode != AudioManager.RINGER_MODE_SILENT;
- default:
- // All other usages ignore ringer mode settings.
- return true;
- }
+ return ringerMode != AudioManager.RINGER_MODE_SILENT;
}
/** Updates all vibration settings and triggers registered listeners. */
diff --git a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
index 3667631..024e319 100644
--- a/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
+++ b/services/core/java/com/android/server/vibrator/VibrationStepConductor.java
@@ -16,6 +16,7 @@
package com.android.server.vibrator;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Build;
import android.os.CombinedVibration;
@@ -24,6 +25,7 @@
import android.os.vibrator.PrimitiveSegment;
import android.os.vibrator.RampSegment;
import android.os.vibrator.VibrationEffectSegment;
+import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
@@ -69,9 +71,17 @@
private final PriorityQueue<Step> mNextSteps = new PriorityQueue<>();
private final Queue<Step> mPendingOnVibratorCompleteSteps = new LinkedList<>();
- @GuardedBy("mLock")
- private Queue<Integer> mCompletionNotifiedVibrators = new LinkedList<>();
+ // Signalling fields.
+ @GuardedBy("mLock")
+ private final IntArray mSignalVibratorsComplete;
+ @GuardedBy("mLock")
+ private boolean mSignalCancel = false;
+ @GuardedBy("mLock")
+ private boolean mSignalCancelImmediate = false;
+
+ private boolean mCancelled = false;
+ private boolean mCancelledImmediately = false; // hard stop
private int mPendingVibrateSteps;
private int mRemainingStartSequentialEffectSteps;
private int mSuccessfulVibratorOnSteps;
@@ -91,6 +101,7 @@
mVibrators.put(availableVibrators.keyAt(i), availableVibrators.valueAt(i));
}
}
+ this.mSignalVibratorsComplete = new IntArray(mVibrators.size());
}
@Nullable
@@ -152,6 +163,10 @@
if (Build.IS_DEBUGGABLE) {
expectIsVibrationThread(true);
}
+ if (mCancelledImmediately) {
+ return true; // Terminate.
+ }
+
// No need to check for vibration complete callbacks - if there were any, they would
// have no steps to notify anyway.
return mPendingOnVibratorCompleteSteps.isEmpty() && mNextSteps.isEmpty();
@@ -166,6 +181,9 @@
expectIsVibrationThread(true);
}
+ if (mCancelled) {
+ return Vibration.Status.CANCELLED;
+ }
if (mPendingVibrateSteps > 0
|| mRemainingStartSequentialEffectSteps > 0) {
return Vibration.Status.RUNNING;
@@ -180,48 +198,65 @@
/**
* Blocks until the next step is due to run. The wait here may be interrupted by calling
- * {@link #notifyWakeUp} or other "notify" methods.
+ * one of the "notify" methods.
*
- * <p>This method returns false if the next step is ready to run now. If the method returns
- * true, then some waiting was done, but may have been interrupted by a wakeUp.
+ * <p>This method returns true if the next step is ready to run now. If the method returns
+ * false, then some waiting was done, but may have been interrupted by a wakeUp, and the
+ * status and isFinished of the vibration should be re-checked before calling this method again.
*
- * @return true if the method waited at all, or false if a step is ready to run now.
+ * @return true if the next step can be run now or the vibration is finished, or false if this
+ * method waited and the conductor state may have changed asynchronously, in which case this
+ * method needs to be run again.
*/
public boolean waitUntilNextStepIsDue() {
if (Build.IS_DEBUGGABLE) {
expectIsVibrationThread(true);
}
- // It's necessary to re-process callbacks if they come in after acquiring the lock to
- // start waiting, but we don't want to hold the lock while processing them.
- // The loop goes until there are no pending callbacks to process.
- while (true) {
- // TODO: cancellation checking could also be integrated here, instead of outside in
- // VibrationThread.
- processVibratorCompleteCallbacks();
- if (!mPendingOnVibratorCompleteSteps.isEmpty()) {
- // Steps resumed by vibrator complete callback should be played right away.
- return false;
- }
- Step nextStep = mNextSteps.peek();
- if (nextStep == null) {
- return false;
- }
- long waitMillis = nextStep.calculateWaitTime();
- if (waitMillis <= 0) {
- return false;
- }
- synchronized (mLock) {
- // Double check for missed wake-ups before sleeping.
- if (!mCompletionNotifiedVibrators.isEmpty()) {
- continue; // Start again: processVibratorCompleteCallbacks will consume it.
- }
- try {
- mLock.wait(waitMillis);
- } catch (InterruptedException e) {
- }
- return true;
- }
+
+ processAllNotifySignals();
+ if (mCancelledImmediately) {
+ // Don't try to run a step for immediate cancel, although there should be none left.
+ // Non-immediate cancellation may have cleanup steps, so it continues processing.
+ return false;
}
+ if (!mPendingOnVibratorCompleteSteps.isEmpty()) {
+ return true; // Resumed step ready.
+ }
+ Step nextStep = mNextSteps.peek();
+ if (nextStep == null) {
+ return true; // Finished
+ }
+ long waitMillis = nextStep.calculateWaitTime();
+ if (waitMillis <= 0) {
+ return true; // Regular step ready
+ }
+ synchronized (mLock) {
+ // Double check for signals before sleeping, as their notify wouldn't interrupt a fresh
+ // wait.
+ if (hasPendingNotifySignalLocked()) {
+ // Don't run the next step, it will loop back to this method and process them.
+ return false;
+ }
+ try {
+ mLock.wait(waitMillis);
+ } catch (InterruptedException e) {
+ }
+ return false; // Caller needs to check isFinished and maybe wait again.
+ }
+ }
+
+ @Nullable
+ private Step pollNext() {
+ if (Build.IS_DEBUGGABLE) {
+ expectIsVibrationThread(true);
+ }
+
+ // Prioritize the steps resumed by a vibrator complete callback, irrespective of their
+ // "next run time".
+ if (!mPendingOnVibratorCompleteSteps.isEmpty()) {
+ return mPendingOnVibratorCompleteSteps.poll();
+ }
+ return mNextSteps.poll();
}
/**
@@ -255,20 +290,27 @@
}
/**
- * Wake up the execution thread, which may be waiting until the next step is due.
- * The caller is responsible for diverting VibrationThread execution.
+ * Notify the execution that cancellation is requested. This will be acted upon
+ * asynchronously in the VibrationThread.
*
- * <p>At the moment this is used after the signal is set that a cancellation needs to be
- * processed. The actual cancellation will be invoked from the VibrationThread.
+ * @param immediate indicates whether cancellation should abort urgently and skip cleanup steps.
*/
- public void notifyWakeUp() {
+ public void notifyCancelled(boolean immediate) {
if (Build.IS_DEBUGGABLE) {
expectIsVibrationThread(false);
}
-
synchronized (mLock) {
+ if (immediate && mSignalCancelImmediate || mSignalCancel) {
+ // Nothing to update: already cancelled previously.
+ return;
+ }
+ mSignalCancelImmediate |= immediate;
+ mSignalCancel = true;
mLock.notify();
}
+ if (DEBUG) {
+ Slog.d(TAG, "Vibration cancel requested, immediate=" + immediate);
+ }
}
/**
@@ -287,7 +329,7 @@
}
synchronized (mLock) {
- mCompletionNotifiedVibrators.offer(vibratorId);
+ mSignalVibratorsComplete.add(vibratorId);
mLock.notify();
}
}
@@ -310,23 +352,80 @@
synchronized (mLock) {
for (int i = 0; i < mVibrators.size(); i++) {
- mCompletionNotifiedVibrators.offer(mVibrators.keyAt(i));
+ mSignalVibratorsComplete.add(mVibrators.keyAt(i));
}
mLock.notify();
}
}
+ @GuardedBy("mLock")
+ private boolean hasPendingNotifySignalLocked() {
+ if (Build.IS_DEBUGGABLE) {
+ expectIsVibrationThread(true); // Reads VibrationThread variables as well as signals.
+ }
+ return (mSignalCancel && !mCancelled)
+ || (mSignalCancelImmediate && !mCancelledImmediately)
+ || (mSignalVibratorsComplete.size() > 0);
+ }
+
+ /**
+ * Process any notified cross-thread signals, applying the necessary VibrationThread state
+ * changes.
+ */
+ private void processAllNotifySignals() {
+ if (Build.IS_DEBUGGABLE) {
+ expectIsVibrationThread(true);
+ }
+
+ int[] vibratorsToProcess = null;
+ boolean doCancel = false;
+ boolean doCancelImmediate = false;
+ // Swap out the queue of completions to process.
+ synchronized (mLock) {
+ if (mSignalCancelImmediate) {
+ if (mCancelledImmediately) {
+ Slog.wtf(TAG, "Immediate cancellation signal processed twice");
+ }
+ // This should only happen once.
+ doCancelImmediate = true;
+ }
+ if (mSignalCancel && !mCancelled) {
+ doCancel = true;
+ }
+ if (!doCancelImmediate && mSignalVibratorsComplete.size() > 0) {
+ vibratorsToProcess = mSignalVibratorsComplete.toArray(); // makes a copy
+ mSignalVibratorsComplete.clear();
+ }
+ }
+
+ // Force cancellation means stop everything and clear all steps, so the execution loop
+ // shouldn't come back to this method. To observe explicitly: this drops vibrator
+ // completion signals that were collected in this call, but we won't process them
+ // anyway as all steps are cancelled.
+ if (doCancelImmediate) {
+ processCancelImmediately();
+ return;
+ }
+ if (doCancel) {
+ processCancel();
+ }
+ if (vibratorsToProcess != null) {
+ processVibratorsComplete(vibratorsToProcess);
+ }
+ }
+
/**
* Cancel the current queue, replacing all remaining steps with respective clean-up steps.
*
- * <p>This will remove all steps and replace them with respective
+ * <p>This will remove all steps and replace them with respective results of
* {@link Step#cancel()}.
*/
- public void cancel() {
+ public void processCancel() {
if (Build.IS_DEBUGGABLE) {
expectIsVibrationThread(true);
}
+ mCancelled = true;
// Vibrator callbacks should wait until all steps from the queue are properly cancelled
// and clean up steps are added back to the queue, so they can handle the callback.
List<Step> cleanUpSteps = new ArrayList<>();
@@ -344,11 +443,13 @@
*
* <p>This will remove and trigger {@link Step#cancelImmediately()} in all steps, in order.
*/
- public void cancelImmediately() {
+ public void processCancelImmediately() {
if (Build.IS_DEBUGGABLE) {
expectIsVibrationThread(true);
}
+ mCancelledImmediately = true;
+ mCancelled = true;
Step step;
while ((step = pollNext()) != null) {
step.cancelImmediately();
@@ -356,44 +457,20 @@
mPendingVibrateSteps = 0;
}
- @Nullable
- private Step pollNext() {
- if (Build.IS_DEBUGGABLE) {
- expectIsVibrationThread(true);
- }
-
- // Prioritize the steps resumed by a vibrator complete callback, irrespective of their
- // "next run time".
- if (!mPendingOnVibratorCompleteSteps.isEmpty()) {
- return mPendingOnVibratorCompleteSteps.poll();
- }
- return mNextSteps.poll();
- }
-
/**
- * Process any notified vibrator completions.
+ * Processes the vibrators that have sent their complete callbacks. A step is found that will
+ * accept the completion callback, and this step is brought forward for execution in the next
+ * run.
*
* <p>This assumes only one of the next steps is waiting on this given vibrator, so the
* first step found will be resumed by this method, in no particular order.
*/
- private void processVibratorCompleteCallbacks() {
+ private void processVibratorsComplete(@NonNull int[] vibratorsToProcess) {
if (Build.IS_DEBUGGABLE) {
expectIsVibrationThread(true);
}
- Queue<Integer> vibratorsToProcess;
- // Swap out the queue of completions to process.
- synchronized (mLock) {
- if (mCompletionNotifiedVibrators.isEmpty()) {
- return; // Nothing to do.
- }
-
- vibratorsToProcess = mCompletionNotifiedVibrators;
- mCompletionNotifiedVibrators = new LinkedList<>();
- }
-
- while (!vibratorsToProcess.isEmpty()) {
- int vibratorId = vibratorsToProcess.poll();
+ for (int vibratorId : vibratorsToProcess) {
Iterator<Step> it = mNextSteps.iterator();
while (it.hasNext()) {
Step step = it.next();
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
index 3fef7f2..26f7e7d 100644
--- a/services/core/java/com/android/server/vibrator/VibrationThread.java
+++ b/services/core/java/com/android/server/vibrator/VibrationThread.java
@@ -108,7 +108,7 @@
if (DEBUG) {
Slog.d(TAG, "Binder died, cancelling vibration...");
}
- cancel();
+ mStepConductor.notifyCancelled(/* immediate= */ false);
}
@Override
@@ -168,28 +168,12 @@
/** Cancel current vibration and ramp down the vibrators gracefully. */
public void cancel() {
- if (mStop) {
- // Already cancelled, running clean-up steps.
- return;
- }
- mStop = true;
- if (DEBUG) {
- Slog.d(TAG, "Vibration cancelled");
- }
- mStepConductor.notifyWakeUp();
+ mStepConductor.notifyCancelled(/* immediate= */ false);
}
/** Cancel current vibration and shuts off the vibrators immediately. */
public void cancelImmediately() {
- if (mForceStop) {
- // Already forced the thread to stop, wait for it to finish.
- return;
- }
- if (DEBUG) {
- Slog.d(TAG, "Vibration cancelled immediately");
- }
- mStop = mForceStop = true;
- mStepConductor.notifyWakeUp();
+ mStepConductor.notifyCancelled(/* immediate= */ true);
}
/** Notify current vibration that a synced step has completed. */
@@ -217,13 +201,10 @@
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "playVibration");
try {
mStepConductor.prepareToStart();
-
while (!mStepConductor.isFinished()) {
- // Skip wait and next step if mForceStop already happened.
- boolean waited = mForceStop || mStepConductor.waitUntilNextStepIsDue();
- // If we waited, don't run the next step, but instead re-evaluate cancellation
- // status
- if (!waited) {
+ boolean readyToRun = mStepConductor.waitUntilNextStepIsDue();
+ // If we waited, don't run the next step, but instead re-evaluate status.
+ if (readyToRun) {
if (DEBUG) {
Slog.d(TAG, "Play vibration consuming next step...");
}
@@ -232,23 +213,12 @@
mStepConductor.runNextStep();
}
- if (mForceStop) {
- // Cancel every step and stop playing them right away, even clean-up steps.
- mStepConductor.cancelImmediately();
- clientVibrationCompleteIfNotAlready(Vibration.Status.CANCELLED);
- break;
- }
-
- Vibration.Status status = mStop ? Vibration.Status.CANCELLED
- : mStepConductor.calculateVibrationStatus();
+ Vibration.Status status = mStepConductor.calculateVibrationStatus();
// This block can only run once due to mCalledVibrationCompleteCallback.
if (status != Vibration.Status.RUNNING && !mCalledVibrationCompleteCallback) {
// First time vibration stopped running, start clean-up tasks and notify
// callback immediately.
clientVibrationCompleteIfNotAlready(status);
- if (status == Vibration.Status.CANCELLED) {
- mStepConductor.cancel();
- }
}
}
} finally {
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 57f77d5..8f703c5 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -263,8 +263,6 @@
FLAGS_MAGNIFICATION_CALLBACK | FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK,
"displayId=" + displayId + "; spec={" + spec + "}");
}
- mAccessibilityWindowsPopulator.setMagnificationSpec(displayId, spec);
-
final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
if (displayMagnifier != null) {
displayMagnifier.setMagnificationSpec(spec);
@@ -456,6 +454,19 @@
return null;
}
+ boolean getMagnificationSpecForDisplay(int displayId, MagnificationSpec outSpec) {
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(TAG + ".getMagnificationSpecForDisplay",
+ FLAGS_MAGNIFICATION_CALLBACK, "displayId=" + displayId);
+ }
+ final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId);
+ if (displayMagnifier == null) {
+ return false;
+ }
+
+ return displayMagnifier.getMagnificationSpec(outSpec);
+ }
+
boolean hasCallbacks() {
if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK
| FLAGS_WINDOWS_FOR_ACCESSIBILITY_CALLBACK)) {
@@ -757,6 +768,25 @@
return spec;
}
+ boolean getMagnificationSpec(MagnificationSpec outSpec) {
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".getMagnificationSpec",
+ FLAGS_MAGNIFICATION_CALLBACK);
+ }
+ MagnificationSpec spec = mMagnifedViewport.getMagnificationSpec();
+ if (spec == null) {
+ return false;
+ }
+
+ outSpec.setTo(spec);
+ if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
+ mAccessibilityTracing.logTrace(LOG_TAG + ".getMagnificationSpec",
+ FLAGS_MAGNIFICATION_CALLBACK, "outSpec={" + outSpec + "}");
+ }
+
+ return true;
+ }
+
void getMagnificationRegion(Region outMagnificationRegion) {
if (mAccessibilityTracing.isTracingEnabled(FLAGS_MAGNIFICATION_CALLBACK)) {
mAccessibilityTracing.logTrace(LOG_TAG + ".getMagnificationRegion",
diff --git a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
index 43317ad..c0fb83b 100644
--- a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
+++ b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
@@ -41,9 +41,7 @@
import com.android.internal.annotations.GuardedBy;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
/**
* This class is the accessibility windows population adapter.
@@ -70,24 +68,13 @@
private final SparseArray<Matrix> mMagnificationSpecInverseMatrix = new SparseArray<>();
@GuardedBy("mLock")
private final SparseArray<DisplayInfo> mDisplayInfos = new SparseArray<>();
- private final SparseArray<MagnificationSpec> mCurrentMagnificationSpec = new SparseArray<>();
- @GuardedBy("mLock")
- private final SparseArray<MagnificationSpec> mPreviousMagnificationSpec = new SparseArray<>();
@GuardedBy("mLock")
private final List<InputWindowHandle> mVisibleWindows = new ArrayList<>();
@GuardedBy("mLock")
private boolean mWindowsNotificationEnabled = false;
- @GuardedBy("mLock")
- private final Map<IBinder, Matrix> mWindowsTransformMatrixMap = new HashMap<>();
private final Object mLock = new Object();
private final Handler mHandler;
- private final Matrix mTempMatrix1 = new Matrix();
- private final Matrix mTempMatrix2 = new Matrix();
- private final float[] mTempFloat1 = new float[9];
- private final float[] mTempFloat2 = new float[9];
- private final float[] mTempFloat3 = new float[9];
-
AccessibilityWindowsPopulator(WindowManagerService service,
AccessibilityController accessibilityController) {
mService = service;
@@ -145,55 +132,28 @@
@Override
public void onWindowInfosChanged(InputWindowHandle[] windowHandles,
DisplayInfo[] displayInfos) {
- final List<InputWindowHandle> tempVisibleWindows = new ArrayList<>();
-
- for (InputWindowHandle window : windowHandles) {
- if (window.visible && window.getWindow() != null) {
- tempVisibleWindows.add(window);
- }
- }
- final HashMap<IBinder, Matrix> windowsTransformMatrixMap =
- getWindowsTransformMatrix(tempVisibleWindows);
-
synchronized (mLock) {
- mWindowsTransformMatrixMap.clear();
- mWindowsTransformMatrixMap.putAll(windowsTransformMatrixMap);
-
mVisibleWindows.clear();
- mVisibleWindows.addAll(tempVisibleWindows);
+ for (InputWindowHandle window : windowHandles) {
+ if (window.visible && window.getWindow() != null) {
+ mVisibleWindows.add(window);
+ }
+ }
mDisplayInfos.clear();
for (final DisplayInfo displayInfo : displayInfos) {
mDisplayInfos.put(displayInfo.mDisplayId, displayInfo);
}
- if (!mHandler.hasMessages(
- MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_TIMEOUT)) {
- mHandler.sendEmptyMessageDelayed(
- MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_TIMEOUT,
- WINDOWS_CHANGED_NOTIFICATION_MAX_DURATION_TIMES_MS);
- }
- populateVisibleWindowHandlesAndNotifyWindowsChangeIfNeeded();
- }
- }
-
- private HashMap<IBinder, Matrix> getWindowsTransformMatrix(List<InputWindowHandle> windows) {
- synchronized (mService.mGlobalLock) {
- final HashMap<IBinder, Matrix> windowsTransformMatrixMap = new HashMap<>();
-
- for (InputWindowHandle inputWindowHandle : windows) {
- final IWindow iWindow = inputWindowHandle.getWindow();
- final WindowState windowState = iWindow != null ? mService.mWindowMap.get(
- iWindow.asBinder()) : null;
-
- if (windowState != null && windowState.shouldMagnify()) {
- final Matrix transformMatrix = new Matrix();
- windowState.getTransformationMatrix(sTempFloats, transformMatrix);
- windowsTransformMatrixMap.put(iWindow.asBinder(), transformMatrix);
+ if (mWindowsNotificationEnabled) {
+ if (!mHandler.hasMessages(
+ MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_TIMEOUT)) {
+ mHandler.sendEmptyMessageDelayed(
+ MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED_BY_TIMEOUT,
+ WINDOWS_CHANGED_NOTIFICATION_MAX_DURATION_TIMES_MS);
}
+ populateVisibleWindowHandlesAndNotifyWindowsChangeIfNeededLocked();
}
-
- return windowsTransformMatrixMap;
}
}
@@ -211,43 +171,14 @@
}
mWindowsNotificationEnabled = register;
if (mWindowsNotificationEnabled) {
- populateVisibleWindowHandlesAndNotifyWindowsChangeIfNeeded();
+ populateVisibleWindowHandlesAndNotifyWindowsChangeIfNeededLocked();
} else {
releaseResources();
}
}
}
- /**
- * Sets the magnification spec for calculating the window bounds of all windows
- * reported from the surface flinger in the magnifying.
- *
- * @param displayId The display Id.
- * @param spec THe magnification spec.
- */
- public void setMagnificationSpec(int displayId, MagnificationSpec spec) {
- synchronized (mLock) {
- MagnificationSpec currentMagnificationSpec = mCurrentMagnificationSpec.get(displayId);
- if (currentMagnificationSpec == null) {
- currentMagnificationSpec = new MagnificationSpec();
- currentMagnificationSpec.setTo(spec);
- mCurrentMagnificationSpec.put(displayId, currentMagnificationSpec);
-
- return;
- }
-
- MagnificationSpec previousMagnificationSpec = mPreviousMagnificationSpec.get(displayId);
- if (previousMagnificationSpec == null) {
- previousMagnificationSpec = new MagnificationSpec();
- mPreviousMagnificationSpec.put(displayId, previousMagnificationSpec);
- }
- previousMagnificationSpec.setTo(currentMagnificationSpec);
- currentMagnificationSpec.setTo(spec);
- }
- }
-
- @GuardedBy("mLock")
- private void populateVisibleWindowHandlesAndNotifyWindowsChangeIfNeeded() {
+ private void populateVisibleWindowHandlesAndNotifyWindowsChangeIfNeededLocked() {
final SparseArray<List<InputWindowHandle>> tempWindowHandleList = new SparseArray<>();
for (final InputWindowHandle windowHandle : mVisibleWindows) {
@@ -257,15 +188,15 @@
if (inputWindowHandles == null) {
inputWindowHandles = new ArrayList<>();
tempWindowHandleList.put(windowHandle.displayId, inputWindowHandles);
+ generateMagnificationSpecInverseMatrixLocked(windowHandle.displayId);
}
inputWindowHandles.add(windowHandle);
}
- findMagnificationSpecInverseMatrixIfNeeded(tempWindowHandleList);
final List<Integer> displayIdsForWindowsChanged = new ArrayList<>();
- getDisplaysForWindowsChanged(displayIdsForWindowsChanged, tempWindowHandleList,
- mInputWindowHandlesOnDisplays);
+ getDisplaysForWindowsChangedLocked(displayIdsForWindowsChanged, tempWindowHandleList,
+ mInputWindowHandlesOnDisplays);
// Clones all windows from the callback of the surface flinger.
mInputWindowHandlesOnDisplays.clear();
for (int i = 0; i < tempWindowHandleList.size(); i++) {
@@ -273,7 +204,7 @@
mInputWindowHandlesOnDisplays.put(displayId, tempWindowHandleList.get(displayId));
}
- if (!displayIdsForWindowsChanged.isEmpty()) {
+ if (displayIdsForWindowsChanged.size() > 0) {
if (!mHandler.hasMessages(MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED)) {
mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED,
displayIdsForWindowsChanged).sendToTarget();
@@ -286,8 +217,7 @@
SURFACE_FLINGER_CALLBACK_WINDOWS_STABLE_TIMES_MS);
}
- @GuardedBy("mLock")
- private static void getDisplaysForWindowsChanged(List<Integer> outDisplayIdsForWindowsChanged,
+ private void getDisplaysForWindowsChangedLocked(List<Integer> outDisplayIdsForWindowsChanged,
SparseArray<List<InputWindowHandle>> newWindowsList,
SparseArray<List<InputWindowHandle>> oldWindowsList) {
for (int i = 0; i < newWindowsList.size(); i++) {
@@ -295,14 +225,13 @@
final List<InputWindowHandle> newWindows = newWindowsList.get(displayId);
final List<InputWindowHandle> oldWindows = oldWindowsList.get(displayId);
- if (hasWindowsChanged(newWindows, oldWindows)) {
+ if (hasWindowsChangedLocked(newWindows, oldWindows)) {
outDisplayIdsForWindowsChanged.add(displayId);
}
}
}
- @GuardedBy("mLock")
- private static boolean hasWindowsChanged(List<InputWindowHandle> newWindows,
+ private boolean hasWindowsChangedLocked(List<InputWindowHandle> newWindows,
List<InputWindowHandle> oldWindows) {
if (oldWindows == null || oldWindows.size() != newWindows.size()) {
return true;
@@ -324,195 +253,34 @@
return false;
}
- @GuardedBy("mLock")
- private void findMagnificationSpecInverseMatrixIfNeeded(SparseArray<List<InputWindowHandle>>
- windowHandleList) {
- MagnificationSpec currentMagnificationSpec;
- MagnificationSpec previousMagnificationSpec;
- for (int i = 0; i < windowHandleList.size(); i++) {
- final int displayId = windowHandleList.keyAt(i);
- List<InputWindowHandle> inputWindowHandles = windowHandleList.get(displayId);
-
- final MagnificationSpec currentSpec = mCurrentMagnificationSpec.get(displayId);
- if (currentSpec == null) {
- continue;
- }
- currentMagnificationSpec = new MagnificationSpec();
- currentMagnificationSpec.setTo(currentSpec);
-
- final MagnificationSpec previousSpec = mPreviousMagnificationSpec.get(displayId);
-
- if (previousSpec == null) {
- final Matrix inverseMatrixForCurrentSpec = new Matrix();
- generateInverseMatrix(currentMagnificationSpec, inverseMatrixForCurrentSpec);
- mMagnificationSpecInverseMatrix.put(displayId, inverseMatrixForCurrentSpec);
- continue;
- }
- previousMagnificationSpec = new MagnificationSpec();
- previousMagnificationSpec.setTo(previousSpec);
-
- generateInverseMatrixBasedOnProperMagnificationSpecForDisplay(inputWindowHandles,
- currentMagnificationSpec, previousMagnificationSpec);
+ private void generateMagnificationSpecInverseMatrixLocked(int displayId) {
+ MagnificationSpec spec = new MagnificationSpec();
+ if (!mAccessibilityController.getMagnificationSpecForDisplay(displayId, spec)) {
+ mMagnificationSpecInverseMatrix.remove(displayId);
+ return;
}
- }
-
- @GuardedBy("mLock")
- private void generateInverseMatrixBasedOnProperMagnificationSpecForDisplay(
- List<InputWindowHandle> inputWindowHandles, MagnificationSpec currentMagnificationSpec,
- MagnificationSpec previousMagnificationSpec) {
- // To decrease the counts of holding the WindowManagerService#mGlogalLock in
- // the method, getWindowTransformMatrix(), this for loop begins from the bottom
- // to top of the z-order windows.
- for (int index = inputWindowHandles.size() - 1; index >= 0; index--) {
- final Matrix windowTransformMatrix = mTempMatrix2;
- final InputWindowHandle windowHandle = inputWindowHandles.get(index);
- final IBinder iBinder = windowHandle.getWindow().asBinder();
-
- if (getWindowTransformMatrix(iBinder, windowTransformMatrix)) {
- generateMagnificationSpecInverseMatrix(windowHandle, currentMagnificationSpec,
- previousMagnificationSpec, windowTransformMatrix);
-
- break;
- }
- }
- }
-
- @GuardedBy("mLock")
- private boolean getWindowTransformMatrix(IBinder iBinder, Matrix outTransform) {
- final Matrix windowMatrix = iBinder != null
- ? mWindowsTransformMatrixMap.get(iBinder) : null;
-
- if (windowMatrix == null) {
- return false;
- }
- outTransform.set(windowMatrix);
-
- return true;
- }
-
- /**
- * Generates the inverse matrix based on the proper magnification spec.
- * The magnification spec associated with the InputWindowHandle might not the current
- * spec set by WM, which might be the previous one. To find the appropriate spec,
- * we store two consecutive magnification specs, and found out which one is the proper
- * one closing the identity matrix for generating the inverse matrix.
- *
- * @param inputWindowHandle The window from the surface flinger.
- * @param currentMagnificationSpec The current magnification spec.
- * @param previousMagnificationSpec The previous magnification spec.
- * @param transformMatrix The transform matrix of the window doesn't consider the
- * magnifying effect.
- */
- @GuardedBy("mLock")
- private void generateMagnificationSpecInverseMatrix(InputWindowHandle inputWindowHandle,
- @NonNull MagnificationSpec currentMagnificationSpec,
- @NonNull MagnificationSpec previousMagnificationSpec, Matrix transformMatrix) {
-
- final float[] identityMatrixFloatsForCurrentSpec = mTempFloat1;
- computeIdentityMatrix(inputWindowHandle, currentMagnificationSpec,
- transformMatrix, identityMatrixFloatsForCurrentSpec);
- final float[] identityMatrixFloatsForPreviousSpec = mTempFloat2;
- computeIdentityMatrix(inputWindowHandle, previousMagnificationSpec,
- transformMatrix, identityMatrixFloatsForPreviousSpec);
-
- Matrix inverseMatrixForMagnificationSpec = new Matrix();
- if (selectProperMagnificationSpecByComparingIdentityDegree(
- identityMatrixFloatsForCurrentSpec, identityMatrixFloatsForPreviousSpec)) {
- generateInverseMatrix(currentMagnificationSpec,
- inverseMatrixForMagnificationSpec);
-
- // Choosing the current spec means the previous spec is out of date,
- // so removing it. And if the current spec is no magnifying, meaning
- // the magnifying is done so removing the inverse matrix of this display.
- mPreviousMagnificationSpec.remove(inputWindowHandle.displayId);
- if (currentMagnificationSpec.isNop()) {
- mCurrentMagnificationSpec.remove(inputWindowHandle.displayId);
- mMagnificationSpecInverseMatrix.remove(inputWindowHandle.displayId);
- return;
- }
- } else {
- generateInverseMatrix(previousMagnificationSpec,
- inverseMatrixForMagnificationSpec);
- }
-
- mMagnificationSpecInverseMatrix.put(inputWindowHandle.displayId,
- inverseMatrixForMagnificationSpec);
- }
-
- /**
- * Computes the identity matrix for generating the
- * inverse matrix based on below formula under window is at the stable state:
- * inputWindowHandle#transform * MagnificationSpecMatrix * WindowState#transform
- * = IdentityMatrix
- */
- @GuardedBy("mLock")
- private void computeIdentityMatrix(InputWindowHandle inputWindowHandle,
- @NonNull MagnificationSpec magnificationSpec,
- Matrix transformMatrix, float[] magnifyMatrixFloats) {
- final Matrix specMatrix = mTempMatrix1;
- transformMagnificationSpecToMatrix(magnificationSpec, specMatrix);
-
- final Matrix resultMatrix = new Matrix(inputWindowHandle.transform);
- resultMatrix.preConcat(specMatrix);
- resultMatrix.preConcat(transformMatrix);
-
- resultMatrix.getValues(magnifyMatrixFloats);
- }
-
- /**
- * @return true if selecting the magnification spec one, otherwise selecting the
- * magnification spec two.
- */
- @GuardedBy("mLock")
- private boolean selectProperMagnificationSpecByComparingIdentityDegree(
- float[] magnifyMatrixFloatsForSpecOne,
- float[] magnifyMatrixFloatsForSpecTwo) {
- final float[] IdentityMatrixValues = mTempFloat3;
- Matrix.IDENTITY_MATRIX.getValues(IdentityMatrixValues);
-
- final float scaleDiffForSpecOne = Math.abs(IdentityMatrixValues[Matrix.MSCALE_X]
- - magnifyMatrixFloatsForSpecOne[Matrix.MSCALE_X]);
- final float scaleDiffForSpecTwo = Math.abs(IdentityMatrixValues[Matrix.MSCALE_X]
- - magnifyMatrixFloatsForSpecTwo[Matrix.MSCALE_X]);
- final float offsetXDiffForSpecOne = Math.abs(IdentityMatrixValues[Matrix.MTRANS_X]
- - magnifyMatrixFloatsForSpecOne[Matrix.MTRANS_X]);
- final float offsetXDiffForSpecTwo = Math.abs(IdentityMatrixValues[Matrix.MTRANS_X]
- - magnifyMatrixFloatsForSpecTwo[Matrix.MTRANS_X]);
- final float offsetYDiffForSpecOne = Math.abs(IdentityMatrixValues[Matrix.MTRANS_Y]
- - magnifyMatrixFloatsForSpecOne[Matrix.MTRANS_Y]);
- final float offsetYDiffForSpecTwo = Math.abs(IdentityMatrixValues[Matrix.MTRANS_Y]
- - magnifyMatrixFloatsForSpecTwo[Matrix.MTRANS_Y]);
- final float offsetDiffForSpecOne = offsetXDiffForSpecOne
- + offsetYDiffForSpecOne;
- final float offsetDiffForSpecTwo = offsetXDiffForSpecTwo
- + offsetYDiffForSpecTwo;
-
- return Float.compare(scaleDiffForSpecTwo, scaleDiffForSpecOne) > 0
- || (Float.compare(scaleDiffForSpecTwo, scaleDiffForSpecOne) == 0
- && Float.compare(offsetDiffForSpecTwo, offsetDiffForSpecOne) > 0);
- }
-
- @GuardedBy("mLock")
- private static void generateInverseMatrix(MagnificationSpec spec, Matrix outMatrix) {
- outMatrix.reset();
+ sTempFloats[Matrix.MSCALE_X] = spec.scale;
+ sTempFloats[Matrix.MSKEW_Y] = 0;
+ sTempFloats[Matrix.MSKEW_X] = 0;
+ sTempFloats[Matrix.MSCALE_Y] = spec.scale;
+ sTempFloats[Matrix.MTRANS_X] = spec.offsetX;
+ sTempFloats[Matrix.MTRANS_Y] = spec.offsetY;
+ sTempFloats[Matrix.MPERSP_0] = 0;
+ sTempFloats[Matrix.MPERSP_1] = 0;
+ sTempFloats[Matrix.MPERSP_2] = 1;
final Matrix tempMatrix = new Matrix();
- transformMagnificationSpecToMatrix(spec, tempMatrix);
+ tempMatrix.setValues(sTempFloats);
- final boolean result = tempMatrix.invert(outMatrix);
+ final Matrix inverseMatrix = new Matrix();
+ final boolean result = tempMatrix.invert(inverseMatrix);
+
if (!result) {
Slog.e(TAG, "Can't inverse the magnification spec matrix with the "
- + "magnification spec = " + spec);
- outMatrix.reset();
+ + "magnification spec = " + spec + " on the displayId = " + displayId);
+ return;
}
- }
-
- @GuardedBy("mLock")
- private static void transformMagnificationSpecToMatrix(MagnificationSpec spec,
- Matrix outMatrix) {
- outMatrix.reset();
- outMatrix.postScale(spec.scale, spec.scale);
- outMatrix.postTranslate(spec.offsetX, spec.offsetY);
+ mMagnificationSpecInverseMatrix.set(displayId, inverseMatrix);
}
private void notifyWindowsChanged(@NonNull List<Integer> displayIdsForWindowsChanged) {
@@ -542,9 +310,6 @@
mMagnificationSpecInverseMatrix.clear();
mVisibleWindows.clear();
mDisplayInfos.clear();
- mCurrentMagnificationSpec.clear();
- mPreviousMagnificationSpec.clear();
- mWindowsTransformMatrixMap.clear();
mWindowsNotificationEnabled = false;
mHandler.removeCallbacksAndMessages(null);
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 9e24f4f..9c910eb 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -44,6 +44,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.WindowConfiguration.activityTypeToString;
import static android.app.WindowConfiguration.isSplitScreenWindowingMode;
import static android.app.admin.DevicePolicyResources.Drawables.Source.PROFILE_SWITCH_ANIMATION;
@@ -5085,10 +5086,9 @@
}
if (!visible) {
- final InsetsControlTarget imeInputTarget = mDisplayContent.getImeTarget(
- DisplayContent.IME_TARGET_INPUT);
- mLastImeShown = imeInputTarget != null && imeInputTarget.getWindow() != null
- && imeInputTarget.getWindow().mActivityRecord == this
+ final InputTarget imeInputTarget = mDisplayContent.getImeInputTarget();
+ mLastImeShown = imeInputTarget != null && imeInputTarget.getWindowState() != null
+ && imeInputTarget.getWindowState().mActivityRecord == this
&& mDisplayContent.mInputMethodWindow != null
&& mDisplayContent.mInputMethodWindow.isVisible();
mImeInsetsFrozenUntilStartInput = true;
@@ -8213,6 +8213,20 @@
@Override
public void onConfigurationChanged(Configuration newParentConfig) {
+ // We want to collect the ActivityRecord if the windowing mode is changed, so that it will
+ // dispatch app transition finished event correctly at the end.
+ // Check #isVisible() because we don't want to animate for activity that stays invisible.
+ // Activity with #isVisibleRequested() changed should be collected when that is requested.
+ if (mTransitionController.isShellTransitionsEnabled() && isVisible()
+ && isVisibleRequested()) {
+ final int projectedWindowingMode =
+ getRequestedOverrideWindowingMode() == WINDOWING_MODE_UNDEFINED
+ ? newParentConfig.windowConfiguration.getWindowingMode()
+ : getRequestedOverrideWindowingMode();
+ if (getWindowingMode() != projectedWindowingMode) {
+ mTransitionController.collect(this);
+ }
+ }
if (mCompatDisplayInsets != null) {
Configuration overrideConfig = getRequestedOverrideConfiguration();
// Adapt to changes in orientation locking. The app is still non-resizable, but
diff --git a/services/core/java/com/android/server/wm/AsyncRotationController.java b/services/core/java/com/android/server/wm/AsyncRotationController.java
index 5c13e81..9e889ad 100644
--- a/services/core/java/com/android/server/wm/AsyncRotationController.java
+++ b/services/core/java/com/android/server/wm/AsyncRotationController.java
@@ -94,6 +94,9 @@
/** Whether the start transaction of the transition is committed (by shell). */
private boolean mIsStartTransactionCommitted;
+ /** Whether the target windows have been requested to sync their draw transactions. */
+ private boolean mIsSyncDrawRequested;
+
private SeamlessRotator mRotator;
private final int mOriginalRotation;
@@ -139,22 +142,10 @@
displayContent.forAllWindows(this, true /* traverseTopToBottom */);
// Legacy animation doesn't need to wait for the start transaction.
- mIsStartTransactionCommitted = mTransitionOp == OP_LEGACY;
- if (mIsStartTransactionCommitted) return;
- // The transition sync group may be finished earlier because it doesn't wait for these
- // target windows. But the windows still need to use sync transaction to keep the appearance
- // in previous rotation, so request a no-op sync to keep the state.
- for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
- if (mHasScreenRotationAnimation
- && mTargetWindowTokens.valueAt(i).mAction == Operation.ACTION_FADE) {
- // The windows are hidden (leash is alpha 0) before finishing drawing so it is
- // unnecessary to request sync.
- continue;
- }
- final WindowToken token = mTargetWindowTokens.keyAt(i);
- for (int j = token.getChildCount() - 1; j >= 0; j--) {
- token.getChildAt(j).applyWithNextDraw(t -> {});
- }
+ if (mTransitionOp == OP_LEGACY) {
+ mIsStartTransactionCommitted = true;
+ } else if (displayContent.mTransitionController.useShellTransitionsRotation()) {
+ keepAppearanceInPreviousRotation();
}
}
@@ -194,6 +185,30 @@
mTargetWindowTokens.put(w.mToken, new Operation(action));
}
+ /**
+ * Enables {@link #handleFinishDrawing(WindowState, SurfaceControl.Transaction)} to capture the
+ * draw transactions of the target windows if needed.
+ */
+ void keepAppearanceInPreviousRotation() {
+ // The transition sync group may be finished earlier because it doesn't wait for these
+ // target windows. But the windows still need to use sync transaction to keep the appearance
+ // in previous rotation, so request a no-op sync to keep the state.
+ for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
+ if (mHasScreenRotationAnimation
+ && mTargetWindowTokens.valueAt(i).mAction == Operation.ACTION_FADE) {
+ // The windows are hidden (leash is alpha 0) before finishing drawing so it is
+ // unnecessary to request sync.
+ continue;
+ }
+ final WindowToken token = mTargetWindowTokens.keyAt(i);
+ for (int j = token.getChildCount() - 1; j >= 0; j--) {
+ token.getChildAt(j).applyWithNextDraw(t -> {});
+ }
+ }
+ mIsSyncDrawRequested = true;
+ if (DEBUG) Slog.d(TAG, "Requested to sync draw transaction");
+ }
+
/** Lets the window fit in new rotation naturally. */
private void finishOp(WindowToken windowToken) {
final Operation op = mTargetWindowTokens.remove(windowToken);
@@ -433,7 +448,7 @@
*/
boolean handleFinishDrawing(WindowState w, SurfaceControl.Transaction postDrawTransaction) {
if (mTransitionOp == OP_LEGACY || postDrawTransaction == null
- || !w.mTransitionController.inTransition()) {
+ || !mIsSyncDrawRequested || !w.mTransitionController.inTransition()) {
return false;
}
final Operation op = mTargetWindowTokens.get(w.mToken);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 159c71b..1c0687a 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -592,7 +592,15 @@
* The window which receives input from the input method. This is also a candidate of the
* input method control target.
*/
- private WindowState mImeInputTarget;
+ private InputTarget mImeInputTarget;
+
+ /**
+ * The last ime input target processed from setImeLayeringTargetInner
+ * this is to ensure we update the control target in the case when the IME
+ * target changes while the IME layering target stays the same, for example
+ * the case of the IME moving to a SurfaceControlViewHost backed EmbeddedWindow
+ */
+ private InputTarget mLastImeInputTarget;
/**
* This controls the visibility and animation of the input method window.
@@ -608,14 +616,6 @@
static final int IME_TARGET_LAYERING = 0;
/**
- * Used by {@link #getImeTarget} to return the IME target which received the input connection
- * from IME.
- *
- * @see #mImeInputTarget
- */
- static final int IME_TARGET_INPUT = 1;
-
- /**
* Used by {@link #getImeTarget} to return the IME target which controls the IME insets
* visibility and animation.
*
@@ -625,7 +625,6 @@
@IntDef(flag = false, prefix = { "IME_TARGET_" }, value = {
IME_TARGET_LAYERING,
- IME_TARGET_INPUT,
IME_TARGET_CONTROL,
})
@Retention(RetentionPolicy.SOURCE)
@@ -1736,19 +1735,19 @@
}
void setFixedRotationLaunchingAppUnchecked(@Nullable ActivityRecord r, int rotation) {
- final boolean useAsyncRotation = !mTransitionController.isShellTransitionsEnabled();
if (mFixedRotationLaunchingApp == null && r != null) {
- mWmService.mDisplayNotificationController.dispatchFixedRotationStarted(this,
- rotation);
- if (useAsyncRotation) {
- startAsyncRotation(
- // Delay the hide animation to avoid blinking by clicking navigation bar
- // that may toggle fixed rotation in a short time.
- r == mFixedRotationTransitionListener.mAnimatingRecents);
- }
+ mWmService.mDisplayNotificationController.dispatchFixedRotationStarted(this, rotation);
+ // Delay the hide animation to avoid blinking by clicking navigation bar that may
+ // toggle fixed rotation in a short time.
+ final boolean shouldDebounce = r == mFixedRotationTransitionListener.mAnimatingRecents
+ || mTransitionController.isTransientLaunch(r);
+ startAsyncRotation(shouldDebounce);
} else if (mFixedRotationLaunchingApp != null && r == null) {
mWmService.mDisplayNotificationController.dispatchFixedRotationFinished(this);
- if (useAsyncRotation) finishAsyncRotationIfPossible();
+ // Keep async rotation controller if the next transition of display is requested.
+ if (!mTransitionController.isCollecting(this)) {
+ finishAsyncRotationIfPossible();
+ }
}
mFixedRotationLaunchingApp = r;
}
@@ -1806,7 +1805,6 @@
}
// Update directly because the app which will change the orientation of display is ready.
if (mDisplayRotation.updateOrientation(getOrientation(), false /* forceUpdate */)) {
- mTransitionController.setSeamlessRotation(this);
sendNewConfiguration();
return;
}
@@ -3235,7 +3233,15 @@
this, this, null /* remoteTransition */, displayChange);
if (t != null) {
mAtmService.startLaunchPowerMode(POWER_MODE_REASON_CHANGE_DISPLAY);
- if (isRotationChanging()) {
+ if (mFixedRotationLaunchingApp != null) {
+ // A fixed-rotation transition is done, then continue to start a seamless display
+ // transition. And be fore the start transaction is applied, the non-app windows
+ // need to keep in previous rotation to avoid showing inconsistent content.
+ t.setSeamlessRotation(this);
+ if (mAsyncRotationController != null) {
+ mAsyncRotationController.keepAppearanceInPreviousRotation();
+ }
+ } else if (isRotationChanging()) {
mWmService.mLatencyTracker.onActionStart(ACTION_ROTATE_SCREEN);
controller.mTransitionMetricsReporter.associate(t,
startTime -> mWmService.mLatencyTracker.onActionEnd(ACTION_ROTATE_SCREEN));
@@ -3308,7 +3314,7 @@
mImeLayeringTarget.dumpDebug(proto, INPUT_METHOD_TARGET, logLevel);
}
if (mImeInputTarget != null) {
- mImeInputTarget.dumpDebug(proto, INPUT_METHOD_INPUT_TARGET, logLevel);
+ mImeInputTarget.dumpProto(proto, INPUT_METHOD_INPUT_TARGET, logLevel);
}
if (mImeControlTarget != null
&& mImeControlTarget.getWindow() != null) {
@@ -3871,7 +3877,7 @@
}
private boolean isImeControlledByApp() {
- return mImeInputTarget != null && !mImeInputTarget.inMultiWindowMode();
+ return mImeInputTarget != null && mImeInputTarget.shouldControlIme();
}
boolean shouldImeAttachedToApp() {
@@ -3934,19 +3940,21 @@
*
* @param type The type of the IME target.
* @see #IME_TARGET_LAYERING
- * @see #IME_TARGET_INPUT
* @see #IME_TARGET_CONTROL
*/
InsetsControlTarget getImeTarget(@InputMethodTarget int type) {
switch (type) {
case IME_TARGET_LAYERING: return mImeLayeringTarget;
- case IME_TARGET_INPUT: return mImeInputTarget;
case IME_TARGET_CONTROL: return mImeControlTarget;
default:
return null;
}
}
+ InputTarget getImeInputTarget() {
+ return mImeInputTarget;
+ }
+
// IMPORTANT: When introducing new dependencies in this method, make sure that
// changes to those result in RootWindowContainer.updateDisplayImePolicyCache()
// being called.
@@ -3995,9 +4003,18 @@
* placed at its parent's surface.
*/
private void setImeLayeringTargetInner(@Nullable WindowState target) {
- if (target == mImeLayeringTarget) {
+ /**
+ * This function is also responsible for updating the IME control target
+ * and so in the case where the IME layering target does not change
+ * but the Input target does (for example, IME moving to a SurfaceControlViewHost
+ * we have to continue executing this function, otherwise there is no work
+ * to do.
+ */
+ if (target == mImeLayeringTarget && mLastImeInputTarget == mImeInputTarget) {
return;
}
+ mLastImeInputTarget = mImeInputTarget;
+
// If the IME target is the input target, before it changes, prepare the IME screenshot
// for the last IME target when its task is applying app transition. This is for the
// better IME transition to keep IME visibility when transitioning to the next task.
@@ -4039,9 +4056,9 @@
}
@VisibleForTesting
- void setImeInputTarget(WindowState target) {
+ void setImeInputTarget(InputTarget target) {
mImeInputTarget = target;
- boolean canScreenshot = mImeInputTarget == null || !mImeInputTarget.isSecureLocked();
+ boolean canScreenshot = mImeInputTarget == null || mImeInputTarget.canScreenshotIme();
if (mImeWindowsContainer.setCanScreenshot(canScreenshot)) {
mWmService.requestTraversal();
}
@@ -4157,7 +4174,7 @@
* The IME input target is the window which receives input from IME. It is also a candidate
* which controls the visibility and animation of the input method window.
*/
- void updateImeInputAndControlTarget(WindowState target) {
+ void updateImeInputAndControlTarget(InputTarget target) {
if (mImeInputTarget != target) {
ProtoLog.i(WM_DEBUG_IME, "setInputMethodInputTarget %s", target);
setImeInputTarget(target);
@@ -4167,8 +4184,8 @@
}
// Unfreeze IME insets after the new target updated, in case updateAboveInsetsState may
// deliver unrelated IME insets change to the non-IME requester.
- if (target != null && target.mActivityRecord != null) {
- target.mActivityRecord.mImeInsetsFrozenUntilStartInput = false;
+ if (target != null) {
+ target.unfreezeInsetsAfterStartInput();
}
}
@@ -4226,11 +4243,11 @@
InsetsControlTarget computeImeControlTarget() {
if (!isImeControlledByApp() && mRemoteInsetsControlTarget != null
|| (mImeInputTarget != null
- && getImeHostOrFallback(mImeInputTarget.getWindow())
- == mRemoteInsetsControlTarget)) {
+ && getImeHostOrFallback(mImeInputTarget.getWindowState())
+ == mRemoteInsetsControlTarget)) {
return mRemoteInsetsControlTarget;
} else {
- return mImeInputTarget;
+ return mImeInputTarget != null ? mImeInputTarget.getWindowState() : null;
}
}
@@ -4243,7 +4260,7 @@
// screen. If it's not covering the entire screen the IME might extend beyond the apps
// bounds.
if (shouldImeAttachedToApp()) {
- if (mImeLayeringTarget.mActivityRecord != mImeInputTarget.mActivityRecord) {
+ if (mImeLayeringTarget.mActivityRecord != mImeInputTarget.getActivityRecord()) {
// Do not change parent if the window hasn't requested IME.
return null;
}
@@ -6351,13 +6368,28 @@
}
/**
- * Return {@code true} if there is an ongoing animation to the "Recents" activity and this
- * activity as a fixed orientation so shouldn't be rotated.
+ * Returns the fixed orientation requested by a transient launch (e.g. recents animation).
+ * If it doesn't return SCREEN_ORIENTATION_UNSET, the rotation change should be deferred.
*/
- boolean isTopFixedOrientationRecentsAnimating() {
- return mAnimatingRecents != null
- && mAnimatingRecents.getRequestedConfigurationOrientation(true /* forDisplay */)
- != ORIENTATION_UNDEFINED && !hasTopFixedRotationLaunchingApp();
+ @ActivityInfo.ScreenOrientation int getTransientFixedOrientation() {
+ ActivityRecord source = null;
+ if (mTransitionController.isShellTransitionsEnabled()) {
+ final ActivityRecord r = mFixedRotationLaunchingApp;
+ if (r != null && mTransitionController.isTransientLaunch(r)) {
+ source = r;
+ }
+ } else if (mAnimatingRecents != null && !hasTopFixedRotationLaunchingApp()) {
+ source = mAnimatingRecents;
+ }
+ if (source == null || source.getRequestedConfigurationOrientation(
+ true /* forDisplay */) == ORIENTATION_UNDEFINED) {
+ return SCREEN_ORIENTATION_UNSET;
+ }
+ if (!mWmService.mPolicy.okToAnimate(false /* ignoreScreenOn */)) {
+ // If screen is off or the device is going to sleep, then still allow to update.
+ return SCREEN_ORIENTATION_UNSET;
+ }
+ return source.mOrientation;
}
@Override
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 6438d79..f116fff 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -419,11 +419,8 @@
* THE SCREEN.
*/
boolean updateRotationUnchecked(boolean forceUpdate) {
- final boolean useShellTransitions =
- mDisplayContent.mTransitionController.isShellTransitionsEnabled();
-
final int displayId = mDisplayContent.getDisplayId();
- if (!forceUpdate && !useShellTransitions) {
+ if (!forceUpdate) {
if (mDeferredRotationPauseCount > 0) {
// Rotation updates have been paused temporarily. Defer the update until updates
// have been resumed.
@@ -449,17 +446,16 @@
return false;
}
- final RecentsAnimationController recentsAnimController =
- mService.getRecentsAnimationController();
- if (recentsAnimController != null && mDisplayContent.mFixedRotationTransitionListener
- .isTopFixedOrientationRecentsAnimating()
- // If screen is off or the device is going to sleep, then still allow to update.
- && mService.mPolicy.okToAnimate(false /* ignoreScreenOn */)) {
+ final int transientFixedOrientation =
+ mDisplayContent.mFixedRotationTransitionListener.getTransientFixedOrientation();
+ if (transientFixedOrientation != SCREEN_ORIENTATION_UNSET) {
+ // Makes sure that after the transition is finished, updateOrientation() can see
+ // the difference from the latest orientation source.
+ mLastOrientation = transientFixedOrientation;
// During the recents animation, the closing app might still be considered on top.
// In order to ignore its requested orientation to avoid a sensor led rotation (e.g
// user rotating the device while the recents animation is running), we ignore
// rotation update while the animation is running.
- recentsAnimController.setCheckRotationAfterCleanup();
return false;
}
}
@@ -513,7 +509,7 @@
mDisplayContent.setLayoutNeeded();
- if (useShellTransitions) {
+ if (mDisplayContent.mTransitionController.isShellTransitionsEnabled()) {
final boolean wasCollecting = mDisplayContent.mTransitionController.isCollecting();
final TransitionRequestInfo.DisplayChange change = wasCollecting ? null
: new TransitionRequestInfo.DisplayChange(mDisplayContent.getDisplayId(),
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index 2ab08e6..dcc16eb 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -17,6 +17,9 @@
package com.android.server.wm;
+import static com.android.server.wm.IdentifierProto.HASH_CODE;
+import static com.android.server.wm.IdentifierProto.TITLE;
+import static com.android.server.wm.WindowContainerProto.IDENTIFIER;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -25,6 +28,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.ArrayMap;
+import android.util.proto.ProtoOutputStream;
import android.util.Slog;
import android.view.IWindow;
import android.view.InputApplicationHandle;
@@ -43,6 +47,8 @@
private ArrayMap<IBinder /*input token */, EmbeddedWindow> mWindows = new ArrayMap<>();
private ArrayMap<IBinder /*focus grant token */, EmbeddedWindow> mWindowsByFocusToken =
new ArrayMap<>();
+ private ArrayMap<IBinder /*window token*/, EmbeddedWindow> mWindowsByWindowToken =
+ new ArrayMap<>();
private final Object mGlobalLock;
private final ActivityTaskManagerService mAtmService;
@@ -63,6 +69,7 @@
mWindows.put(inputToken, window);
final IBinder focusToken = window.getFocusGrantToken();
mWindowsByFocusToken.put(focusToken, window);
+ mWindowsByWindowToken.put(window.getWindowToken(), window);
updateProcessController(window);
window.mClient.asBinder().linkToDeath(()-> {
synchronized (mGlobalLock) {
@@ -116,6 +123,7 @@
if (ew.mClient.asBinder() == client.asBinder()) {
mWindows.removeAt(i).onRemoved();
mWindowsByFocusToken.remove(ew.getFocusGrantToken());
+ mWindowsByWindowToken.remove(ew.getWindowToken());
return;
}
}
@@ -127,6 +135,7 @@
if (ew.mHostWindowState == host) {
mWindows.removeAt(i).onRemoved();
mWindowsByFocusToken.remove(ew.getFocusGrantToken());
+ mWindowsByWindowToken.remove(ew.getWindowToken());
}
}
}
@@ -139,6 +148,10 @@
return mWindowsByFocusToken.get(focusGrantToken);
}
+ EmbeddedWindow getByWindowToken(IBinder windowToken) {
+ return mWindowsByWindowToken.get(windowToken);
+ }
+
void onActivityRemoved(ActivityRecord activityRecord) {
for (int i = mWindows.size() - 1; i >= 0; i--) {
final EmbeddedWindow window = mWindows.valueAt(i);
@@ -244,15 +257,29 @@
}
@Override
+ public DisplayContent getDisplayContent() {
+ return mWmService.mRoot.getDisplayContent(getDisplayId());
+ }
+
+ @Override
public IWindow getIWindow() {
return mClient;
}
+ public IBinder getWindowToken() {
+ return mClient.asBinder();
+ }
+
@Override
public int getPid() {
return mOwnerPid;
}
+ @Override
+ public int getUid() {
+ return mOwnerUid;
+ }
+
void setIsOverlay() {
mIsOverlay = true;
}
@@ -297,5 +324,46 @@
public void handleTapOutsideFocusInsideSelf() {
handleTap(true);
}
+
+ @Override
+ public boolean shouldControlIme() {
+ return false;
+ }
+
+ @Override
+ public boolean canScreenshotIme() {
+ return true;
+ }
+
+ @Override
+ public void unfreezeInsetsAfterStartInput() {
+ }
+
+ @Override
+ public InsetsControlTarget getImeControlTarget() {
+ return mWmService.getDefaultDisplayContentLocked().mRemoteInsetsControlTarget;
+ }
+
+ @Override
+ public boolean isInputMethodClientFocus(int uid, int pid) {
+ return uid == mOwnerUid && pid == mOwnerPid;
+ }
+
+ @Override
+ public ActivityRecord getActivityRecord() {
+ return null;
+ }
+
+ @Override
+ public void dumpProto(ProtoOutputStream proto, long fieldId,
+ @WindowTraceLogLevel int logLevel) {
+ final long token = proto.start(fieldId);
+
+ final long token2 = proto.start(IDENTIFIER);
+ proto.write(HASH_CODE, System.identityHashCode(this));
+ proto.write(TITLE, "EmbeddedWindow");
+ proto.end(token2);
+ proto.end(token);
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index f24e429..199517c 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -21,7 +21,6 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IME;
import static com.android.server.wm.DisplayContent.IME_TARGET_CONTROL;
-import static com.android.server.wm.DisplayContent.IME_TARGET_INPUT;
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
import static com.android.server.wm.ImeInsetsSourceProviderProto.IME_TARGET_FROM_IME;
import static com.android.server.wm.ImeInsetsSourceProviderProto.INSETS_SOURCE_PROVIDER;
@@ -249,7 +248,7 @@
}
private boolean isImeInputTarget(InsetsControlTarget target) {
- return target == mDisplayContent.getImeTarget(IME_TARGET_INPUT);
+ return target == mDisplayContent.getImeInputTarget();
}
private boolean sameAsImeControlTarget() {
diff --git a/services/core/java/com/android/server/wm/InputTarget.java b/services/core/java/com/android/server/wm/InputTarget.java
index 5166b8a..b5ab62b 100644
--- a/services/core/java/com/android/server/wm/InputTarget.java
+++ b/services/core/java/com/android/server/wm/InputTarget.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import android.view.IWindow;
+import android.util.proto.ProtoOutputStream;
/**
* Common interface between focusable objects.
@@ -36,6 +37,7 @@
/* Owning pid of the target. */
int getPid();
+ int getUid();
/**
* Indicates whether a target should receive focus from server side
@@ -45,7 +47,25 @@
*/
boolean receiveFocusFromTapOutside();
+ // Gaining focus
void handleTapOutsideFocusInsideSelf();
+ // Losing focus
void handleTapOutsideFocusOutsideSelf();
+
+ // Whether this input target can control the IME itself
+ boolean shouldControlIme();
+ // Whether this input target can be screenshoted by the IME system
+ boolean canScreenshotIme();
+
+ ActivityRecord getActivityRecord();
+ void unfreezeInsetsAfterStartInput();
+
+ boolean isInputMethodClientFocus(int uid, int pid);
+
+ DisplayContent getDisplayContent();
+ InsetsControlTarget getImeControlTarget();
+
+ void dumpProto(ProtoOutputStream proto, long fieldId,
+ @WindowTraceLogLevel int logLevel);
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 30906e5..93714e8 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -122,7 +122,6 @@
private final int mDisplayId;
private boolean mWillFinishToHome = false;
private final Runnable mFailsafeRunnable = this::onFailsafe;
- private Runnable mCheckRotationAfterCleanup;
// The recents component app token that is shown behind the visibile tasks
private ActivityRecord mTargetActivityRecord;
@@ -920,24 +919,6 @@
}
/**
- * If the display rotation change is ignored while recents animation is running, make sure that
- * the pending rotation change will be applied after the animation finishes.
- */
- void setCheckRotationAfterCleanup() {
- if (mCheckRotationAfterCleanup != null) return;
- mCheckRotationAfterCleanup = () -> {
- synchronized (mService.mGlobalLock) {
- if (mDisplayContent.getDisplayRotation()
- .updateRotationAndSendNewConfigIfChanged()) {
- if (mTargetActivityRecord != null) {
- mTargetActivityRecord.finishFixedRotationTransform();
- }
- }
- }
- };
- }
-
- /**
* @return Whether we should defer the cancel from a root task order change until the next app
* transition.
*/
@@ -1037,10 +1018,6 @@
if (mStatusBar != null) {
mStatusBar.onRecentsAnimationStateChanged(false /* running */);
}
- if (mCheckRotationAfterCleanup != null) {
- mService.mH.post(mCheckRotationAfterCleanup);
- mCheckRotationAfterCleanup = null;
- }
}
void scheduleFailsafe() {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index cc03c60..f3933c5 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3292,7 +3292,7 @@
// We intend to let organizer manage task visibility but it doesn't
// have enough information until we finish shell transitions.
// In the mean time we do an easy fix here.
- final boolean show = isVisible() || isAnimating(TRANSITION | PARENTS);
+ final boolean show = isVisible() || isAnimating(TRANSITION | PARENTS | CHILDREN);
if (mSurfaceControl != null) {
if (show != mLastSurfaceShowing) {
getSyncTransaction().setVisibility(mSurfaceControl, show);
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 0f5828c..29d1742 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -80,9 +80,12 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
+import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.internal.protolog.ProtoLogGroup;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.server.LocalServices;
+import com.android.server.inputmethod.InputMethodManagerInternal;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -548,18 +551,28 @@
for (int i = 0; i < mTargetDisplays.size(); ++i) {
final DisplayContent dc = mTargetDisplays.get(i);
final AsyncRotationController asyncRotationController = dc.getAsyncRotationController();
- if (asyncRotationController != null) {
+ if (asyncRotationController != null && mTargets.contains(dc)) {
asyncRotationController.onTransitionFinished();
}
if (mTransientLaunches != null) {
+ InsetsControlTarget prevImeTarget = dc.getImeTarget(
+ DisplayContent.IME_TARGET_CONTROL);
+ InsetsControlTarget newImeTarget = null;
// Transient-launch activities cannot be IME target (WindowState#canBeImeTarget),
// so re-compute in case the IME target is changed after transition.
for (int t = 0; t < mTransientLaunches.size(); ++t) {
if (mTransientLaunches.keyAt(t).getDisplayContent() == dc) {
- dc.computeImeTarget(true /* updateImeTarget */);
+ newImeTarget = dc.computeImeTarget(true /* updateImeTarget */);
break;
}
}
+ if (mRecentsDisplayId != INVALID_DISPLAY && prevImeTarget == newImeTarget) {
+ // Restore IME icon only when moving the original app task to front from
+ // recents, in case IME icon may missing if the moving task has already been
+ // the current focused task.
+ InputMethodManagerInternal.get().updateImeWindowStatus(
+ false /* disableImeIcon */);
+ }
}
dc.handleCompleteDeferredRemoval();
}
@@ -696,7 +709,7 @@
// This is non-null only if display has changes. It handles the visible windows that don't
// need to be participated in the transition.
final AsyncRotationController controller = dc.getAsyncRotationController();
- if (controller != null) {
+ if (controller != null && mTargets.contains(dc)) {
controller.setupStartTransaction(transaction);
}
mStartTransaction = transaction;
@@ -781,6 +794,26 @@
}
}
+ // Hiding IME/IME icon when starting quick-step with resents animation.
+ if (!mTargetDisplays.get(mRecentsDisplayId).isImeAttachedToApp()) {
+ // Hiding IME if IME window is not attached to app.
+ // Since some windowing mode is not proper to snapshot Task with IME window
+ // while the app transitioning to the next task (e.g. split-screen mode)
+ final InputMethodManagerInternal inputMethodManagerInternal =
+ LocalServices.getService(InputMethodManagerInternal.class);
+ if (inputMethodManagerInternal != null) {
+ inputMethodManagerInternal.hideCurrentInputMethod(
+ SoftInputShowHideReason.HIDE_RECENTS_ANIMATION);
+ }
+ } else {
+ // Disable IME icon explicitly when IME attached to the app in case
+ // IME icon might flickering while swiping to the next app task still
+ // in animating before the next app window focused, or IME icon
+ // persists on the bottom when swiping the task to recents.
+ InputMethodManagerInternal.get().updateImeWindowStatus(
+ true /* disableImeIcon */);
+ }
+
// The rest of this function handles nav-bar reparenting
if (!dc.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition()
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index eee8b98..0436233 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS;
@@ -536,16 +535,16 @@
// TODO(b/188669821): Remove once legacy recents behavior is moved to shell.
// Also interpret HOME transient launch as recents
- if (activity.getActivityType() == ACTIVITY_TYPE_HOME) {
+ if (activity.isActivityTypeHomeOrRecents()) {
mCollectingTransition.addFlag(TRANSIT_FLAG_IS_RECENTS);
+ // When starting recents animation, we assume the recents activity is behind the app
+ // task and should not affect system bar appearance,
+ // until WMS#setRecentsAppBehindSystemBars be called from launcher when passing
+ // the gesture threshold.
+ activity.getTask().setCanAffectSystemUiFlags(false);
}
}
- void setSeamlessRotation(@NonNull WindowContainer wc) {
- if (mCollectingTransition == null) return;
- mCollectingTransition.setSeamlessRotation(wc);
- }
-
void legacyDetachNavigationBarFromApp(@NonNull IBinder token) {
final Transition transition = Transition.fromBinder(token);
if (transition == null || !mPlayingTransitions.contains(transition)) {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 3dac770..56014ad 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1024,7 +1024,7 @@
return mProvidedInsetsSources;
}
- DisplayContent getDisplayContent() {
+ public DisplayContent getDisplayContent() {
return mDisplayContent;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 5db72ee..04f135e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -25,6 +25,7 @@
import static android.Manifest.permission.READ_FRAME_BUFFER;
import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS;
import static android.Manifest.permission.RESTRICTED_VR_ACCESS;
+import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
@@ -114,7 +115,6 @@
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.ActivityTaskManagerService.POWER_MODE_REASON_CHANGE_DISPLAY;
import static com.android.server.wm.DisplayContent.IME_TARGET_CONTROL;
-import static com.android.server.wm.DisplayContent.IME_TARGET_INPUT;
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL;
@@ -5069,6 +5069,15 @@
return null;
}
+ @Nullable InputTarget getInputTargetFromWindowTokenLocked(IBinder windowToken) {
+ InputTarget window = mWindowMap.get(windowToken);
+ if (window != null) {
+ return window;
+ }
+ window = mEmbeddedWindowController.getByWindowToken(windowToken);
+ return window;
+ }
+
void reportFocusChanged(IBinder oldToken, IBinder newToken) {
InputTarget lastTarget;
InputTarget newTarget;
@@ -6506,7 +6515,7 @@
mRoot.forAllDisplays(dc -> {
final int displayId = dc.getDisplayId();
final InsetsControlTarget imeLayeringTarget = dc.getImeTarget(IME_TARGET_LAYERING);
- final InsetsControlTarget imeInputTarget = dc.getImeTarget(IME_TARGET_INPUT);
+ final InputTarget imeInputTarget = dc.getImeInputTarget();
final InsetsControlTarget imeControlTarget = dc.getImeTarget(IME_TARGET_CONTROL);
if (imeLayeringTarget != null) {
pw.print(" imeLayeringTarget in display# "); pw.print(displayId);
@@ -7714,7 +7723,8 @@
+ " imeTargetWindowToken=" + imeTargetWindowToken);
}
synchronized (mGlobalLock) {
- final WindowState imeTarget = mWindowMap.get(imeTargetWindowToken);
+ InputTarget imeTarget =
+ getInputTargetFromWindowTokenLocked(imeTargetWindowToken);
if (imeTarget != null) {
imeTarget.getDisplayContent().updateImeInputAndControlTarget(imeTarget);
}
@@ -7794,11 +7804,11 @@
}
synchronized (mGlobalLock) {
final DisplayContent displayContent = mRoot.getTopFocusedDisplayContent();
- final WindowState window = mWindowMap.get(windowToken);
- if (window == null) {
+ InputTarget target = getInputTargetFromWindowTokenLocked(windowToken);
+ if (target == null) {
return ImeClientFocusResult.NOT_IME_TARGET_WINDOW;
}
- final int tokenDisplayId = window.getDisplayContent().getDisplayId();
+ final int tokenDisplayId = target.getDisplayContent().getDisplayId();
if (tokenDisplayId != displayId) {
Slog.e(TAG, "isInputMethodClientFocus: display ID mismatch."
+ " from client: " + displayId
@@ -7811,7 +7821,7 @@
return ImeClientFocusResult.INVALID_DISPLAY_ID;
}
- if (displayContent.isInputMethodClientFocus(uid, pid)) {
+ if (target.isInputMethodClientFocus(uid, pid)) {
return ImeClientFocusResult.HAS_IME_FOCUS;
}
// Okay, how about this... what is the current focus?
@@ -7835,7 +7845,7 @@
@Override
public void showImePostLayout(IBinder imeTargetWindowToken) {
synchronized (mGlobalLock) {
- WindowState imeTarget = mWindowMap.get(imeTargetWindowToken);
+ InputTarget imeTarget = getInputTargetFromWindowTokenLocked(imeTargetWindowToken);
if (imeTarget == null) {
return;
}
@@ -8974,4 +8984,24 @@
return Bitmap.wrapHardwareBuffer(taskSnapshot.getHardwareBuffer(),
taskSnapshot.getColorSpace());
}
+
+ @Override
+ public void setRecentsAppBehindSystemBars(boolean behindSystemBars) {
+ if (!checkCallingPermission(START_TASKS_FROM_RECENTS, "setRecentsAppBehindSystemBars()")) {
+ throw new SecurityException("Requires START_TASKS_FROM_RECENTS permission");
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final Task recentsApp = mRoot.getTask(task -> task.isActivityTypeHomeOrRecents()
+ && task.getTopVisibleActivity() != null);
+ if (recentsApp != null) {
+ recentsApp.getTask().setCanAffectSystemUiFlags(behindSystemBars);
+ mWindowPlacerLocked.requestTraversal();
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 26acf43..517837c 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -120,7 +120,6 @@
import static com.android.server.policy.WindowManagerPolicy.TRANSIT_EXIT;
import static com.android.server.policy.WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
import static com.android.server.wm.AnimationSpecProto.MOVE;
-import static com.android.server.wm.DisplayContent.IME_TARGET_INPUT;
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
import static com.android.server.wm.DisplayContent.logsGestureExclusionRestrictions;
import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
@@ -1602,14 +1601,14 @@
}
@Override
- DisplayContent getDisplayContent() {
+ public DisplayContent getDisplayContent() {
return mToken.getDisplayContent();
}
@Override
void onDisplayChanged(DisplayContent dc) {
if (dc != null && mDisplayContent != null && dc != mDisplayContent
- && getImeInputTarget() == this) {
+ && mDisplayContent.getImeInputTarget() == this) {
dc.updateImeInputAndControlTarget(getImeInputTarget());
mDisplayContent.setImeInputTarget(null);
}
@@ -1749,6 +1748,11 @@
return mSession.mPid;
}
+ @Override
+ public int getUid() {
+ return mSession.mUid;
+ }
+
Task getTask() {
return mActivityRecord != null ? mActivityRecord.getTask() : null;
}
@@ -2348,7 +2352,7 @@
@Override
public void onConfigurationChanged(Configuration newParentConfig) {
- if (getDisplayContent().getImeTarget(IME_TARGET_INPUT) != this && !isImeLayeringTarget()) {
+ if (getDisplayContent().getImeInputTarget() != this && !isImeLayeringTarget()) {
super.onConfigurationChanged(newParentConfig);
return;
}
@@ -2424,7 +2428,7 @@
dc.setImeLayeringTarget(null);
dc.computeImeTarget(true /* updateImeTarget */);
}
- if (dc.getImeTarget(IME_TARGET_INPUT) == this) {
+ if (dc.getImeInputTarget() == this) {
dc.updateImeInputAndControlTarget(null);
}
@@ -5596,7 +5600,8 @@
* @return {@link InsetsControlTarget} of host that controls the IME.
* When window is doesn't have a parent, it is returned as-is.
*/
- InsetsControlTarget getImeControlTarget() {
+ @Override
+ public InsetsControlTarget getImeControlTarget() {
return getDisplayContent().getImeHostOrFallback(this);
}
@@ -5731,8 +5736,8 @@
}
WindowState getImeInputTarget() {
- final InsetsControlTarget target = mDisplayContent.getImeTarget(IME_TARGET_INPUT);
- return target != null ? target.getWindow() : null;
+ final InputTarget target = mDisplayContent.getImeInputTarget();
+ return target != null ? target.getWindowState() : null;
}
void forceReportingResized() {
@@ -6117,4 +6122,37 @@
mTouchableInsets = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
mGivenTouchableRegion.setEmpty();
}
+
+ @Override
+ public boolean shouldControlIme() {
+ return !inMultiWindowMode();
+ }
+
+ @Override
+ public boolean canScreenshotIme() {
+ return !isSecureLocked();
+ }
+
+ @Override
+ public ActivityRecord getActivityRecord() {
+ return mActivityRecord;
+ }
+
+ @Override
+ public void unfreezeInsetsAfterStartInput() {
+ if (mActivityRecord != null) {
+ mActivityRecord.mImeInsetsFrozenUntilStartInput = false;
+ }
+ }
+
+ @Override
+ public boolean isInputMethodClientFocus(int uid, int pid) {
+ return getDisplayContent().isInputMethodClientFocus(uid, pid);
+ }
+
+ @Override
+ public void dumpProto(ProtoOutputStream proto, long fieldId,
+ @WindowTraceLogLevel int logLevel) {
+ dumpDebug(proto, fieldId, logLevel);
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index ef61fbf..1ac0c26 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -214,6 +214,7 @@
import android.app.admin.SystemUpdateInfo;
import android.app.admin.SystemUpdatePolicy;
import android.app.admin.UnsafeStateException;
+import android.app.admin.WifiSsidPolicy;
import android.app.backup.IBackupManager;
import android.app.compat.CompatChanges;
import android.app.role.RoleManager;
@@ -269,6 +270,7 @@
import android.net.VpnManager;
import android.net.metrics.IpConnectivityLog;
import android.net.wifi.WifiManager;
+import android.net.wifi.WifiSsid;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -389,6 +391,7 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
+import java.nio.charset.StandardCharsets;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
@@ -18514,9 +18517,21 @@
);
}
- private void validateCurrentWifiMeetsAdminRequirements() {
+ private void notifyMinimumRequiredWifiSecurityLevelChanged(int level) {
mInjector.binderWithCleanCallingIdentity(
- () -> mInjector.getWifiManager().validateCurrentWifiMeetsAdminRequirements());
+ () -> mInjector.getWifiManager()
+ .notifyMinimumRequiredWifiSecurityLevelChanged(level));
+ }
+
+ private void notifyWifiSsidPolicyChanged(int policyType, List<String> ssids) {
+ List<WifiSsid> wifiSsidList = new ArrayList<>();
+ for (String ssid : ssids) {
+ wifiSsidList.add(
+ WifiSsid.fromBytes(ssid.getBytes(StandardCharsets.UTF_8)));
+ }
+ WifiSsidPolicy policy = new WifiSsidPolicy(policyType, new ArraySet<>(wifiSsidList));
+ mInjector.binderWithCleanCallingIdentity(
+ () -> mInjector.getWifiManager().notifyWifiSsidPolicyChanged(policy));
}
@Override
@@ -18536,7 +18551,7 @@
valueChanged = true;
}
}
- if (valueChanged) validateCurrentWifiMeetsAdminRequirements();
+ if (valueChanged) notifyMinimumRequiredWifiSecurityLevelChanged(level);
}
@Override
@@ -18568,7 +18583,9 @@
}
if (changed) saveSettingsLocked(caller.getUserId());
}
- if (changed) validateCurrentWifiMeetsAdminRequirements();
+ if (changed && !ssids.isEmpty()) {
+ notifyWifiSsidPolicyChanged(WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_ALLOWLIST, ssids);
+ }
}
@Override
@@ -18607,7 +18624,9 @@
}
if (changed) saveSettingsLocked(caller.getUserId());
}
- if (changed) validateCurrentWifiMeetsAdminRequirements();
+ if (changed) {
+ notifyWifiSsidPolicyChanged(WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_DENYLIST, ssids);
+ }
}
@Override
diff --git a/services/proguard.flags b/services/proguard.flags
index 0e081f1..425da6c 100644
--- a/services/proguard.flags
+++ b/services/proguard.flags
@@ -40,9 +40,15 @@
# Global entities normally kept through explicit Manifest entries
# TODO(b/210510433): Revisit and consider generating from frameworks/base/core/res/AndroidManifest.xml,
# by including that manifest with the library rule that triggers optimization.
--keep,allowoptimization,allowaccessmodification class * extends android.app.backup.BackupAgent
--keep,allowoptimization,allowaccessmodification class * extends android.content.BroadcastReceiver
--keep,allowoptimization,allowaccessmodification class * extends android.content.ContentProvider
+-keep,allowoptimization,allowaccessmodification class com.android.server.** extends android.app.Activity
+-keep,allowoptimization,allowaccessmodification class com.android.server.** extends android.app.Service
+-keep,allowoptimization,allowaccessmodification class com.android.server.** extends android.app.backup.BackupAgent
+-keep,allowoptimization,allowaccessmodification class com.android.server.** extends android.content.BroadcastReceiver
+-keep,allowoptimization,allowaccessmodification class com.android.server.** extends android.content.ContentProvider
+-keep,allowoptimization,allowaccessmodification class com.android.server.** extends android.preference.Preference
+-keep,allowoptimization,allowaccessmodification class com.android.server.** extends android.view.View {
+ public <init>(...);
+}
# Various classes subclassed in or referenced via JNI in ethernet-service
-keep public class android.net.** { *; }
@@ -67,6 +73,7 @@
-keep,allowoptimization,allowaccessmodification class com.android.server.location.gnss.GnssConfiguration$HalInterfaceVersion { *; }
-keep,allowoptimization,allowaccessmodification class com.android.server.location.gnss.GnssPowerStats { *; }
-keep,allowoptimization,allowaccessmodification class com.android.server.location.gnss.hal.GnssNative { *; }
+-keep,allowoptimization,allowaccessmodification class com.android.server.pm.PackageManagerShellCommandDataLoader { *; }
-keep,allowoptimization,allowaccessmodification class com.android.server.sensors.SensorManagerInternal$ProximityActiveListener { *; }
-keep,allowoptimization,allowaccessmodification class com.android.server.sensors.SensorService { *; }
-keep,allowoptimization,allowaccessmodification class com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareImpl$AudioSessionProvider$AudioSession { *; }
diff --git a/services/tests/inprocesstests/AndroidTest.xml b/services/tests/inprocesstests/AndroidTest.xml
index b541512..f5fea1b 100644
--- a/services/tests/inprocesstests/AndroidTest.xml
+++ b/services/tests/inprocesstests/AndroidTest.xml
@@ -18,6 +18,8 @@
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-instrumentation" />
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/>
+
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true"/>
<option name="test-file-name" value="FrameworksInProcessTests.apk"/>
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 a0ac506..9a4f8e2 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
@@ -16,11 +16,6 @@
package com.android.server.job.controllers;
-import static android.app.job.JobInfo.PRIORITY_DEFAULT;
-import static android.app.job.JobInfo.PRIORITY_HIGH;
-import static android.app.job.JobInfo.PRIORITY_LOW;
-import static android.app.job.JobInfo.PRIORITY_MIN;
-
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -275,14 +270,14 @@
}
private void setCharging() {
- when(mJobSchedulerService.isBatteryCharging()).thenReturn(true);
+ doReturn(true).when(mJobSchedulerService).isBatteryCharging();
synchronized (mQuotaController.mLock) {
mQuotaController.onBatteryStateChangedLocked();
}
}
private void setDischarging() {
- when(mJobSchedulerService.isBatteryCharging()).thenReturn(false);
+ doReturn(false).when(mJobSchedulerService).isBatteryCharging();
synchronized (mQuotaController.mLock) {
mQuotaController.onBatteryStateChangedLocked();
}
@@ -415,14 +410,6 @@
}
}
- private void setDeviceConfigFloat(String key, float val) {
- mDeviceConfigPropertiesBuilder.setFloat(key, val);
- synchronized (mQuotaController.mLock) {
- mQuotaController.prepareForUpdatedConstantsLocked();
- mQcConstants.processConstantLocked(mDeviceConfigPropertiesBuilder.build(), key);
- }
- }
-
private void waitForNonDelayedMessagesProcessed() {
mQuotaController.getHandler().runWithScissors(() -> {}, 15_000);
}
@@ -861,7 +848,7 @@
SOURCE_USER_ID, SOURCE_PACKAGE, inputStats);
assertEquals(expectedStats, inputStats);
assertTrue(mQuotaController.isWithinQuotaLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX, PRIORITY_DEFAULT));
+ SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX));
}
assertTrue("Job not ready: " + jobStatus, jobStatus.isReady());
}
@@ -885,7 +872,7 @@
assertEquals(expectedStats, inputStats);
assertFalse(
mQuotaController.isWithinQuotaLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX, PRIORITY_DEFAULT));
+ SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX));
}
// Quota should be exceeded due to activity in active timer.
@@ -910,7 +897,7 @@
assertEquals(expectedStats, inputStats);
assertFalse(
mQuotaController.isWithinQuotaLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX, PRIORITY_DEFAULT));
+ SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX));
assertFalse("Job unexpectedly ready: " + jobStatus, jobStatus.isReady());
}
}
@@ -1508,7 +1495,7 @@
SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 30 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ SOURCE_USER_ID, SOURCE_PACKAGE));
}
}
@@ -1541,7 +1528,7 @@
SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ SOURCE_USER_ID, SOURCE_PACKAGE));
}
setStandbyBucket(FREQUENT_INDEX);
@@ -1551,7 +1538,7 @@
SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ SOURCE_USER_ID, SOURCE_PACKAGE));
}
setStandbyBucket(WORKING_INDEX);
@@ -1561,7 +1548,7 @@
SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(7 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ SOURCE_USER_ID, SOURCE_PACKAGE));
}
// ACTIVE window = allowed time, so jobs can essentially run non-stop until they reach the
@@ -1573,7 +1560,7 @@
SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 9 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ SOURCE_USER_ID, SOURCE_PACKAGE));
}
}
@@ -1597,7 +1584,7 @@
// Max time will phase out, so should use bucket limit.
assertEquals(10 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ SOURCE_USER_ID, SOURCE_PACKAGE));
}
mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear();
@@ -1613,7 +1600,7 @@
SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(10 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ SOURCE_USER_ID, SOURCE_PACKAGE));
}
mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear();
@@ -1630,7 +1617,7 @@
SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(3 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ SOURCE_USER_ID, SOURCE_PACKAGE));
}
}
@@ -1663,7 +1650,7 @@
// window time.
assertEquals(10 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ SOURCE_USER_ID, SOURCE_PACKAGE));
}
mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear();
@@ -1690,107 +1677,7 @@
// Max time only has one minute phase out. Bucket time has 2 minute phase out.
assertEquals(9 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
- }
- }
-
- /**
- * Test getTimeUntilQuotaConsumedLocked when the determination is based on the job's priority.
- */
- @Test
- public void testGetTimeUntilQuotaConsumedLocked_Priority() {
- final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
- // Close to RARE boundary.
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (24 * HOUR_IN_MILLIS - 30 * SECOND_IN_MILLIS),
- 150 * SECOND_IN_MILLIS, 5), false);
- // Far away from FREQUENT boundary.
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (7 * HOUR_IN_MILLIS), 2 * MINUTE_IN_MILLIS, 5), false);
- // Overlap WORKING_SET boundary.
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS),
- 2 * MINUTE_IN_MILLIS, 5), false);
- // Close to ACTIVE boundary.
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (9 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
-
- setStandbyBucket(RARE_INDEX);
- synchronized (mQuotaController.mLock) {
- assertEquals(30 * SECOND_IN_MILLIS,
- mQuotaController.getRemainingExecutionTimeLocked(
SOURCE_USER_ID, SOURCE_PACKAGE));
- assertEquals(3 * MINUTE_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_HIGH));
- assertEquals(3 * MINUTE_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
- assertEquals(0,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_LOW));
- assertEquals(0,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_MIN));
- }
-
- setStandbyBucket(FREQUENT_INDEX);
- synchronized (mQuotaController.mLock) {
- assertEquals(3 * MINUTE_IN_MILLIS,
- mQuotaController.getRemainingExecutionTimeLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE));
- assertEquals(3 * MINUTE_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_HIGH));
- assertEquals(3 * MINUTE_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
- assertEquals(30 * SECOND_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_LOW));
- assertEquals(0,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_MIN));
- }
-
- setStandbyBucket(WORKING_INDEX);
- synchronized (mQuotaController.mLock) {
- assertEquals(6 * MINUTE_IN_MILLIS,
- mQuotaController.getRemainingExecutionTimeLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE));
- assertEquals(7 * MINUTE_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_HIGH));
- assertEquals(7 * MINUTE_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
- assertEquals(4 * MINUTE_IN_MILLIS + 30 * SECOND_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_LOW));
- assertEquals(2 * MINUTE_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_MIN));
- }
-
- // ACTIVE window = allowed time, so jobs can essentially run non-stop until they reach the
- // max execution time.
- setStandbyBucket(ACTIVE_INDEX);
- synchronized (mQuotaController.mLock) {
- assertEquals(7 * MINUTE_IN_MILLIS,
- mQuotaController.getRemainingExecutionTimeLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE));
- assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 7 * MINUTE_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_HIGH));
- assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 7 * MINUTE_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
- assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 7 * MINUTE_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_LOW));
- assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 7 * MINUTE_IN_MILLIS,
- mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_MIN));
}
}
@@ -1820,7 +1707,7 @@
SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 10 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ SOURCE_USER_ID, SOURCE_PACKAGE));
}
}
@@ -1842,7 +1729,7 @@
SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(10 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ SOURCE_USER_ID, SOURCE_PACKAGE));
}
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
@@ -1854,7 +1741,7 @@
SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(10 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ SOURCE_USER_ID, SOURCE_PACKAGE));
}
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
@@ -1867,7 +1754,7 @@
SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(10 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ SOURCE_USER_ID, SOURCE_PACKAGE));
}
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
@@ -1882,15 +1769,15 @@
SOURCE_USER_ID, SOURCE_PACKAGE));
assertEquals(mQcConstants.MAX_EXECUTION_TIME_MS - 30 * MINUTE_IN_MILLIS,
mQuotaController.getTimeUntilQuotaConsumedLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, PRIORITY_DEFAULT));
+ SOURCE_USER_ID, SOURCE_PACKAGE));
}
}
@Test
public void testIsWithinQuotaLocked_NeverApp() {
synchronized (mQuotaController.mLock) {
- assertFalse(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test.never", NEVER_INDEX, PRIORITY_DEFAULT));
+ assertFalse(
+ mQuotaController.isWithinQuotaLocked(0, "com.android.test.never", NEVER_INDEX));
}
}
@@ -1898,8 +1785,7 @@
public void testIsWithinQuotaLocked_Charging() {
setCharging();
synchronized (mQuotaController.mLock) {
- assertTrue(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", RARE_INDEX, PRIORITY_DEFAULT));
+ assertTrue(mQuotaController.isWithinQuotaLocked(0, "com.android.test", RARE_INDEX));
}
}
@@ -1913,8 +1799,7 @@
createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
synchronized (mQuotaController.mLock) {
mQuotaController.incrementJobCountLocked(0, "com.android.test", 5);
- assertTrue(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", WORKING_INDEX, PRIORITY_DEFAULT));
+ assertTrue(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX));
}
}
@@ -1931,7 +1816,7 @@
synchronized (mQuotaController.mLock) {
mQuotaController.incrementJobCountLocked(0, "com.android.test.spam", jobCount);
assertFalse(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test.spam", WORKING_INDEX, PRIORITY_DEFAULT));
+ 0, "com.android.test.spam", WORKING_INDEX));
}
mQuotaController.saveTimingSession(0, "com.android.test.frequent",
@@ -1941,7 +1826,7 @@
createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 500), false);
synchronized (mQuotaController.mLock) {
assertFalse(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test.frequent", FREQUENT_INDEX, PRIORITY_DEFAULT));
+ 0, "com.android.test.frequent", FREQUENT_INDEX));
}
}
@@ -1957,8 +1842,7 @@
createTimingSession(now - (5 * MINUTE_IN_MILLIS), 4 * MINUTE_IN_MILLIS, 5), false);
synchronized (mQuotaController.mLock) {
mQuotaController.incrementJobCountLocked(0, "com.android.test", 5);
- assertFalse(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", WORKING_INDEX, PRIORITY_DEFAULT));
+ assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX));
}
}
@@ -1974,8 +1858,7 @@
false);
synchronized (mQuotaController.mLock) {
mQuotaController.incrementJobCountLocked(0, "com.android.test", jobCount);
- assertFalse(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", WORKING_INDEX, PRIORITY_DEFAULT));
+ assertFalse(mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX));
}
}
@@ -2128,66 +2011,22 @@
assertEquals("Rare has incorrect quota status with " + (i + 1) + " sessions",
i < 2,
- mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", RARE_INDEX, PRIORITY_DEFAULT));
+ mQuotaController.isWithinQuotaLocked(0, "com.android.test", RARE_INDEX));
assertEquals("Frequent has incorrect quota status with " + (i + 1) + " sessions",
i < 3,
mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", FREQUENT_INDEX, PRIORITY_DEFAULT));
+ 0, "com.android.test", FREQUENT_INDEX));
assertEquals("Working has incorrect quota status with " + (i + 1) + " sessions",
i < 4,
- mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", WORKING_INDEX, PRIORITY_DEFAULT));
+ mQuotaController.isWithinQuotaLocked(0, "com.android.test", WORKING_INDEX));
assertEquals("Active has incorrect quota status with " + (i + 1) + " sessions",
i < 5,
- mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", ACTIVE_INDEX, PRIORITY_DEFAULT));
+ mQuotaController.isWithinQuotaLocked(0, "com.android.test", ACTIVE_INDEX));
}
}
}
@Test
- public void testIsWithinQuotaLocked_Priority() {
- setDischarging();
- final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
- mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - (7 * HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
- mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
- mQuotaController.saveTimingSession(0, "com.android.test",
- createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 5), false);
- synchronized (mQuotaController.mLock) {
- mQuotaController.incrementJobCountLocked(0, "com.android.test", 5);
- assertTrue(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", FREQUENT_INDEX, PRIORITY_HIGH));
- assertTrue(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", FREQUENT_INDEX, PRIORITY_DEFAULT));
- assertFalse(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", FREQUENT_INDEX, PRIORITY_LOW));
- assertFalse(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", FREQUENT_INDEX, PRIORITY_MIN));
-
- assertTrue(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", WORKING_INDEX, PRIORITY_HIGH));
- assertTrue(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", WORKING_INDEX, PRIORITY_DEFAULT));
- assertTrue(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", WORKING_INDEX, PRIORITY_LOW));
- assertFalse(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", WORKING_INDEX, PRIORITY_MIN));
-
- assertTrue(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", ACTIVE_INDEX, PRIORITY_HIGH));
- assertTrue(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", ACTIVE_INDEX, PRIORITY_DEFAULT));
- assertTrue(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", ACTIVE_INDEX, PRIORITY_LOW));
- assertTrue(mQuotaController.isWithinQuotaLocked(
- 0, "com.android.test", ACTIVE_INDEX, PRIORITY_MIN));
- }
- }
-
- @Test
public void testIsWithinEJQuotaLocked_NeverApp() {
JobStatus js = createExpeditedJobStatus("testIsWithinEJQuotaLocked_NeverApp", 1);
setStandbyBucket(NEVER_INDEX, js);
@@ -2737,8 +2576,7 @@
mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
createTimingSession(now - 25 * HOUR_IN_MILLIS, 5 * MINUTE_IN_MILLIS, 1), false);
synchronized (mQuotaController.mLock) {
- mQuotaController.maybeScheduleStartAlarmLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, standbyBucket);
+ mQuotaController.maybeScheduleStartAlarmLocked(0, "com.android.test", standbyBucket);
}
verify(mAlarmManager, timeout(1000).times(0)).setWindow(
anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
@@ -2790,128 +2628,6 @@
anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
}
- @Test
- public void testMaybeScheduleStartAlarmLocked_Priority() {
- // saveTimingSession calls maybeScheduleCleanupAlarmLocked which interferes with these tests
- // because it schedules an alarm too. Prevent it from doing so.
- spyOn(mQuotaController);
- doNothing().when(mQuotaController).maybeScheduleCleanupAlarmLocked();
-
- setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_RARE, 5);
-
- final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (24 * HOUR_IN_MILLIS), MINUTE_IN_MILLIS, 1), false);
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (7 * HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 1), false);
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (HOUR_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 1), false);
- mQuotaController.saveTimingSession(SOURCE_USER_ID, SOURCE_PACKAGE,
- createTimingSession(now - (5 * MINUTE_IN_MILLIS), 3 * MINUTE_IN_MILLIS, 1), false);
-
- InOrder inOrder = inOrder(mAlarmManager);
-
- JobStatus jobDef = createJobStatus("testMaybeScheduleStartAlarmLocked_Priority",
- SOURCE_PACKAGE, CALLING_UID,
- new JobInfo.Builder(1, new ComponentName(mContext, "TestQuotaJobService"))
- .setPriority(PRIORITY_DEFAULT)
- .build());
- JobStatus jobLow = createJobStatus("testMaybeScheduleStartAlarmLocked_Priority",
- SOURCE_PACKAGE, CALLING_UID,
- new JobInfo.Builder(2, new ComponentName(mContext, "TestQuotaJobService"))
- .setPriority(PRIORITY_LOW)
- .build());
- JobStatus jobMin = createJobStatus("testMaybeScheduleStartAlarmLocked_Priority",
- SOURCE_PACKAGE, CALLING_UID,
- new JobInfo.Builder(3, new ComponentName(mContext, "TestQuotaJobService"))
- .setPriority(PRIORITY_MIN)
- .build());
-
- setStandbyBucket(RARE_INDEX, jobDef, jobLow, jobMin);
- synchronized (mQuotaController.mLock) {
- mQuotaController.maybeStartTrackingJobLocked(jobMin, null);
- mQuotaController.maybeScheduleStartAlarmLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX);
- // Min job requires 5 mins of surplus.
- long expectedAlarmTime = now + 23 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS;
- inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
- anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
-
- mQuotaController.maybeStartTrackingJobLocked(jobLow, null);
- mQuotaController.maybeScheduleStartAlarmLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX);
- // Low job requires 2.5 mins of surplus.
- expectedAlarmTime = now + 17 * HOUR_IN_MILLIS + 90 * SECOND_IN_MILLIS;
- inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
- anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
-
- mQuotaController.maybeStartTrackingJobLocked(jobDef, null);
- mQuotaController.maybeScheduleStartAlarmLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, RARE_INDEX);
- // Default+ jobs require IN_QUOTA_BUFFER_MS.
- expectedAlarmTime = now + mQcConstants.IN_QUOTA_BUFFER_MS;
- inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
- anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
-
- mQuotaController.maybeStopTrackingJobLocked(jobMin, null, false);
- mQuotaController.maybeStopTrackingJobLocked(jobLow, null, false);
- mQuotaController.maybeStopTrackingJobLocked(jobDef, null, false);
-
- setStandbyBucket(FREQUENT_INDEX, jobDef, jobLow, jobMin);
-
- mQuotaController.maybeStartTrackingJobLocked(jobMin, null);
- mQuotaController.maybeScheduleStartAlarmLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, FREQUENT_INDEX);
- // Min job requires 5 mins of surplus.
- expectedAlarmTime = now + 7 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS;
- inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
- anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
-
- mQuotaController.maybeStartTrackingJobLocked(jobLow, null);
- mQuotaController.maybeScheduleStartAlarmLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, FREQUENT_INDEX);
- // Low job requires 2.5 mins of surplus.
- expectedAlarmTime = now + HOUR_IN_MILLIS + 90 * SECOND_IN_MILLIS;
- inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
- anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
-
- mQuotaController.maybeStartTrackingJobLocked(jobDef, null);
- mQuotaController.maybeScheduleStartAlarmLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, FREQUENT_INDEX);
- // Default+ jobs already have enough quota.
- inOrder.verify(mAlarmManager, timeout(1000).times(0)).setWindow(
- anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
-
- mQuotaController.maybeStopTrackingJobLocked(jobMin, null, false);
- mQuotaController.maybeStopTrackingJobLocked(jobLow, null, false);
- mQuotaController.maybeStopTrackingJobLocked(jobDef, null, false);
-
- setStandbyBucket(WORKING_INDEX, jobDef, jobLow, jobMin);
-
- mQuotaController.maybeStartTrackingJobLocked(jobMin, null);
- mQuotaController.maybeScheduleStartAlarmLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX);
- // Min job requires 5 mins of surplus.
- expectedAlarmTime = now + HOUR_IN_MILLIS + MINUTE_IN_MILLIS;
- inOrder.verify(mAlarmManager, timeout(1000).times(1)).setWindow(
- anyInt(), eq(expectedAlarmTime), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
-
- mQuotaController.maybeStartTrackingJobLocked(jobLow, null);
- mQuotaController.maybeScheduleStartAlarmLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX);
- // Low job has enough surplus.
- inOrder.verify(mAlarmManager, timeout(1000).times(0)).setWindow(
- anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
-
- mQuotaController.maybeStartTrackingJobLocked(jobDef, null);
- mQuotaController.maybeScheduleStartAlarmLocked(
- SOURCE_USER_ID, SOURCE_PACKAGE, WORKING_INDEX);
- // Default+ jobs already have enough quota.
- inOrder.verify(mAlarmManager, timeout(1000).times(0)).setWindow(
- anyInt(), anyLong(), anyLong(), eq(TAG_QUOTA_CHECK), any(), any());
- }
- }
-
/** Tests that the start alarm is properly rescheduled if the app's bucket is changed. */
@Test
public void testMaybeScheduleStartAlarmLocked_BucketChange() {
@@ -3212,8 +2928,6 @@
setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
11 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, 2 * MINUTE_IN_MILLIS);
- setDeviceConfigFloat(QcConstants.KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW, .7f);
- setDeviceConfigFloat(QcConstants.KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN, .2f);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, 99 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_ACTIVE_MS, 15 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_WORKING_MS, 30 * MINUTE_IN_MILLIS);
@@ -3269,8 +2983,6 @@
assertEquals(11 * MINUTE_IN_MILLIS,
mQuotaController.getAllowedTimePerPeriodMs()[RESTRICTED_INDEX]);
assertEquals(2 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs());
- assertEquals(.7f, mQuotaController.getAllowedTimeSurplusPriorityLow(), 1e-6);
- assertEquals(.2f, mQuotaController.getAllowedTimeSurplusPriorityMin(), 1e-6);
assertEquals(99 * MINUTE_IN_MILLIS,
mQuotaController.getBucketWindowSizes()[EXEMPTED_INDEX]);
assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[ACTIVE_INDEX]);
@@ -3327,8 +3039,6 @@
setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
-MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, -MINUTE_IN_MILLIS);
- setDeviceConfigFloat(QcConstants.KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW, -.1f);
- setDeviceConfigFloat(QcConstants.KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN, -.01f);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, -MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_ACTIVE_MS, -MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_WORKING_MS, -MINUTE_IN_MILLIS);
@@ -3379,8 +3089,6 @@
assertEquals(MINUTE_IN_MILLIS,
mQuotaController.getAllowedTimePerPeriodMs()[RESTRICTED_INDEX]);
assertEquals(0, mQuotaController.getInQuotaBufferMs());
- assertEquals(0f, mQuotaController.getAllowedTimeSurplusPriorityLow(), 1e-6);
- assertEquals(0f, mQuotaController.getAllowedTimeSurplusPriorityMin(), 1e-6);
assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[EXEMPTED_INDEX]);
assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[ACTIVE_INDEX]);
assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[WORKING_INDEX]);
@@ -3451,8 +3159,6 @@
setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
25 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, 25 * HOUR_IN_MILLIS);
- setDeviceConfigFloat(QcConstants.KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW, 1f);
- setDeviceConfigFloat(QcConstants.KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN, .95f);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, 25 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_ACTIVE_MS, 25 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_WORKING_MS, 25 * HOUR_IN_MILLIS);
@@ -3492,8 +3198,6 @@
assertEquals(24 * HOUR_IN_MILLIS,
mQuotaController.getAllowedTimePerPeriodMs()[RESTRICTED_INDEX]);
assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs());
- assertEquals(.9f, mQuotaController.getAllowedTimeSurplusPriorityLow(), 1e-6);
- assertEquals(.9f, mQuotaController.getAllowedTimeSurplusPriorityMin(), 1e-6);
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[EXEMPTED_INDEX]);
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[ACTIVE_INDEX]);
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[WORKING_INDEX]);
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 53cab9e..a6194df 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -98,6 +98,8 @@
<uses-permission android:name="android.permission.MAINLINE_NETWORK_STACK"/>
<uses-permission
android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD"/>
+ <uses-permission android:name="android.permission.ASSOCIATE_INPUT_DEVICE_TO_DISPLAY" />
+ <uses-permission android:name="android.permission.READ_NEARBY_STREAMING_POLICY" />
<uses-permission android:name="android.permission.PACKAGE_VERIFICATION_AGENT" />
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
index 64be569..eab96c0 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java
@@ -40,12 +40,14 @@
import com.android.server.biometrics.log.BiometricLogger;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
@Presubmit
@RunWith(AndroidTestingRunner.class)
@@ -62,6 +64,9 @@
}
}
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
@Mock
private InterruptableMonitor<FakeHal> mClientMonitor;
@Mock
@@ -76,7 +81,6 @@
@Before
public void setUp() {
- MockitoAnnotations.initMocks(this);
mHandler = new Handler(TestableLooper.get(this).getLooper());
mOperation = new BiometricSchedulerOperation(mClientMonitor, mClientCallback);
}
@@ -311,10 +315,12 @@
private void cancelWatchdog(boolean start) {
when(mClientMonitor.getFreshDaemon()).thenReturn(mHal);
- mOperation.start(mock(ClientMonitorCallback.class));
+ final ClientMonitorCallback opStartCallback = mock(ClientMonitorCallback.class);
+ mOperation.start(opStartCallback);
if (start) {
verify(mClientMonitor).start(mStartCallback.capture());
mStartCallback.getValue().onClientStarted(mClientMonitor);
+ verify(opStartCallback).onClientStarted(eq(mClientMonitor));
}
mOperation.cancel(mHandler, mock(ClientMonitorCallback.class));
@@ -325,6 +331,7 @@
assertThat(mOperation.isFinished()).isTrue();
assertThat(mOperation.isCanceling()).isFalse();
+ verify(opStartCallback).onClientFinished(eq(mClientMonitor), eq(false));
verify(mClientMonitor).destroy();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index 9aac81c..7b921ab 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -58,6 +58,7 @@
import android.os.IPowerManager;
import android.os.IThermalService;
import android.os.PowerManager;
+import android.os.Process;
import android.os.RemoteException;
import android.os.WorkSource;
import android.platform.test.annotations.Presubmit;
@@ -136,6 +137,7 @@
LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
mContext = Mockito.spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
+ doReturn(mContext).when(mContext).createContextAsUser(eq(Process.myUserHandle()), anyInt());
doNothing().when(mContext).enforceCallingOrSelfPermission(
eq(Manifest.permission.CREATE_VIRTUAL_DEVICE), anyString());
when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE)).thenReturn(
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
index 3160272..f0c907d 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
@@ -25,6 +25,7 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import android.companion.virtual.VirtualDeviceParams;
import android.companion.virtual.audio.IAudioSessionCallback;
import android.content.Context;
import android.content.ContextWrapper;
@@ -72,7 +73,8 @@
/* allowedUsers= */ new ArraySet<>(),
/* allowedActivities= */ new ArraySet<>(),
/* blockedActivities= */ new ArraySet<>(),
- /* activityListener= */null,
+ VirtualDeviceParams.ACTIVITY_POLICY_DEFAULT_ALLOWED,
+ /* activityListener= */ null,
/* activityBlockedCallback= */ null);
}
diff --git a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java
index 0287510..bd35be4 100644
--- a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java
@@ -107,6 +107,7 @@
private PackageManager mMockPackageManager;
@Mock
private LocaleManagerService mMockLocaleManagerService;
+
BroadcastReceiver mUserMonitor;
PackageMonitor mPackageMonitor;
@@ -131,6 +132,7 @@
mMockPackageManagerInternal = mock(PackageManagerInternal.class);
mMockPackageManager = mock(PackageManager.class);
mMockLocaleManagerService = mock(LocaleManagerService.class);
+ SystemAppUpdateTracker systemAppUpdateTracker = mock(SystemAppUpdateTracker.class);
doReturn(mMockPackageManager).when(mMockContext).getPackageManager();
@@ -144,7 +146,8 @@
doNothing().when(mBackupHelper).notifyBackupManager();
mUserMonitor = mBackupHelper.getUserMonitor();
- mPackageMonitor = new LocaleManagerServicePackageMonitor(mBackupHelper);
+ mPackageMonitor = new LocaleManagerServicePackageMonitor(mBackupHelper,
+ systemAppUpdateTracker);
setCurrentTimeMillis(DEFAULT_CREATION_TIME_MILLIS);
}
diff --git a/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java b/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java
new file mode 100644
index 0000000..5185e15
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locales/SystemAppUpdateTrackerTest.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locales;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.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 static org.mockito.Mockito.verifyZeroInteractions;
+
+import android.app.ActivityManagerInternal;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.InstallSourceInfo;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.os.Binder;
+import android.os.Environment;
+import android.os.LocaleList;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.AtomicFile;
+import android.util.TypedXmlPullParser;
+import android.util.Xml;
+
+import com.android.internal.content.PackageMonitor;
+import com.android.internal.util.XmlUtils;
+import com.android.server.wm.ActivityTaskManagerInternal;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Unit tests for {@link SystemAppUpdateTracker}.
+ */
+public class SystemAppUpdateTrackerTest {
+ private static final String DEFAULT_PACKAGE_NAME_1 = "com.android.myapp1";
+ private static final String DEFAULT_PACKAGE_NAME_2 = "com.android.myapp2";
+ private static final String DEFAULT_LOCALE_TAGS = "en-XC,ar-XB";
+ private static final LocaleList DEFAULT_LOCALES =
+ LocaleList.forLanguageTags(DEFAULT_LOCALE_TAGS);
+ private static final String PACKAGE_XML_TAG = "package";
+ private static final String ATTR_NAME = "name";
+ private static final String SYSTEM_APPS_XML_TAG = "system_apps";
+ private static final int DEFAULT_USER_ID = 0;
+
+ private AtomicFile mStoragefile;
+ private static final String DEFAULT_INSTALLER_PACKAGE_NAME = "com.android.myapp.installer";
+ private static final InstallSourceInfo DEFAULT_INSTALL_SOURCE_INFO = new InstallSourceInfo(
+ /* initiatingPackageName = */ null, /* initiatingPackageSigningInfo = */ null,
+ /* originatingPackageName = */ null,
+ /* installingPackageName = */ DEFAULT_INSTALLER_PACKAGE_NAME,
+ /* packageSource = */ PackageInstaller.PACKAGE_SOURCE_UNSPECIFIED);
+
+ @Mock
+ private Context mMockContext;
+ @Mock
+ PackageManager mMockPackageManager;
+ @Mock
+ private PackageManagerInternal mMockPackageManagerInternal;
+ @Mock
+ private ActivityTaskManagerInternal mMockActivityTaskManager;
+ @Mock
+ private ActivityManagerInternal mMockActivityManager;
+ @Mock
+ private LocaleManagerBackupHelper mMockLocaleManagerBackupHelper;
+ @Mock
+ PackageMonitor mMockPackageMonitor;
+
+ private LocaleManagerService mLocaleManagerService;
+
+ // Object under test.
+ private SystemAppUpdateTracker mSystemAppUpdateTracker;
+
+ @Before
+ public void setUp() throws Exception {
+ mMockContext = mock(Context.class);
+ mMockActivityTaskManager = mock(ActivityTaskManagerInternal.class);
+ mMockActivityManager = mock(ActivityManagerInternal.class);
+ mMockPackageManagerInternal = mock(PackageManagerInternal.class);
+ mMockPackageMonitor = mock(PackageMonitor.class);
+ mMockLocaleManagerBackupHelper = mock(ShadowLocaleManagerBackupHelper.class);
+ mLocaleManagerService = new LocaleManagerService(mMockContext,
+ mMockActivityTaskManager, mMockActivityManager,
+ mMockPackageManagerInternal, mMockLocaleManagerBackupHelper, mMockPackageMonitor);
+
+ doReturn(DEFAULT_USER_ID).when(mMockActivityManager)
+ .handleIncomingUser(anyInt(), anyInt(), eq(DEFAULT_USER_ID), anyBoolean(), anyInt(),
+ anyString(), anyString());
+
+ mMockPackageManager = mock(PackageManager.class);
+ doReturn(DEFAULT_INSTALL_SOURCE_INFO).when(mMockPackageManager)
+ .getInstallSourceInfo(anyString());
+ doReturn(mMockPackageManager).when(mMockContext).getPackageManager();
+
+ mStoragefile = new AtomicFile(new File(
+ Environment.getExternalStorageDirectory(), "systemUpdateUnitTests.xml"));
+
+ mSystemAppUpdateTracker = new SystemAppUpdateTracker(mMockContext,
+ mLocaleManagerService, mStoragefile);
+ }
+
+ @After
+ public void tearDown() {
+ mStoragefile.delete();
+ }
+
+ @Test
+ public void testInit_loadsCorrectly() throws Exception {
+ doReturn(createApplicationInfoForApp(DEFAULT_PACKAGE_NAME_1,
+ /* isUpdatedSystemApp = */ true))
+ .when(mMockPackageManager).getApplicationInfo(eq(DEFAULT_PACKAGE_NAME_1), any());
+
+ // Updates the app once so that it writes to the file.
+ mSystemAppUpdateTracker.onPackageUpdateFinished(DEFAULT_PACKAGE_NAME_1,
+ Binder.getCallingUid());
+ // Clear the in-memory data of updated apps
+ mSystemAppUpdateTracker.getUpdatedApps().clear();
+ // Invoke init to verify if it correctly populates in-memory set.
+ mSystemAppUpdateTracker.init();
+
+ assertEquals(Set.of(DEFAULT_PACKAGE_NAME_1), mSystemAppUpdateTracker.getUpdatedApps());
+ }
+
+ @Test
+ public void testOnPackageUpdatedFinished_systemAppFirstUpdate_writesToFile() throws Exception {
+ doReturn(createApplicationInfoForApp(DEFAULT_PACKAGE_NAME_1,
+ /* isUpdatedSystemApp = */ true))
+ .when(mMockPackageManager).getApplicationInfo(eq(DEFAULT_PACKAGE_NAME_1), any());
+ doReturn(new ActivityTaskManagerInternal.PackageConfig(/* nightMode = */ 0,
+ DEFAULT_LOCALES)).when(mMockActivityTaskManager)
+ .getApplicationConfig(anyString(), anyInt());
+
+ mSystemAppUpdateTracker.onPackageUpdateFinished(DEFAULT_PACKAGE_NAME_1,
+ Binder.getCallingUid());
+
+ assertBroadcastSentToInstaller(DEFAULT_PACKAGE_NAME_1, DEFAULT_LOCALES);
+ Set<String> expectedAppList = Set.of(DEFAULT_PACKAGE_NAME_1);
+ assertEquals(expectedAppList, mSystemAppUpdateTracker.getUpdatedApps());
+ verifyStorageFileContents(expectedAppList);
+ }
+
+ @Test
+ public void testOnPackageUpdatedFinished_systemAppSecondUpdate_doesNothing() throws Exception {
+ doReturn(createApplicationInfoForApp(DEFAULT_PACKAGE_NAME_1,
+ /* isUpdatedSystemApp = */ true))
+ .when(mMockPackageManager).getApplicationInfo(eq(DEFAULT_PACKAGE_NAME_1), any());
+ doReturn(new ActivityTaskManagerInternal.PackageConfig(/* nightMode = */ 0,
+ DEFAULT_LOCALES)).when(mMockActivityTaskManager)
+ .getApplicationConfig(anyString(), anyInt());
+
+ // first update
+ mSystemAppUpdateTracker.onPackageUpdateFinished(DEFAULT_PACKAGE_NAME_1,
+ Binder.getCallingUid());
+
+ assertBroadcastSentToInstaller(DEFAULT_PACKAGE_NAME_1, DEFAULT_LOCALES);
+ Set<String> expectedAppList = Set.of(DEFAULT_PACKAGE_NAME_1);
+ assertEquals(expectedAppList, mSystemAppUpdateTracker.getUpdatedApps());
+ verifyStorageFileContents(expectedAppList);
+
+ // second update
+ mSystemAppUpdateTracker.onPackageUpdateFinished(DEFAULT_PACKAGE_NAME_1,
+ Binder.getCallingUid());
+ // getApplicationLocales should be invoked only once on the first update.
+ verify(mMockActivityTaskManager, times(1))
+ .getApplicationConfig(anyString(), anyInt());
+ // Broadcast should be sent only once on first update.
+ verify(mMockContext, times(1)).sendBroadcastAsUser(any(), any());
+ // Verify that the content remains the same.
+ verifyStorageFileContents(expectedAppList);
+ }
+
+ @Test
+ public void testOnPackageUpdatedFinished_notSystemApp_doesNothing() throws Exception {
+ doReturn(createApplicationInfoForApp(DEFAULT_PACKAGE_NAME_2,
+ /* isUpdatedSystemApp = */false))
+ .when(mMockPackageManager).getApplicationInfo(eq(DEFAULT_PACKAGE_NAME_2), any());
+
+ mSystemAppUpdateTracker.onPackageUpdateFinished(DEFAULT_PACKAGE_NAME_2,
+ Binder.getCallingUid());
+
+ assertTrue(!mSystemAppUpdateTracker.getUpdatedApps().contains(DEFAULT_PACKAGE_NAME_2));
+ // getApplicationLocales should be never be invoked if not a system app.
+ verifyZeroInteractions(mMockActivityTaskManager);
+ // Broadcast should be never sent if not a system app.
+ verify(mMockContext, never()).sendBroadcastAsUser(any(), any());
+ // It shouldn't write to the file if not a system app.
+ assertTrue(!mStoragefile.getBaseFile().isFile());
+ }
+
+ @Test
+ public void testOnPackageUpdatedFinished_noInstaller_doesNothing() throws Exception {
+ doReturn(createApplicationInfoForApp(DEFAULT_PACKAGE_NAME_1,
+ /* isUpdatedSystemApp = */ true))
+ .when(mMockPackageManager).getApplicationInfo(eq(DEFAULT_PACKAGE_NAME_1), any());
+ doReturn(null).when(mMockPackageManager).getInstallSourceInfo(anyString());
+
+ mSystemAppUpdateTracker.onPackageUpdateFinished(DEFAULT_PACKAGE_NAME_1,
+ Binder.getCallingUid());
+
+ // getApplicationLocales should be never be invoked if not installer is not present.
+ verifyZeroInteractions(mMockActivityTaskManager);
+ // Broadcast should be never sent if installer is not present.
+ verify(mMockContext, never()).sendBroadcastAsUser(any(), any());
+ // It shouldn't write to file if no installer present.
+ assertTrue(!mStoragefile.getBaseFile().isFile());
+ }
+
+ private void verifyStorageFileContents(Set<String> expectedAppList)
+ throws IOException, XmlPullParserException {
+ assertTrue(mStoragefile.getBaseFile().isFile());
+ try (InputStream storageInputStream = mStoragefile.openRead()) {
+ assertEquals(expectedAppList, readFromXml(storageInputStream));
+ } catch (IOException | XmlPullParserException e) {
+ throw e;
+ }
+ }
+
+ private Set<String> readFromXml(InputStream storageInputStream)
+ throws XmlPullParserException, IOException {
+ Set<String> outputList = new HashSet<>();
+ final TypedXmlPullParser parser = Xml.newFastPullParser();
+ parser.setInput(storageInputStream, StandardCharsets.UTF_8.name());
+ XmlUtils.beginDocument(parser, SYSTEM_APPS_XML_TAG);
+ int depth = parser.getDepth();
+ while (XmlUtils.nextElementWithin(parser, depth)) {
+ if (parser.getName().equals(PACKAGE_XML_TAG)) {
+ String packageName = parser.getAttributeValue(/* namespace= */ null,
+ ATTR_NAME);
+ if (!TextUtils.isEmpty(packageName)) {
+ outputList.add(packageName);
+ }
+ }
+ }
+ return outputList;
+ }
+
+ /**
+ * Verifies the broadcast sent to the installer of the updated app.
+ */
+ private void assertBroadcastSentToInstaller(String packageName, LocaleList locales) {
+ ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
+ verify(mMockContext).sendBroadcastAsUser(captor.capture(), any(UserHandle.class));
+ for (Intent intent : captor.getAllValues()) {
+ assertTrue(Intent.ACTION_APPLICATION_LOCALE_CHANGED.equals(intent.getAction()));
+ assertTrue(DEFAULT_INSTALLER_PACKAGE_NAME.equals(intent.getPackage()));
+ assertTrue(packageName.equals(intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME)));
+ assertTrue(locales.equals(intent.getParcelableExtra(Intent.EXTRA_LOCALE_LIST)));
+ }
+ }
+
+ private ApplicationInfo createApplicationInfoForApp(String packageName,
+ boolean isUpdatedSystemApp) {
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.packageName = packageName;
+ if (isUpdatedSystemApp) {
+ applicationInfo.flags = ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+ }
+ return applicationInfo;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
index 5d4ffbb..b7cfbaf 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -285,7 +285,7 @@
}
@Test
- public void shouldIgnoreVibration_withRingerModeSilent_ignoresRingtoneAndTouch() {
+ public void shouldIgnoreVibration_withRingerModeSilent_ignoresRingtoneOnly() {
// Vibrating settings on are overruled by ringer mode.
setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 1);
setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1);
@@ -293,7 +293,7 @@
setRingerMode(AudioManager.RINGER_MODE_SILENT);
for (int usage : ALL_USAGES) {
- if (usage == USAGE_RINGTONE || usage == USAGE_TOUCH) {
+ if (usage == USAGE_RINGTONE) {
assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_RINGER_MODE);
} else {
assertVibrationNotIgnoredForUsage(usage);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
index 721641a..5458a5b 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
@@ -174,7 +174,7 @@
}
verify(mCallback, times(AUTOGROUP_AT_COUNT + 1))
- .updateAutogroupSummary(anyString(), eq(true));
+ .updateAutogroupSummary(anyInt(), anyString(), eq(true));
int userId = UserHandle.SYSTEM.getIdentifier();
assertEquals(mGroupHelper.getOngoingGroupCount(
@@ -203,7 +203,7 @@
mGroupHelper.onNotificationUpdated(notifications.get(0), true);
verify(mCallback, times(AUTOGROUP_AT_COUNT + 2))
- .updateAutogroupSummary(anyString(), eq(true));
+ .updateAutogroupSummary(anyInt(), anyString(), eq(true));
int userId = UserHandle.SYSTEM.getIdentifier();
assertEquals(mGroupHelper.getOngoingGroupCount(
@@ -236,7 +236,7 @@
mGroupHelper.onNotificationUpdated(notifications.get(0), true);
verify(mCallback, times(AUTOGROUP_AT_COUNT + 3))
- .updateAutogroupSummary(anyString(), eq(true));
+ .updateAutogroupSummary(anyInt(), anyString(), eq(true));
int userId = UserHandle.SYSTEM.getIdentifier();
assertEquals(mGroupHelper.getOngoingGroupCount(
@@ -263,7 +263,7 @@
mGroupHelper.onNotificationRemoved(notifications.get(0));
verify(mCallback, times(AUTOGROUP_AT_COUNT + 2))
- .updateAutogroupSummary(anyString(), eq(true));
+ .updateAutogroupSummary(anyInt(), anyString(), eq(true));
int userId = UserHandle.SYSTEM.getIdentifier();
assertEquals(mGroupHelper.getOngoingGroupCount(
@@ -291,7 +291,7 @@
mGroupHelper.onNotificationUpdated(notifications.get(0), true);
verify(mCallback, times(1))
- .updateAutogroupSummary(anyString(), eq(true));
+ .updateAutogroupSummary(anyInt(), anyString(), eq(true));
int userId = UserHandle.SYSTEM.getIdentifier();
assertEquals(mGroupHelper.getOngoingGroupCount(
@@ -315,7 +315,7 @@
}
verify(mCallback, times(1))
- .updateAutogroupSummary(anyString(), eq(true));
+ .updateAutogroupSummary(anyInt(), anyString(), eq(true));
int userId = UserHandle.SYSTEM.getIdentifier();
assertEquals(mGroupHelper.getOngoingGroupCount(
@@ -339,7 +339,7 @@
}
verify(mCallback, times(0))
- .updateAutogroupSummary(anyString(), eq(true));
+ .updateAutogroupSummary(anyInt(), anyString(), eq(true));
int userId = UserHandle.SYSTEM.getIdentifier();
assertEquals(mGroupHelper.getOngoingGroupCount(userId, pkg, AUTOGROUP_KEY), 0);
diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
index 90dac47..57bbe40 100644
--- a/services/tests/wmtests/Android.bp
+++ b/services/tests/wmtests/Android.bp
@@ -57,6 +57,7 @@
"ub-uiautomator",
"hamcrest-library",
"platform-compat-test-rules",
+ "CtsSurfaceValidatorLib",
],
libs: [
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index c2298d0..2918365 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -43,6 +43,8 @@
<uses-permission android:name="android.permission.LOG_COMPAT_CHANGE" />
<uses-permission android:name="android.permission.CAPTURE_BLACKOUT_CONTENT"/>
<uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG" />
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!-- TODO: Remove largeHeap hack when memory leak is fixed (b/123984854) -->
<application android:debuggable="true"
@@ -78,6 +80,12 @@
android:turnScreenOn="true"
android:showWhenLocked="true" />
<activity android:name="com.android.server.wm.ScreenshotTests$ScreenshotActivity" />
+ <activity android:name="android.view.cts.surfacevalidator.CapturedActivity"/>
+
+ <service android:name="android.view.cts.surfacevalidator.LocalMediaProjectionService"
+ android:foregroundServiceType="mediaProjection"
+ android:enabled="true">
+ </service>
</application>
<instrumentation
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index b3d6b3e..2ea0bdc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -24,6 +24,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
import static android.content.pm.ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE;
@@ -54,6 +55,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
+import static android.view.WindowManager.TRANSIT_PIP;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_LEGACY_SPLASH_SCREEN;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
@@ -3397,6 +3399,24 @@
assertFalse(activity.mVisibleRequested);
}
+ @Test
+ public void testShellTransitionTaskWindowingModeChange() {
+ final ActivityRecord activity = createActivityWithTask();
+ final Task task = activity.getTask();
+ task.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+
+ assertTrue(activity.isVisible());
+ assertTrue(activity.isVisibleRequested());
+ assertEquals(WINDOWING_MODE_FULLSCREEN, activity.getWindowingMode());
+
+ registerTestTransitionPlayer();
+ task.mTransitionController.requestTransitionIfNeeded(TRANSIT_PIP, task);
+ task.setWindowingMode(WINDOWING_MODE_PINNED);
+
+ // Collect activity in the transition if the Task windowing mode is going to change.
+ assertTrue(activity.inTransition());
+ }
+
private ICompatCameraControlCallback getCompatCameraControlCallback() {
return new ICompatCameraControlCallback.Stub() {
@Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 9f7130e..c083870 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -75,7 +75,6 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
-import static com.android.server.wm.DisplayContent.IME_TARGET_INPUT;
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_FIXED_TRANSFORM;
@@ -1117,7 +1116,7 @@
app.removeImmediately();
- assertNull(dc.getImeTarget(IME_TARGET_INPUT));
+ assertNull(dc.getImeInputTarget());
assertNull(dc.computeImeControlTarget());
}
@@ -1126,19 +1125,19 @@
final DisplayContent dc = createNewDisplay();
dc.setRemoteInsetsController(createDisplayWindowInsetsController());
dc.setImeInputTarget(createWindow(null, TYPE_BASE_APPLICATION, "app"));
- dc.setImeLayeringTarget(dc.getImeTarget(IME_TARGET_INPUT).getWindow());
- assertEquals(dc.getImeTarget(IME_TARGET_INPUT).getWindow(), dc.computeImeControlTarget());
+ dc.setImeLayeringTarget(dc.getImeInputTarget().getWindowState());
+ assertEquals(dc.getImeInputTarget().getWindowState(), dc.computeImeControlTarget());
}
@Test
public void testComputeImeControlTarget_splitscreen() throws Exception {
final DisplayContent dc = createNewDisplay();
dc.setImeInputTarget(createWindow(null, TYPE_BASE_APPLICATION, "app"));
- dc.getImeTarget(IME_TARGET_INPUT).getWindow().setWindowingMode(
+ dc.getImeInputTarget().getWindowState().setWindowingMode(
WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW);
- dc.setImeLayeringTarget(dc.getImeTarget(IME_TARGET_INPUT).getWindow());
+ dc.setImeLayeringTarget(dc.getImeInputTarget().getWindowState());
dc.setRemoteInsetsController(createDisplayWindowInsetsController());
- assertNotEquals(dc.getImeTarget(IME_TARGET_INPUT).getWindow(),
+ assertNotEquals(dc.getImeInputTarget().getWindowState(),
dc.computeImeControlTarget());
}
@@ -1149,7 +1148,7 @@
doReturn(false).when(mAppWindow.mActivityRecord).matchParentBounds();
mDisplayContent.setImeInputTarget(mAppWindow);
mDisplayContent.setImeLayeringTarget(
- mDisplayContent.getImeTarget(IME_TARGET_INPUT).getWindow());
+ mDisplayContent.getImeInputTarget().getWindowState());
mDisplayContent.setRemoteInsetsController(createDisplayWindowInsetsController());
assertEquals(mAppWindow, mDisplayContent.computeImeControlTarget());
}
@@ -1927,7 +1926,7 @@
child1.removeImmediately();
verify(mDisplayContent).computeImeTarget(true);
- assertNull(mDisplayContent.getImeTarget(IME_TARGET_INPUT));
+ assertNull(mDisplayContent.getImeInputTarget());
verify(child1, never()).needsRelativeLayeringToIme();
}
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 e387615..90a6918 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -30,7 +30,6 @@
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;
@@ -182,12 +181,13 @@
// Make IME and stay visible during the test.
mImeWindow.setHasSurface(true);
getController().getSourceProvider(ITYPE_IME).setWindowContainer(mImeWindow, null, null);
- getController().onImeControlTargetChanged(mDisplayContent.getImeTarget(IME_TARGET_INPUT));
+ getController().onImeControlTargetChanged(
+ mDisplayContent.getImeInputTarget().getWindowState());
final InsetsVisibilities requestedVisibilities = new InsetsVisibilities();
requestedVisibilities.setVisibility(ITYPE_IME, true);
- mDisplayContent.getImeTarget(IME_TARGET_INPUT).getWindow()
+ mDisplayContent.getImeInputTarget().getWindowState()
.setRequestedVisibilities(requestedVisibilities);
- getController().onInsetsModified(mDisplayContent.getImeTarget(IME_TARGET_INPUT));
+ getController().onInsetsModified(mDisplayContent.getImeInputTarget().getWindowState());
// Send our spy window (app) into the system so that we can detect the invocation.
final WindowState win = createWindow(null, TYPE_APPLICATION, "app");
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index e091190..3c3351c0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
@@ -440,16 +441,26 @@
public void testCheckRotationAfterCleanup() {
mWm.setRecentsAnimationController(mController);
spyOn(mDisplayContent.mFixedRotationTransitionListener);
- doReturn(true).when(mDisplayContent.mFixedRotationTransitionListener)
- .isTopFixedOrientationRecentsAnimating();
+ final ActivityRecord recents = mock(ActivityRecord.class);
+ recents.mOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
+ doReturn(ORIENTATION_PORTRAIT).when(recents)
+ .getRequestedConfigurationOrientation(anyBoolean());
+ mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(recents);
+
// Rotation update is skipped while the recents animation is running.
- assertFalse(mDisplayContent.getDisplayRotation().updateOrientation(DisplayContentTests
- .getRotatedOrientation(mDefaultDisplay), false /* forceUpdate */));
+ final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation();
+ final int topOrientation = DisplayContentTests.getRotatedOrientation(mDefaultDisplay);
+ assertFalse(displayRotation.updateOrientation(topOrientation, false /* forceUpdate */));
+ assertEquals(recents.mOrientation, displayRotation.getLastOrientation());
final int prevRotation = mDisplayContent.getRotation();
mWm.cleanupRecentsAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION);
- waitHandlerIdle(mWm.mH);
+
+ // In real case, it is called from RecentsAnimation#finishAnimation -> continueWindowLayout
+ // -> handleAppTransitionReady -> add FINISH_LAYOUT_REDO_CONFIG, and DisplayContent#
+ // applySurfaceChangesTransaction will call updateOrientation for FINISH_LAYOUT_REDO_CONFIG.
+ assertTrue(displayRotation.updateOrientation(topOrientation, false /* forceUpdate */));
// The display should be updated to the changed orientation after the animation is finished.
- assertNotEquals(mDisplayContent.getRotation(), prevRotation);
+ assertNotEquals(displayRotation.getRotation(), prevRotation);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncerContinuousTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncerContinuousTest.java
new file mode 100644
index 0000000..1e32500
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncerContinuousTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.server.wm.UiDeviceUtils.pressUnlockButton;
+import static android.server.wm.UiDeviceUtils.pressWakeupButton;
+import static android.server.wm.WindowManagerState.getLogicalDisplaySize;
+
+import android.app.KeyguardManager;
+import android.os.PowerManager;
+import android.view.SurfaceControl;
+import android.view.cts.surfacevalidator.CapturedActivity;
+import android.window.SurfaceSyncer;
+
+import androidx.test.rule.ActivityTestRule;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestName;
+
+import java.util.Objects;
+
+public class SurfaceSyncerContinuousTest {
+ @Rule
+ public TestName mName = new TestName();
+
+ @Rule
+ public ActivityTestRule<CapturedActivity> mActivityRule =
+ new ActivityTestRule<>(CapturedActivity.class);
+
+ public CapturedActivity mCapturedActivity;
+
+ @Before
+ public void setup() {
+ mCapturedActivity = mActivityRule.getActivity();
+ mCapturedActivity.setLogicalDisplaySize(getLogicalDisplaySize());
+
+ final KeyguardManager km = mCapturedActivity.getSystemService(KeyguardManager.class);
+ if (km != null && km.isKeyguardLocked() || !Objects.requireNonNull(
+ mCapturedActivity.getSystemService(PowerManager.class)).isInteractive()) {
+ pressWakeupButton();
+ pressUnlockButton();
+ }
+ }
+
+ @Test
+ public void testSurfaceViewSyncDuringResize() throws Throwable {
+ SurfaceSyncer.setTransactionFactory(SurfaceControl.Transaction::new);
+ mCapturedActivity.verifyTest(new SurfaceSyncerValidatorTestCase(), mName);
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncerTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncerTest.java
new file mode 100644
index 0000000..cc28ea6
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncerTest.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.Presubmit;
+import android.view.SurfaceControl;
+import android.window.SurfaceSyncer;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@SmallTest
+@Presubmit
+public class SurfaceSyncerTest {
+ private SurfaceSyncer mSurfaceSyncer;
+
+ @Before
+ public void setup() {
+ mSurfaceSyncer = new SurfaceSyncer();
+ SurfaceSyncer.setTransactionFactory(StubTransaction::new);
+ }
+
+ @Test
+ public void testSyncOne() throws InterruptedException {
+ final CountDownLatch finishedLatch = new CountDownLatch(1);
+ int startSyncId = mSurfaceSyncer.setupSync(transaction -> finishedLatch.countDown());
+ Syncable syncable = new Syncable();
+ mSurfaceSyncer.addToSync(startSyncId, syncable);
+ mSurfaceSyncer.markSyncReady(startSyncId);
+
+ syncable.onBufferReady();
+
+ finishedLatch.await(5, TimeUnit.SECONDS);
+ assertEquals(0, finishedLatch.getCount());
+ }
+
+ @Test
+ public void testSyncMultiple() throws InterruptedException {
+ final CountDownLatch finishedLatch = new CountDownLatch(1);
+ int startSyncId = mSurfaceSyncer.setupSync(transaction -> finishedLatch.countDown());
+ Syncable syncable1 = new Syncable();
+ Syncable syncable2 = new Syncable();
+ Syncable syncable3 = new Syncable();
+
+ mSurfaceSyncer.addToSync(startSyncId, syncable1);
+ mSurfaceSyncer.addToSync(startSyncId, syncable2);
+ mSurfaceSyncer.addToSync(startSyncId, syncable3);
+ mSurfaceSyncer.markSyncReady(startSyncId);
+
+ syncable1.onBufferReady();
+ assertNotEquals(0, finishedLatch.getCount());
+
+ syncable3.onBufferReady();
+ assertNotEquals(0, finishedLatch.getCount());
+
+ syncable2.onBufferReady();
+
+ finishedLatch.await(5, TimeUnit.SECONDS);
+ assertEquals(0, finishedLatch.getCount());
+ }
+
+ @Test
+ public void testInvalidSyncId() {
+ assertFalse(mSurfaceSyncer.addToSync(0, new Syncable()));
+ }
+
+ @Test
+ public void testAddSyncWhenSyncComplete() throws InterruptedException {
+ final CountDownLatch finishedLatch = new CountDownLatch(1);
+ int startSyncId = mSurfaceSyncer.setupSync(transaction -> finishedLatch.countDown());
+
+ Syncable syncable1 = new Syncable();
+ Syncable syncable2 = new Syncable();
+
+ assertTrue(mSurfaceSyncer.addToSync(startSyncId, syncable1));
+ mSurfaceSyncer.markSyncReady(startSyncId);
+ // Adding to a sync that has been completed is also invalid since the sync id has been
+ // cleared.
+ assertFalse(mSurfaceSyncer.addToSync(startSyncId, syncable2));
+ }
+
+ @Test
+ public void testMultipleSyncSets() throws InterruptedException {
+ final CountDownLatch finishedLatch1 = new CountDownLatch(1);
+ final CountDownLatch finishedLatch2 = new CountDownLatch(1);
+ int startSyncId1 = mSurfaceSyncer.setupSync(transaction -> finishedLatch1.countDown());
+ int startSyncId2 = mSurfaceSyncer.setupSync(transaction -> finishedLatch2.countDown());
+
+ Syncable syncable1 = new Syncable();
+ Syncable syncable2 = new Syncable();
+
+ assertTrue(mSurfaceSyncer.addToSync(startSyncId1, syncable1));
+ assertTrue(mSurfaceSyncer.addToSync(startSyncId2, syncable2));
+ mSurfaceSyncer.markSyncReady(startSyncId1);
+ mSurfaceSyncer.markSyncReady(startSyncId2);
+
+ syncable1.onBufferReady();
+
+ finishedLatch1.await(5, TimeUnit.SECONDS);
+ assertEquals(0, finishedLatch1.getCount());
+ assertNotEquals(0, finishedLatch2.getCount());
+
+ syncable2.onBufferReady();
+
+ finishedLatch2.await(5, TimeUnit.SECONDS);
+ assertEquals(0, finishedLatch2.getCount());
+ }
+
+ private static class Syncable implements SurfaceSyncer.SyncTarget {
+ private SurfaceSyncer.SyncBufferCallback mSyncBufferCallback;
+
+ @Override
+ public void onReadyToSync(SurfaceSyncer.SyncBufferCallback syncBufferCallback) {
+ mSyncBufferCallback = syncBufferCallback;
+ }
+
+ void onBufferReady() {
+ SurfaceControl.Transaction t = new StubTransaction();
+ mSyncBufferCallback.onBufferReady(t);
+ }
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncerValidatorTestCase.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncerValidatorTestCase.java
new file mode 100644
index 0000000..77a8615
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncerValidatorTestCase.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.util.Log;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.ViewGroup;
+import android.view.cts.surfacevalidator.ISurfaceValidatorTestCase;
+import android.view.cts.surfacevalidator.PixelChecker;
+import android.widget.FrameLayout;
+import android.window.SurfaceSyncer;
+
+import androidx.annotation.NonNull;
+
+/**
+ * A validator class that will create a SurfaceView and then update its size over and over. The code
+ * will request to sync the SurfaceView content with the main window and validate that there was
+ * never an empty area (black color). The test uses {@link SurfaceSyncer} class to gather the
+ * content it wants to synchronize.
+ */
+public class SurfaceSyncerValidatorTestCase implements ISurfaceValidatorTestCase {
+ private static final String TAG = "SurfaceSyncerValidatorTestCase";
+
+ private final Runnable mRunnable = new Runnable() {
+ @Override
+ public void run() {
+ updateSurfaceViewSize();
+ mHandler.postDelayed(this, 100);
+ }
+ };
+
+ private Handler mHandler;
+ private SurfaceView mSurfaceView;
+ private boolean mLastExpanded = true;
+ private final SurfaceSyncer mSurfaceSyncer = new SurfaceSyncer();
+
+ private RenderingThread mRenderingThread;
+ private FrameLayout mParent;
+
+ private int mLastSyncId = -1;
+
+ final SurfaceHolder.Callback mCallback = new SurfaceHolder.Callback() {
+ @Override
+ public void surfaceCreated(@NonNull SurfaceHolder holder) {
+ final Canvas canvas = holder.lockCanvas();
+ canvas.drawARGB(255, 100, 100, 100);
+ holder.unlockCanvasAndPost(canvas);
+ Log.d(TAG, "surfaceCreated");
+ mRenderingThread = new RenderingThread(holder);
+ mRenderingThread.start();
+ }
+
+ @Override
+ public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width,
+ int height) {
+ if (mLastSyncId >= 0) {
+ mSurfaceSyncer.addToSync(mLastSyncId, mSurfaceView, frameCallback ->
+ mRenderingThread.setFrameCallback(frameCallback));
+ mSurfaceSyncer.markSyncReady(mLastSyncId);
+ mLastSyncId = -1;
+ }
+ }
+
+ @Override
+ public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
+ mRenderingThread.stopRendering();
+ }
+ };
+
+ @Override
+ public PixelChecker getChecker() {
+ return new PixelChecker(Color.BLACK) {
+ @Override
+ public boolean checkPixels(int matchingPixelCount, int width, int height) {
+ return matchingPixelCount == 0;
+ }
+ };
+ }
+
+ @Override
+ public void start(Context context, FrameLayout parent) {
+ mSurfaceView = new SurfaceView(context);
+ mSurfaceView.getHolder().addCallback(mCallback);
+ mParent = parent;
+
+ FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(MATCH_PARENT, 600);
+ parent.addView(mSurfaceView, layoutParams);
+ mHandler = new Handler(Looper.getMainLooper());
+ mHandler.post(mRunnable);
+ }
+
+ @Override
+ public void end() {
+ mHandler.removeCallbacks(mRunnable);
+ }
+
+ public void updateSurfaceViewSize() {
+ if (mRenderingThread == null || mLastSyncId >= 0 || !mRenderingThread.isReadyToSync()) {
+ return;
+ }
+
+ Log.d(TAG, "updateSurfaceViewSize");
+
+ final int height;
+ if (mLastExpanded) {
+ height = 300;
+ } else {
+ height = 600;
+ }
+ mLastExpanded = !mLastExpanded;
+
+ mRenderingThread.pauseRendering();
+ mLastSyncId = mSurfaceSyncer.setupSync(() -> { });
+ mSurfaceSyncer.addToSync(mLastSyncId, mParent);
+
+ ViewGroup.LayoutParams svParams = mSurfaceView.getLayoutParams();
+ svParams.height = height;
+ mSurfaceView.setLayoutParams(svParams);
+ }
+
+ private static class RenderingThread extends HandlerThread {
+ private final SurfaceHolder mSurfaceHolder;
+ private SurfaceSyncer.SurfaceViewFrameCallback mFrameCallback;
+ private boolean mPauseRendering;
+ private boolean mComplete;
+
+ int mColorValue = 0;
+ int mColorDelta = 10;
+
+ @Override
+ public void run() {
+ try {
+ while (true) {
+ sleep(10);
+ synchronized (this) {
+ if (mComplete) {
+ break;
+ }
+ if (mPauseRendering) {
+ continue;
+ }
+
+ if (mFrameCallback != null) {
+ Log.d(TAG, "onFrameStarted");
+ mFrameCallback.onFrameStarted();
+ }
+
+ mColorValue += mColorDelta;
+ if (mColorValue > 245 || mColorValue < 10) {
+ mColorDelta *= -1;
+ }
+
+ Canvas c = mSurfaceHolder.lockCanvas();
+ if (c != null) {
+ c.drawRGB(255, mColorValue, 255 - mColorValue);
+ mSurfaceHolder.unlockCanvasAndPost(c);
+ }
+
+ if (mFrameCallback != null) {
+ Log.d(TAG, "onFrameComplete");
+ mFrameCallback.onFrameComplete();
+ }
+
+ mFrameCallback = null;
+ }
+ }
+ } catch (InterruptedException e) {
+ }
+ }
+
+ RenderingThread(SurfaceHolder holder) {
+ super("RenderingThread");
+ mSurfaceHolder = holder;
+ }
+
+ public void pauseRendering() {
+ synchronized (this) {
+ mPauseRendering = true;
+ }
+ }
+
+ private boolean isReadyToSync() {
+ synchronized (this) {
+ return mFrameCallback == null;
+ }
+ }
+ public void setFrameCallback(SurfaceSyncer.SurfaceViewFrameCallback frameCallback) {
+ synchronized (this) {
+ mFrameCallback = frameCallback;
+ mPauseRendering = false;
+ }
+ }
+
+ public void stopRendering() {
+ synchronized (this) {
+ mComplete = true;
+ }
+ }
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 29289a4..501f0c4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -19,6 +19,8 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
+import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
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_NAVIGATION_BAR_PANEL;
@@ -32,13 +34,17 @@
import static android.window.TransitionInfo.isIndependent;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeFalse;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
@@ -557,11 +563,20 @@
@Test
public void testAppTransitionWithRotationChange() {
+ final TestTransitionPlayer player = registerTestTransitionPlayer();
+ final boolean useFixedRotation = !player.mController.useShellTransitionsRotation();
+ if (useFixedRotation) {
+ testFixedRotationOpen(player);
+ } else {
+ testShellRotationOpen(player);
+ }
+ }
+
+ private void testShellRotationOpen(TestTransitionPlayer player) {
final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, "statusBar");
makeWindowVisible(statusBar);
mDisplayContent.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs);
final ActivityRecord app = createActivityRecord(mDisplayContent);
- final TestTransitionPlayer player = registerTestTransitionPlayer();
final Transition transition = app.mTransitionController.createTransition(TRANSIT_OPEN);
app.mTransitionController.requestStartTransition(transition, app.getTask(),
null /* remoteTransition */, null /* displayChange */);
@@ -605,6 +620,85 @@
assertNull(mDisplayContent.getAsyncRotationController());
}
+ private void testFixedRotationOpen(TestTransitionPlayer player) {
+ final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, "statusBar");
+ makeWindowVisible(statusBar);
+ mDisplayContent.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs);
+ final ActivityRecord app = createActivityRecord(mDisplayContent);
+ final Transition transition = app.mTransitionController.createTransition(TRANSIT_OPEN);
+ app.mTransitionController.requestStartTransition(transition, app.getTask(),
+ null /* remoteTransition */, null /* displayChange */);
+ app.mTransitionController.collectExistenceChange(app.getTask());
+ mDisplayContent.setFixedRotationLaunchingAppUnchecked(app);
+ final AsyncRotationController asyncRotationController =
+ mDisplayContent.getAsyncRotationController();
+ assertNotNull(asyncRotationController);
+ assertTrue(asyncRotationController.shouldFreezeInsetsPosition(statusBar));
+ assertTrue(app.getTask().inTransition());
+
+ player.start();
+ player.finish();
+ app.getTask().clearSyncState();
+
+ // The open transition is finished. Continue to play seamless display change transition,
+ // so the previous async rotation controller should still exist.
+ mDisplayContent.getDisplayRotation().setRotation(mDisplayContent.getRotation() + 1);
+ mDisplayContent.setLastHasContent();
+ mDisplayContent.requestChangeTransitionIfNeeded(1 /* changes */, null /* displayChange */);
+ assertTrue(mDisplayContent.hasTopFixedRotationLaunchingApp());
+ assertNotNull(mDisplayContent.getAsyncRotationController());
+
+ statusBar.setOrientationChanging(true);
+ player.startTransition();
+ // Non-app windows should not be collected.
+ assertFalse(statusBar.mToken.inTransition());
+
+ onRotationTransactionReady(player, mWm.mTransactionFactory.get()).onTransactionCommitted();
+ assertEquals(ROTATION_ANIMATION_SEAMLESS, player.mLastReady.getChange(
+ mDisplayContent.mRemoteToken.toWindowContainerToken()).getRotationAnimation());
+ player.finish();
+
+ // The controller should be cleared if the target windows are drawn.
+ statusBar.finishDrawing(mWm.mTransactionFactory.get());
+ statusBar.setOrientationChanging(false);
+ assertNull(mDisplayContent.getAsyncRotationController());
+ }
+
+ @Test
+ public void testDeferRotationForTransientLaunch() {
+ final TestTransitionPlayer player = registerTestTransitionPlayer();
+ assumeFalse(mDisplayContent.mTransitionController.useShellTransitionsRotation());
+ final ActivityRecord app = new ActivityBuilder(mAtm).setCreateTask(true).build();
+ final ActivityRecord home = new ActivityBuilder(mAtm)
+ .setTask(mDisplayContent.getDefaultTaskDisplayArea().getRootHomeTask())
+ .setScreenOrientation(SCREEN_ORIENTATION_NOSENSOR).setVisible(false).build();
+ final Transition transition = home.mTransitionController.createTransition(TRANSIT_OPEN);
+ final int prevRotation = mDisplayContent.getRotation();
+ transition.setTransientLaunch(home, null /* restoreBelow */);
+ home.mTransitionController.requestStartTransition(transition, home.getTask(),
+ null /* remoteTransition */, null /* displayChange */);
+ transition.collectExistenceChange(home);
+ home.mVisibleRequested = true;
+ mDisplayContent.setFixedRotationLaunchingAppUnchecked(home);
+ doReturn(true).when(home).hasFixedRotationTransform(any());
+ player.startTransition();
+ player.onTransactionReady(mDisplayContent.getSyncTransaction());
+
+ final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation();
+ spyOn(displayRotation);
+ doReturn(true).when(displayRotation).isWaitingForRemoteRotation();
+ doReturn(prevRotation + 1).when(displayRotation).rotationForOrientation(
+ anyInt() /* orientation */, anyInt() /* lastRotation */);
+ // Rotation update is skipped while the recents animation is running.
+ assertFalse(mDisplayContent.updateRotationUnchecked());
+ assertEquals(SCREEN_ORIENTATION_NOSENSOR, displayRotation.getLastOrientation());
+ // Return to the app without fixed orientation from recents.
+ app.moveFocusableActivityToTop("test");
+ player.finish();
+ // The display should be updated to the changed orientation after the animation is finish.
+ assertNotEquals(mDisplayContent.getRotation(), prevRotation);
+ }
+
@Test
public void testIntermediateVisibility() {
final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class);
diff --git a/services/usage/java/com/android/server/usage/BroadcastResponseStatsLogger.java b/services/usage/java/com/android/server/usage/BroadcastResponseStatsLogger.java
new file mode 100644
index 0000000..bfc1771
--- /dev/null
+++ b/services/usage/java/com/android/server/usage/BroadcastResponseStatsLogger.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.usage;
+
+import static android.app.ActivityManager.procStateToString;
+
+import static com.android.server.usage.BroadcastResponseStatsTracker.NOTIFICATION_EVENT_TYPE_CANCELLED;
+import static com.android.server.usage.BroadcastResponseStatsTracker.NOTIFICATION_EVENT_TYPE_POSTED;
+import static com.android.server.usage.BroadcastResponseStatsTracker.NOTIFICATION_EVENT_TYPE_UPDATED;
+import static com.android.server.usage.BroadcastResponseStatsTracker.TAG;
+import static com.android.server.usage.UsageStatsService.DEBUG_RESPONSE_STATS;
+
+import android.annotation.ElapsedRealtimeLong;
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.app.ActivityManager.ProcessState;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Slog;
+import android.util.TimeUtils;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.RingBuffer;
+import com.android.server.usage.BroadcastResponseStatsTracker.NotificationEventType;
+
+public class BroadcastResponseStatsLogger {
+
+ private static final int MAX_LOG_SIZE =
+ ActivityManager.isLowRamDeviceStatic() ? 20 : 50;
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private final LogBuffer mBroadcastEventsBuffer = new LogBuffer(
+ BroadcastEvent.class, MAX_LOG_SIZE);
+ @GuardedBy("mLock")
+ private final LogBuffer mNotificationEventsBuffer = new LogBuffer(
+ NotificationEvent.class, MAX_LOG_SIZE);
+
+ void logBroadcastDispatchEvent(int sourceUid, @NonNull String targetPackage,
+ UserHandle targetUser, long idForResponseEvent,
+ @ElapsedRealtimeLong long timeStampMs, @ProcessState int targetUidProcessState) {
+ synchronized (mLock) {
+ if (DEBUG_RESPONSE_STATS) {
+ Slog.d(TAG, getBroadcastDispatchEventLog(sourceUid, targetPackage,
+ targetUser.getIdentifier(), idForResponseEvent, timeStampMs,
+ targetUidProcessState));
+ }
+ mBroadcastEventsBuffer.logBroadcastDispatchEvent(sourceUid, targetPackage,
+ targetUser, idForResponseEvent, timeStampMs, targetUidProcessState);
+ }
+ }
+
+ void logNotificationEvent(@NotificationEventType int event,
+ @NonNull String packageName, UserHandle user, @ElapsedRealtimeLong long timestampMs) {
+ synchronized (mLock) {
+ if (DEBUG_RESPONSE_STATS) {
+ Slog.d(TAG, getNotificationEventLog(event, packageName, user.getIdentifier(),
+ timestampMs));
+ }
+ mNotificationEventsBuffer.logNotificationEvent(event, packageName, user, timestampMs);
+ }
+ }
+
+ void dumpLogs(IndentingPrintWriter ipw) {
+ synchronized (mLock) {
+ ipw.println("Broadcast events (most recent first):");
+ ipw.increaseIndent();
+ mBroadcastEventsBuffer.reverseDump(ipw);
+ ipw.decreaseIndent();
+
+ ipw.println();
+ ipw.println("Notification events (most recent first):");
+ ipw.increaseIndent();
+ mNotificationEventsBuffer.reverseDump(ipw);
+ ipw.decreaseIndent();
+ }
+ }
+
+ private static final class LogBuffer<T extends Data> extends RingBuffer<T> {
+
+ LogBuffer(Class<T> classType, int capacity) {
+ super(classType, capacity);
+ }
+
+ void logBroadcastDispatchEvent(int sourceUid, @NonNull String targetPackage,
+ UserHandle targetUser, long idForResponseEvent,
+ @ElapsedRealtimeLong long timeStampMs, @ProcessState int targetUidProcessState) {
+ final Data data = getNextSlot();
+ if (data == null) return;
+
+ data.reset();
+ final BroadcastEvent event = (BroadcastEvent) data;
+ event.sourceUid = sourceUid;
+ event.targetUserId = targetUser.getIdentifier();
+ event.targetUidProcessState = targetUidProcessState;
+ event.targetPackage = targetPackage;
+ event.idForResponseEvent = idForResponseEvent;
+ event.timestampMs = timeStampMs;
+ }
+
+ void logNotificationEvent(@NotificationEventType int type,
+ @NonNull String packageName, UserHandle user,
+ @ElapsedRealtimeLong long timestampMs) {
+ final Data data = getNextSlot();
+ if (data == null) return;
+
+ data.reset();
+ final NotificationEvent event = (NotificationEvent) data;
+ event.type = type;
+ event.packageName = packageName;
+ event.userId = user.getIdentifier();
+ event.timestampMs = timestampMs;
+ }
+
+ public void reverseDump(IndentingPrintWriter pw) {
+ final Data[] allData = toArray();
+ for (int i = allData.length - 1; i >= 0; --i) {
+ if (allData[i] == null) {
+ continue;
+ }
+ pw.println(getContent(allData[i]));
+ }
+ }
+
+ @NonNull
+ public String getContent(Data data) {
+ return data.toString();
+ }
+ }
+
+ @NonNull
+ private static String getBroadcastDispatchEventLog(int sourceUid, @NonNull String targetPackage,
+ @UserIdInt int targetUserId, long idForResponseEvent,
+ @ElapsedRealtimeLong long timestampMs, @ProcessState int targetUidProcState) {
+ return TextUtils.formatSimple(
+ "broadcast:%s; srcUid=%d, tgtPkg=%s, tgtUsr=%d, id=%d, state=%s",
+ TimeUtils.formatDuration(timestampMs), sourceUid, targetPackage, targetUserId,
+ idForResponseEvent, procStateToString(targetUidProcState));
+ }
+
+ @NonNull
+ private static String getNotificationEventLog(@NotificationEventType int event,
+ @NonNull String packageName, @UserIdInt int userId,
+ @ElapsedRealtimeLong long timestampMs) {
+ return TextUtils.formatSimple("notification:%s; event=<%s>, pkg=%s, usr=%d",
+ TimeUtils.formatDuration(timestampMs), notificationEventToString(event),
+ packageName, userId);
+ }
+
+ @NonNull
+ private static String notificationEventToString(@NotificationEventType int event) {
+ switch (event) {
+ case NOTIFICATION_EVENT_TYPE_POSTED:
+ return "posted";
+ case NOTIFICATION_EVENT_TYPE_UPDATED:
+ return "updated";
+ case NOTIFICATION_EVENT_TYPE_CANCELLED:
+ return "cancelled";
+ default:
+ return String.valueOf(event);
+ }
+ }
+
+ public static final class BroadcastEvent implements Data {
+ public int sourceUid;
+ public int targetUserId;
+ public int targetUidProcessState;
+ public String targetPackage;
+ public long idForResponseEvent;
+ public long timestampMs;
+
+ @Override
+ public void reset() {
+ targetPackage = null;
+ }
+
+ @Override
+ public String toString() {
+ return getBroadcastDispatchEventLog(sourceUid, targetPackage, targetUserId,
+ idForResponseEvent, timestampMs, targetUidProcessState);
+ }
+ }
+
+ public static final class NotificationEvent implements Data {
+ public int type;
+ public String packageName;
+ public int userId;
+ public long timestampMs;
+
+ @Override
+ public void reset() {
+ packageName = null;
+ }
+
+ @Override
+ public String toString() {
+ return getNotificationEventLog(type, packageName, userId, timestampMs);
+ }
+ }
+
+ public interface Data {
+ void reset();
+ }
+}
diff --git a/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java b/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java
index ab8f69b..76d2fe7 100644
--- a/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java
+++ b/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java
@@ -16,10 +16,6 @@
package com.android.server.usage;
-import static android.app.ActivityManager.procStateToString;
-
-import static com.android.server.usage.UsageStatsService.DEBUG_RESPONSE_STATS;
-
import android.annotation.ElapsedRealtimeLong;
import android.annotation.IntDef;
import android.annotation.IntRange;
@@ -29,11 +25,9 @@
import android.app.ActivityManager.ProcessState;
import android.app.usage.BroadcastResponseStats;
import android.os.UserHandle;
-import android.text.TextUtils;
import android.util.LongSparseArray;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.TimeUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
@@ -44,19 +38,19 @@
import java.util.List;
class BroadcastResponseStatsTracker {
- private static final String TAG = "ResponseStatsTracker";
+ static final String TAG = "ResponseStatsTracker";
@Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = {"NOTIFICATION_EVENT"}, value = {
- NOTIFICATION_EVENT_POSTED,
- NOTIFICATION_EVENT_UPDATED,
- NOTIFICATION_EVENT_CANCELLED
+ @IntDef(prefix = {"NOTIFICATION_EVENT_TYPE_"}, value = {
+ NOTIFICATION_EVENT_TYPE_POSTED,
+ NOTIFICATION_EVENT_TYPE_UPDATED,
+ NOTIFICATION_EVENT_TYPE_CANCELLED
})
- public @interface NotificationEvent {}
+ public @interface NotificationEventType {}
- private static final int NOTIFICATION_EVENT_POSTED = 0;
- private static final int NOTIFICATION_EVENT_UPDATED = 1;
- private static final int NOTIFICATION_EVENT_CANCELLED = 2;
+ static final int NOTIFICATION_EVENT_TYPE_POSTED = 0;
+ static final int NOTIFICATION_EVENT_TYPE_UPDATED = 1;
+ static final int NOTIFICATION_EVENT_TYPE_CANCELLED = 2;
private final Object mLock = new Object();
@@ -76,21 +70,19 @@
new SparseArray<>();
private AppStandbyInternal mAppStandby;
+ private BroadcastResponseStatsLogger mLogger;
BroadcastResponseStatsTracker(@NonNull AppStandbyInternal appStandby) {
mAppStandby = appStandby;
+ mLogger = new BroadcastResponseStatsLogger();
}
// TODO (206518114): Move all callbacks handling to a handler thread.
void reportBroadcastDispatchEvent(int sourceUid, @NonNull String targetPackage,
UserHandle targetUser, long idForResponseEvent,
@ElapsedRealtimeLong long timestampMs, @ProcessState int targetUidProcState) {
- if (DEBUG_RESPONSE_STATS) {
- Slog.d(TAG, TextUtils.formatSimple("reportBroadcastDispatchEvent; "
- + "srcUid=%d, tgtPkg=%s, tgtUsr=%d, id=%d, ts=%s, state=%s",
- sourceUid, targetPackage, targetUser, idForResponseEvent,
- TimeUtils.formatDuration(timestampMs), procStateToString(targetUidProcState)));
- }
+ mLogger.logBroadcastDispatchEvent(sourceUid, targetPackage, targetUser,
+ idForResponseEvent, timestampMs, targetUidProcState);
if (targetUidProcState <= mAppStandby.getBroadcastResponseFgThresholdState()) {
// No need to track the broadcast response state while the target app is
// in the foreground.
@@ -110,29 +102,23 @@
void reportNotificationPosted(@NonNull String packageName, UserHandle user,
@ElapsedRealtimeLong long timestampMs) {
- reportNotificationEvent(NOTIFICATION_EVENT_POSTED, packageName, user, timestampMs);
+ reportNotificationEvent(NOTIFICATION_EVENT_TYPE_POSTED, packageName, user, timestampMs);
}
void reportNotificationUpdated(@NonNull String packageName, UserHandle user,
@ElapsedRealtimeLong long timestampMs) {
- reportNotificationEvent(NOTIFICATION_EVENT_UPDATED, packageName, user, timestampMs);
+ reportNotificationEvent(NOTIFICATION_EVENT_TYPE_UPDATED, packageName, user, timestampMs);
}
void reportNotificationCancelled(@NonNull String packageName, UserHandle user,
@ElapsedRealtimeLong long timestampMs) {
- reportNotificationEvent(NOTIFICATION_EVENT_CANCELLED, packageName, user, timestampMs);
+ reportNotificationEvent(NOTIFICATION_EVENT_TYPE_CANCELLED, packageName, user, timestampMs);
}
- private void reportNotificationEvent(@NotificationEvent int event,
+ private void reportNotificationEvent(@NotificationEventType int event,
@NonNull String packageName, UserHandle user, @ElapsedRealtimeLong long timestampMs) {
- if (DEBUG_RESPONSE_STATS) {
- Slog.d(TAG, TextUtils.formatSimple(
- "reportNotificationEvent; event=<%s>, pkg=%s, usr=%d, ts=%s",
- notificationEventToString(event), packageName, user.getIdentifier(),
- TimeUtils.formatDuration(timestampMs)));
- }
- // TODO (206518114): Store last N events to dump for debugging purposes.
+ mLogger.logNotificationEvent(event, packageName, user, timestampMs);
synchronized (mLock) {
final LongSparseArray<BroadcastEvent> broadcastEvents =
getBroadcastEventsLocked(packageName, user);
@@ -157,13 +143,13 @@
continue;
}
switch (event) {
- case NOTIFICATION_EVENT_POSTED:
+ case NOTIFICATION_EVENT_TYPE_POSTED:
responseStats.incrementNotificationsPostedCount(1);
break;
- case NOTIFICATION_EVENT_UPDATED:
+ case NOTIFICATION_EVENT_TYPE_UPDATED:
responseStats.incrementNotificationsUpdatedCount(1);
break;
- case NOTIFICATION_EVENT_CANCELLED:
+ case NOTIFICATION_EVENT_TYPE_CANCELLED:
responseStats.incrementNotificationsCancelledCount(1);
break;
default:
@@ -329,20 +315,6 @@
return userResponseStats.getOrCreateBroadcastResponseStats(broadcastEvent);
}
- @NonNull
- private String notificationEventToString(@NotificationEvent int event) {
- switch (event) {
- case NOTIFICATION_EVENT_POSTED:
- return "posted";
- case NOTIFICATION_EVENT_UPDATED:
- return "updated";
- case NOTIFICATION_EVENT_CANCELLED:
- return "cancelled";
- default:
- return String.valueOf(event);
- }
- }
-
void dump(@NonNull IndentingPrintWriter ipw) {
ipw.println("Broadcast response stats:");
ipw.increaseIndent();
@@ -351,6 +323,8 @@
dumpBroadcastEventsLocked(ipw);
ipw.println();
dumpResponseStatsLocked(ipw);
+ ipw.println();
+ mLogger.dumpLogs(ipw);
}
ipw.decreaseIndent();
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 7f70301..1999cfc 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -2187,7 +2187,7 @@
* @param uid Uid of the caller
*/
public ParcelFileDescriptor openAccessory(UsbAccessory accessory,
- UsbUserPermissionManager permissions, int uid) {
+ UsbUserPermissionManager permissions, int pid, int uid) {
UsbAccessory currentAccessory = mHandler.getCurrentAccessory();
if (currentAccessory == null) {
throw new IllegalArgumentException("no accessory attached");
@@ -2198,7 +2198,7 @@
+ currentAccessory;
throw new IllegalArgumentException(error);
}
- permissions.checkPermission(accessory, uid);
+ permissions.checkPermission(accessory, pid, uid);
return nativeOpenAccessory();
}
diff --git a/services/usb/java/com/android/server/usb/UsbSerialReader.java b/services/usb/java/com/android/server/usb/UsbSerialReader.java
index f241e65..9dda0e7 100644
--- a/services/usb/java/com/android/server/usb/UsbSerialReader.java
+++ b/services/usb/java/com/android/server/usb/UsbSerialReader.java
@@ -98,7 +98,7 @@
.checkPermission((UsbDevice) mDevice, packageName, pid, uid);
} else {
mPermissionManager.getPermissionsForUser(userId)
- .checkPermission((UsbAccessory) mDevice, uid);
+ .checkPermission((UsbAccessory) mDevice, pid, uid);
}
}
}
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index c0ecf58..e06ab02 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -321,6 +321,7 @@
public ParcelFileDescriptor openAccessory(UsbAccessory accessory) {
if (mDeviceManager != null) {
int uid = Binder.getCallingUid();
+ int pid = Binder.getCallingPid();
int user = UserHandle.getUserId(uid);
final long ident = clearCallingIdentity();
@@ -328,7 +329,7 @@
synchronized (mLock) {
if (mUserManager.isSameProfileGroup(user, mCurrentUserId)) {
return mDeviceManager.openAccessory(accessory, getPermissionsForUser(user),
- uid);
+ pid, uid);
} else {
Slog.w(TAG, "Cannot open " + accessory + " for user " + user
+ " as user is not active.");
@@ -505,11 +506,12 @@
@Override
public boolean hasAccessoryPermission(UsbAccessory accessory) {
final int uid = Binder.getCallingUid();
+ final int pid = Binder.getCallingPid();
final int userId = UserHandle.getUserId(uid);
final long token = Binder.clearCallingIdentity();
try {
- return getPermissionsForUser(userId).hasPermission(accessory, uid);
+ return getPermissionsForUser(userId).hasPermission(accessory, pid, uid);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -533,11 +535,12 @@
public void requestAccessoryPermission(
UsbAccessory accessory, String packageName, PendingIntent pi) {
final int uid = Binder.getCallingUid();
+ final int pid = Binder.getCallingPid();
final int userId = UserHandle.getUserId(uid);
final long token = Binder.clearCallingIdentity();
try {
- getPermissionsForUser(userId).requestPermission(accessory, packageName, pi, uid);
+ getPermissionsForUser(userId).requestPermission(accessory, packageName, pi, pid, uid);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
index 286cff9..dd5f153 100644
--- a/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
+++ b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
@@ -246,9 +246,13 @@
* @param uid to check permission for
* @return {@code true} if caller has permssion
*/
- boolean hasPermission(@NonNull UsbAccessory accessory, int uid) {
+ boolean hasPermission(@NonNull UsbAccessory accessory, int pid, int uid) {
synchronized (mLock) {
- if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
+ if (uid == Process.SYSTEM_UID
+ || mDisablePermissionDialogs
+ || mContext.checkPermission(
+ android.Manifest.permission.MANAGE_USB, pid, uid)
+ == android.content.pm.PackageManager.PERMISSION_GRANTED) {
return true;
}
AccessoryFilter filter = new AccessoryFilter(accessory);
@@ -675,8 +679,8 @@
}
}
- public void checkPermission(UsbAccessory accessory, int uid) {
- if (!hasPermission(accessory, uid)) {
+ public void checkPermission(UsbAccessory accessory, int pid, int uid) {
+ if (!hasPermission(accessory, pid, uid)) {
throw new SecurityException("User has not given " + uid + " permission to accessory "
+ accessory);
}
@@ -745,9 +749,9 @@
}
public void requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi,
- int uid) {
+ int pid, int uid) {
// respond immediately if permission has already been granted
- if (hasPermission(accessory, uid)) {
+ if (hasPermission(accessory, pid, uid)) {
Intent intent = new Intent();
intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
diff --git a/telephony/java/android/service/euicc/EuiccService.java b/telephony/java/android/service/euicc/EuiccService.java
index 8d92520..7129a27 100644
--- a/telephony/java/android/service/euicc/EuiccService.java
+++ b/telephony/java/android/service/euicc/EuiccService.java
@@ -29,7 +29,6 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
-import android.telephony.TelephonyManager;
import android.telephony.euicc.DownloadableSubscription;
import android.telephony.euicc.EuiccInfo;
import android.telephony.euicc.EuiccManager;
@@ -744,18 +743,16 @@
public void run() {
DownloadSubscriptionResult result;
try {
- result =
- EuiccService.this.onDownloadSubscription(
- slotId, subscription, switchAfterDownload, forceDeactivateSim,
- resolvedBundle);
+ result = EuiccService.this.onDownloadSubscription(
+ slotId, portIndex, subscription, switchAfterDownload,
+ forceDeactivateSim, resolvedBundle);
} catch (AbstractMethodError e) {
- Log.w(TAG, "The new onDownloadSubscription(int, "
+ Log.w(TAG, "The new onDownloadSubscription(int, int, "
+ "DownloadableSubscription, boolean, boolean, Bundle) is not "
+ "implemented. Fall back to the old one.", e);
- int resultCode = EuiccService.this.onDownloadSubscription(
- slotId, subscription, switchAfterDownload, forceDeactivateSim);
- result = new DownloadSubscriptionResult(resultCode,
- 0 /* resolvableErrors */, TelephonyManager.UNSUPPORTED_CARD_ID);
+ result = EuiccService.this.onDownloadSubscription(
+ slotId, subscription, switchAfterDownload,
+ forceDeactivateSim, resolvedBundle);
}
try {
callback.onComplete(result);
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index adb51c4..837cf8b 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3850,30 +3850,42 @@
public static final String KEY_OPPORTUNISTIC_ESIM_DOWNLOAD_VIA_WIFI_ONLY_BOOL =
"opportunistic_esim_download_via_wifi_only_bool";
- /**
- * Controls RSRP threshold at which OpportunisticNetworkService will decide whether
+/**
+ * Controls RSRP threshold, in dBm, at which OpportunisticNetworkService will decide whether
* the opportunistic network is good enough for internet data.
+ *
+ * <p>The value of {@link CellSignalStrengthLte#getRsrp()} will be compared with this
+ * threshold.
*/
public static final String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT =
"opportunistic_network_entry_threshold_rsrp_int";
/**
- * Controls RSSNR threshold at which OpportunisticNetworkService will decide whether
- * the opportunistic network is good enough for internet data.
+ * Controls RSSNR threshold, in dB, at which OpportunisticNetworkService will
+ * decide whether the opportunistic network is good enough for internet data.
+ *
+ * <p>The value of {@link CellSignalStrengthLte#getRssnr()} will be compared with this
+ * threshold.
*/
public static final String KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSSNR_INT =
"opportunistic_network_entry_threshold_rssnr_int";
/**
- * Controls RSRP threshold below which OpportunisticNetworkService will decide whether
+ * Controls RSRP threshold, in dBm, below which OpportunisticNetworkService will decide whether
* the opportunistic network available is not good enough for internet data.
+ *
+ * <p>The value of {@link CellSignalStrengthLte#getRsrp()} will be compared with this
+ * threshold.
*/
public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSRP_INT =
"opportunistic_network_exit_threshold_rsrp_int";
/**
- * Controls RSSNR threshold below which OpportunisticNetworkService will decide whether
- * the opportunistic network available is not good enough for internet data.
+ * Controls RSSNR threshold, in dB, below which OpportunisticNetworkService will
+ * decide whether the opportunistic network available is not good enough for internet data.
+ *
+ * <p>The value of {@link CellSignalStrengthLte#getRssnr()} will be compared with this
+ * threshold.
*/
public static final String KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT =
"opportunistic_network_exit_threshold_rssnr_int";
@@ -3971,7 +3983,7 @@
* good enough for internet data. Note other factors may be considered for the final
* decision.
*
- * <p>The value of {@link CellSignalStrengthNr#getSsRsrp} will be compared with this
+ * <p>The value of {@link CellSignalStrengthNr#getSsRsrp()} will be compared with this
* threshold.
*
* @hide
@@ -3998,7 +4010,7 @@
* good enough for internet data. Note other factors may be considered for the final
* decision.
*
- * <p>The value of {@link CellSignalStrengthNr#getSsRsrq} will be compared with this
+ * <p>The value of {@link CellSignalStrengthNr#getSsRsrq()} will be compared with this
* threshold.
*
* @hide
@@ -4025,6 +4037,9 @@
* be considered good enough for internet data. Note other factors may be considered
* for the final decision.
*
+ * <p>The value of {@link CellSignalStrengthNr#getSsRsrp()} will be compared with this
+ * threshold.
+ *
* @hide
*/
public static final String KEY_EXIT_THRESHOLD_SS_RSRP_INT =
@@ -4048,6 +4063,9 @@
* be considered good enough for internet data. Note other factors may be considered
* for the final decision.
*
+ * <p>The value of {@link CellSignalStrengthNr#getSsRsrq()} will be compared with this
+ * threshold.
+ *
* @hide
*/
public static final String KEY_EXIT_THRESHOLD_SS_RSRQ_DOUBLE =
@@ -8995,9 +9013,9 @@
/* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_MODERATE */
sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSRP_INT, -118);
/* Default value is minimum RSSNR level needed for SIGNAL_STRENGTH_GOOD */
- sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSSNR_INT, 45);
+ sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSSNR_INT, 5);
/* Default value is minimum RSSNR level needed for SIGNAL_STRENGTH_MODERATE */
- sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT, 10);
+ sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_EXIT_THRESHOLD_RSSNR_INT, 1);
/* Default value is 1024 kbps */
sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_BANDWIDTH_INT, 1024);
/* Default value is 10 seconds */
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index 947dc01..5e90261 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -125,13 +125,13 @@
/**
* Construct a cell signal strength
*
- * @param rssi in dBm [-113,-51], UNKNOWN
- * @param rsrp in dBm [-140,-43], UNKNOWN
- * @param rsrq in dB [-34, 3], UNKNOWN
- * @param rssnr in dB [-20, +30], UNKNOWN
- * @param cqiTableIndex [1, 6], UNKNOWN
- * @param cqi [0, 15], UNKNOWN
- * @param timingAdvance [0, 1282], UNKNOWN
+ * @param rssi in dBm [-113,-51], {@link CellInfo#UNAVAILABLE}
+ * @param rsrp in dBm [-140,-43], {@link CellInfo#UNAVAILABLE}
+ * @param rsrq in dB [-34, 3], {@link CellInfo#UNAVAILABLE}
+ * @param rssnr in dB [-20, +30], {@link CellInfo#UNAVAILABLE}
+ * @param cqiTableIndex [1, 6], {@link CellInfo#UNAVAILABLE}
+ * @param cqi [0, 15], {@link CellInfo#UNAVAILABLE}
+ * @param timingAdvance [0, 1282], {@link CellInfo#UNAVAILABLE}
*
*/
/** @hide */
@@ -151,12 +151,12 @@
/**
* Construct a cell signal strength
*
- * @param rssi in dBm [-113,-51], UNKNOWN
- * @param rsrp in dBm [-140,-43], UNKNOWN
- * @param rsrq in dB [-34, 3], UNKNOWN
- * @param rssnr in dB [-20, +30], UNKNOWN
- * @param cqi [0, 15], UNKNOWN
- * @param timingAdvance [0, 1282], UNKNOWN
+ * @param rssi in dBm [-113,-51], {@link CellInfo#UNAVAILABLE}
+ * @param rsrp in dBm [-140,-43], {@link CellInfo#UNAVAILABLE}
+ * @param rsrq in dB [-34, 3], {@link CellInfo#UNAVAILABLE}
+ * @param rssnr in dB [-20, +30], {@link CellInfo#UNAVAILABLE}
+ * @param cqi [0, 15], {@link CellInfo#UNAVAILABLE}
+ * @param timingAdvance [0, 1282], {@link CellInfo#UNAVAILABLE}
*
*/
/** @hide */
@@ -403,10 +403,11 @@
}
/**
- * Get reference signal signal-to-noise ratio
+ * Get reference signal signal-to-noise ratio in dB
+ * Range: -20 dB to +30 dB.
*
* @return the RSSNR if available or
- * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+ * {@link android.telephony.CellInfo#UNAVAILABLE} if unavailable.
*/
public int getRssnr() {
return mRssnr;
@@ -414,8 +415,10 @@
/**
* Get reference signal received power in dBm
+ * Range: -140 dBm to -43 dBm.
*
- * @return the RSRP of the measured cell.
+ * @return the RSRP of the measured cell or {@link CellInfo#UNAVAILABLE} if
+ * unavailable.
*/
public int getRsrp() {
return mRsrp;
diff --git a/tests/AttestationVerificationTest/Android.bp b/tests/AttestationVerificationTest/Android.bp
index a4741eed..b98f8cb 100644
--- a/tests/AttestationVerificationTest/Android.bp
+++ b/tests/AttestationVerificationTest/Android.bp
@@ -40,5 +40,6 @@
"androidx.test.rules",
"androidx.test.ext.junit",
"platform-test-annotations",
+ "services.core",
],
}
diff --git a/tests/AttestationVerificationTest/AndroidManifest.xml b/tests/AttestationVerificationTest/AndroidManifest.xml
index c42bde9..37321ad8 100755
--- a/tests/AttestationVerificationTest/AndroidManifest.xml
+++ b/tests/AttestationVerificationTest/AndroidManifest.xml
@@ -24,6 +24,7 @@
<application>
<uses-library android:name="android.test.runner"/>
<activity android:name=".SystemAttestationVerificationTest$TestActivity" />
+ <activity android:name=".PeerDeviceSystemAttestationVerificationTest$TestActivity" />
</application>
<!-- self-instrumenting test package. -->
diff --git a/tests/AttestationVerificationTest/assets/test_attestation_with_root_certs.pem b/tests/AttestationVerificationTest/assets/test_attestation_with_root_certs.pem
new file mode 100644
index 0000000..e29ff48
--- /dev/null
+++ b/tests/AttestationVerificationTest/assets/test_attestation_with_root_certs.pem
@@ -0,0 +1,81 @@
+-----BEGIN CERTIFICATE-----
+MIICkjCCAjmgAwIBAgIBATAKBggqhkjOPQQDAjA5MQwwCgYDVQQMDANURUUxKTAn
+BgNVBAUTIDg2ZTQ0MjRhMjY2NDlhZDcyZWZhNWM0MWEwM2IyN2QxMCAXDTcwMDEw
+MTAwMDAwMFoYDzIxMDYwMjA3MDYyODE1WjAfMR0wGwYDVQQDDBRBbmRyb2lkIEtl
+eXN0b3JlIEtleTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABIlTwcvhe+DLV45X
+RCTO7HoN20Ib7IbCEhV5+YdMiYOp/0AdKk8oYvsri1XODeC4zcoPfHNdQGt/68i0
+ADbilJmjggFIMIIBRDAOBgNVHQ8BAf8EBAMCB4AwggEwBgorBgEEAdZ5AgERBIIB
+IDCCARwCAQMKAQECAQQKAQEECXBsYXllcjQ1NgQAMFe/hT0IAgYBfvkgVei/hUVH
+BEUwQzEdMBsEFmNvbS5nb29nbGUuYXR0ZXN0YXRpb24CAQExIgQgOqyVXRJUdAGY
+/XVx8y/uRPiebqlyELt1EpqIz29h5tUwgaehCDEGAgECAgEDogMCAQOjBAICAQCl
+CDEGAgEEAgEGqgMCAQG/g3cCBQC/hT4DAgEAv4VATDBKBCCEZx8qY8Ys0HC2TqPq
+74eYPzh5L/agxD7Bn7zVBQHoNAEB/woBAAQguJwoDfWBjRaedzQ6TJPFJJKs+ytr
++8Vu2CSmqifFBHW/hUEFAgMB1MC/hUIFAgMDFdm/hU4GAgQBNIjJv4VPBgIEATSI
+yTAKBggqhkjOPQQDAgNHADBEAiBdGxfMEx59k5+zo+hV3Q9kgjbGi0zU3WH355P5
+JZttBwIgY4FZsSreUJL8RY3JvfvD8BRw8GuXcB1OQ600hwaYYC4=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIB8zCCAXqgAwIBAgIRAOuuukN0OHbNQvKngECkewEwCgYIKoZIzj0EAwIwOTEM
+MAoGA1UEDAwDVEVFMSkwJwYDVQQFEyA3MDkxMmRmNDYxMDRmYWFlOTQ3ODY0ZTU4
+MDRmMWY4ZDAeFw0yMDA5MjgyMDI3NTZaFw0zMDA5MjYyMDI3NTZaMDkxDDAKBgNV
+BAwMA1RFRTEpMCcGA1UEBRMgODZlNDQyNGEyNjY0OWFkNzJlZmE1YzQxYTAzYjI3
+ZDEwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAT3Mjl05ewv6G8zAR4fXJy2iadU
+yK7rNvzlECy2+nhEieL8BFXDvo0tx5fYs8qr67j/KvluFBfp2r9s+ckWz3Kzo2Mw
+YTAdBgNVHQ4EFgQUsVKBzAs1lMXAauQ3mGAJZJqK5tAwHwYDVR0jBBgwFoAUEsQA
+i8d2oLULSi5Ix4BTGGbvUEkwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
+AgQwCgYIKoZIzj0EAwIDZwAwZAIwfFziBCwuM1thLUSUNI61Xx/vnDnNkSv/aX5D
+yLjxbLlgnFSzIrc+6vf6h6L/+TjYAjAq6h9GKtMn4R0286MoqYqzp/rHn6JD2sqH
+iM8KZ0oA+Ut242EcmGjAoNfGZGZGddQ=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIDkzCCAXugAwIBAgIQNTAX5z3CBac6nD3hQiMDcDANBgkqhkiG9w0BAQsFADAb
+MRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MB4XDTIwMDkyODIwMjUwMloXDTMw
+MDkyNjIwMjUwMlowOTEMMAoGA1UEDAwDVEVFMSkwJwYDVQQFEyA3MDkxMmRmNDYx
+MDRmYWFlOTQ3ODY0ZTU4MDRmMWY4ZDB2MBAGByqGSM49AgEGBSuBBAAiA2IABA/7
+xZFlFtTjdy2B3p7E+FsrBjyhBSqY4a9FywawXMJRSja3HAK36ruzJjWlEkD+D0vq
+HI2joY39FHmWoZWwm2cq9gOleFGYOSCpMr4ib7xtq/6nefvKTP5rutxudF97t6Nj
+MGEwHQYDVR0OBBYEFBLEAIvHdqC1C0ouSMeAUxhm71BJMB8GA1UdIwQYMBaAFDZh
+4QB8iAUJUYtEbEf/GkzJ6k8SMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD
+AgIEMA0GCSqGSIb3DQEBCwUAA4ICAQAaMONDQxJz3PRn9gHQW5KP+TIoBPJZyGa1
+QFuEBcMDTtIxBxEh5Pj3ivPBc76PrdYu5U47Ve5YYCPsTpUTj7dOxbzGSZjfjvHF
+fNwy24g1Lah2iAdQRVErhWKBlpnQhBnnRrrNmTTmzhl8NvSExqAPP746dqwm1kQ7
+YesC5yoEAHpxamhlZpIKAjSxSZeHWace2qV00M8qWd/7lIpqttJjFFrhCjzR0dtr
+oIIpC5EtmqIWdLeg6yZjJkX+Cjv4F8mRfBtwuNuxFsfALQ3D5l8WKw3iwPebmCy1
+kEby8Eoq88FxzXQp/XgAaljlrKXyuxptrc1noRuob4g42Oh6wetueYRSCtO6Bkym
+0UMnld/kG77aeiHOMVVb86wrhNuAGir1vgDGOBsclITVyuu9ka0YVQjjDm3phTpd
+O8JV16gbei2Phn+FfRV1MSDsZo/wu0i2KVzgs27bfJocMHXv+GzvwfefYgMJ/rYq
+Bg27lpsWzmFEPv2cyhA5PwwbG8ceswa3RZE/2eS9o7STkz93jr/KsKLcMBY6cX2C
+q4CBJByKFJtVANOVj+neFNxc2sQgeTT33yYNKbe4b5bm7Ki1FbrhFVckpzUGDnKs
+gL+AxvALWOoryDGwNbJiW8PRiD3HHByiMvSEQ7e7BSc2KjbsaWbCfYZAMZJEhEsc
+P1l8lcUVuA==
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFHDCCAwSgAwIBAgIJANUP8luj8tazMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNV
+BAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMTkxMTIyMjAzNzU4WhcNMzQxMTE4MjAz
+NzU4WjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdS
+Sxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7
+tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggj
+nar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGq
+C4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQ
+oVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+O
+JtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/Eg
+sTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRi
+igHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+M
+RPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9E
+aDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5Um
+AGMCAwEAAaNjMGEwHQYDVR0OBBYEFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMB8GA1Ud
+IwQYMBaAFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMA8GA1UdEwEB/wQFMAMBAf8wDgYD
+VR0PAQH/BAQDAgIEMA0GCSqGSIb3DQEBCwUAA4ICAQBOMaBc8oumXb2voc7XCWnu
+XKhBBK3e2KMGz39t7lA3XXRe2ZLLAkLM5y3J7tURkf5a1SutfdOyXAmeE6SRo83U
+h6WszodmMkxK5GM4JGrnt4pBisu5igXEydaW7qq2CdC6DOGjG+mEkN8/TA6p3cno
+L/sPyz6evdjLlSeJ8rFBH6xWyIZCbrcpYEJzXaUOEaxxXxgYz5/cTiVKN2M1G2ok
+QBUIYSY6bjEL4aUN5cfo7ogP3UvliEo3Eo0YgwuzR2v0KR6C1cZqZJSTnghIC/vA
+D32KdNQ+c3N+vl2OTsUVMC1GiWkngNx1OO1+kXW+YTnnTUOtOIswUP/Vqd5SYgAI
+mMAfY8U9/iIgkQj6T2W6FsScy94IN9fFhE1UtzmLoBIuUFsVXJMTz+Jucth+IqoW
+Fua9v1R93/k98p41pjtFX+H8DslVgfP097vju4KDlqN64xV1grw3ZLl4CiOe/A91
+oeLm2UHOq6wn3esB4r2EIQKb6jTVGu5sYCcdWpXr0AUVqcABPdgL+H7qJguBw09o
+jm6xNIrw2OocrDKsudk/okr/AwqEyPKw9WnMlQgLIKw1rODG2NvU9oR3GVGdMkUB
+ZutL8VuFkERQGt6vQ2OCw0sV47VMkuYbacK/xyZFiRcrPJPb41zgbQj9XAEyLKCH
+ex0SdDrx+tWUDqG8At2JHA==
+-----END CERTIFICATE-----
diff --git a/tests/AttestationVerificationTest/assets/test_attestation_wrong_root_certs.pem b/tests/AttestationVerificationTest/assets/test_attestation_wrong_root_certs.pem
new file mode 100644
index 0000000..3d6410a
--- /dev/null
+++ b/tests/AttestationVerificationTest/assets/test_attestation_wrong_root_certs.pem
@@ -0,0 +1,30 @@
+-----BEGIN CERTIFICATE-----
+MIIGCDCCBHCgAwIBAgIBATANBgkqhkiG9w0BAQsFADApMRkwFwYDVQQFExAyZGM1OGIyZDFhMjQx
+MzI2MQwwCgYDVQQMDANURUUwIBcNNzAwMTAxMDAwMDAwWhgPMjEwNjAyMDcwNjI4MTVaMB8xHTAb
+BgNVBAMMFEFuZHJvaWQgS2V5c3RvcmUgS2V5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
+AQEApNVcnyN40MANMbbo2nMGNq2NNysDSjfLm0W3i6wPKf0ffCYkhWM4dCmQKKf50uAZTBeTit4c
+NwXeZn3qellMlOsIN3Qc384rfN/8cikrRvUAgibz0Jy7STykjwa7x6tKwqITxbO8HqAhKo8/BQXU
+xzrOdIg5ciy+UM7Vgh7a7ogen0KL2iGgrsalb1ti7Vlzb6vIJ4WzIC3TGD2sCkoPahghwqFDZZCo
+/FzaLoNY0jAUX2mL+kf8aUaoxz7xA9FTvgara+1pLBR1s4c8xPS2HdZipcVXWfey0wujv1VAKs4+
+tXjKlHkYBHBBceEjxUtEmrapSQEdpHPv7Xh9Uanq4QIDAQABo4ICwTCCAr0wDgYDVR0PAQH/BAQD
+AgeAMIICqQYKKwYBBAHWeQIBEQSCApkwggKVAgEDCgEBAgEECgEBBANhYmMEADCCAc2/hT0IAgYB
+ZOYGEYe/hUWCAbsEggG3MIIBszGCAYswDAQHYW5kcm9pZAIBHTAZBBRjb20uYW5kcm9pZC5rZXlj
+aGFpbgIBHTAZBBRjb20uYW5kcm9pZC5zZXR0aW5ncwIBHTAZBBRjb20ucXRpLmRpYWdzZXJ2aWNl
+cwIBHTAaBBVjb20uYW5kcm9pZC5keW5zeXN0ZW0CAR0wHQQYY29tLmFuZHJvaWQuaW5wdXRkZXZp
+Y2VzAgEdMB8EGmNvbS5hbmRyb2lkLmxvY2FsdHJhbnNwb3J0AgEdMB8EGmNvbS5hbmRyb2lkLmxv
+Y2F0aW9uLmZ1c2VkAgEdMB8EGmNvbS5hbmRyb2lkLnNlcnZlci50ZWxlY29tAgEdMCAEG2NvbS5h
+bmRyb2lkLndhbGxwYXBlcmJhY2t1cAIBHTAhBBxjb20uZ29vZ2xlLlNTUmVzdGFydERldGVjdG9y
+AgEdMCIEHWNvbS5nb29nbGUuYW5kcm9pZC5oaWRkZW5tZW51AgEBMCMEHmNvbS5hbmRyb2lkLnBy
+b3ZpZGVycy5zZXR0aW5ncwIBHTEiBCAwGqPLCBE0UBxF8UIqvGbCQiT9Xe1f3I8X5pcXb9hmqjCB
+rqEIMQYCAQICAQOiAwIBAaMEAgIIAKUFMQMCAQSmCDEGAgEDAgEFv4FIBQIDAQABv4N3AgUAv4U+
+AwIBAL+FQEwwSgQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQAKAQIEIHKNsSdP
+HxzxVx3kOAsEilVKxKOA529TVQg1KQhKk3gBv4VBAwIBAL+FQgUCAwMUs7+FTgUCAwMUs7+FTwUC
+AwMUszANBgkqhkiG9w0BAQsFAAOCAYEAJMIuzdNUdfrE6sIdmsnMn/scSG2odbphj8FkX9JGdF2S
+OT599HuDY9qhvkru2Dza4sLKK3f4ViBhuR9lpfeprKvstxbtBO7jkLYfVn0ZRzHRHVEyiW5IVKh+
+qOXVJ9S1lMShOTlsaYJytLKIlcrRAZBEXZiNbzTuVh1CH6X9Ni1dog14snm+lcOeORdL9fht2CHa
+u/caRnpWiZbjoAoJp0O89uBrRkXPpln51+3jPY6AFny30grNAvKguauDcPPhNV1yR+ylSsQi2gm3
+Rs4pgtlxFLMfZLgT0cbkl+9zk/QUqlpBP8ftUBsOI0ARr8xhFN3cvq9kXGLtJ9hEP9PRaflAFREk
+DK3IBIbVcAFZBFoAQOdE9zy0+F5bQrznPGaZg4Dzhcx33qMDUTgHtWoy+k3ePGQMEtmoTTLgQywW
+OIkXEoFqqGi9GKJXUT1KYi5NsigaYqu7FoN4Qsvs61pMUEfZSPP2AFwkA8uNFbmb9uxcxaGHCA8i
+3i9VM6yOLIrP
+-----END CERTIFICATE-----
diff --git a/tests/AttestationVerificationTest/assets/test_no_attestation_ext_certs.pem b/tests/AttestationVerificationTest/assets/test_no_attestation_ext_certs.pem
new file mode 100644
index 0000000..6d261fa
--- /dev/null
+++ b/tests/AttestationVerificationTest/assets/test_no_attestation_ext_certs.pem
@@ -0,0 +1,33 @@
+-----BEGIN CERTIFICATE-----
+MIIFoDCCA4igAwIBAgIQTfpKgAsLZJhp2V4xUriMADANBgkqhkiG9w0BAQ0FADBp
+MQswCQYDVQQGEwJVUzEUMBIGA1UECgwLR29vZ2xlIEluYy4xFzAVBgNVBAsMDkFu
+ZHJvaWQgVGhpbmdzMSswKQYDVQQDDCJBbmRyb2lkIFRoaW5ncyBBdHRlc3RhdGlv
+biBSb290IENBMCAXDTE3MDYyMTIwMjQzN1oYDzIwNTcwNjExMjAyNDM3WjBpMQsw
+CQYDVQQGEwJVUzEUMBIGA1UECgwLR29vZ2xlIEluYy4xFzAVBgNVBAsMDkFuZHJv
+aWQgVGhpbmdzMSswKQYDVQQDDCJBbmRyb2lkIFRoaW5ncyBBdHRlc3RhdGlvbiBS
+b290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuO82oerGivb9
+G9bWyM8Pg0y6SOnAC8/8b92dp1v4Npnc+QpjPRUKgn8lzjQ9Jo6IGY3OShRBiQYl
+bbZYkfJnC5HtqbOETdPLZclErVE/G6Oda1IeZWvQVMjNImEYOLL5ct2RxiPttd8v
+SLyOSNFPf5/SeFqX/La0NcmXMOvPSrTW3qO34brnC+ih7mlpJFLz6Up93N3Umxsl
+IElz2wCG72t6k3+caWLyIPVgIPmsQrfTeBK/hN5dAJgAN65BsTevLHRP9J610wj3
+RagSIK1NdTuJRnr5ZyTQrwE2nA8H3IJ7/eo6IlGhXPwLKDhbdxYygPxdlCq6Rl96
+aVLjfpqDPtJ9ow+QKZuEDbYJ4z4olNXC6O5G7vqnCuULA/2E7y7DZObjzXOrdx2z
+9YKd8BrIDMTN/5mmw2us8tywiaQhbl8vOtjU+A+iBBnkj/wt9TYyLKopdrDlo5mz
+wy5l750HOkVZXC3VkeECnp+9duSHdS4qeUf/W1j9nPM7kY0HFLPUVX9AFVp2JXnC
+iKZC32GQAVsDc1iyAZWAVTqA7E0fBHhk9jUnA0W9b5Lq06oW95ngNR1MIFY871i8
+aLHCBpIix8DuMe8NB9spCIP6WCQqGiWQQpzbeuBPtoi424xwZTO4oectTd77bs9V
+Rvunn49fz308KnoWjk/by1N7gWyTb38CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB
+/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMDQ1I0RKwFCI+Fy9uIIJ/HrXuqu
+MA0GCSqGSIb3DQEBDQUAA4ICAQB09qkyEpEDocdN5pPeXqtjj9d0AXREUGH2LhnC
+z9KZcUFR+JskwEMHCaOENOmKI3zWRmxT7d8cVywwGk+ExE7EBQoeHlh3Yo44M8aj
+ZL7RHCvHRYsePhAJkYpJ02IMR60TV+1jhMqE8+BnqFivS7kft4t295EyrnLRZE3b
+Nfc0t011j02RwUrioR57mdvS9EZBRnMQkobhn+jWt9O+V3mtplW+1A2n4ec6uni1
+2MMgAWHuO1sKVYd5Sp4JMUpNnfmQAMnNiOMF6VxkpaoF1lZWo4TrLxuDKJG3O8h1
+fByjCpNVY8kOvvYEadbldzh6Agy/3ppb9yfG7X7FtHr1ghNjuNT6w5VgvbRtoRja
+/ZSKuJMaKm5emMWNkls/cwVSPJIvTOzPTeYK1BKSyAL2LDJ93HI7x8h79/Q7gKRi
+kL8qT7GW2FqpWTK0253sJHqCJJP4A5Rxtf2+Afwqadfc6Ga4jJHb7rPXngz4j1ZB
+gl5yjXgWF9wHGxqrjKWe2EA3d47BC4HG3Rf5L56KQiRPhTqTk5vtZwtwLRLFDLt7
+Hdff13O1oLhn+2z9xkASUL3rFE/qWajZP7fk3CvzcuXwKDTZomIC4nNaglx4nLdj
+lHhOq+6ON8MZC46sLStD+D4a9A1HOoihJgI/yGGkwdrp4KQIveRkEBO/x9v3NNBE
+bMwG9w==
+-----END CERTIFICATE-----
diff --git a/tests/AttestationVerificationTest/assets/test_root_certs.pem b/tests/AttestationVerificationTest/assets/test_root_certs.pem
new file mode 100644
index 0000000..c51851fe
--- /dev/null
+++ b/tests/AttestationVerificationTest/assets/test_root_certs.pem
@@ -0,0 +1,61 @@
+-----BEGIN CERTIFICATE-----
+MIIFYDCCA0igAwIBAgIJAOj6GWMU0voYMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNV
+BAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMTYwNTI2MTYyODUyWhcNMjYwNTI0MTYy
+ODUyWjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdS
+Sxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7
+tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggj
+nar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGq
+C4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQ
+oVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+O
+JtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/Eg
+sTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRi
+igHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+M
+RPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9E
+aDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5Um
+AGMCAwEAAaOBpjCBozAdBgNVHQ4EFgQUNmHhAHyIBQlRi0RsR/8aTMnqTxIwHwYD
+VR0jBBgwFoAUNmHhAHyIBQlRi0RsR/8aTMnqTxIwDwYDVR0TAQH/BAUwAwEB/zAO
+BgNVHQ8BAf8EBAMCAYYwQAYDVR0fBDkwNzA1oDOgMYYvaHR0cHM6Ly9hbmRyb2lk
+Lmdvb2dsZWFwaXMuY29tL2F0dGVzdGF0aW9uL2NybC8wDQYJKoZIhvcNAQELBQAD
+ggIBACDIw41L3KlXG0aMiS//cqrG+EShHUGo8HNsw30W1kJtjn6UBwRM6jnmiwfB
+Pb8VA91chb2vssAtX2zbTvqBJ9+LBPGCdw/E53Rbf86qhxKaiAHOjpvAy5Y3m00m
+qC0w/Zwvju1twb4vhLaJ5NkUJYsUS7rmJKHHBnETLi8GFqiEsqTWpG/6ibYCv7rY
+DBJDcR9W62BW9jfIoBQcxUCUJouMPH25lLNcDc1ssqvC2v7iUgI9LeoM1sNovqPm
+QUiG9rHli1vXxzCyaMTjwftkJLkf6724DFhuKug2jITV0QkXvaJWF4nUaHOTNA4u
+JU9WDvZLI1j83A+/xnAJUucIv/zGJ1AMH2boHqF8CY16LpsYgBt6tKxxWH00XcyD
+CdW2KlBCeqbQPcsFmWyWugxdcekhYsAWyoSf818NUsZdBWBaR/OukXrNLfkQ79Iy
+ZohZbvabO/X+MVT3rriAoKc8oE2Uws6DF+60PV7/WIPjNvXySdqspImSN78mflxD
+qwLqRBYkA3I75qppLGG9rp7UCdRjxMl8ZDBld+7yvHVgt1cVzJx9xnyGCC23Uaic
+MDSXYrB4I4WHXPGjxhZuCuPBLTdOLU8YRvMYdEvYebWHMpvwGCF6bAx3JBpIeOQ1
+wDB5y0USicV3YgYGmi+NZfhA4URSh77Yd6uuJOJENRaNVTzk
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFHDCCAwSgAwIBAgIJANUP8luj8tazMA0GCSqGSIb3DQEBCwUAMBsxGTAXBgNV
+BAUTEGY5MjAwOWU4NTNiNmIwNDUwHhcNMTkxMTIyMjAzNzU4WhcNMzQxMTE4MjAz
+NzU4WjAbMRkwFwYDVQQFExBmOTIwMDllODUzYjZiMDQ1MIICIjANBgkqhkiG9w0B
+AQEFAAOCAg8AMIICCgKCAgEAr7bHgiuxpwHsK7Qui8xUFmOr75gvMsd/dTEDDJdS
+Sxtf6An7xyqpRR90PL2abxM1dEqlXnf2tqw1Ne4Xwl5jlRfdnJLmN0pTy/4lj4/7
+tv0Sk3iiKkypnEUtR6WfMgH0QZfKHM1+di+y9TFRtv6y//0rb+T+W8a9nsNL/ggj
+nar86461qO0rOs2cXjp3kOG1FEJ5MVmFmBGtnrKpa73XpXyTqRxB/M0n1n/W9nGq
+C4FSYa04T6N5RIZGBN2z2MT5IKGbFlbC8UrW0DxW7AYImQQcHtGl/m00QLVWutHQ
+oVJYnFPlXTcHYvASLu+RhhsbDmxMgJJ0mcDpvsC4PjvB+TxywElgS70vE0XmLD+O
+JtvsBslHZvPBKCOdT0MS+tgSOIfga+z1Z1g7+DVagf7quvmag8jfPioyKvxnK/Eg
+sTUVi2ghzq8wm27ud/mIM7AY2qEORR8Go3TVB4HzWQgpZrt3i5MIlCaY504LzSRi
+igHCzAPlHws+W0rB5N+er5/2pJKnfBSDiCiFAVtCLOZ7gLiMm0jhO2B6tUXHI/+M
+RPjy02i59lINMRRev56GKtcd9qO/0kUJWdZTdA2XoS82ixPvZtXQpUpuL12ab+9E
+aDK8Z4RHJYYfCT3Q5vNAXaiWQ+8PTWm2QgBR/bkwSWc+NpUFgNPN9PvQi8WEg5Um
+AGMCAwEAAaNjMGEwHQYDVR0OBBYEFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMB8GA1Ud
+IwQYMBaAFDZh4QB8iAUJUYtEbEf/GkzJ6k8SMA8GA1UdEwEB/wQFMAMBAf8wDgYD
+VR0PAQH/BAQDAgIEMA0GCSqGSIb3DQEBCwUAA4ICAQBOMaBc8oumXb2voc7XCWnu
+XKhBBK3e2KMGz39t7lA3XXRe2ZLLAkLM5y3J7tURkf5a1SutfdOyXAmeE6SRo83U
+h6WszodmMkxK5GM4JGrnt4pBisu5igXEydaW7qq2CdC6DOGjG+mEkN8/TA6p3cno
+L/sPyz6evdjLlSeJ8rFBH6xWyIZCbrcpYEJzXaUOEaxxXxgYz5/cTiVKN2M1G2ok
+QBUIYSY6bjEL4aUN5cfo7ogP3UvliEo3Eo0YgwuzR2v0KR6C1cZqZJSTnghIC/vA
+D32KdNQ+c3N+vl2OTsUVMC1GiWkngNx1OO1+kXW+YTnnTUOtOIswUP/Vqd5SYgAI
+mMAfY8U9/iIgkQj6T2W6FsScy94IN9fFhE1UtzmLoBIuUFsVXJMTz+Jucth+IqoW
+Fua9v1R93/k98p41pjtFX+H8DslVgfP097vju4KDlqN64xV1grw3ZLl4CiOe/A91
+oeLm2UHOq6wn3esB4r2EIQKb6jTVGu5sYCcdWpXr0AUVqcABPdgL+H7qJguBw09o
+jm6xNIrw2OocrDKsudk/okr/AwqEyPKw9WnMlQgLIKw1rODG2NvU9oR3GVGdMkUB
+ZutL8VuFkERQGt6vQ2OCw0sV47VMkuYbacK/xyZFiRcrPJPb41zgbQj9XAEyLKCH
+ex0SdDrx+tWUDqG8At2JHA==
+-----END CERTIFICATE-----
diff --git a/tests/AttestationVerificationTest/assets/test_virtual_device_attestation_certs.pem b/tests/AttestationVerificationTest/assets/test_virtual_device_attestation_certs.pem
new file mode 100644
index 0000000..2827710
--- /dev/null
+++ b/tests/AttestationVerificationTest/assets/test_virtual_device_attestation_certs.pem
@@ -0,0 +1,50 @@
+-----BEGIN CERTIFICATE-----
+MIIC7DCCApGgAwIBAgIBATAKBggqhkjOPQQDAjCBiDELMAkGA1UEBhMCVVMxEzAR
+BgNVBAgMCkNhbGlmb3JuaWExFTATBgNVBAoMDEdvb2dsZSwgSW5jLjEQMA4GA1UE
+CwwHQW5kcm9pZDE7MDkGA1UEAwwyQW5kcm9pZCBLZXlzdG9yZSBTb2Z0d2FyZSBB
+dHRlc3RhdGlvbiBJbnRlcm1lZGlhdGUwHhcNNzAwMTAxMDAwMDAwWhcNNjkxMjMx
+MjM1OTU5WjAfMR0wGwYDVQQDDBRBbmRyb2lkIEtleXN0b3JlIEtleTBZMBMGByqG
+SM49AgEGCCqGSM49AwEHA0IABEYtCH28qu+St0F0TixVsQz0L/Y7DcRHgYAU98E6
+edwOpACFmmseYxMjvmZv/4jURSG2/Z0J1s3A/qFzIY96/tyjggFSMIIBTjALBgNV
+HQ8EBAMCB4AwggEcBgorBgEEAdZ5AgERBIIBDDCCAQgCAQQKAQACASkKAQAECXBs
+YXllcjQ1NgQAMIHqoQgxBgIBAgIBA6IDAgEDowQCAgEApQgxBgIBBAIBBqoDAgEB
+v4N3AgUAv4U9CAIGAX8DoY9Qv4U+AwIBAL+FQEwwSgQgAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAAAAAAAAAAAABAQAKAQIEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+AAAAAAAAAAAAv4VBBQIDAa2wv4VCBQIDAxUbv4VFRwRFMEMxHTAbBBZjb20uZ29v
+Z2xlLmF0dGVzdGF0aW9uAgEBMSIEIDqslV0SVHQBmP11cfMv7kT4nm6pchC7dRKa
+iM9vYebVMAAwHwYDVR0jBBgwFoAUP/ys1hqxOp6BILjVJRzFZbsekakwCgYIKoZI
+zj0EAwIDSQAwRgIhAMzs7gWWBIITpeLeEEx9B8ihdhkFqpMGlsYLRO01ZIOeAiEA
+uKs9xfK3fIOpVAhDmsrp+zE8KUwyvqCU/IS13tXz7Ng=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICeDCCAh6gAwIBAgICEAEwCgYIKoZIzj0EAwIwgZgxCzAJBgNVBAYTAlVTMRMw
+EQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRUwEwYD
+VQQKDAxHb29nbGUsIEluYy4xEDAOBgNVBAsMB0FuZHJvaWQxMzAxBgNVBAMMKkFu
+ZHJvaWQgS2V5c3RvcmUgU29mdHdhcmUgQXR0ZXN0YXRpb24gUm9vdDAeFw0xNjAx
+MTEwMDQ2MDlaFw0yNjAxMDgwMDQ2MDlaMIGIMQswCQYDVQQGEwJVUzETMBEGA1UE
+CAwKQ2FsaWZvcm5pYTEVMBMGA1UECgwMR29vZ2xlLCBJbmMuMRAwDgYDVQQLDAdB
+bmRyb2lkMTswOQYDVQQDDDJBbmRyb2lkIEtleXN0b3JlIFNvZnR3YXJlIEF0dGVz
+dGF0aW9uIEludGVybWVkaWF0ZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOue
+efhCY1msyyqRTImGzHCtkGaTgqlzJhP+rMv4ISdMIXSXSir+pblNf2bU4GUQZjW8
+U7ego6ZxWD7bPhGuEBSjZjBkMB0GA1UdDgQWBBQ//KzWGrE6noEguNUlHMVlux6R
+qTAfBgNVHSMEGDAWgBTIrel3TEXDo88NFhDkeUM6IVowzzASBgNVHRMBAf8ECDAG
+AQH/AgEAMA4GA1UdDwEB/wQEAwIChDAKBggqhkjOPQQDAgNIADBFAiBLipt77oK8
+wDOHri/AiZi03cONqycqRZ9pDMfDktQPjgIhAO7aAV229DLp1IQ7YkyUBO86fMy9
+Xvsiu+f+uXc/WT/7
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICizCCAjKgAwIBAgIJAKIFntEOQ1tXMAoGCCqGSM49BAMCMIGYMQswCQYDVQQG
+EwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmll
+dzEVMBMGA1UECgwMR29vZ2xlLCBJbmMuMRAwDgYDVQQLDAdBbmRyb2lkMTMwMQYD
+VQQDDCpBbmRyb2lkIEtleXN0b3JlIFNvZnR3YXJlIEF0dGVzdGF0aW9uIFJvb3Qw
+HhcNMTYwMTExMDA0MzUwWhcNMzYwMTA2MDA0MzUwWjCBmDELMAkGA1UEBhMCVVMx
+EzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxFTAT
+BgNVBAoMDEdvb2dsZSwgSW5jLjEQMA4GA1UECwwHQW5kcm9pZDEzMDEGA1UEAwwq
+QW5kcm9pZCBLZXlzdG9yZSBTb2Z0d2FyZSBBdHRlc3RhdGlvbiBSb290MFkwEwYH
+KoZIzj0CAQYIKoZIzj0DAQcDQgAE7l1ex+HA220Dpn7mthvsTWpdamguD/9/SQ59
+dx9EIm29sa/6FsvHrcV30lacqrewLVQBXT5DKyqO107sSHVBpKNjMGEwHQYDVR0O
+BBYEFMit6XdMRcOjzw0WEOR5QzohWjDPMB8GA1UdIwQYMBaAFMit6XdMRcOjzw0W
+EOR5QzohWjDPMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgKEMAoGCCqG
+SM49BAMCA0cAMEQCIDUho++LNEYenNVg8x1YiSBq3KNlQfYNns6KGYxmSGB7AiBN
+C/NR2TB8fVvaNTQdqEcbY6WFZTytTySn502vQX3xvw==
+-----END CERTIFICATE-----
diff --git a/tests/AttestationVerificationTest/src/android/security/attestationverification/PeerDeviceSystemAttestationVerificationTest.kt b/tests/AttestationVerificationTest/src/android/security/attestationverification/PeerDeviceSystemAttestationVerificationTest.kt
new file mode 100644
index 0000000..32c2230
--- /dev/null
+++ b/tests/AttestationVerificationTest/src/android/security/attestationverification/PeerDeviceSystemAttestationVerificationTest.kt
@@ -0,0 +1,161 @@
+package android.security.attestationverification
+
+import android.app.Activity
+import android.os.Bundle
+import android.security.attestationverification.AttestationVerificationManager.PARAM_CHALLENGE
+import android.security.attestationverification.AttestationVerificationManager.PARAM_PUBLIC_KEY
+import android.security.attestationverification.AttestationVerificationManager.PROFILE_PEER_DEVICE
+import android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE
+import android.security.attestationverification.AttestationVerificationManager.TYPE_CHALLENGE
+import android.security.attestationverification.AttestationVerificationManager.TYPE_PUBLIC_KEY
+import android.security.attestationverification.AttestationVerificationManager.TYPE_UNKNOWN
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.io.ByteArrayOutputStream
+import java.security.cert.CertificateFactory
+import java.util.concurrent.CompletableFuture
+import java.util.concurrent.TimeUnit
+
+/** Test for system-defined attestation verifiers. */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class PeerDeviceSystemAttestationVerificationTest {
+
+ @get:Rule
+ val rule = ActivityScenarioRule(TestActivity::class.java)
+
+ private val certifcateFactory = CertificateFactory.getInstance("X.509")
+ private lateinit var activity: Activity
+ private lateinit var avm: AttestationVerificationManager
+ private lateinit var invalidAttestationByteArray: ByteArray
+
+ @Before
+ fun setup() {
+ rule.getScenario().onActivity {
+ avm = it.getSystemService(AttestationVerificationManager::class.java)
+ activity = it
+ }
+ invalidAttestationByteArray = TEST_ATTESTATION_CERT_FILENAME.fromPEMFileToByteArray()
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailureWrongBindingType() {
+ val future = CompletableFuture<Int>()
+ val profile = AttestationProfile(PROFILE_PEER_DEVICE)
+ avm.verifyAttestation(profile, TYPE_UNKNOWN, Bundle(),
+ invalidAttestationByteArray, activity.mainExecutor) { result, _ ->
+ future.complete(result)
+ }
+
+ assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailureEmptyRequirements() {
+ val future = CompletableFuture<Int>()
+ val profile = AttestationProfile(PROFILE_PEER_DEVICE)
+ avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(),
+ invalidAttestationByteArray, activity.mainExecutor) { result, _ ->
+ future.complete(result)
+ }
+
+ assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailureMismatchBindingType() {
+ val future = CompletableFuture<Int>()
+ val profile = AttestationProfile(PROFILE_PEER_DEVICE)
+ val publicKeyRequirements = Bundle()
+ publicKeyRequirements.putByteArray(PARAM_PUBLIC_KEY, "publicKeyStr".encodeToByteArray())
+ avm.verifyAttestation(profile, TYPE_CHALLENGE, publicKeyRequirements,
+ invalidAttestationByteArray, activity.mainExecutor) { result, _ ->
+ future.complete(result)
+ }
+
+ assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+
+ val future2 = CompletableFuture<Int>()
+ val challengeRequirements = Bundle()
+ challengeRequirements.putByteArray(PARAM_CHALLENGE, "challengeStr".encodeToByteArray())
+ avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, challengeRequirements,
+ invalidAttestationByteArray, activity.mainExecutor) { result, _ ->
+ future2.complete(result)
+ }
+
+ assertThat(future2.getSoon()).isEqualTo(RESULT_FAILURE)
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailureWrongResourceKey() {
+ val future = CompletableFuture<Int>()
+ val profile = AttestationProfile(PROFILE_PEER_DEVICE)
+ val wrongKeyRequirements = Bundle()
+ wrongKeyRequirements.putByteArray("wrongReqKey", "publicKeyStr".encodeToByteArray())
+ avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, wrongKeyRequirements,
+ invalidAttestationByteArray, activity.mainExecutor) { result, _ ->
+ future.complete(result)
+ }
+
+ assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailureEmptyAttestation() {
+ val future = CompletableFuture<Int>()
+ val profile = AttestationProfile(PROFILE_PEER_DEVICE)
+ val requirements = Bundle()
+ requirements.putByteArray(PARAM_PUBLIC_KEY, "publicKeyStr".encodeToByteArray())
+ avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, requirements, ByteArray(0),
+ activity.mainExecutor) { result, _ ->
+ future.complete(result)
+ }
+
+ assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailureTrustAnchorMismatch() {
+ val future = CompletableFuture<Int>()
+ val profile = AttestationProfile(PROFILE_PEER_DEVICE)
+ val challengeRequirements = Bundle()
+ challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
+ avm.verifyAttestation(profile, TYPE_CHALLENGE, challengeRequirements,
+ invalidAttestationByteArray, activity.mainExecutor) { result, _ ->
+ future.complete(result)
+ }
+ assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+ }
+
+ private fun <T> CompletableFuture<T>.getSoon(): T {
+ return this.get(1, TimeUnit.SECONDS)
+ }
+
+ private fun String.fromPEMFileToByteArray(): ByteArray {
+ val certs = certifcateFactory.generateCertificates(
+ InstrumentationRegistry.getInstrumentation().getContext().getResources().getAssets()
+ .open(this))
+ val bos = ByteArrayOutputStream()
+ certs.forEach {
+ bos.write(it.encoded)
+ }
+ return bos.toByteArray()
+ }
+
+ class TestActivity : Activity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ }
+ }
+
+ companion object {
+ private const val TEST_ATTESTATION_CERT_FILENAME = "test_attestation_wrong_root_certs.pem"
+ }
+}
diff --git a/tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt b/tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt
index 6290292..169effa 100644
--- a/tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt
+++ b/tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt
@@ -12,8 +12,8 @@
import org.junit.runner.RunWith
import com.google.common.truth.Truth.assertThat
import android.security.attestationverification.AttestationVerificationManager.PARAM_CHALLENGE
-import android.security.attestationverification.AttestationVerificationManager.PROFILE_PEER_DEVICE
import android.security.attestationverification.AttestationVerificationManager.PROFILE_SELF_TRUSTED
+import android.security.attestationverification.AttestationVerificationManager.PROFILE_UNKNOWN
import android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE
import android.security.attestationverification.AttestationVerificationManager.RESULT_SUCCESS
import android.security.attestationverification.AttestationVerificationManager.RESULT_UNKNOWN
@@ -52,7 +52,7 @@
@Test
fun verifyAttestation_returnsUnknown() {
val future = CompletableFuture<Int>()
- val profile = AttestationProfile(PROFILE_PEER_DEVICE)
+ val profile = AttestationProfile(PROFILE_UNKNOWN)
avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(), ByteArray(0),
activity.mainExecutor) { result, _ ->
future.complete(result)
@@ -137,7 +137,7 @@
@Test
fun verifyToken_returnsUnknown() {
val future = CompletableFuture<Int>()
- val profile = AttestationProfile(PROFILE_PEER_DEVICE)
+ val profile = AttestationProfile(PROFILE_SELF_TRUSTED)
avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(), ByteArray(0),
activity.mainExecutor) { _, token ->
val result = avm.verifyToken(profile, TYPE_PUBLIC_KEY, Bundle(), token, null)
@@ -150,7 +150,7 @@
@Test
fun verifyToken_tooBigMaxAgeThrows() {
val future = CompletableFuture<VerificationToken>()
- val profile = AttestationProfile(PROFILE_PEER_DEVICE)
+ val profile = AttestationProfile(PROFILE_SELF_TRUSTED)
avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(), ByteArray(0),
activity.mainExecutor) { _, token ->
future.complete(token)
diff --git a/tests/AttestationVerificationTest/src/com/android/server/security/AndroidKeystoreAttestationVerificationAttributesTest.java b/tests/AttestationVerificationTest/src/com/android/server/security/AndroidKeystoreAttestationVerificationAttributesTest.java
new file mode 100644
index 0000000..0d15fe7
--- /dev/null
+++ b/tests/AttestationVerificationTest/src/com/android/server/security/AndroidKeystoreAttestationVerificationAttributesTest.java
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.security;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.hamcrest.CoreMatchers;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/** Test for data class holding parsed X509Certificate attestation attributes. */
+@RunWith(AndroidJUnit4.class)
+public class AndroidKeystoreAttestationVerificationAttributesTest {
+ @Rule public ExpectedException mException = ExpectedException.none();
+ private static final String TEST_PHYSCIAL_DEVICE_CERTS =
+ "test_attestation_wrong_root_certs.pem";
+ private static final String TEST_PHYSICAL_DEVICE_CERTS_2 =
+ "test_attestation_with_root_certs.pem";
+ private static final String TEST_VIRTUAL_DEVICE_CERTS =
+ "test_virtual_device_attestation_certs.pem";
+ private static final String TEST_CERT_NO_ATTESTATION_EXTENSION =
+ "test_no_attestation_ext_certs.pem";
+ private static final String TEST_CERTS_NO_ATTESTATION_EXTENSION_2 =
+ "test_root_certs.pem";
+
+
+ private CertificateFactory mFactory;
+ private AndroidKeystoreAttestationVerificationAttributes mPhysicalDeviceAttributes;
+ private AndroidKeystoreAttestationVerificationAttributes mPhysicalDeviceAttributes2;
+ private AndroidKeystoreAttestationVerificationAttributes mVirtualDeviceAttributes;
+
+ @Before
+ public void setUp() throws Exception {
+ mFactory = CertificateFactory.getInstance("X.509");
+ mPhysicalDeviceAttributes =
+ AndroidKeystoreAttestationVerificationAttributes.fromCertificate(
+ generateCertificate(TEST_PHYSCIAL_DEVICE_CERTS));
+ mPhysicalDeviceAttributes2 =
+ AndroidKeystoreAttestationVerificationAttributes.fromCertificate(
+ generateCertificates(TEST_PHYSICAL_DEVICE_CERTS_2).get(0));
+ mVirtualDeviceAttributes =
+ AndroidKeystoreAttestationVerificationAttributes.fromCertificate(
+ generateCertificates(TEST_VIRTUAL_DEVICE_CERTS).get(0));
+ }
+
+ @Test
+ public void parseCertificate_noAttestationExtension() throws Exception {
+ List<X509Certificate> certsNoAttestation =
+ generateCertificates(TEST_CERTS_NO_ATTESTATION_EXTENSION_2);
+ certsNoAttestation.add(generateCertificate(TEST_CERT_NO_ATTESTATION_EXTENSION));
+ for (X509Certificate cert: certsNoAttestation) {
+ mException.expect(CertificateEncodingException.class);
+ mException.expectMessage(
+ CoreMatchers.containsString("No attestation extension found in certificate."));
+
+ AndroidKeystoreAttestationVerificationAttributes.fromCertificate(cert);
+ }
+ }
+
+ @Test
+ public void parseCertificate_attestationLevel() {
+ assertThat(mPhysicalDeviceAttributes.getAttestationVersion()).isEqualTo(3);
+ assertThat(mPhysicalDeviceAttributes2.getAttestationVersion()).isEqualTo(3);
+ assertThat(mVirtualDeviceAttributes.getAttestationVersion()).isEqualTo(4);
+ }
+
+ @Test
+ public void parseCertificate_attestationSecurityLevel() {
+ assertThat(mPhysicalDeviceAttributes.getAttestationSecurityLevel()).isEqualTo(
+ AndroidKeystoreAttestationVerificationAttributes.SecurityLevel.TRUSTED_ENVIRONMENT);
+ assertThat(mPhysicalDeviceAttributes2.getAttestationSecurityLevel()).isEqualTo(
+ AndroidKeystoreAttestationVerificationAttributes.SecurityLevel.TRUSTED_ENVIRONMENT);
+ assertThat(mVirtualDeviceAttributes.getAttestationSecurityLevel()).isEqualTo(
+ AndroidKeystoreAttestationVerificationAttributes.SecurityLevel.SOFTWARE);
+ }
+
+ @Test
+ public void parseCertificate_isAttestationHardwareBacked() {
+ assertThat(mPhysicalDeviceAttributes.isAttestationHardwareBacked()).isTrue();
+ assertThat(mPhysicalDeviceAttributes2.isAttestationHardwareBacked()).isTrue();
+ assertThat(mVirtualDeviceAttributes.isAttestationHardwareBacked()).isFalse();
+ }
+
+ @Test
+ public void parseCertificate_keymasterLevel() {
+ assertThat(mPhysicalDeviceAttributes.getKeymasterVersion()).isEqualTo(4);
+ assertThat(mPhysicalDeviceAttributes2.getKeymasterVersion()).isEqualTo(4);
+ assertThat(mVirtualDeviceAttributes.getKeymasterVersion()).isEqualTo(41);
+ }
+
+ @Test
+ public void parseCertificate_keymasterSecurityLevel() {
+ assertThat(mPhysicalDeviceAttributes.getKeymasterSecurityLevel()).isEqualTo(
+ AndroidKeystoreAttestationVerificationAttributes.SecurityLevel.TRUSTED_ENVIRONMENT);
+ assertThat(mPhysicalDeviceAttributes2.getKeymasterSecurityLevel()).isEqualTo(
+ AndroidKeystoreAttestationVerificationAttributes.SecurityLevel.TRUSTED_ENVIRONMENT);
+ assertThat(mVirtualDeviceAttributes.getKeymasterSecurityLevel()).isEqualTo(
+ AndroidKeystoreAttestationVerificationAttributes.SecurityLevel.SOFTWARE);
+ }
+
+ @Test
+ public void parseCertificate_isKeymasterHardwareBacked() {
+ assertThat(mPhysicalDeviceAttributes.isKeymasterHardwareBacked()).isTrue();
+ assertThat(mPhysicalDeviceAttributes2.isKeymasterHardwareBacked()).isTrue();
+ assertThat(mVirtualDeviceAttributes.isKeymasterHardwareBacked()).isFalse();
+ }
+
+ @Test
+ public void parseCertificate_attestationChallenge() {
+ assertThat(mPhysicalDeviceAttributes.getAttestationChallenge().toByteArray()).isEqualTo(
+ "abc".getBytes(UTF_8));
+ assertThat(mPhysicalDeviceAttributes2.getAttestationChallenge().toByteArray()).isEqualTo(
+ "player456".getBytes(UTF_8));
+ assertThat(mVirtualDeviceAttributes.getAttestationChallenge().toByteArray()).isEqualTo(
+ "player456".getBytes(UTF_8));
+ }
+
+ @Test
+ public void parseCertificate_verifiedBootState() {
+ assertThat(mPhysicalDeviceAttributes.getVerifiedBootState()).isEqualTo(
+ AndroidKeystoreAttestationVerificationAttributes.VerifiedBootState.UNVERIFIED);
+ assertThat(mPhysicalDeviceAttributes2.getVerifiedBootState()).isEqualTo(
+ AndroidKeystoreAttestationVerificationAttributes.VerifiedBootState.VERIFIED);
+ assertThat(mVirtualDeviceAttributes.getVerifiedBootState()).isNull();
+ }
+
+ @Test
+ public void parseCertificate_keyBootPatchLevel() {
+ assertThat(mPhysicalDeviceAttributes.getKeyBootPatchLevel()).isEqualTo(201907);
+ assertThat(mPhysicalDeviceAttributes2.getKeyBootPatchLevel()).isEqualTo(20220105);
+ }
+
+ @Test
+ public void parseCertificate_keyBootPatchLevelNotSetException() {
+ mException.expect(IllegalStateException.class);
+ mException.expectMessage(
+ CoreMatchers.containsString("KeyBootPatchLevel is not set."));
+
+ mVirtualDeviceAttributes.getKeyBootPatchLevel();
+ }
+
+ @Test
+ public void parseCertificate_keyOsPatchLevel() {
+ assertThat(mPhysicalDeviceAttributes.getKeyOsPatchLevel()).isEqualTo(201907);
+ assertThat(mPhysicalDeviceAttributes2.getKeyOsPatchLevel()).isEqualTo(202201);
+ }
+
+ @Test
+ public void parseCertificate_keyOsPatchLevelNotSetException() {
+ mException.expect(IllegalStateException.class);
+ mException.expectMessage(
+ CoreMatchers.containsString("KeyOsPatchLevel is not set."));
+
+ mVirtualDeviceAttributes.getKeyOsPatchLevel();
+ }
+
+ @Test
+ public void parseCertificate_keyVendorPatchLevel() {
+ assertThat(mPhysicalDeviceAttributes.getKeyVendorPatchLevel()).isEqualTo(201907);
+ assertThat(mPhysicalDeviceAttributes2.getKeyVendorPatchLevel()).isEqualTo(20220105);
+ }
+
+ @Test
+ public void parseCertificate_keyVendorPatchLevelNotSetException() {
+ mException.expect(IllegalStateException.class);
+ mException.expectMessage(
+ CoreMatchers.containsString("KeyVendorPatchLevel is not set."));
+
+ mVirtualDeviceAttributes.getKeyVendorPatchLevel();
+ }
+
+ @Test
+ public void parseCertificate_keyAuthenticatorType() {
+ assertThat(mPhysicalDeviceAttributes.getKeyAuthenticatorType()).isEqualTo(0);
+ assertThat(mPhysicalDeviceAttributes2.getKeyAuthenticatorType()).isEqualTo(0);
+ }
+
+ @Test
+ public void parseCertificate_keyOsVersion() {
+ assertThat(mPhysicalDeviceAttributes.getKeyOsVersion()).isEqualTo(0);
+ assertThat(mPhysicalDeviceAttributes2.getKeyOsVersion()).isEqualTo(120000);
+ }
+
+ @Test
+ public void parseCertificate_keyOsVersionNotSetException() {
+ mException.expect(IllegalStateException.class);
+ mException.expectMessage(
+ CoreMatchers.containsString("KeyOsVersion is not set."));
+
+ mVirtualDeviceAttributes.getKeyOsVersion();
+ }
+
+ @Test
+ public void parseCertificate_verifiedBootHash() {
+ assertThat(mPhysicalDeviceAttributes.getVerifiedBootHash()).isNotEmpty();
+ assertThat(mPhysicalDeviceAttributes2.getVerifiedBootHash()).isNotEmpty();
+ }
+
+ @Test
+ public void parseCertificate_verifiedBootKey() {
+ assertThat(mPhysicalDeviceAttributes.getVerifiedBootKey()).isNotEmpty();
+ assertThat(mPhysicalDeviceAttributes2.getVerifiedBootKey()).isNotEmpty();
+ }
+
+ @Test
+ public void parseCertificate_isVerifiedBootLocked() {
+ assertThat(mPhysicalDeviceAttributes.isVerifiedBootLocked()).isFalse();
+ assertThat(mPhysicalDeviceAttributes2.isVerifiedBootLocked()).isTrue();
+ }
+
+ @Test
+ public void parseCertificate_isVerifiedBootLockedNotSetException() {
+ mException.expect(IllegalStateException.class);
+ mException.expectMessage(
+ CoreMatchers.containsString("VerifiedBootLocked is not set."));
+
+ mVirtualDeviceAttributes.isVerifiedBootLocked();
+ }
+
+ @Test
+ public void parseCertificate_applicationPackageNameVersion() {
+ assertThat(mPhysicalDeviceAttributes.getApplicationPackageNameVersion()).isNotEmpty();
+ }
+
+ @Test
+ public void parseCertificate_applicationCertificateDigests() {
+ assertThat(mPhysicalDeviceAttributes.getApplicationCertificateDigests()).isNotEmpty();
+ }
+
+ @Test
+ public void parseCertificate_valuesNotSet() {
+ assertThat(mPhysicalDeviceAttributes.getDeviceBrand()).isNull();
+ assertThat(mPhysicalDeviceAttributes.getDeviceName()).isNull();
+ assertThat(mPhysicalDeviceAttributes.getDeviceProductName()).isNull();
+ assertThat(mPhysicalDeviceAttributes.isKeyAllowedForAllApplications()).isFalse();
+ assertThat(mPhysicalDeviceAttributes2.getDeviceBrand()).isNull();
+ assertThat(mPhysicalDeviceAttributes2.getDeviceName()).isNull();
+ assertThat(mPhysicalDeviceAttributes2.getDeviceProductName()).isNull();
+ assertThat(mPhysicalDeviceAttributes2.isKeyAllowedForAllApplications()).isFalse();
+ }
+
+ @Test
+ public void parseCertificate_keyRequiresUnlockedDeviceNotSetException() {
+ mException.expect(IllegalStateException.class);
+ mException.expectMessage(
+ CoreMatchers.containsString("KeyRequiresUnlockedDevice is not set."));
+
+ mPhysicalDeviceAttributes.isKeyRequiresUnlockedDevice();
+ }
+
+ private X509Certificate generateCertificate(String certificateString)
+ throws Exception {
+ return generateCertificates(certificateString).get(0);
+ }
+
+ private List<X509Certificate> generateCertificates(String certificateString)
+ throws Exception {
+ Collection<? extends Certificate> certificates = mFactory.generateCertificates(
+ InstrumentationRegistry.getInstrumentation().getContext().getResources().getAssets()
+ .open(certificateString));
+
+ ArrayList<X509Certificate> x509Certs = new ArrayList<>();
+ for (Certificate cert : certificates) {
+ x509Certs.add((X509Certificate) cert);
+ }
+ return x509Certs;
+ }
+}
diff --git a/tests/AttestationVerificationTest/src/com/android/server/security/AttestationVerificationPeerDeviceVerifierTest.kt b/tests/AttestationVerificationTest/src/com/android/server/security/AttestationVerificationPeerDeviceVerifierTest.kt
new file mode 100644
index 0000000..45f2e5c
--- /dev/null
+++ b/tests/AttestationVerificationTest/src/com/android/server/security/AttestationVerificationPeerDeviceVerifierTest.kt
@@ -0,0 +1,175 @@
+package com.android.server.security
+
+import android.app.Activity
+import android.content.Context
+import android.os.Bundle
+import android.security.attestationverification.AttestationVerificationManager.PARAM_CHALLENGE
+import android.security.attestationverification.AttestationVerificationManager.PARAM_PUBLIC_KEY
+import android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE
+import android.security.attestationverification.AttestationVerificationManager.RESULT_SUCCESS
+import android.security.attestationverification.AttestationVerificationManager.TYPE_CHALLENGE
+import android.security.attestationverification.AttestationVerificationManager.TYPE_PUBLIC_KEY
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import java.io.ByteArrayOutputStream
+import java.security.cert.Certificate
+import java.security.cert.CertificateFactory
+import java.security.cert.TrustAnchor
+import java.security.cert.X509Certificate
+import java.time.LocalDate
+
+/** Test for Peer Device attestation verifier. */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AttestationVerificationPeerDeviceVerifierTest {
+ private val certificateFactory = CertificateFactory.getInstance("X.509")
+ @Mock private lateinit var context: Context
+ private lateinit var trustAnchors: HashSet<TrustAnchor>
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+
+ val rootCerts = TEST_ROOT_CERT_FILENAME.fromPEMFileToCerts()
+ trustAnchors = HashSet<TrustAnchor>()
+ rootCerts.forEach {
+ trustAnchors.add(TrustAnchor(it as X509Certificate, null))
+ }
+ }
+
+ @Test
+ fun verifyAttestation_returnsSuccessTypeChallenge() {
+ val verifier = AttestationVerificationPeerDeviceVerifier(
+ context, trustAnchors, false, LocalDate.of(2022, 2, 1),
+ LocalDate.of(2021, 8, 1))
+ val challengeRequirements = Bundle()
+ challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
+
+ val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements,
+ TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray())
+ assertThat(result).isEqualTo(RESULT_SUCCESS)
+ }
+
+ @Test
+ fun verifyAttestation_returnsSuccessLocalPatchOlderThanOneYear() {
+ val verifier = AttestationVerificationPeerDeviceVerifier(
+ context, trustAnchors, false, LocalDate.of(2022, 2, 1),
+ LocalDate.of(2021, 1, 1))
+ val challengeRequirements = Bundle()
+ challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
+
+ val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements,
+ TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray())
+ assertThat(result).isEqualTo(RESULT_SUCCESS)
+ }
+
+ @Test
+ fun verifyAttestation_returnsSuccessTypePublicKey() {
+ val verifier = AttestationVerificationPeerDeviceVerifier(
+ context, trustAnchors, false, LocalDate.of(2022, 2, 1),
+ LocalDate.of(2021, 8, 1))
+
+ val leafCert =
+ (TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToCerts() as List)[0]
+ as X509Certificate
+ val pkRequirements = Bundle()
+ pkRequirements.putByteArray(PARAM_PUBLIC_KEY, leafCert.publicKey.encoded)
+
+ val result = verifier.verifyAttestation(
+ TYPE_PUBLIC_KEY, pkRequirements,
+ TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray())
+ assertThat(result).isEqualTo(RESULT_SUCCESS)
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailurePatchDateNotWithinOneYearLocalPatch() {
+ val verifier = AttestationVerificationPeerDeviceVerifier(
+ context, trustAnchors, false, LocalDate.of(2023, 3, 1),
+ LocalDate.of(2023, 2, 1))
+ val challengeRequirements = Bundle()
+ challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
+
+ val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements,
+ TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray())
+ assertThat(result).isEqualTo(RESULT_FAILURE)
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailureTrustedAnchorEmpty() {
+ val verifier = AttestationVerificationPeerDeviceVerifier(
+ context, HashSet(), false, LocalDate.of(2022, 1, 1),
+ LocalDate.of(2022, 1, 1))
+ val challengeRequirements = Bundle()
+ challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
+
+ val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements,
+ TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray())
+ assertThat(result).isEqualTo(RESULT_FAILURE)
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailureTrustedAnchorMismatch() {
+ val badTrustAnchorsCerts = TEST_ATTESTATION_CERT_FILENAME.fromPEMFileToCerts()
+ val badTrustAnchors = HashSet<TrustAnchor>()
+ badTrustAnchorsCerts.forEach {
+ badTrustAnchors.add(TrustAnchor(it as X509Certificate, null))
+ }
+
+ val verifier = AttestationVerificationPeerDeviceVerifier(
+ context, badTrustAnchors, false, LocalDate.of(2022, 1, 1),
+ LocalDate.of(2022, 1, 1))
+ val challengeRequirements = Bundle()
+ challengeRequirements.putByteArray(PARAM_CHALLENGE, "player456".encodeToByteArray())
+
+ val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements,
+ TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray())
+ assertThat(result).isEqualTo(RESULT_FAILURE)
+ }
+
+ fun verifyAttestation_returnsFailureChallenge() {
+ val verifier = AttestationVerificationPeerDeviceVerifier(
+ context, trustAnchors, false, LocalDate.of(2022, 1, 1),
+ LocalDate.of(2022, 1, 1))
+ val challengeRequirements = Bundle()
+ challengeRequirements.putByteArray(PARAM_CHALLENGE, "wrong".encodeToByteArray())
+
+ val result = verifier.verifyAttestation(TYPE_CHALLENGE, challengeRequirements,
+ TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME.fromPEMFileToByteArray())
+ assertThat(result).isEqualTo(RESULT_FAILURE)
+ }
+
+ private fun String.fromPEMFileToCerts(): Collection<Certificate> {
+ return certificateFactory.generateCertificates(
+ InstrumentationRegistry.getInstrumentation().getContext().getResources().getAssets()
+ .open(this))
+ }
+
+ private fun String.fromPEMFileToByteArray(): ByteArray {
+ val certs = this.fromPEMFileToCerts()
+ val bos = ByteArrayOutputStream()
+ certs.forEach {
+ bos.write(it.encoded)
+ }
+ return bos.toByteArray()
+ }
+
+ class TestActivity : Activity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ }
+ }
+
+ companion object {
+ private const val TEST_ROOT_CERT_FILENAME = "test_root_certs.pem"
+ private const val TEST_ATTESTATION_WITH_ROOT_CERT_FILENAME =
+ "test_attestation_with_root_certs.pem"
+ private const val TEST_ATTESTATION_CERT_FILENAME = "test_attestation_wrong_root_certs.pem"
+ }
+}
diff --git a/tests/SurfaceViewSyncTest/Android.bp b/tests/SurfaceViewSyncTest/Android.bp
new file mode 100644
index 0000000..1c6e380
--- /dev/null
+++ b/tests/SurfaceViewSyncTest/Android.bp
@@ -0,0 +1,31 @@
+//
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "SurfaceViewSyncTest",
+ srcs: ["**/*.java"],
+ platform_apis: true,
+ certificate: "platform",
+}
diff --git a/tests/SurfaceViewSyncTest/AndroidManifest.xml b/tests/SurfaceViewSyncTest/AndroidManifest.xml
new file mode 100644
index 0000000..d085f8c
--- /dev/null
+++ b/tests/SurfaceViewSyncTest/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.test">
+ <application>
+ <activity android:name="SurfaceViewSyncActivity"
+ android:label="SurfaceView Sync Test"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/tests/SurfaceViewSyncTest/OWNERS b/tests/SurfaceViewSyncTest/OWNERS
new file mode 100644
index 0000000..0862c05
--- /dev/null
+++ b/tests/SurfaceViewSyncTest/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/wm/OWNERS
diff --git a/tests/SurfaceViewSyncTest/res/layout/activity_surfaceview_sync.xml b/tests/SurfaceViewSyncTest/res/layout/activity_surfaceview_sync.xml
new file mode 100644
index 0000000..4433b21
--- /dev/null
+++ b/tests/SurfaceViewSyncTest/res/layout/activity_surfaceview_sync.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:background="@android:color/darker_gray"
+ tools:context="com.example.mysurfaceview.MainActivity">
+
+ <SurfaceView
+ android:id="@+id/surface_view"
+ android:layout_width="match_parent"
+ android:layout_height="600dp" />
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <Button
+ android:text="COLLAPSE SV"
+ android:id="@+id/expand_sv"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ <Switch
+ android:id="@+id/enable_sync_switch"
+ android:text="Enable Sync"
+ android:checked="true"
+ android:layout_alignParentEnd="true"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"/>
+ </RelativeLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/SurfaceViewSyncTest/src/com/android/test/SurfaceViewSyncActivity.java b/tests/SurfaceViewSyncTest/src/com/android/test/SurfaceViewSyncActivity.java
new file mode 100644
index 0000000..06accec
--- /dev/null
+++ b/tests/SurfaceViewSyncTest/src/com/android/test/SurfaceViewSyncActivity.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test;
+
+import android.annotation.NonNull;
+import android.app.Activity;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.WindowMetrics;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.Switch;
+import android.window.SurfaceSyncer;
+
+/**
+ * Test app that allows the user to resize the SurfaceView and have the new buffer sync with the
+ * main window. This tests that {@link SurfaceSyncer} is working correctly.
+ */
+public class SurfaceViewSyncActivity extends Activity implements SurfaceHolder.Callback {
+ private static final String TAG = "SurfaceViewSyncActivity";
+
+ private SurfaceView mSurfaceView;
+ private boolean mLastExpanded = true;
+
+ private RenderingThread mRenderingThread;
+
+ private final SurfaceSyncer mSurfaceSyncer = new SurfaceSyncer();
+
+ private Button mExpandButton;
+ private Switch mEnableSyncSwitch;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_surfaceview_sync);
+ mSurfaceView = findViewById(R.id.surface_view);
+ mSurfaceView.getHolder().addCallback(this);
+
+ WindowManager windowManager = getWindowManager();
+ WindowMetrics metrics = windowManager.getCurrentWindowMetrics();
+ Rect bounds = metrics.getBounds();
+
+ LinearLayout container = findViewById(R.id.container);
+ mExpandButton = findViewById(R.id.expand_sv);
+ mEnableSyncSwitch = findViewById(R.id.enable_sync_switch);
+ mExpandButton.setOnClickListener(view -> updateSurfaceViewSize(bounds, container));
+
+ mRenderingThread = new RenderingThread(mSurfaceView.getHolder());
+ }
+
+ private void updateSurfaceViewSize(Rect bounds, View container) {
+ final float height;
+ if (mLastExpanded) {
+ height = bounds.height() / 2f;
+ mExpandButton.setText("EXPAND SV");
+ } else {
+ height = bounds.height() / 1.5f;
+ mExpandButton.setText("COLLAPSE SV");
+ }
+ mLastExpanded = !mLastExpanded;
+
+ if (mEnableSyncSwitch.isChecked()) {
+ int syncId = mSurfaceSyncer.setupSync(() -> { });
+ mSurfaceSyncer.addToSync(syncId, mSurfaceView, frameCallback ->
+ mRenderingThread.setFrameCallback(frameCallback));
+ mSurfaceSyncer.addToSync(syncId, container);
+ mSurfaceSyncer.markSyncReady(syncId);
+ } else {
+ mRenderingThread.renderSlow();
+ }
+
+ ViewGroup.LayoutParams svParams = mSurfaceView.getLayoutParams();
+ svParams.height = (int) height;
+ mSurfaceView.setLayoutParams(svParams);
+ }
+
+ @Override
+ public void surfaceCreated(@NonNull SurfaceHolder holder) {
+ final Canvas canvas = holder.lockCanvas();
+ canvas.drawARGB(255, 100, 100, 100);
+ holder.unlockCanvasAndPost(canvas);
+ mRenderingThread.startRendering();
+ }
+
+ @Override
+ public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
+ }
+
+ @Override
+ public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
+ mRenderingThread.stopRendering();
+ }
+
+ private static class RenderingThread extends HandlerThread {
+ private final SurfaceHolder mSurfaceHolder;
+ private Handler mHandler;
+ private SurfaceSyncer.SurfaceViewFrameCallback mFrameCallback;
+ private boolean mRenderSlow;
+
+ int mColorValue = 0;
+ int mColorDelta = 10;
+
+ RenderingThread(SurfaceHolder holder) {
+ super("RenderingThread");
+ mSurfaceHolder = holder;
+ }
+
+ public void setFrameCallback(SurfaceSyncer.SurfaceViewFrameCallback frameCallback) {
+ if (mHandler != null) {
+ mHandler.post(() -> {
+ mFrameCallback = frameCallback;
+ mRenderSlow = true;
+ });
+ }
+ }
+
+ public void renderSlow() {
+ if (mHandler != null) {
+ mHandler.post(() -> mRenderSlow = true);
+ }
+ }
+
+ private final Runnable mRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (mFrameCallback != null) {
+ mFrameCallback.onFrameStarted();
+ }
+
+ if (mRenderSlow) {
+ try {
+ // Long delay from start to finish to mimic slow draw
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ }
+ mRenderSlow = false;
+ }
+
+ mColorValue += mColorDelta;
+ if (mColorValue > 245 || mColorValue < 10) {
+ mColorDelta *= -1;
+ }
+
+ Canvas c = mSurfaceHolder.lockCanvas();
+ c.drawRGB(255, mColorValue, 255 - mColorValue);
+ mSurfaceHolder.unlockCanvasAndPost(c);
+
+ if (mFrameCallback != null) {
+ mFrameCallback.onFrameComplete();
+ }
+ mFrameCallback = null;
+
+ mHandler.postDelayed(this, 50);
+ }
+ };
+
+ public void startRendering() {
+ start();
+ mHandler = new Handler(getLooper());
+ mHandler.post(mRunnable);
+ }
+
+ public void stopRendering() {
+ if (mHandler != null) {
+ mHandler.post(() -> mHandler.removeCallbacks(mRunnable));
+ }
+ }
+ }
+}